mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-02-09 02:29:07 +08:00
145 lines
4.5 KiB
C
145 lines
4.5 KiB
C
/* Adapted from Kerrisk's script.c by Ken Brown */
|
|
|
|
/*
|
|
Create a pty pair and fork/exec a shell running on the slave. Send
|
|
the slave fd across an AF_UNIX socket to a process running
|
|
recv_pty_slave.
|
|
*/
|
|
|
|
/*************************************************************************\
|
|
* Copyright (C) Michael Kerrisk, 2018. *
|
|
* *
|
|
* This program is free software. You may use, modify, and redistribute it *
|
|
* under the terms of the GNU General Public License as published by the *
|
|
* Free Software Foundation, either version 3 or (at your option) any *
|
|
* later version. This program is distributed without any warranty. See *
|
|
* the file COPYING.gpl-v3 for details. *
|
|
\*************************************************************************/
|
|
|
|
/* Listing 64-3 */
|
|
|
|
/* script.c
|
|
|
|
A simple version of script(1).
|
|
*/
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <libgen.h>
|
|
#include <termios.h>
|
|
#if ! defined(__hpux)
|
|
/* HP-UX 11 doesn't have this header file */
|
|
#include <sys/select.h>
|
|
#endif
|
|
#include "pty_fork.h" /* Declaration of ptyFork() */
|
|
#include "tty_functions.h" /* Declaration of ttySetRaw() */
|
|
#include "af_unix_hdr.h"
|
|
#include "pty_slave.h"
|
|
|
|
#define BUF_SIZE 256
|
|
#define MAX_SNAME 1000
|
|
|
|
struct termios ttyOrig;
|
|
|
|
static void /* Reset terminal mode on program exit */
|
|
ttyReset(void)
|
|
{
|
|
if (tcsetattr(STDIN_FILENO, TCSANOW, &ttyOrig) == -1)
|
|
errExit("tcsetattr");
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char slaveName[MAX_SNAME];
|
|
char *shell;
|
|
int masterFd, slaveFd, connFd;
|
|
struct winsize ws;
|
|
fd_set inFds;
|
|
char buf[BUF_SIZE];
|
|
ssize_t numRead;
|
|
pid_t childPid;
|
|
|
|
/* Retrieve the attributes of terminal on which we are started */
|
|
|
|
if (tcgetattr(STDIN_FILENO, &ttyOrig) == -1)
|
|
errExit("tcgetattr");
|
|
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
|
|
errExit("ioctl-TIOCGWINSZ");
|
|
|
|
/* Create a child process, with parent and child connected via a
|
|
pty pair. The child is connected to the pty slave and its terminal
|
|
attributes are set to be the same as those retrieved above. */
|
|
|
|
childPid = ptyFork(&masterFd, slaveName, MAX_SNAME, &ttyOrig, &ws);
|
|
if (childPid == -1)
|
|
errExit("ptyFork");
|
|
|
|
if (childPid == 0) { /* Child: execute a shell on pty slave */
|
|
|
|
/* If the SHELL variable is set, use its value to determine
|
|
the shell execed in child. Otherwise use /bin/sh. */
|
|
|
|
shell = getenv("SHELL");
|
|
if (shell == NULL || *shell == '\0')
|
|
shell = "/bin/sh";
|
|
|
|
execlp(shell, shell, (char *) NULL);
|
|
errExit("execlp"); /* If we get here, something went wrong */
|
|
}
|
|
|
|
/* Parent */
|
|
|
|
if ((connFd = unixConnect (SOCK_PATH, SOCK_STREAM)) < 0)
|
|
errExit ("unixConnect");
|
|
|
|
/* Open slave and send its fd across the socket. */
|
|
if ((slaveFd = open (slaveName, O_RDWR | O_NOCTTY)) < 0)
|
|
errExit ("open");
|
|
if (sendfd (connFd, slaveFd) < 0)
|
|
errExit ("sendfd");
|
|
if (close (slaveFd) < 0)
|
|
errMsg ("close");
|
|
if (close (connFd) < 0)
|
|
errMsg ("close");
|
|
|
|
/* Place terminal in raw mode so that we can pass all terminal
|
|
input to the pseudoterminal master untouched */
|
|
|
|
ttySetRaw(STDIN_FILENO, &ttyOrig);
|
|
|
|
if (atexit(ttyReset) != 0)
|
|
errExit("atexit");
|
|
|
|
/* Loop monitoring terminal and pty master for input. If the
|
|
terminal is ready for input, then read some bytes and write
|
|
them to the pty master. If the pty master is ready for input,
|
|
then read some bytes and write them to the terminal. */
|
|
|
|
for (;;) {
|
|
FD_ZERO(&inFds);
|
|
FD_SET(STDIN_FILENO, &inFds);
|
|
FD_SET(masterFd, &inFds);
|
|
|
|
if (select(masterFd + 1, &inFds, NULL, NULL, NULL) == -1)
|
|
errExit("select");
|
|
|
|
if (FD_ISSET(STDIN_FILENO, &inFds)) { /* stdin --> pty */
|
|
numRead = read(STDIN_FILENO, buf, BUF_SIZE);
|
|
if (numRead <= 0)
|
|
exit(EXIT_SUCCESS);
|
|
|
|
if (write(masterFd, buf, numRead) != numRead)
|
|
fatal("partial/failed write (masterFd)");
|
|
}
|
|
|
|
if (FD_ISSET(masterFd, &inFds)) { /* pty --> stdout */
|
|
numRead = read(masterFd, buf, BUF_SIZE);
|
|
if (numRead <= 0)
|
|
exit(EXIT_SUCCESS);
|
|
|
|
if (write(STDOUT_FILENO, buf, numRead) != numRead)
|
|
fatal("partial/failed write (STDOUT_FILENO)");
|
|
}
|
|
}
|
|
}
|