1 /*
   2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
   7 /*        All Rights Reserved   */
   8 
   9 
  10 /*
  11  * Copyright (c) 1980 Regents of the University of California.
  12  * All rights reserved.  The Berkeley software License Agreement
  13  * specifies the terms and conditions for redistribution.
  14  */
  15 
  16 /*      Portions Copyright(c) 1988, Sun Microsystems, Inc.      */
  17 /*      All Rights Reserved.                                    */
  18 
  19 /*
  20  * script: Produce a record of a terminal session.
  21  */
  22 #include <stdio.h>
  23 #include <stdlib.h>
  24 #include <unistd.h>
  25 #include <signal.h>
  26 #include <fcntl.h>
  27 #include <locale.h>
  28 #include <time.h>
  29 #include <sys/stropts.h>
  30 #include <sys/types.h>
  31 #include <sys/stat.h>
  32 #include <sys/termios.h>
  33 #include <sys/file.h>
  34 #include <errno.h>
  35 
  36 int     grantpt();
  37 int     unlockpt();
  38 char    *ptsname();
  39 void    doinput() __NORETURN;
  40 void    dooutput();
  41 void    doshell();
  42 void    fixtty();
  43 void    fail();
  44 void    done() __NORETURN;
  45 void    getmaster();
  46 void    getslave();
  47 
  48 char    *shell;
  49 FILE    *fscript;
  50 int     master;                 /* file descriptor for master pseudo-tty */
  51 int     slave;                  /* file descriptor for slave pseudo-tty */
  52 int     child;
  53 int     subchild;
  54 char    *fname = "typescript";
  55 void    sigwinch();
  56 void    finish();
  57 
  58 struct  termios b;
  59 struct  winsize size;
  60 int     lb;
  61 int     l;
  62 char    *mptname = "/dev/ptmx"; /* master pseudo-tty device */
  63 
  64 int     aflg;
  65 
  66 int
  67 main(int argc, char *argv[])
  68 {
  69         uid_t ruidt;
  70         gid_t gidt;
  71 
  72         (void) setlocale(LC_ALL, "");
  73 #if !defined(TEXT_DOMAIN)
  74 #define TEXT_DOMAIN     "SYS_TEST"
  75 #endif
  76         (void) textdomain(TEXT_DOMAIN);
  77 
  78         shell = getenv("SHELL");
  79         if (shell == NULL)
  80                 shell = "/bin/sh";
  81         argc--, argv++;
  82         while (argc > 0 && argv[0][0] == '-') {
  83                 switch (argv[0][1]) {
  84 
  85                 case 'a':
  86                         aflg++;
  87                         break;
  88 
  89                 default:
  90                         fprintf(stderr,
  91                             gettext("usage: script [ -a ] [ typescript ]\n"));
  92                         exit(1);
  93                 }
  94                 argc--, argv++;
  95         }
  96         if (argc > 0)
  97                 fname = argv[0];
  98         ruidt = getuid();
  99         gidt = getgid();
 100         if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
 101                 perror(fname);
 102                 fail();
 103         }
 104         setbuf(fscript, NULL);
 105         chown(fname, ruidt, gidt);
 106         getmaster();
 107         printf(gettext("Script started, file is %s\n"), fname);
 108         fixtty();
 109 
 110         (void) signal(SIGCHLD, finish);
 111         child = fork();
 112         if (child < 0) {
 113                 perror("fork");
 114                 fail();
 115         }
 116         if (child == 0) {
 117                 subchild = child = fork();
 118                 if (child < 0) {
 119                         perror("fork");
 120                         fail();
 121                 }
 122                 if (child)
 123                         dooutput();
 124                 else
 125                         doshell();
 126         }
 127         doinput();
 128         /* NOTREACHED */
 129         return (0);
 130 }
 131 
 132 void
 133 doinput()
 134 {
 135         char ibuf[BUFSIZ];
 136         int cc;
 137 
 138         (void) fclose(fscript);
 139         sigset(SIGWINCH, sigwinch);
 140 
 141         while ((cc = read(0, ibuf, BUFSIZ)) != 0) {
 142                 if (cc == -1) {
 143                         if (errno == EINTR) {   /* SIGWINCH probably */
 144                                 continue;
 145                         } else {
 146                                 break;
 147                         }
 148                 }
 149                 (void) write(master, ibuf, cc);
 150         }
 151         done();
 152 }
 153 
 154 void
 155 sigwinch()
 156 {
 157         struct winsize ws;
 158 
 159         if (ioctl(0, TIOCGWINSZ, &ws) == 0)
 160                 (void) ioctl(master, TIOCSWINSZ, &ws);
 161 }
 162 
 163 #include <sys/wait.h>
 164 
 165 void
 166 finish()
 167 {
 168         int status;
 169         register int pid;
 170         register int die = 0;
 171 
 172         while ((pid = wait(&status)) > 0)
 173                 if (pid == child)
 174                         die = 1;
 175 
 176         if (die)
 177                 done();
 178 }
 179 
 180 void
 181 dooutput()
 182 {
 183         time_t tvec;
 184         char obuf[BUFSIZ];
 185         char tbuf[BUFSIZ];
 186         int cc;
 187 
 188         (void) close(0);
 189         tvec = time((time_t *)0);
 190         strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
 191         fprintf(fscript, gettext("Script started on %s\n"), tbuf);
 192         for (;;) {
 193                 cc = read(master, obuf, sizeof (obuf));
 194                 if (cc <= 0)
 195                         break;
 196                 (void) write(1, obuf, cc);
 197                 (void) fwrite(obuf, 1, cc, fscript);
 198         }
 199         done();
 200 }
 201 
 202 void
 203 doshell()
 204 {
 205 
 206         setpgrp();      /* relinquish control terminal */
 207         getslave();
 208         (void) close(master);
 209         (void) fclose(fscript);
 210         (void) dup2(slave, 0);
 211         (void) dup2(slave, 1);
 212         (void) dup2(slave, 2);
 213         (void) close(slave);
 214         execl(shell, shell, "-i", (char *)0);
 215         perror(shell);
 216         fail();
 217 }
 218 
 219 void
 220 fixtty()
 221 {
 222         struct termios sbuf;
 223 
 224         sbuf = b;
 225         sbuf.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|IXON);
 226         sbuf.c_oflag &= ~OPOST;
 227         sbuf.c_lflag &= ~(ICANON|ISIG|ECHO);
 228         sbuf.c_cc[VMIN] = 1;
 229         sbuf.c_cc[VTIME] = 0;
 230         (void) ioctl(0, TCSETSF, (char *)&sbuf);
 231 }
 232 
 233 void
 234 fail()
 235 {
 236 
 237         (void) kill(0, SIGTERM);
 238         done();
 239 }
 240 
 241 void
 242 done()
 243 {
 244         time_t tvec;
 245         char tbuf[BUFSIZ];
 246 
 247         if (subchild) {
 248                 tvec = time((time_t *)0);
 249                 strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
 250                 fprintf(fscript, gettext("\nscript done on %s\n"), tbuf);
 251                 (void) fclose(fscript);
 252                 (void) close(master);
 253         } else {
 254                 (void) ioctl(0, TCSETSW, (char *)&b);
 255                 printf(gettext("Script done, file is %s\n"), fname);
 256         }
 257         exit(0);
 258 }
 259 
 260 void
 261 getmaster()
 262 {
 263         struct stat stb;
 264 
 265         if ((master = open(mptname, O_RDWR)) >= 0) { /* a pseudo-tty is free */
 266                 (void) ioctl(0, TCGETS, (char *)&b);
 267                 (void) ioctl(0, TIOCGWINSZ, (char *)&size);
 268                 return;
 269         } else {                                /* out of pseudo-tty's */
 270                 perror(mptname);
 271                 fprintf(stderr, gettext("Out of pseudo-tty's\n"));
 272                 fail();
 273         }
 274 }
 275 
 276 void
 277 getslave()
 278 {
 279         char *slavename;        /* name of slave pseudo-tty */
 280 
 281         grantpt(master);                /* change permissions of slave */
 282         unlockpt(master);                       /* unlock slave */
 283         slavename = ptsname(master);            /* get name of slave */
 284         slave = open(slavename, O_RDWR);        /* open slave */
 285         if (slave < 0) {                     /* error on opening slave */
 286                 perror(slavename);
 287                 fail();
 288         }
 289         ioctl(slave, I_PUSH, "ptem");   /* push pt hw emulation module */
 290         ioctl(slave, I_PUSH, "ldterm");         /* push line discipline */
 291 
 292         (void) ioctl(slave, TCSETSF, (char *)&b);
 293         (void) ioctl(slave, TIOCSWINSZ, (char *)&size);
 294 }