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