watch.c revision 6775
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
45int             opt_reconn_close = 0;
46int             opt_reconn_oflow = 0;
47int             opt_interactive = 1;
48int             opt_timestamp = 0;
49int		opt_write = 0;
50
51char            dev_name[DEV_NAME_LEN];
52int             snp_io;
53dev_t		snp_tty;
54int             std_in = 0, std_out = 1;
55
56
57int             clear_ok = 0;
58struct sgttyb   sgo;
59char            tbuf[1024], buf[1024];
60
61
62void
63clear()
64{
65	if (clear_ok)
66		tputs(buf, 1, putchar);
67	fflush(stdout);
68}
69
70void
71timestamp(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
86void
87set_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
98void
99unset_tty()
100{
101	ioctl(std_in, TIOCSETP, &sgo);
102}
103
104
105void
106fatal(buf)
107	char           *buf;
108{
109	unset_tty();
110	if (buf)
111		fprintf(stderr, "Fatal: %s\n", buf);
112	exit(1);
113}
114
115int
116open_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
137void
138cleanup()
139{
140	if (opt_timestamp)
141		timestamp("Logging Exited.");
142	close(snp_io);
143	unset_tty();
144	exit(0);
145}
146
147
148void
149show_usage()
150{
151	printf("watch -[ciotW] [tty name]\n");
152	exit(1);
153}
154
155void
156setup_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
170int
171ctoh(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
184void
185detach_snp()
186{
187	dev_t		dev;
188
189	dev = -1;
190	ioctl(snp_io, SNPSTTY, &dev);
191}
192
193void
194attach_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
203void
204set_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
226void
227ask_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
255void
256main(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