xref: /freebsd/usr.sbin/watch/watch.c (revision e627b39baccd1ec9129690167cf5e6d860509655)
1 /*
2  * Copyright (c) 1995 Ugen J.S.Antsilevich
3  *
4  * Redistribution and use in source forms, with and without modification,
5  * are permitted provided that this entire comment appears intact.
6  *
7  * Redistribution in binary form may occur without any restrictions.
8  * Obviously, it would be nice if you gave credit where credit is due
9  * but requiring it would be too onerous.
10  *
11  * This software is provided ``AS IS'' without any warranties of any kind.
12  *
13  * Snoop stuff.
14  */
15 
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <signal.h>
20 #include <string.h>
21 #include <termcap.h>
22 #include <sgtty.h>
23 #include <locale.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/time.h>
27 #include <sys/select.h>
28 #include <sys/fcntl.h>
29 #include <sys/snoop.h>
30 
31 
32 #define MSG_INIT	"Snoop started."
33 #define MSG_OFLOW	"Snoop stopped due to overflow. Reconnecting."
34 #define MSG_CLOSED	"Snoop stopped due to tty close. Reconnecting."
35 #define MSG_CHANGE	"Snoop device change by user request."
36 #define MSG_NOWRITE	"Snoop device change due to write failure."
37 
38 
39 #define DEV_NAME_LEN	1024	/* for /dev/ttyXX++ */
40 #define MIN_SIZE	256
41 
42 #define CHR_SWITCH	24	/* Ctrl+X	 */
43 #define CHR_CLEAR	23	/* Ctrl+V	 */
44 
45 
46 int             opt_reconn_close = 0;
47 int             opt_reconn_oflow = 0;
48 int             opt_interactive = 1;
49 int             opt_timestamp = 0;
50 int		opt_write = 0;
51 
52 char            dev_name[DEV_NAME_LEN];
53 int             snp_io;
54 dev_t		snp_tty;
55 int             std_in = 0, std_out = 1;
56 
57 
58 int             clear_ok = 0;
59 struct sgttyb   sgo;
60 struct tchars	tco;
61 char            tbuf[1024], buf[1024];
62 
63 
64 void
65 clear()
66 {
67 	if (clear_ok)
68 		tputs(buf, 1, putchar);
69 	fflush(stdout);
70 }
71 
72 void
73 timestamp(buf)
74 	char           *buf;
75 {
76 	time_t          t;
77 	char            btmp[1024];
78 	clear();
79 	printf("\n---------------------------------------------\n");
80 	t = time(NULL);
81 	strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t));
82 	printf("%s\n", btmp);
83 	printf("%s\n", buf);
84 	printf("---------------------------------------------\n");
85 	fflush(stdout);
86 }
87 
88 void
89 set_tty()
90 {
91 	struct sgttyb   sgn;
92 	struct tchars	tc;
93 
94 	ioctl(std_in, TIOCGETP, &sgo);
95 	ioctl(std_in, TIOCGETC, &tco);
96 	sgn = sgo;
97 	tc = tco;
98 	sgn.sg_flags |= CBREAK;
99 	sgn.sg_flags &= ~ECHO;
100 	ospeed = sgo.sg_ospeed;
101 	tc.t_intrc = 17;	/* ^Q */
102 	tc.t_quitc = 17;	/* ^Q */
103 	ioctl(std_in, TIOCSETP, &sgn);
104 	ioctl(std_in, TIOCSETC, &tc);
105 }
106 
107 void
108 unset_tty()
109 {
110 	ioctl(std_in, TIOCSETP, &sgo);
111 	ioctl(std_in, TIOCSETC, &tco);
112 }
113 
114 
115 void
116 fatal(buf)
117 	char           *buf;
118 {
119 	unset_tty();
120 	if (buf)
121 		fprintf(stderr, "Fatal: %s\n", buf);
122 	exit(1);
123 }
124 
125 int
126 open_snp()
127 {
128 	char            snp[] = {"/dev/snpX"};
129 	char            c;
130 	int             f, mode;
131 
132 	if (opt_write)
133 		mode = O_RDWR;
134 	else
135 		mode = O_RDONLY;
136 
137 	for (c = '0'; c <= '9'; c++) {
138 		snp[8] = c;
139 		if ((f = open(snp, mode)) < 0)
140 			continue;
141 		return f;
142 	}
143 	fatal("Cannot open snoop device.");
144 }
145 
146 
147 void
148 cleanup()
149 {
150 	if (opt_timestamp)
151 		timestamp("Logging Exited.");
152 	close(snp_io);
153 	unset_tty();
154 	exit(0);
155 }
156 
157 
158 void
159 show_usage()
160 {
161 	printf("watch -[ciotW] [tty name]\n");
162 	exit(1);
163 }
164 
165 void
166 setup_scr()
167 {
168 	char           *cbuf = buf, *term;
169 	if (!opt_interactive)
170 		return;
171 	if ((term = getenv("TERM")))
172 		if (tgetent(tbuf, term) == 1)
173 			if (tgetstr("cl", &cbuf))
174 				clear_ok = 1;
175 	set_tty();
176 	clear();
177 }
178 
179 
180 int
181 ctoh(c)
182 	char            c;
183 {
184 	if (c >= '0' && c <= '9')
185 		return (int) (c - '0');
186 
187 	if (c >= 'a' && c <= 'f')
188 		return (int) (c - 'a' + 10);
189 
190 	fatal("Bad tty number.");
191 }
192 
193 
194 void
195 detach_snp()
196 {
197 	dev_t		dev;
198 
199 	dev = -1;
200 	ioctl(snp_io, SNPSTTY, &dev);
201 }
202 
203 void
204 attach_snp()
205 {
206 	if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
207 		fatal("Cannot attach to tty.");
208 	if (opt_timestamp)
209 		timestamp("Logging Started.");
210 }
211 
212 
213 void
214 set_dev(name)
215 	char           *name;
216 {
217 	char            buf[DEV_NAME_LEN];
218 	struct stat	sb;
219 
220 	if (strlen(name) > 5 && !strncmp(name, "/dev/", 5))
221 		strcpy(buf, name);
222 	else {
223 		if (strlen(name) == 2)
224 			sprintf(buf, "/dev/tty%s", name);
225 		else
226 			sprintf(buf, "/dev/%s", name);
227 	}
228 
229 	if (stat(buf, &sb) < 0)
230 		fatal("Bad device name.");
231 
232 	snp_tty = sb.st_rdev;
233 	attach_snp();
234 }
235 
236 void
237 ask_dev(dev_name, msg)
238 	char           *dev_name, *msg;
239 {
240 	char            buf[DEV_NAME_LEN];
241 	int             len;
242 
243 	clear();
244 	unset_tty();
245 
246 	if (msg)
247 		printf("%s\n", msg);
248 	if (dev_name)
249 		printf("Enter device name [%s]:", dev_name);
250 	else
251 		printf("Enter device name:");
252 
253 	if (fgets(buf, DEV_NAME_LEN - 1, stdin)) {
254 		len = strlen(buf);
255 		if (buf[len - 1] == '\n')
256 			buf[len - 1] = '\0';
257 		if (buf[0] != '\0' && buf[0] != ' ')
258 			strcpy(dev_name, buf);
259 	}
260 	set_tty();
261 }
262 
263 #define READB_LEN	5
264 
265 void
266 main(ac, av)
267 	int             ac;
268 	char          **av;
269 {
270 	int             res, nread, b_size = MIN_SIZE;
271 	extern int      optind;
272 	char            ch, *buf, chb[READB_LEN];
273 	fd_set          fd_s;
274 
275 	(void) setlocale(LC_TIME, "");
276 
277 	if (getuid() != 0)
278 		fatal(NULL);
279 
280 	if (isatty(std_out))
281 		opt_interactive = 1;
282 	else
283 		opt_interactive = 0;
284 
285 
286 	while ((ch = getopt(ac, av, "Wciot")) != EOF)
287 		switch (ch) {
288 		case 'W':
289 			opt_write = 1;
290 			break;
291 		case 'c':
292 			opt_reconn_close = 1;
293 			break;
294 		case 'i':
295 			opt_interactive = 1;
296 			break;
297 		case 'o':
298 			opt_reconn_oflow = 1;
299 			break;
300 		case 't':
301 			opt_timestamp = 1;
302 			break;
303 		case '?':
304 		default:
305 			show_usage();
306 			exit(1);
307 		}
308 
309 	signal(SIGINT, cleanup);
310 
311 	setup_scr();
312 	snp_io = open_snp();
313 
314 	if (*(av += optind) == NULL) {
315 		if (opt_interactive)
316 			ask_dev(dev_name, MSG_INIT);
317 		else
318 			fatal("No device name given.");
319 	} else
320 		strncpy(dev_name, *av, DEV_NAME_LEN);
321 
322 	set_dev(dev_name);
323 
324 	if (!(buf = (char *) malloc(b_size)))
325 		fatal("Cannot malloc().");
326 
327 	FD_ZERO(&fd_s);
328 
329 	while (1) {
330 		if (opt_interactive)
331 			FD_SET(std_in, &fd_s);
332 		FD_SET(snp_io, &fd_s);
333 		res = select(snp_io + 1, &fd_s, NULL, NULL, NULL);
334 		if (opt_interactive && FD_ISSET(std_in, &fd_s)) {
335 
336 			if ((res = ioctl(std_in, FIONREAD, &nread)) != 0)
337 				fatal("ioctl() failed.");
338 			if (nread > READB_LEN)
339 				nread = READB_LEN;
340 			if (read(std_in,chb,nread)!=nread)
341 				fatal("read (stdin) failed.");
342 
343 			switch (chb[0]) {
344 			case CHR_CLEAR:
345 				clear();
346 				break;
347 			case CHR_SWITCH:
348 				detach_snp();
349 				ask_dev(dev_name, MSG_CHANGE);
350 				set_dev(dev_name);
351 				break;
352 			default:
353 				if (opt_write) {
354 					if (write(snp_io,chb,nread) != nread) {
355 						detach_snp();
356 						ask_dev(dev_name, MSG_NOWRITE);
357 						set_dev(dev_name);
358 					}
359 				}
360 
361 			}
362 		}
363 		if (!FD_ISSET(snp_io, &fd_s))
364 			continue;
365 
366 		if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0)
367 			fatal("ioctl() failed.");
368 
369 		switch (nread) {
370 		case SNP_OFLOW:
371 			if (opt_reconn_oflow)
372 				attach_snp();
373 			else if (opt_interactive) {
374 				ask_dev(dev_name, MSG_OFLOW);
375 				set_dev(dev_name);
376 			} else
377 				cleanup();
378 		case SNP_DETACH:
379 		case SNP_TTYCLOSE:
380 			if (opt_reconn_close)
381 				attach_snp();
382 			else if (opt_interactive) {
383 				ask_dev(dev_name, MSG_CLOSED);
384 				set_dev(dev_name);
385 			} else
386 				cleanup();
387 		default:
388 			if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) {
389 				free(buf);
390 				if (!(buf = (char *) malloc(b_size / 2)))
391 					fatal("Cannot malloc()");
392 				b_size = b_size / 2;
393 			}
394 			if (nread > b_size) {
395 				b_size = (nread % 2) ? (nread + 1) : (nread);
396 				free(buf);
397 				if (!(buf = (char *) malloc(b_size)))
398 					fatal("Cannot malloc()");
399 			}
400 			if (read(snp_io, buf, nread) < nread)
401 				fatal("read failed.");
402 			if (write(std_out, buf, nread) < nread)
403 				fatal("write failed.");
404 		}
405 	}			/* While */
406 }
407