4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-02-09 02:29:07 +08:00

144 lines
4.4 KiB
C
Raw Normal View History

2021-01-07 12:22:41 -05:00
/* Adapted from Kerrisk's script.c by Ken Brown */
/*
Create a pty pair and fork/exec a shell running on the slave. Send
the master 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_master.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, connFd, junk;
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");
/* Send master fd across the socket. */
if (sendfd (connFd, masterFd) < 0)
errExit ("sendfd");
/* 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)
break;
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)
break;
if (write(STDOUT_FILENO, buf, numRead) != numRead)
fatal("partial/failed write (STDOUT_FILENO)");
}
}
/* Notify receiver that we're done. */
if (write (connFd, &junk, sizeof junk) != sizeof junk)
errMsg ("write");
if (close (connFd) < 0)
errMsg ("close");
}