xref: /freebsd/usr.bin/script/script.c (revision 1c8af8787354e20c2b38cab5801698133ff8b403)
1 /*
2  * Copyright (c) 1980, 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static char copyright[] =
36 "@(#) Copyright (c) 1980, 1992, 1993\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 static char sccsid[] = "@(#)script.c	8.1 (Berkeley) 6/6/93";
42 #endif /* not lint */
43 
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 #include <sys/stat.h>
47 #include <sys/ioctl.h>
48 #include <sys/time.h>
49 
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <paths.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <termios.h>
58 #include <unistd.h>
59 
60 FILE	*fscript;
61 int	master, slave;
62 int	child, subchild;
63 int	outcc;
64 char	*fname;
65 
66 struct	termios tt;
67 
68 void	done __P((void)) __dead2;
69 void	dooutput __P((void));
70 void	doshell __P((void));
71 void	err __P((const char *, ...));
72 void	fail __P((void));
73 void	finish __P((int));
74 void	scriptflush __P((int));
75 
76 int
77 main(argc, argv)
78 	int argc;
79 	char *argv[];
80 {
81 	register int cc;
82 	struct termios rtt;
83 	struct winsize win;
84 	int aflg, ch;
85 	char ibuf[BUFSIZ];
86 
87 	aflg = 0;
88 	while ((ch = getopt(argc, argv, "a")) != -1)
89 		switch(ch) {
90 		case 'a':
91 			aflg = 1;
92 			break;
93 		case '?':
94 		default:
95 			(void)fprintf(stderr, "usage: script [-a] [file]\n");
96 			exit(1);
97 		}
98 	argc -= optind;
99 	argv += optind;
100 
101 	if (argc > 0)
102 		fname = argv[0];
103 	else
104 		fname = "typescript";
105 
106 	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL)
107 		err("%s: %s", fname, strerror(errno));
108 
109 	(void)tcgetattr(STDIN_FILENO, &tt);
110 	(void)ioctl(STDIN_FILENO, TIOCGWINSZ, &win);
111 	if (openpty(&master, &slave, NULL, &tt, &win) == -1)
112 		err("openpty: %s", strerror(errno));
113 
114 	(void)printf("Script started, output file is %s\n", fname);
115 	rtt = tt;
116 	cfmakeraw(&rtt);
117 	rtt.c_lflag &= ~ECHO;
118 	(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
119 
120 	(void)signal(SIGCHLD, finish);
121 	child = fork();
122 	if (child < 0) {
123 		perror("fork");
124 		fail();
125 	}
126 	if (child == 0) {
127 		subchild = child = fork();
128 		if (child < 0) {
129 			perror("fork");
130 			fail();
131 		}
132 		if (child)
133 			dooutput();
134 		else
135 			doshell();
136 	}
137 
138 	(void)fclose(fscript);
139 	while ((cc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0)
140 		(void)write(master, ibuf, cc);
141 	done();
142 }
143 
144 void
145 finish(signo)
146 	int signo;
147 {
148 	register int die, pid;
149 	union wait status;
150 
151 	die = 0;
152 	while ((pid = wait3((int *)&status, WNOHANG, 0)) > 0)
153 		if (pid == child)
154 			die = 1;
155 
156 	if (die)
157 		done();
158 }
159 
160 void
161 dooutput()
162 {
163 	struct itimerval value;
164 	register int cc;
165 	time_t tvec;
166 	char obuf[BUFSIZ];
167 
168 	(void)close(STDIN_FILENO);
169 	tvec = time(NULL);
170 	(void)fprintf(fscript, "Script started on %s", ctime(&tvec));
171 
172 	(void)signal(SIGALRM, scriptflush);
173 	value.it_interval.tv_sec = 60 / 2;
174 	value.it_interval.tv_usec = 0;
175 	value.it_value = value.it_interval;
176 	(void)setitimer(ITIMER_REAL, &value, NULL);
177 	for (;;) {
178 		cc = read(master, obuf, sizeof (obuf));
179 		if (cc <= 0)
180 			break;
181 		(void)write(1, obuf, cc);
182 		(void)fwrite(obuf, 1, cc, fscript);
183 		outcc += cc;
184 	}
185 	done();
186 }
187 
188 void
189 scriptflush(signo)
190 	int signo;
191 {
192 	if (outcc) {
193 		(void)fflush(fscript);
194 		outcc = 0;
195 	}
196 }
197 
198 void
199 doshell()
200 {
201 	char *shell;
202 
203 	shell = getenv("SHELL");
204 	if (shell == NULL)
205 		shell = _PATH_BSHELL;
206 
207 	(void)close(master);
208 	(void)fclose(fscript);
209 	login_tty(slave);
210 	execl(shell, "sh", "-i", NULL);
211 	perror(shell);
212 	fail();
213 }
214 
215 void
216 fail()
217 {
218 
219 	(void)kill(0, SIGTERM);
220 	done();
221 }
222 
223 void
224 done()
225 {
226 	time_t tvec;
227 
228 	if (subchild) {
229 		tvec = time(NULL);
230 		(void)fprintf(fscript,"\nScript done on %s", ctime(&tvec));
231 		(void)fclose(fscript);
232 		(void)close(master);
233 	} else {
234 		(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
235 		(void)printf("Script done, output file is %s\n", fname);
236 	}
237 	exit(0);
238 }
239 
240 #if __STDC__
241 #include <stdarg.h>
242 #else
243 #include <varargs.h>
244 #endif
245 
246 void
247 #if __STDC__
248 err(const char *fmt, ...)
249 #else
250 err(fmt, va_alist)
251 	char *fmt;
252 	va_dcl
253 #endif
254 {
255 	va_list ap;
256 #if __STDC__
257 	va_start(ap, fmt);
258 #else
259 	va_start(ap);
260 #endif
261 	(void)fprintf(stderr, "script: ");
262 	(void)vfprintf(stderr, fmt, ap);
263 	va_end(ap);
264 	(void)fprintf(stderr, "\n");
265 	exit(1);
266 	/* NOTREACHED */
267 }
268