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