xref: /freebsd/usr.sbin/watch/watch.c (revision 61afd5bb22d787b0641523e7b9b95c964d669bd5)
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 int		opt_no_switch = 0;
52 
53 char            dev_name[DEV_NAME_LEN];
54 int             snp_io;
55 dev_t		snp_tty;
56 int             std_in = 0, std_out = 1;
57 
58 
59 int             clear_ok = 0;
60 struct sgttyb   sgo;
61 struct tchars	tco;
62 char            tbuf[1024], buf[1024];
63 
64 
65 void
66 clear()
67 {
68 	if (clear_ok)
69 		tputs(buf, 1, putchar);
70 	fflush(stdout);
71 }
72 
73 void
74 timestamp(buf)
75 	char           *buf;
76 {
77 	time_t          t;
78 	char            btmp[1024];
79 	clear();
80 	printf("\n---------------------------------------------\n");
81 	t = time(NULL);
82 	strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t));
83 	printf("%s\n", btmp);
84 	printf("%s\n", buf);
85 	printf("---------------------------------------------\n");
86 	fflush(stdout);
87 }
88 
89 void
90 set_tty()
91 {
92 	struct sgttyb   sgn;
93 	struct tchars	tc;
94 
95 	ioctl(std_in, TIOCGETP, &sgo);
96 	ioctl(std_in, TIOCGETC, &tco);
97 	sgn = sgo;
98 	tc = tco;
99 	sgn.sg_flags |= CBREAK;
100 	sgn.sg_flags &= ~ECHO;
101 	ospeed = sgo.sg_ospeed;
102 	tc.t_intrc = 07;	/* ^G */
103 	tc.t_quitc = 07;	/* ^G */
104 	ioctl(std_in, TIOCSETP, &sgn);
105 	ioctl(std_in, TIOCSETC, &tc);
106 }
107 
108 void
109 unset_tty()
110 {
111 	ioctl(std_in, TIOCSETP, &sgo);
112 	ioctl(std_in, TIOCSETC, &tco);
113 }
114 
115 
116 void
117 fatal(buf)
118 	char           *buf;
119 {
120 	unset_tty();
121 	if (buf)
122 		fprintf(stderr, "Fatal: %s\n", buf);
123 	exit(1);
124 }
125 
126 int
127 open_snp()
128 {
129 	char            snp[] = {"/dev/snpX"};
130 	char            c;
131 	int             f, mode;
132 
133 	if (opt_write)
134 		mode = O_RDWR;
135 	else
136 		mode = O_RDONLY;
137 
138 	for (c = '0'; c <= '9'; c++) {
139 		snp[8] = c;
140 		if ((f = open(snp, mode)) < 0)
141 			continue;
142 		return f;
143 	}
144 	fatal("Cannot open snoop device.");
145 }
146 
147 
148 void
149 cleanup()
150 {
151 	if (opt_timestamp)
152 		timestamp("Logging Exited.");
153 	close(snp_io);
154 	unset_tty();
155 	exit(0);
156 }
157 
158 
159 void
160 show_usage()
161 {
162 	printf("watch -[ciotnW] [tty name]\n");
163 	exit(1);
164 }
165 
166 void
167 setup_scr()
168 {
169 	char           *cbuf = buf, *term;
170 	if (!opt_interactive)
171 		return;
172 	if ((term = getenv("TERM")))
173 		if (tgetent(tbuf, term) == 1)
174 			if (tgetstr("cl", &cbuf))
175 				clear_ok = 1;
176 	set_tty();
177 	clear();
178 }
179 
180 
181 int
182 ctoh(c)
183 	char            c;
184 {
185 	if (c >= '0' && c <= '9')
186 		return (int) (c - '0');
187 
188 	if (c >= 'a' && c <= 'f')
189 		return (int) (c - 'a' + 10);
190 
191 	fatal("Bad tty number.");
192 }
193 
194 
195 void
196 detach_snp()
197 {
198 	dev_t		dev;
199 
200 	dev = -1;
201 	ioctl(snp_io, SNPSTTY, &dev);
202 }
203 
204 void
205 attach_snp()
206 {
207 	if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
208 		fatal("Cannot attach to tty.");
209 	if (opt_timestamp)
210 		timestamp("Logging Started.");
211 }
212 
213 
214 void
215 set_dev(name)
216 	char           *name;
217 {
218 	char            buf[DEV_NAME_LEN];
219 	struct stat	sb;
220 
221 	if (strlen(name) > 5 && !strncmp(name, "/dev/", 5))
222 		strcpy(buf, name);
223 	else {
224 		if (strlen(name) == 2)
225 			sprintf(buf, "/dev/tty%s", name);
226 		else
227 			sprintf(buf, "/dev/%s", name);
228 	}
229 
230 	if (stat(buf, &sb) < 0)
231 		fatal("Bad device name.");
232 
233 	snp_tty = sb.st_rdev;
234 	attach_snp();
235 }
236 
237 void
238 ask_dev(dev_name, msg)
239 	char           *dev_name, *msg;
240 {
241 	char            buf[DEV_NAME_LEN];
242 	int             len;
243 
244 	clear();
245 	unset_tty();
246 
247 	if (msg)
248 		printf("%s\n", msg);
249 	if (dev_name)
250 		printf("Enter device name [%s]:", dev_name);
251 	else
252 		printf("Enter device name:");
253 
254 	if (fgets(buf, DEV_NAME_LEN - 1, stdin)) {
255 		len = strlen(buf);
256 		if (buf[len - 1] == '\n')
257 			buf[len - 1] = '\0';
258 		if (buf[0] != '\0' && buf[0] != ' ')
259 			strcpy(dev_name, buf);
260 	}
261 	set_tty();
262 }
263 
264 #define READB_LEN	5
265 
266 void
267 main(ac, av)
268 	int             ac;
269 	char          **av;
270 {
271 	int             res, nread, b_size = MIN_SIZE;
272 	extern int      optind;
273 	char            ch, *buf, chb[READB_LEN];
274 	fd_set          fd_s;
275 
276 	(void) setlocale(LC_TIME, "");
277 
278 	if (isatty(std_out))
279 		opt_interactive = 1;
280 	else
281 		opt_interactive = 0;
282 
283 
284 	while ((ch = getopt(ac, av, "Wciotn")) != EOF)
285 		switch (ch) {
286 		case 'W':
287 			opt_write = 1;
288 			break;
289 		case 'c':
290 			opt_reconn_close = 1;
291 			break;
292 		case 'i':
293 			opt_interactive = 1;
294 			break;
295 		case 'o':
296 			opt_reconn_oflow = 1;
297 			break;
298 		case 't':
299 			opt_timestamp = 1;
300 			break;
301 		case 'n':
302 			opt_no_switch = 1;
303 			break;
304 		case '?':
305 		default:
306 			show_usage();
307 			exit(1);
308 		}
309 
310 	signal(SIGINT, cleanup);
311 
312 	setup_scr();
313 	snp_io = open_snp();
314 
315 	if (*(av += optind) == NULL) {
316 		if (opt_interactive && !opt_no_switch)
317 			ask_dev(dev_name, MSG_INIT);
318 		else
319 			fatal("No device name given.");
320 	} else
321 		strncpy(dev_name, *av, DEV_NAME_LEN);
322 
323 	set_dev(dev_name);
324 
325 	if (!(buf = (char *) malloc(b_size)))
326 		fatal("Cannot malloc().");
327 
328 	FD_ZERO(&fd_s);
329 
330 	while (1) {
331 		if (opt_interactive)
332 			FD_SET(std_in, &fd_s);
333 		FD_SET(snp_io, &fd_s);
334 		res = select(snp_io + 1, &fd_s, NULL, NULL, NULL);
335 		if (opt_interactive && FD_ISSET(std_in, &fd_s)) {
336 
337 			if ((res = ioctl(std_in, FIONREAD, &nread)) != 0)
338 				fatal("ioctl() failed.");
339 			if (nread > READB_LEN)
340 				nread = READB_LEN;
341 			if (read(std_in,chb,nread)!=nread)
342 				fatal("read (stdin) failed.");
343 
344 			switch (chb[0]) {
345 			case CHR_CLEAR:
346 				clear();
347 				break;
348 			case CHR_SWITCH:
349 				if (opt_no_switch)
350 					break;
351 				detach_snp();
352 				ask_dev(dev_name, MSG_CHANGE);
353 				set_dev(dev_name);
354 				break;
355 			default:
356 				if (opt_write) {
357 					if (write(snp_io,chb,nread) != nread) {
358 						detach_snp();
359 						if (opt_no_switch)
360 							fatal("Write failed.");
361 						ask_dev(dev_name, MSG_NOWRITE);
362 						set_dev(dev_name);
363 					}
364 				}
365 
366 			}
367 		}
368 		if (!FD_ISSET(snp_io, &fd_s))
369 			continue;
370 
371 		if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0)
372 			fatal("ioctl() failed.");
373 
374 		switch (nread) {
375 		case SNP_OFLOW:
376 			if (opt_reconn_oflow)
377 				attach_snp();
378 			else if (opt_interactive && !opt_no_switch) {
379 				ask_dev(dev_name, MSG_OFLOW);
380 				set_dev(dev_name);
381 			} else
382 				cleanup();
383 		case SNP_DETACH:
384 		case SNP_TTYCLOSE:
385 			if (opt_reconn_close)
386 				attach_snp();
387 			else if (opt_interactive && !opt_no_switch) {
388 				ask_dev(dev_name, MSG_CLOSED);
389 				set_dev(dev_name);
390 			} else
391 				cleanup();
392 		default:
393 			if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) {
394 				free(buf);
395 				if (!(buf = (char *) malloc(b_size / 2)))
396 					fatal("Cannot malloc()");
397 				b_size = b_size / 2;
398 			}
399 			if (nread > b_size) {
400 				b_size = (nread % 2) ? (nread + 1) : (nread);
401 				free(buf);
402 				if (!(buf = (char *) malloc(b_size)))
403 					fatal("Cannot malloc()");
404 			}
405 			if (read(snp_io, buf, nread) < nread)
406 				fatal("read failed.");
407 			if (write(std_out, buf, nread) < nread)
408 				fatal("write failed.");
409 		}
410 	}			/* While */
411 }
412