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