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