16521Sugen/*
26521Sugen * Copyright (c) 1995 Ugen J.S.Antsilevich
36521Sugen *
46521Sugen * Redistribution and use in source forms, with and without modification,
56521Sugen * are permitted provided that this entire comment appears intact.
66521Sugen *
76521Sugen * Redistribution in binary form may occur without any restrictions.
86521Sugen * Obviously, it would be nice if you gave credit where credit is due
96521Sugen * but requiring it would be too onerous.
106521Sugen *
116521Sugen * This software is provided ``AS IS'' without any warranties of any kind.
126521Sugen *
136521Sugen * Snoop stuff.
146521Sugen */
156521Sugen
16114601Sobrien#include <sys/cdefs.h>
17114601Sobrien__FBSDID("$FreeBSD$");
1830773Scharnier
1979759Sdd#include <sys/param.h>
2024035Sbde#include <sys/fcntl.h>
2124035Sbde#include <sys/filio.h>
2224035Sbde#include <sys/snoop.h>
2324035Sbde#include <sys/stat.h>
2483249Sdd#include <sys/linker.h>
2583249Sdd#include <sys/module.h>
2624035Sbde
2730773Scharnier#include <err.h>
28133419Scsjp#include <errno.h>
2924035Sbde#include <locale.h>
3069793Sobrien#include <paths.h>
3124035Sbde#include <signal.h>
326521Sugen#include <stdio.h>
336521Sugen#include <stdlib.h>
346775Sugen#include <string.h>
3524035Sbde#include <sysexits.h>
369895Sache#include <termcap.h>
3723837Sroberto#include <termios.h>
3891229Sbde#include <time.h>
3924035Sbde#include <unistd.h>
406521Sugen
416521Sugen#define MSG_INIT	"Snoop started."
426713Spst#define MSG_OFLOW	"Snoop stopped due to overflow. Reconnecting."
436713Spst#define MSG_CLOSED	"Snoop stopped due to tty close. Reconnecting."
446521Sugen#define MSG_CHANGE	"Snoop device change by user request."
456775Sugen#define MSG_NOWRITE	"Snoop device change due to write failure."
466521Sugen
476713Spst#define DEV_NAME_LEN	1024	/* for /dev/ttyXX++ */
486521Sugen#define MIN_SIZE	256
496521Sugen
506521Sugen#define CHR_SWITCH	24	/* Ctrl+X	 */
516521Sugen#define CHR_CLEAR	23	/* Ctrl+V	 */
526521Sugen
5398140Simpstatic void	clear(void);
5498140Simpstatic void	timestamp(const char *);
5598140Simpstatic void	set_tty(void);
5698140Simpstatic void	unset_tty(void);
5798140Simpstatic void	fatal(int, const char *);
5898140Simpstatic int	open_snp(void);
5998140Simpstatic void	cleanup(int);
6098140Simpstatic void	usage(void) __dead2;
6198140Simpstatic void	setup_scr(void);
6298140Simpstatic void	attach_snp(void);
6398140Simpstatic void	detach_snp(void);
6498140Simpstatic void	set_dev(const char *);
6598140Simpstatic void	ask_dev(char *, const char *);
666521Sugen
67232780Sedint		opt_reconn_close = 0;
68232780Sedint		opt_reconn_oflow = 0;
69232780Sedint		opt_interactive = 1;
70232780Sedint		opt_timestamp = 0;
716775Sugenint		opt_write = 0;
7220085Sjkhint		opt_no_switch = 0;
7386858Sddconst char	*opt_snpdev;
746521Sugen
75232780Sedchar		dev_name[DEV_NAME_LEN];
76232780Sedint		snp_io;
77232780Sedint		std_in = 0, std_out = 1;
786521Sugen
79232780Sedint		clear_ok = 0;
80232780Sedstruct termios	otty;
81232780Sedchar		tbuf[1024], gbuf[1024];
826521Sugen
8379759Sddstatic void
8498140Simpclear(void)
856521Sugen{
86232780Sed
876521Sugen	if (clear_ok)
8879759Sdd		tputs(gbuf, 1, putchar);
896521Sugen	fflush(stdout);
906521Sugen}
916521Sugen
9279759Sddstatic void
9398140Simptimestamp(const char *buf)
946521Sugen{
95232780Sed	time_t		t;
96232780Sed	char		btmp[1024];
97232780Sed
986521Sugen	clear();
996521Sugen	printf("\n---------------------------------------------\n");
1006521Sugen	t = time(NULL);
1016521Sugen	strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t));
1026521Sugen	printf("%s\n", btmp);
1036521Sugen	printf("%s\n", buf);
1046521Sugen	printf("---------------------------------------------\n");
1056521Sugen	fflush(stdout);
1066521Sugen}
1076521Sugen
10879759Sddstatic void
10998140Simpset_tty(void)
1106521Sugen{
111232780Sed	struct termios	ntty;
11212887Sjkh
11323837Sroberto	ntty = otty;
114232780Sed	ntty.c_lflag &= ~ICANON;	/* disable canonical operation */
11523837Sroberto	ntty.c_lflag &= ~ECHO;
11623837Sroberto#ifdef FLUSHO
11723837Sroberto	ntty.c_lflag &= ~FLUSHO;
11823837Sroberto#endif
11923837Sroberto#ifdef PENDIN
12023837Sroberto	ntty.c_lflag &= ~PENDIN;
12123837Sroberto#endif
12223837Sroberto#ifdef IEXTEN
12323837Sroberto	ntty.c_lflag &= ~IEXTEN;
12423837Sroberto#endif
125232780Sed	ntty.c_cc[VMIN] = 1;		/* minimum of one character */
126232780Sed	ntty.c_cc[VTIME] = 0;		/* timeout value */
12723837Sroberto
128232780Sed	ntty.c_cc[VINTR] = 07;		/* ^G */
129232780Sed	ntty.c_cc[VQUIT] = 07;		/* ^G */
130232781Sed	tcsetattr(std_in, TCSANOW, &ntty);
1316521Sugen}
1326521Sugen
13379759Sddstatic void
13498140Simpunset_tty(void)
1356521Sugen{
136232781Sed
137232781Sed	tcsetattr(std_in, TCSANOW, &otty);
1386521Sugen}
1396521Sugen
14079759Sddstatic void
14198140Simpfatal(int error, const char *buf)
1426521Sugen{
143232781Sed
1446521Sugen	unset_tty();
1456521Sugen	if (buf)
14679759Sdd		errx(error, "fatal: %s", buf);
14730773Scharnier	else
14879759Sdd		exit(error);
1496521Sugen}
1506521Sugen
15179759Sddstatic int
15298140Simpopen_snp(void)
1536521Sugen{
154181755Sed	int		f, mode;
1556775Sugen
1566775Sugen	if (opt_write)
1576775Sugen		mode = O_RDWR;
1586775Sugen	else
1596775Sugen		mode = O_RDONLY;
1606775Sugen
16186858Sdd	if (opt_snpdev == NULL)
162181755Sed		f = open(_PATH_DEV "snp", mode);
16386858Sdd	else
164181755Sed		f = open(opt_snpdev, mode);
165181755Sed	if (f == -1)
166181755Sed		fatal(EX_OSFILE, "cannot open snoop device");
167181755Sed
168181755Sed	return (f);
1696521Sugen}
1706521Sugen
17179759Sddstatic void
17298140Simpcleanup(int signo __unused)
1736521Sugen{
174232781Sed
1756521Sugen	if (opt_timestamp)
1766521Sugen		timestamp("Logging Exited.");
1776521Sugen	close(snp_io);
1786521Sugen	unset_tty();
17923837Sroberto	exit(EX_OK);
1806521Sugen}
1816521Sugen
18230773Scharnierstatic void
18398140Simpusage(void)
1846521Sugen{
185232781Sed
18630773Scharnier	fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n");
18723837Sroberto	exit(EX_USAGE);
1886521Sugen}
1896521Sugen
19079759Sddstatic void
19198140Simpsetup_scr(void)
1926521Sugen{
193232780Sed	char		*cbuf = gbuf, *term;
194232780Sed
1956521Sugen	if (!opt_interactive)
1966521Sugen		return;
1976521Sugen	if ((term = getenv("TERM")))
1986521Sugen		if (tgetent(tbuf, term) == 1)
1996521Sugen			if (tgetstr("cl", &cbuf))
2006521Sugen				clear_ok = 1;
2019895Sache	set_tty();
2026521Sugen	clear();
2036521Sugen}
2046521Sugen
20579759Sddstatic void
20698140Simpdetach_snp(void)
2076521Sugen{
208150322Sru	int		fd;
2096713Spst
210150322Sru	fd = -1;
211150322Sru	ioctl(snp_io, SNPSTTY, &fd);
2126521Sugen}
2136521Sugen
21479759Sddstatic void
21598140Simpattach_snp(void)
2166521Sugen{
217150417Scognet	int		snp_tty;
218150417Scognet
219150417Scognet	snp_tty = open(dev_name, O_RDONLY | O_NONBLOCK);
220150417Scognet	if (snp_tty < 0)
221150417Scognet		fatal(EX_DATAERR, "can't open device");
2226521Sugen	if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
22330773Scharnier		fatal(EX_UNAVAILABLE, "cannot attach to tty");
224150417Scognet	close(snp_tty);
2256521Sugen	if (opt_timestamp)
2266521Sugen		timestamp("Logging Started.");
2276521Sugen}
2286521Sugen
22979759Sddstatic void
23098140Simpset_dev(const char *name)
2316521Sugen{
232232780Sed	char		buf[DEV_NAME_LEN];
2336713Spst	struct stat	sb;
2346521Sugen
23569793Sobrien	if (strlen(name) > 5 && !strncmp(name, _PATH_DEV, sizeof _PATH_DEV - 1)) {
23623837Sroberto		snprintf(buf, sizeof buf, "%s", name);
23798140Simp	} else {
2386775Sugen		if (strlen(name) == 2)
23969793Sobrien			sprintf(buf, "%s%s", _PATH_TTY, name);
2406775Sugen		else
24169793Sobrien			sprintf(buf, "%s%s", _PATH_DEV, name);
2426775Sugen	}
2436521Sugen
24423837Sroberto	if (*name == '\0' || stat(buf, &sb) < 0)
24530773Scharnier		fatal(EX_DATAERR, "bad device name");
2466521Sugen
24723837Sroberto	if ((sb.st_mode & S_IFMT) != S_IFCHR)
24830773Scharnier		fatal(EX_DATAERR, "must be a character device");
24923837Sroberto
250150417Scognet	strncpy(dev_name, buf, DEV_NAME_LEN);
251150417Scognet
2526521Sugen	attach_snp();
2536521Sugen}
2546521Sugen
2556521Sugenvoid
25698140Simpask_dev(char *dbuf, const char *msg)
2576521Sugen{
258232780Sed	char		buf[DEV_NAME_LEN];
259232780Sed	int		len;
2606521Sugen
2616521Sugen	clear();
2626521Sugen	unset_tty();
2636521Sugen
2646521Sugen	if (msg)
2656521Sugen		printf("%s\n", msg);
26679759Sdd	if (dbuf)
26779759Sdd		printf("Enter device name [%s]:", dbuf);
2686521Sugen	else
2696521Sugen		printf("Enter device name:");
2706521Sugen
2716521Sugen	if (fgets(buf, DEV_NAME_LEN - 1, stdin)) {
2726521Sugen		len = strlen(buf);
2736521Sugen		if (buf[len - 1] == '\n')
2746521Sugen			buf[len - 1] = '\0';
2756521Sugen		if (buf[0] != '\0' && buf[0] != ' ')
27679759Sdd			strcpy(dbuf, buf);
2776521Sugen	}
2786521Sugen	set_tty();
2796521Sugen}
2806521Sugen
2816775Sugen#define READB_LEN	5
2826521Sugen
28351287Speterint
28498140Simpmain(int ac, char *av[])
2856521Sugen{
286232780Sed	int		ch, res, rv, nread;
287106769Stmm	size_t		b_size = MIN_SIZE;
288232780Sed	char		*buf, chb[READB_LEN];
289232780Sed	fd_set		fd_s;
2906521Sugen
29111828Sache	(void) setlocale(LC_TIME, "");
29211828Sache
2936521Sugen	if (isatty(std_out))
2946521Sugen		opt_interactive = 1;
2956521Sugen	else
2966521Sugen		opt_interactive = 0;
2976521Sugen
29886858Sdd	while ((ch = getopt(ac, av, "Wciotnf:")) != -1)
2996521Sugen		switch (ch) {
3006775Sugen		case 'W':
3016775Sugen			opt_write = 1;
3026775Sugen			break;
3036521Sugen		case 'c':
3046521Sugen			opt_reconn_close = 1;
3056521Sugen			break;
3066521Sugen		case 'i':
3076521Sugen			opt_interactive = 1;
3086521Sugen			break;
3096521Sugen		case 'o':
3106521Sugen			opt_reconn_oflow = 1;
3116521Sugen			break;
3126521Sugen		case 't':
3136521Sugen			opt_timestamp = 1;
3146521Sugen			break;
31520085Sjkh		case 'n':
31620085Sjkh			opt_no_switch = 1;
31720085Sjkh			break;
31886858Sdd		case 'f':
31986858Sdd			opt_snpdev = optarg;
32086858Sdd			break;
3216521Sugen		case '?':
3226521Sugen		default:
32330773Scharnier			usage();
3246521Sugen		}
3256521Sugen
326255261Sjilles	tcgetattr(std_in, &otty);
327255261Sjilles
32883249Sdd	if (modfind("snp") == -1)
32983249Sdd		if (kldload("snp") == -1 || modfind("snp") == -1)
33083249Sdd			warn("snp module not available");
33183249Sdd
3326521Sugen	signal(SIGINT, cleanup);
3336521Sugen
334133419Scsjp	snp_io = open_snp();
3356521Sugen	setup_scr();
3366521Sugen
3376521Sugen	if (*(av += optind) == NULL) {
33820085Sjkh		if (opt_interactive && !opt_no_switch)
3396521Sugen			ask_dev(dev_name, MSG_INIT);
3406521Sugen		else
34130773Scharnier			fatal(EX_DATAERR, "no device name given");
3426521Sugen	} else
3436521Sugen		strncpy(dev_name, *av, DEV_NAME_LEN);
3446521Sugen
3456521Sugen	set_dev(dev_name);
3466521Sugen
3476521Sugen	if (!(buf = (char *) malloc(b_size)))
34830773Scharnier		fatal(EX_UNAVAILABLE, "malloc failed");
3496521Sugen
3506521Sugen	FD_ZERO(&fd_s);
3516521Sugen
352232781Sed	for (;;) {
3536521Sugen		if (opt_interactive)
3546521Sugen			FD_SET(std_in, &fd_s);
3556521Sugen		FD_SET(snp_io, &fd_s);
3566521Sugen		res = select(snp_io + 1, &fd_s, NULL, NULL, NULL);
3576521Sugen		if (opt_interactive && FD_ISSET(std_in, &fd_s)) {
3586775Sugen
3596775Sugen			if ((res = ioctl(std_in, FIONREAD, &nread)) != 0)
36030773Scharnier				fatal(EX_OSERR, "ioctl(FIONREAD)");
3616775Sugen			if (nread > READB_LEN)
3626775Sugen				nread = READB_LEN;
36379759Sdd			rv = read(std_in, chb, nread);
364106769Stmm			if (rv == -1 || rv != nread)
36530773Scharnier				fatal(EX_IOERR, "read (stdin) failed");
3666775Sugen
3676775Sugen			switch (chb[0]) {
3686521Sugen			case CHR_CLEAR:
3696521Sugen				clear();
3706521Sugen				break;
3716521Sugen			case CHR_SWITCH:
37286169Srwatson				if (!opt_no_switch) {
37386169Srwatson					detach_snp();
37486169Srwatson					ask_dev(dev_name, MSG_CHANGE);
37586169Srwatson					set_dev(dev_name);
37620085Sjkh					break;
37786169Srwatson				}
3786521Sugen			default:
3796775Sugen				if (opt_write) {
38079759Sdd					rv = write(snp_io, chb, nread);
381106769Stmm					if (rv == -1 || rv != nread) {
3826775Sugen						detach_snp();
38320085Sjkh						if (opt_no_switch)
38498140Simp							fatal(EX_IOERR,
385232780Sed							    "write failed");
3866775Sugen						ask_dev(dev_name, MSG_NOWRITE);
3876775Sugen						set_dev(dev_name);
3886775Sugen					}
3896775Sugen				}
3908857Srgrimes
3916521Sugen			}
3926521Sugen		}
3936521Sugen		if (!FD_ISSET(snp_io, &fd_s))
3946521Sugen			continue;
3956521Sugen
396106769Stmm		if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0)
39730773Scharnier			fatal(EX_OSERR, "ioctl(FIONREAD)");
3986521Sugen
399106769Stmm		switch (nread) {
4006521Sugen		case SNP_OFLOW:
4016521Sugen			if (opt_reconn_oflow)
4026521Sugen				attach_snp();
40320085Sjkh			else if (opt_interactive && !opt_no_switch) {
4046521Sugen				ask_dev(dev_name, MSG_OFLOW);
4056521Sugen				set_dev(dev_name);
4066521Sugen			} else
40779759Sdd				cleanup(-1);
408101636Smikeh			break;
4096521Sugen		case SNP_DETACH:
4106521Sugen		case SNP_TTYCLOSE:
4116521Sugen			if (opt_reconn_close)
4126521Sugen				attach_snp();
41320085Sjkh			else if (opt_interactive && !opt_no_switch) {
4146521Sugen				ask_dev(dev_name, MSG_CLOSED);
4156521Sugen				set_dev(dev_name);
4166521Sugen			} else
41779759Sdd				cleanup(-1);
418101636Smikeh			break;
4196521Sugen		default:
4206521Sugen			if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) {
4216521Sugen				free(buf);
4226521Sugen				if (!(buf = (char *) malloc(b_size / 2)))
42330773Scharnier					fatal(EX_UNAVAILABLE, "malloc failed");
4246521Sugen				b_size = b_size / 2;
4256521Sugen			}
4266521Sugen			if (nread > b_size) {
4276521Sugen				b_size = (nread % 2) ? (nread + 1) : (nread);
4286521Sugen				free(buf);
4296521Sugen				if (!(buf = (char *) malloc(b_size)))
43030773Scharnier					fatal(EX_UNAVAILABLE, "malloc failed");
4316521Sugen			}
43279759Sdd			rv = read(snp_io, buf, nread);
433106769Stmm			if (rv == -1 || rv != nread)
43430773Scharnier				fatal(EX_IOERR, "read failed");
43579759Sdd			rv = write(std_out, buf, nread);
436106769Stmm			if (rv == -1 || rv != nread)
43730773Scharnier				fatal(EX_IOERR, "write failed");
4386521Sugen		}
4396521Sugen	}			/* While */
44056489Scharnier	return(0);
4416521Sugen}
442