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