viric@0: /* viric@0: * Copyright (c) 1980 Regents of the University of California. viric@0: * All rights reserved. viric@0: * viric@0: * Redistribution and use in source and binary forms, with or without viric@0: * modification, are permitted provided that the following conditions viric@0: * are met: viric@0: * 1. Redistributions of source code must retain the above copyright viric@0: * notice, this list of conditions and the following disclaimer. viric@0: * 2. Redistributions in binary form must reproduce the above copyright viric@0: * notice, this list of conditions and the following disclaimer in the viric@0: * documentation and/or other materials provided with the distribution. viric@0: * 3. All advertising materials mentioning features or use of this software viric@0: * must display the following acknowledgement: viric@0: * This product includes software developed by the University of viric@0: * California, Berkeley and its contributors. viric@0: * 4. Neither the name of the University nor the names of its contributors viric@0: * may be used to endorse or promote products derived from this software viric@0: * without specific prior written permission. viric@0: * viric@0: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND viric@0: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE viric@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE viric@0: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE viric@0: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL viric@0: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS viric@0: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) viric@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT viric@0: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY viric@0: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF viric@0: * SUCH DAMAGE. viric@0: */ viric@0: viric@0: /* viric@0: * 1999-02-22 Arkadiusz Mi¶kiewicz viric@0: * - added Native Language Support viric@0: * viric@0: * 2000-07-30 Per Andreas Buer - added "q"-option viric@0: */ viric@0: viric@0: /* viric@6: * tcpscript - Modified from 'script' - 2007 Lluis Batlle i Rossell viric@0: */ viric@0: #include viric@0: #include viric@0: #include viric@0: #include viric@0: #include viric@0: #include viric@0: #include viric@0: #include viric@1: #include viric@1: #include viric@0: viric@0: #ifdef __linux__ viric@0: #include viric@0: #include viric@0: #endif viric@0: viric@1: #define HAVE_openpty viric@1: #define _PATH_BSHELL "/bin/sh" viric@0: #ifdef HAVE_openpty viric@0: #include viric@0: #endif viric@0: viric@0: void finish(int); viric@0: void done(void); viric@0: void fail(void); viric@0: void resize(int); viric@0: void fixtty(void); viric@0: void getmaster(void); viric@0: void getslave(void); viric@0: void doinput(void); viric@0: void dooutput(void); viric@0: void doshell(void); viric@0: viric@0: char *shell; viric@0: int master; viric@0: int slave; viric@0: int child; viric@0: int subchild; viric@0: char *fname; viric@0: viric@0: struct termios tt; viric@0: struct winsize win; viric@0: int lb; viric@0: int l; viric@0: #ifndef HAVE_openpty viric@0: char line[] = "/dev/ptyXX"; viric@0: #endif viric@0: char *cflg = NULL; viric@0: int qflg = 0; viric@1: int tcp_port = 4000; viric@3: int limit_conns = 1; viric@1: viric@1: enum { viric@1: MAXCONNS = 10 viric@1: }; viric@5: static int accept_socket; viric@6: const char version[] = "0.1"; viric@0: viric@0: static char *progname; viric@0: viric@0: static void viric@0: die_if_link(char *fn) { viric@0: struct stat s; viric@0: viric@0: if (lstat(fn, &s) == 0 && (S_ISLNK(s.st_mode) || s.st_nlink > 1)) { viric@0: fprintf(stderr, viric@1: "Warning: `%s' is a link.\n" viric@0: "Use `%s [options] %s' if you really " viric@0: "want to use it.\n" viric@1: "Script not started.\n", viric@0: fn, progname, fn); viric@0: exit(1); viric@0: } viric@0: } viric@0: viric@0: /* viric@0: * script -t prints time delays as floating point numbers viric@0: * The example program (scriptreplay) that we provide to handle this viric@0: * timing output is a perl script, and does not handle numbers in viric@0: * locale format (not even when "use locale;" is added). viric@0: * So, since these numbers are not for human consumption, it seems viric@0: * easiest to set LC_NUMERIC here. viric@0: */ viric@0: viric@0: int viric@0: main(int argc, char **argv) { viric@0: extern int optind; viric@0: char *p; viric@0: int ch; viric@0: viric@0: progname = argv[0]; viric@0: if ((p = strrchr(progname, '/')) != NULL) viric@0: progname = p+1; viric@0: viric@0: viric@0: if (argc == 2) { viric@0: if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) { viric@6: printf("%s %s, modification of 'script' by %s\n", viric@6: progname, version, viric@6: "Lluis Batlle i Rossell 2007"); viric@0: return 0; viric@0: } viric@0: } viric@0: viric@3: while ((ch = getopt(argc, argv, "c:qp:l:")) != -1) viric@0: switch((char)ch) { viric@0: case 'c': viric@0: cflg = optarg; viric@0: break; viric@1: case 'p': viric@1: tcp_port = atoi(optarg); viric@1: break; viric@3: case 'l': viric@3: limit_conns = atoi(optarg); viric@3: break; viric@0: case 'q': viric@0: qflg++; viric@0: break; viric@0: case '?': viric@0: default: viric@0: fprintf(stderr, viric@7: "usage: script [-l maxconns] [-p port] [-q] [-t] \n"); viric@0: exit(1); viric@0: } viric@0: argc -= optind; viric@0: argv += optind; viric@0: viric@0: shell = getenv("SHELL"); viric@0: if (shell == NULL) viric@0: shell = _PATH_BSHELL; viric@0: viric@0: getmaster(); viric@0: fixtty(); viric@0: viric@5: accept_socket = listen_tcp(); viric@5: viric@0: (void) signal(SIGCHLD, finish); viric@0: child = fork(); viric@0: if (child < 0) { viric@0: perror("fork"); viric@0: fail(); viric@0: } viric@0: if (child == 0) { viric@0: subchild = child = fork(); viric@0: if (child < 0) { viric@0: perror("fork"); viric@0: fail(); viric@0: } viric@0: if (child) viric@0: dooutput(); viric@0: else viric@5: { viric@5: close(accept_socket); viric@0: doshell(); viric@5: } viric@0: } else viric@5: { viric@5: close(accept_socket); viric@0: (void) signal(SIGWINCH, resize); viric@5: } viric@0: doinput(); viric@0: viric@0: return 0; viric@0: } viric@0: viric@0: void viric@0: doinput() { viric@0: register int cc; viric@0: char ibuf[BUFSIZ]; viric@0: viric@0: while ((cc = read(0, ibuf, BUFSIZ)) > 0) viric@0: (void) write(master, ibuf, cc); viric@0: done(); viric@0: } viric@0: viric@0: #include viric@0: viric@0: void viric@0: finish(int dummy) { viric@0: int status; viric@0: register int pid; viric@0: register int die = 0; viric@0: viric@0: while ((pid = wait3(&status, WNOHANG, 0)) > 0) viric@0: if (pid == child) viric@0: die = 1; viric@0: viric@0: if (die) viric@0: done(); viric@0: } viric@0: viric@0: void viric@0: resize(int dummy) { viric@0: /* transmit window change information to the child */ viric@0: (void) ioctl(0, TIOCGWINSZ, (char *)&win); viric@0: (void) ioctl(slave, TIOCSWINSZ, (char *)&win); viric@0: viric@0: kill(child, SIGWINCH); viric@0: } viric@0: viric@1: void fatal() viric@1: { viric@5: done(); viric@1: exit(-1); viric@1: } viric@1: viric@3: int set_reusable(int s) viric@3: { viric@3: int boolean = 1; viric@3: int res; viric@3: viric@3: res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &boolean, sizeof(boolean)); viric@3: if (res != 0) viric@3: perror("Setsockopt REUSE failed"), fatal(); viric@3: } viric@3: viric@1: int listen_tcp() viric@1: { viric@1: int s; viric@1: struct sockaddr_in addr; viric@1: int res; viric@1: viric@1: s = socket(PF_INET, SOCK_STREAM, 0); viric@1: if (s == -1) viric@1: perror("Failed socket()"), fatal(); viric@1: viric@3: set_reusable(s); viric@3: viric@1: addr.sin_family = AF_INET; viric@1: addr.sin_port = htons(tcp_port); viric@1: addr.sin_addr.s_addr = INADDR_ANY; viric@1: res = bind(s, (struct sockaddr *) &addr, sizeof(addr)); viric@1: if (s != 0) viric@1: perror("Failed bind()"), fatal(); viric@1: viric@1: res = listen(s, 1); viric@1: if (s != 0) viric@1: perror("Failed listen()"), fatal(); viric@1: viric@2: if (!qflg) viric@2: printf("Listening on port %i\n", tcp_port); viric@1: return s; viric@1: } viric@1: viric@1: void remove_element(int index, int *array, int total) viric@1: { viric@1: int i; viric@1: for (i = index; i < (total - 1); ++i) viric@1: array[i] = array[i+1]; viric@1: } viric@1: viric@0: void viric@0: dooutput() { viric@1: int cc; viric@0: char obuf[BUFSIZ]; viric@1: int conns[MAXCONNS]; viric@1: int nconns; viric@1: int i; viric@0: viric@0: (void) close(0); viric@0: #ifdef HAVE_openpty viric@0: (void) close(slave); viric@0: #endif viric@1: fd_set read_set; viric@1: fd_set should_read_set; viric@1: fd_set tcp_set; viric@0: viric@1: FD_ZERO(&should_read_set); viric@1: FD_SET(accept_socket, &should_read_set); viric@1: FD_SET(master, &should_read_set); viric@1: viric@1: nconns = 0; viric@0: for (;;) { viric@1: int max; viric@1: read_set = should_read_set; viric@1: viric@1: /* Calculate max fd */ viric@1: max = accept_socket; viric@1: if (master > max) viric@1: max = master; viric@1: for (i = 0; i < nconns; ++i) viric@1: if (conns[i] > max) viric@1: max = conns[i]; viric@1: viric@1: select(max + 1, &read_set, 0, 0, 0); viric@1: viric@1: if (FD_ISSET(master, &read_set)) viric@1: { viric@1: cc = read(master, obuf, sizeof (obuf)); viric@1: if (cc <= 0) viric@1: break; viric@1: /* Write to the terminal output */ viric@1: (void) write(1, obuf, cc); viric@1: /* Broadcast messages */ viric@1: for (i = 0; i < nconns; ++i) viric@1: (void) write(conns[i], obuf, cc); viric@1: } viric@1: for (i = 0; i < nconns; ++i) viric@1: { viric@1: if (FD_ISSET(conns[i], &read_set)) viric@1: { viric@1: int size; viric@1: size = read(conns[i], obuf, sizeof(obuf)); viric@1: if (size == 0) viric@1: { viric@1: close(conns[i]); viric@1: FD_CLR(conns[i], &should_read_set); viric@1: remove_element(i, conns, nconns); viric@1: nconns--; viric@1: } viric@1: } viric@1: } viric@1: if (FD_ISSET(accept_socket, &read_set)) viric@1: { viric@1: int s; viric@1: s = accept(accept_socket, 0, 0); viric@1: if (s == -1) viric@1: { viric@1: perror("accept() failed"); viric@1: break; viric@1: } viric@3: if (nconns < (MAXCONNS - 1) && nconns < limit_conns) viric@1: { viric@1: FD_SET(s, &should_read_set); viric@1: conns[nconns] = s; viric@1: nconns++; viric@1: } else viric@1: close(s); viric@1: } viric@0: } viric@1: viric@1: close(accept_socket); viric@1: for (i = 0; i < nconns; ++i) viric@1: close(conns[i]); viric@1: viric@0: done(); viric@0: } viric@0: viric@0: void viric@0: doshell() { viric@0: char *shname; viric@0: viric@0: #if 0 viric@0: int t; viric@0: viric@0: t = open(_PATH_TTY, O_RDWR); viric@0: if (t >= 0) { viric@0: (void) ioctl(t, TIOCNOTTY, (char *)0); viric@0: (void) close(t); viric@0: } viric@0: #endif viric@0: viric@0: getslave(); viric@0: (void) close(master); viric@0: (void) dup2(slave, 0); viric@0: (void) dup2(slave, 1); viric@0: (void) dup2(slave, 2); viric@0: (void) close(slave); viric@0: viric@0: shname = strrchr(shell, '/'); viric@0: if (shname) viric@0: shname++; viric@0: else viric@0: shname = shell; viric@0: viric@0: if (cflg) viric@0: execl(shell, shname, "-c", cflg, 0); viric@0: else viric@0: execl(shell, shname, "-i", 0); viric@0: viric@0: perror(shell); viric@0: fail(); viric@0: } viric@0: viric@0: void viric@0: fixtty() { viric@0: struct termios rtt; viric@0: viric@0: rtt = tt; viric@0: cfmakeraw(&rtt); viric@0: rtt.c_lflag &= ~ECHO; viric@0: (void) tcsetattr(0, TCSAFLUSH, &rtt); viric@0: } viric@0: viric@0: void viric@0: fail() { viric@0: viric@0: (void) kill(0, SIGTERM); viric@0: done(); viric@0: } viric@0: viric@0: void viric@0: done() { viric@0: time_t tvec; viric@0: viric@0: if (subchild) { viric@0: (void) close(master); viric@0: } else { viric@0: (void) tcsetattr(0, TCSAFLUSH, &tt); viric@0: } viric@0: exit(0); viric@0: } viric@0: viric@0: void viric@0: getmaster() { viric@0: #ifdef HAVE_openpty viric@0: (void) tcgetattr(0, &tt); viric@0: (void) ioctl(0, TIOCGWINSZ, (char *)&win); viric@0: if (openpty(&master, &slave, NULL, &tt, &win) < 0) { viric@1: fprintf(stderr, "openpty failed\n"); viric@0: fail(); viric@0: } viric@0: #else viric@0: char *pty, *bank, *cp; viric@0: struct stat stb; viric@0: viric@0: pty = &line[strlen("/dev/ptyp")]; viric@0: for (bank = "pqrs"; *bank; bank++) { viric@0: line[strlen("/dev/pty")] = *bank; viric@0: *pty = '0'; viric@0: if (stat(line, &stb) < 0) viric@0: break; viric@0: for (cp = "0123456789abcdef"; *cp; cp++) { viric@0: *pty = *cp; viric@0: master = open(line, O_RDWR); viric@0: if (master >= 0) { viric@0: char *tp = &line[strlen("/dev/")]; viric@0: int ok; viric@0: viric@0: /* verify slave side is usable */ viric@0: *tp = 't'; viric@0: ok = access(line, R_OK|W_OK) == 0; viric@0: *tp = 'p'; viric@0: if (ok) { viric@0: (void) tcgetattr(0, &tt); viric@0: (void) ioctl(0, TIOCGWINSZ, viric@0: (char *)&win); viric@0: return; viric@0: } viric@0: (void) close(master); viric@0: } viric@0: } viric@0: } viric@1: fprintf(stderr, "Out of pty's\n"); viric@0: fail(); viric@0: #endif /* not HAVE_openpty */ viric@0: } viric@0: viric@0: void viric@0: getslave() { viric@0: #ifndef HAVE_openpty viric@0: line[strlen("/dev/")] = 't'; viric@0: slave = open(line, O_RDWR); viric@0: if (slave < 0) { viric@0: perror(line); viric@0: fail(); viric@0: } viric@0: (void) tcsetattr(slave, TCSAFLUSH, &tt); viric@0: (void) ioctl(slave, TIOCSWINSZ, (char *)&win); viric@0: #endif viric@0: (void) setsid(); viric@0: (void) ioctl(slave, TIOCSCTTY, 0); viric@0: }