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