watch.c revision 150299
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 <sys/cdefs.h>
17__FBSDID("$FreeBSD: head/usr.sbin/watch/watch.c 150299 2005-09-18 19:24:05Z cognet $");
18
19#include <sys/param.h>
20#include <sys/fcntl.h>
21#include <sys/filio.h>
22#include <sys/snoop.h>
23#include <sys/stat.h>
24#include <sys/linker.h>
25#include <sys/module.h>
26
27#include <err.h>
28#include <errno.h>
29#include <locale.h>
30#include <paths.h>
31#include <signal.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <sysexits.h>
36#include <termcap.h>
37#include <termios.h>
38#include <time.h>
39#include <unistd.h>
40
41#define MSG_INIT	"Snoop started."
42#define MSG_OFLOW	"Snoop stopped due to overflow. Reconnecting."
43#define MSG_CLOSED	"Snoop stopped due to tty close. Reconnecting."
44#define MSG_CHANGE	"Snoop device change by user request."
45#define MSG_NOWRITE	"Snoop device change due to write failure."
46
47
48#define DEV_NAME_LEN	1024	/* for /dev/ttyXX++ */
49#define MIN_SIZE	256
50
51#define CHR_SWITCH	24	/* Ctrl+X	 */
52#define CHR_CLEAR	23	/* Ctrl+V	 */
53
54static void	clear(void);
55static void	timestamp(const char *);
56static void	set_tty(void);
57static void	unset_tty(void);
58static void	fatal(int, const char *);
59static int	open_snp(void);
60static void	cleanup(int);
61static void	usage(void) __dead2;
62static void	setup_scr(void);
63static void	attach_snp(void);
64static void	detach_snp(void);
65static void	set_dev(const char *);
66static void	ask_dev(char *, const char *);
67
68int             opt_reconn_close = 0;
69int             opt_reconn_oflow = 0;
70int             opt_interactive = 1;
71int             opt_timestamp = 0;
72int		opt_write = 0;
73int		opt_no_switch = 0;
74const char	*opt_snpdev;
75
76char            dev_name[DEV_NAME_LEN];
77int             snp_io;
78int		snp_tty = 0;
79int             std_in = 0, std_out = 1;
80
81
82int             clear_ok = 0;
83struct termios  otty;
84char            tbuf[1024], gbuf[1024];
85
86
87static void
88clear(void)
89{
90	if (clear_ok)
91		tputs(gbuf, 1, putchar);
92	fflush(stdout);
93}
94
95static void
96timestamp(const char *buf)
97{
98	time_t          t;
99	char            btmp[1024];
100	clear();
101	printf("\n---------------------------------------------\n");
102	t = time(NULL);
103	strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t));
104	printf("%s\n", btmp);
105	printf("%s\n", buf);
106	printf("---------------------------------------------\n");
107	fflush(stdout);
108}
109
110static void
111set_tty(void)
112{
113	struct termios  ntty;
114
115	tcgetattr (std_in, &otty);
116	ntty = otty;
117	ntty.c_lflag &= ~ICANON;    /* disable canonical operation  */
118	ntty.c_lflag &= ~ECHO;
119#ifdef FLUSHO
120	ntty.c_lflag &= ~FLUSHO;
121#endif
122#ifdef PENDIN
123	ntty.c_lflag &= ~PENDIN;
124#endif
125#ifdef IEXTEN
126	ntty.c_lflag &= ~IEXTEN;
127#endif
128	ntty.c_cc[VMIN] = 1;        /* minimum of one character */
129	ntty.c_cc[VTIME] = 0;       /* timeout value        */
130
131	ntty.c_cc[VINTR] = 07;   /* ^G */
132	ntty.c_cc[VQUIT] = 07;   /* ^G */
133	tcsetattr (std_in, TCSANOW, &ntty);
134}
135
136static void
137unset_tty(void)
138{
139	tcsetattr (std_in, TCSANOW, &otty);
140}
141
142
143static void
144fatal(int error, const char *buf)
145{
146	unset_tty();
147	if (buf)
148		errx(error, "fatal: %s", buf);
149	else
150		exit(error);
151}
152
153static int
154open_snp(void)
155{
156	char            snp[] = {_PATH_DEV "snpX"};
157	char            c;
158	int             f, mode, pos;
159
160	pos = strlen(snp) - 1;
161	if (opt_write)
162		mode = O_RDWR;
163	else
164		mode = O_RDONLY;
165
166	if (opt_snpdev == NULL)
167		for (c = '0'; c <= '9'; c++) {
168			snp[pos] = c;
169			if ((f = open(snp, mode)) < 0) {
170				if (errno == EBUSY)
171					continue;
172				err(1, "open %s", snp);
173			}
174			return f;
175		}
176	else
177		if ((f = open(opt_snpdev, mode)) != -1)
178			return (f);
179	fatal(EX_OSFILE, "cannot open snoop device");
180	return (0);
181}
182
183
184static void
185cleanup(int signo __unused)
186{
187	if (opt_timestamp)
188		timestamp("Logging Exited.");
189	close(snp_io);
190	if (snp_tty != 0)
191		close(snp_tty);
192	unset_tty();
193	exit(EX_OK);
194}
195
196
197static void
198usage(void)
199{
200	fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n");
201	exit(EX_USAGE);
202}
203
204static void
205setup_scr(void)
206{
207	char           *cbuf = gbuf, *term;
208	if (!opt_interactive)
209		return;
210	if ((term = getenv("TERM")))
211		if (tgetent(tbuf, term) == 1)
212			if (tgetstr("cl", &cbuf))
213				clear_ok = 1;
214	set_tty();
215	clear();
216}
217
218static void
219detach_snp(void)
220{
221	dev_t		dev;
222
223	dev = NODEV;
224	ioctl(snp_io, SNPSTTY, &dev);
225}
226
227static void
228attach_snp(void)
229{
230	if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
231		fatal(EX_UNAVAILABLE, "cannot attach to tty");
232	if (opt_timestamp)
233		timestamp("Logging Started.");
234}
235
236
237static void
238set_dev(const char *name)
239{
240	char            buf[DEV_NAME_LEN];
241	struct stat	sb;
242
243	if (strlen(name) > 5 && !strncmp(name, _PATH_DEV, sizeof _PATH_DEV - 1)) {
244		snprintf(buf, sizeof buf, "%s", name);
245	} else {
246		if (strlen(name) == 2)
247			sprintf(buf, "%s%s", _PATH_TTY, name);
248		else
249			sprintf(buf, "%s%s", _PATH_DEV, name);
250	}
251
252	if (*name == '\0' || stat(buf, &sb) < 0)
253		fatal(EX_DATAERR, "bad device name");
254
255	if ((sb.st_mode & S_IFMT) != S_IFCHR)
256		fatal(EX_DATAERR, "must be a character device");
257
258	if (snp_tty != 0)
259		close(snp_tty);
260	snp_tty = open(buf, O_RDONLY);
261	if (snp_tty < 0)
262		fatal(EX_DATAERR, "can't open device");
263	attach_snp();
264}
265
266void
267ask_dev(char *dbuf, const char *msg)
268{
269	char            buf[DEV_NAME_LEN];
270	int             len;
271
272	clear();
273	unset_tty();
274
275	if (msg)
276		printf("%s\n", msg);
277	if (dbuf)
278		printf("Enter device name [%s]:", dbuf);
279	else
280		printf("Enter device name:");
281
282	if (fgets(buf, DEV_NAME_LEN - 1, stdin)) {
283		len = strlen(buf);
284		if (buf[len - 1] == '\n')
285			buf[len - 1] = '\0';
286		if (buf[0] != '\0' && buf[0] != ' ')
287			strcpy(dbuf, buf);
288	}
289	set_tty();
290}
291
292#define READB_LEN	5
293
294int
295main(int ac, char *av[])
296{
297	int             ch, res, rv, nread;
298	size_t		b_size = MIN_SIZE;
299	char            *buf, chb[READB_LEN];
300	fd_set          fd_s;
301
302	(void) setlocale(LC_TIME, "");
303
304	if (isatty(std_out))
305		opt_interactive = 1;
306	else
307		opt_interactive = 0;
308
309
310	while ((ch = getopt(ac, av, "Wciotnf:")) != -1)
311		switch (ch) {
312		case 'W':
313			opt_write = 1;
314			break;
315		case 'c':
316			opt_reconn_close = 1;
317			break;
318		case 'i':
319			opt_interactive = 1;
320			break;
321		case 'o':
322			opt_reconn_oflow = 1;
323			break;
324		case 't':
325			opt_timestamp = 1;
326			break;
327		case 'n':
328			opt_no_switch = 1;
329			break;
330		case 'f':
331			opt_snpdev = optarg;
332			break;
333		case '?':
334		default:
335			usage();
336		}
337
338	if (modfind("snp") == -1)
339		if (kldload("snp") == -1 || modfind("snp") == -1)
340			warn("snp module not available");
341
342	signal(SIGINT, cleanup);
343
344	snp_io = open_snp();
345	setup_scr();
346
347	if (*(av += optind) == NULL) {
348		if (opt_interactive && !opt_no_switch)
349			ask_dev(dev_name, MSG_INIT);
350		else
351			fatal(EX_DATAERR, "no device name given");
352	} else
353		strncpy(dev_name, *av, DEV_NAME_LEN);
354
355	set_dev(dev_name);
356
357	if (!(buf = (char *) malloc(b_size)))
358		fatal(EX_UNAVAILABLE, "malloc failed");
359
360	FD_ZERO(&fd_s);
361
362	while (1) {
363		if (opt_interactive)
364			FD_SET(std_in, &fd_s);
365		FD_SET(snp_io, &fd_s);
366		res = select(snp_io + 1, &fd_s, NULL, NULL, NULL);
367		if (opt_interactive && FD_ISSET(std_in, &fd_s)) {
368
369			if ((res = ioctl(std_in, FIONREAD, &nread)) != 0)
370				fatal(EX_OSERR, "ioctl(FIONREAD)");
371			if (nread > READB_LEN)
372				nread = READB_LEN;
373			rv = read(std_in, chb, nread);
374			if (rv == -1 || rv != nread)
375				fatal(EX_IOERR, "read (stdin) failed");
376
377			switch (chb[0]) {
378			case CHR_CLEAR:
379				clear();
380				break;
381			case CHR_SWITCH:
382				if (!opt_no_switch) {
383					detach_snp();
384					ask_dev(dev_name, MSG_CHANGE);
385					set_dev(dev_name);
386					break;
387				}
388			default:
389				if (opt_write) {
390					rv = write(snp_io, chb, nread);
391					if (rv == -1 || rv != nread) {
392						detach_snp();
393						if (opt_no_switch)
394							fatal(EX_IOERR,
395							  "write failed");
396						ask_dev(dev_name, MSG_NOWRITE);
397						set_dev(dev_name);
398					}
399				}
400
401			}
402		}
403		if (!FD_ISSET(snp_io, &fd_s))
404			continue;
405
406		if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0)
407			fatal(EX_OSERR, "ioctl(FIONREAD)");
408
409		switch (nread) {
410		case SNP_OFLOW:
411			if (opt_reconn_oflow)
412				attach_snp();
413			else if (opt_interactive && !opt_no_switch) {
414				ask_dev(dev_name, MSG_OFLOW);
415				set_dev(dev_name);
416			} else
417				cleanup(-1);
418			break;
419		case SNP_DETACH:
420		case SNP_TTYCLOSE:
421			if (opt_reconn_close)
422				attach_snp();
423			else if (opt_interactive && !opt_no_switch) {
424				ask_dev(dev_name, MSG_CLOSED);
425				set_dev(dev_name);
426			} else
427				cleanup(-1);
428			break;
429		default:
430			if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) {
431				free(buf);
432				if (!(buf = (char *) malloc(b_size / 2)))
433					fatal(EX_UNAVAILABLE, "malloc failed");
434				b_size = b_size / 2;
435			}
436			if (nread > b_size) {
437				b_size = (nread % 2) ? (nread + 1) : (nread);
438				free(buf);
439				if (!(buf = (char *) malloc(b_size)))
440					fatal(EX_UNAVAILABLE, "malloc failed");
441			}
442			rv = read(snp_io, buf, nread);
443			if (rv == -1 || rv != nread)
444				fatal(EX_IOERR, "read failed");
445			rv = write(std_out, buf, nread);
446			if (rv == -1 || rv != nread)
447				fatal(EX_IOERR, "write failed");
448		}
449	}			/* While */
450	return(0);
451}
452
453