dennis@rlgvax.UUCP (Dennis Bednar) (07/04/86)
Here is a version of the 4.2 BSD script(1) for System 5. ONLY THIS VERSION DOESN'T WORK COMPLETELY. READ ON. This version works marginally, but has two giant problems: If you run a program such as an editor, which expects to do ioctl()'s to condition the terminal, script will fail. The reason is that the shell's file descriptor 0 is attached to the end of a UNIX pipe, not to a /dev/tty. Weird stuff happens at this point. And when you hit interrupt, script stops. Anyway this tool can be useful for saving the output of *simple* commands, and provided you don't hit DELETE. To stop script, type ^D or hit Interrupt. Any solutions or ideas on how to fix these problems are welcome. PS, I call this version scriptd, because here at rlgvax we already have script, and I didn't want a name conflict. I wrote this version for fun and for self-education. -dennis bednar #--------------- CUT HERE --------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # scriptd.c # This archive created: Thu Jul 3 22:07:33 EDT 1986 # if test -f scriptd.c then echo shar: will not over-write existing file 'scriptd.c' else echo x - scriptd.c # ............ F I L E B E G .......... scriptd.c cat << '\SHAR_EOF' > scriptd.c /* * scriptd.c * Public domain script.c command. * Dennis Bednar * rlgvax!dennis (dennis@rlgvax.UUCP) * June 10 1986 * * DOESN'T WORK: some applications which run under the shell and * do ioctl()'s to fd 0, 1, or 2 are really trying to do an ioctl * to a regular old file descriptor (for the end of a pipe), which * unfortunately doesn't work. * Examples: stty, ev, pro. * * ALSO the interrupt key is fielded by the shell, and he dies (at * least I think). * * * KB input ---> pipe2sh ---> shell input * SCR output <--- pipe2tty <--- shell output * / * typescript <-/ * file * * There are 3 processes: * kbinput() reads from keyboard input until EOF, * and passes through a pipe (pipe2sh) to the shell input. * This process terminates when EOF is typed in. * shell_proc() a UNIX shell that is reading from one pipe, and writing * to another pipe. This process terminates when EOF * is read from pipe2sh, because kbinput() has gone away. * kboutput() reads from pipe2tty, and writes both to the screen, * and to a file. Because the shell is echoing, the file * captures both input and output. This process terminates * when EOF is read because the shell has gone away. * * Note currently that the 'typescript' output file is written to by * 2 processes (kbinput() and kboutput()). * The output to the 'typescript' outfd file descriptor * is raw (ie uses write(), not fwrite()). This is because there are two * processes competing to write to the same file. Because of the fork(), * both processes are appending to the very end of the same typescript file * without interference (neat UNIX trick, huh?). * * * Also note that the kbinput() task is echoing, *NOT* the * shell. If you thing about it, its not the shell that normally * echoes your commands when you type, its the tty driver. * * */ #include <stdio.h> #include <errno.h> #include <signal.h> /* globals */ char *cmd; /* name of argv[0] for error messages */ int pipe2sh [2]; /* pipe from kb input to shell input */ int pipe2tty [2]; /* pipe from sh output to screen output plus file */ char *filename = "typescript"; int filfp; /* for above output file */ char *shellname = "/bin/sh"; /* int pipefd[2] indices */ #define P_READ 0 #define P_WRITE 1 /* forward non-int functions */ char *u_errmesg(); /* error string for perror() like error messages */ extern char *getenv(); main(argc, argv) int argc; char *argv[]; { char *cp; /* save the command name in case of error */ cmd = argv[0]; /* if SHELL is an enviroment variable, use it, else use default */ if ( (cp = getenv("SHELL")) != NULL) shellname = cp; /* the output file MUST be unbuffered, otherwise stdio causes * collision of arbitrary() lower level writes. */ filfp = creat(filename, 0660); if (filfp == -1) { fprintf(stderr, "%s: %s: %s\n", cmd, filename, u_errmesg()); exit(1); } /* create the 2 single directional pipes */ (void) makepipe( pipe2sh ); (void) makepipe( pipe2tty ); /* at this point there will be 0,1,2, plus 4 pipe file descriptors, * plus one file descriptor for the 'typescript' file. */ /* start the 3 tasks */ /* the first two routines fork() a child. The third routine * loops until EOF. */ printf("Script started, file is typescript\n"); spawn_in(); signal(SIGINT, SIG_IGN); /* so shell doesn't die on INTERRUPT */ spawn_sh(); task_output(); } /* * create a unix pipe. Added a little error checking. */ makepipe(pipefd) int pipefd[]; { register int rtn; rtn = pipe( pipefd ); if (rtn == -1) { fprintf( stderr, "%s: Cannot make interprocess channel (pipe): %s\n", cmd, u_errmesg()); exit(1); } } /* * The output task reads the pipe2tty pipe (output from the shell), * and writes the same to both stdout, and to the 'typescript' file. */ task_output() { register int in, out, numread, numwrote; char buf [20]; /* close unused fd's */ close(0); out = 1; /* keep 1 open */ close(2); close( pipe2sh[ P_READ ] ); close( pipe2sh[ P_WRITE ] ); in = pipe2tty[ P_READ ]; /* keep pipe2tty[ P_READ ] open */ close( pipe2tty[ P_WRITE ] ); /* copy output from shell to stdout and to the file */ while ( (numread = read(in, buf, sizeof(buf))) > 0) { write(out, buf, numread); write(filfp, buf, numread); } close(filfp); printf("Script done, file is typescript\n"); exit(0); } /* * spawn input task. * create an input child task that reads from the keyboard, and writes * through the pipe2sh pipe. */ spawn_in() { int child; int infd, outfd; child = do_fork(); if (child < 0) { fprintf(stderr, "%s: spawn_in could not fork: %s", cmd, u_errmesg()); exit(1); } if (child > 0) return; /* parent returns */ infd = 0; /* 0 is input */ close(1); /* close(2); */ close( pipe2sh[ P_READ ] ); outfd = pipe2sh[P_WRITE]; /* kept open */ close( pipe2tty[ P_READ ] ); close( pipe2tty[ P_WRITE ] ); /* fixtty(); /* must be in raw mode */ /* read from kb write to shell, and to file */ do_copy(infd, outfd); /* child does input task */ } /* * copy task from fd 'in' to fd 'out' * PLUS write to the 'typescript' file. * This copy loop is used both by kb input, and screen output. */ do_copy(in, out) int in; /* fd for tty input */ int out; /* fd for output to shell */ { char buf[20]; int numread; int numwrote; while ( (numread = read(in, buf, sizeof(buf)) ) > 0) { numwrote = write(out, buf, numread); if (numread != numwrote) exit(0); /* stderr is closed */ write(filfp, buf, numread); } exit(0); } /* * spawn a shell. The shell is attached to 2 ends of UNIX pipes. */ spawn_sh() { int child; child = do_fork(); if (child < 0) { fprintf(stderr, "%s: spawn_in could not fork: %s", cmd, u_errmesg()); exit(1); } if (child > 0) return; /* parent returns */ /* close unused fd's */ close(0); close(1); close(2); /* pipe2sh[ P_READ ] kept open */ close( pipe2sh[ P_WRITE ] ); close( pipe2tty[ P_READ ] ); /* close(pipe2tty[P_WRITE]); */ /* redirect all shell stdin, stdout, and stderr to the pipes */ dup2( pipe2sh[ P_READ ], 0); dup2( pipe2tty [ P_WRITE ], 1); dup2( pipe2tty [ P_WRITE ], 2); /* do_copy(0, 1); /* stub for now */ /* the shell should die when it reads EOF, right?? */ execl(shellname, "sh", "-i", (char *)0); } /* * can beef this up to retry in the future if desired */ do_fork() { return fork(); } /****** library routines ****/ /* * return basename of full path name */ char * basename(path) char *path; { char *cp; /* general char pointer */ char *strrchr(); if ((cp = strrchr(path, '/')) == NULL) /* no rightmost slash */ return path; else return cp+1; } /* * return UNIX error message associated with errno * more flexible than perror(3) */ char * u_errmesg() { extern int errno; extern int sys_nerr; extern char *sys_errlist[]; static char buffer[50]; if (errno < 0 || errno >= sys_nerr) { sprintf( buffer, "errno %d undefined (%d=max)", errno, sys_nerr); return(buffer); } return( sys_errlist[errno] ); } \SHAR_EOF # ............ F I L E E N D .......... scriptd.c fi # end of overwriting check # end of shell archive exit 0 -- -Dennis Bednar {decvax,ihnp4,harpo,allegra}!seismo!rlgvax!dennis UUCP
sgt@alice.UucP (Steve Tell) (07/10/86)
>[ a program that takes the output from a shell session and makes > a file of it] Supposedly, System V, release 3 has the necessary things to do this right. (Without the limitations cited in the original article) Has anyone out there figured out in any detail exactly how one uses the Streams system to accomplish things like this in SVR3? (I'm working through the 1-foot high stack of manuals, but its going slowly) Thanks -- Steve Tell AT&T Bell Laboratories, Murray Hill, NJ (... is not responsible for any of these opinions)