watch.c revision 6521
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
166521Sugen#include <stdio.h>
176521Sugen#include <unistd.h>
186521Sugen#include <stdlib.h>
196521Sugen#include <signal.h>
206521Sugen#include <sys/types.h>
216521Sugen#include <sys/time.h>
226521Sugen#include <sys/select.h>
236521Sugen#include <sys/fcntl.h>
246521Sugen#include <sys/ioctl.h>
256521Sugen#include <sys/ioctl_compat.h>
266521Sugen#include <sys/snoop.h>
276521Sugen
286521Sugen
296521Sugen#define MSG_INIT	"Snoop started."
306521Sugen#define MSG_OFLOW	"Snoop stopped due to overflow.Reconnecting."
316521Sugen#define MSG_CLOSED	"Snoop stopped due to tty close.Reconnecting."
326521Sugen#define MSG_CHANGE	"Snoop device change by user request."
336521Sugen
346521Sugen
356521Sugen#define DEV_NAME_LEN	12	/* for /dev/ttyXX++ */
366521Sugen#define MIN_SIZE	256
376521Sugen
386521Sugen#define CHR_SWITCH	24	/* Ctrl+X	 */
396521Sugen#define CHR_CLEAR	23	/* Ctrl+V	 */
406521Sugen
416521Sugen
426521Sugenint             opt_reconn_close = 0;
436521Sugenint             opt_reconn_oflow = 0;
446521Sugenint             opt_interactive = 1;
456521Sugenint             opt_timestamp = 0;
466521Sugen
476521Sugenchar            dev_name[DEV_NAME_LEN];
486521Sugenint             snp_io;
496521Sugenstruct snptty   snp_tty;
506521Sugenint             std_in = 0, std_out = 1;
516521Sugen
526521Sugen
536521Sugenint             clear_ok = 0;
546521Sugenstruct sgttyb   sgo;
556521Sugenchar            tbuf[1024], buf[1024];
566521Sugen
576521Sugen
586521Sugenvoid
596521Sugenclear()
606521Sugen{
616521Sugen	if (clear_ok)
626521Sugen		tputs(buf, 1, putchar);
636521Sugen	fflush(stdout);
646521Sugen}
656521Sugen
666521Sugenvoid
676521Sugentimestamp(buf)
686521Sugen	char           *buf;
696521Sugen{
706521Sugen	time_t          t;
716521Sugen	char            btmp[1024];
726521Sugen	clear();
736521Sugen	printf("\n---------------------------------------------\n");
746521Sugen	t = time(NULL);
756521Sugen	strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t));
766521Sugen	printf("%s\n", btmp);
776521Sugen	printf("%s\n", buf);
786521Sugen	printf("---------------------------------------------\n");
796521Sugen	fflush(stdout);
806521Sugen}
816521Sugen
826521Sugenvoid
836521Sugenset_tty()
846521Sugen{
856521Sugen	struct sgttyb   sgn;
866521Sugen	ioctl(std_in, TIOCGETP, &sgo);
876521Sugen	/* bcopy(&sgn, &sgo, sizeof(struct sgttyb)); */
886521Sugen	sgn = sgo;
896521Sugen	sgn.sg_flags |= CBREAK;
906521Sugen	sgn.sg_flags &= ~ECHO;
916521Sugen	ioctl(std_in, TIOCSETP, &sgn);
926521Sugen}
936521Sugen
946521Sugenvoid
956521Sugenunset_tty()
966521Sugen{
976521Sugen	ioctl(std_in, TIOCSETP, &sgo);
986521Sugen}
996521Sugen
1006521Sugen
1016521Sugenvoid
1026521Sugenfatal(buf)
1036521Sugen	char           *buf;
1046521Sugen{
1056521Sugen	unset_tty();
1066521Sugen	if (buf)
1076521Sugen		fprintf(stderr, "Fatal: %s\n", buf);
1086521Sugen	exit(1);
1096521Sugen}
1106521Sugen
1116521Sugenint
1126521Sugenopen_snp()
1136521Sugen{
1146521Sugen	char            snp[DEV_NAME_LEN] = "/dev/snpX";
1156521Sugen	char            c;
1166521Sugen	int             f;
1176521Sugen	for (c = '0'; c <= '9'; c++) {
1186521Sugen		snp[8] = c;
1196521Sugen		if ((f = open(snp, O_RDONLY)) < 0)
1206521Sugen			continue;
1216521Sugen		return f;
1226521Sugen	}
1236521Sugen	fatal("Cannot open snoop device.");
1246521Sugen}
1256521Sugen
1266521Sugen
1276521Sugenvoid
1286521Sugencleanup()
1296521Sugen{
1306521Sugen	if (opt_timestamp)
1316521Sugen		timestamp("Logging Exited.");
1326521Sugen	close(snp_io);
1336521Sugen	unset_tty();
1346521Sugen	exit(0);
1356521Sugen}
1366521Sugen
1376521Sugen
1386521Sugenvoid
1396521Sugenshow_usage()
1406521Sugen{
1416521Sugen	printf("watch -[ciot] [tty name]\n");
1426521Sugen	exit(1);
1436521Sugen}
1446521Sugen
1456521Sugenvoid
1466521Sugensetup_scr()
1476521Sugen{
1486521Sugen	char           *cbuf = buf, *term;
1496521Sugen	if (!opt_interactive)
1506521Sugen		return;
1516521Sugen	if ((term = getenv("TERM")))
1526521Sugen		if (tgetent(tbuf, term) == 1)
1536521Sugen			if (tgetstr("cl", &cbuf))
1546521Sugen				clear_ok = 1;
1556521Sugen	clear();
1566521Sugen	set_tty();
1576521Sugen}
1586521Sugen
1596521Sugen
1606521Sugenint
1616521Sugenctoh(c)
1626521Sugen	char            c;
1636521Sugen{
1646521Sugen	if (c >= '0' && c <= '9')
1656521Sugen		return (int) (c - '0');
1666521Sugen
1676521Sugen	if (c >= 'a' && c <= 'f')
1686521Sugen		return (int) (c - 'a' + 10);
1696521Sugen
1706521Sugen	fatal("Bad tty number.");
1716521Sugen}
1726521Sugen
1736521Sugen
1746521Sugenvoid
1756521Sugendetach_snp()
1766521Sugen{
1776521Sugen	struct snptty   st;
1786521Sugen	st.st_type = -1;
1796521Sugen	st.st_unit = -1;
1806521Sugen	ioctl(snp_io, SNPSTTY, &st);
1816521Sugen}
1826521Sugen
1836521Sugenvoid
1846521Sugenattach_snp()
1856521Sugen{
1866521Sugen	if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
1876521Sugen		fatal("Cannot attach to tty.");
1886521Sugen	if (opt_timestamp)
1896521Sugen		timestamp("Logging Started.");
1906521Sugen}
1916521Sugen
1926521Sugen
1936521Sugenvoid
1946521Sugenset_dev(name)
1956521Sugen	char           *name;
1966521Sugen{
1976521Sugen	char            buf[DEV_NAME_LEN], num[DEV_NAME_LEN];
1986521Sugen	int             unitbase = 0;
1996521Sugen
2006521Sugen	if (strlen(name) > 5 && !strncmp(name, "/dev/", 5))
2016521Sugen		strcpy(buf, &(name[5]));
2026521Sugen	else
2036521Sugen		strcpy(buf, name);
2046521Sugen
2056521Sugen	if (strlen(buf) < 4)
2066521Sugen		fatal("Bad tty name.");
2076521Sugen
2086521Sugen	if (!strncmp(buf, "tty", 3))
2096521Sugen		switch (buf[3]) {
2106521Sugen		case 'v':
2116521Sugen			snp_tty.st_unit = ctoh(buf[4]);
2126521Sugen			snp_tty.st_type = ST_VTY;
2136521Sugen			goto got_num;
2146521Sugen		case 'r':
2156521Sugen			unitbase += 16;
2166521Sugen		case 'q':
2176521Sugen			unitbase += 16;
2186521Sugen		case 'p':
2196521Sugen			snp_tty.st_unit = ctoh(buf[4]) + unitbase;
2206521Sugen			snp_tty.st_type = ST_PTY;
2216521Sugen			goto got_num;
2226521Sugen		case '0':
2236521Sugen		case 'd':
2246521Sugen			snp_tty.st_unit = ctoh(buf[4]);
2256521Sugen			snp_tty.st_type = ST_SIO;
2266521Sugen			goto got_num;
2276521Sugen		default:
2286521Sugen			fatal("Bad tty name.");
2296521Sugen
2306521Sugen		}
2316521Sugen
2326521Sugen
2336521Sugen	if (!strncmp(buf, "vty", 3)) {
2346521Sugen		strcpy(num, &(buf[3]));
2356521Sugen		snp_tty.st_unit = atoi(num);
2366521Sugen		snp_tty.st_type = ST_VTY;
2376521Sugen		goto got_num;
2386521Sugen	}
2396521Sugen	if (!strncmp(buf, "pty", 3)) {
2406521Sugen		strcpy(num, &(buf[3]));
2416521Sugen		snp_tty.st_unit = atoi(num);
2426521Sugen		snp_tty.st_type = ST_PTY;
2436521Sugen		goto got_num;
2446521Sugen	}
2456521Sugen	if (!strncmp(buf, "sio", 3) || !strncmp(buf, "cua", 3)) {
2466521Sugen		strcpy(num, &(buf[3]));
2476521Sugen		snp_tty.st_unit = atoi(num);
2486521Sugen		snp_tty.st_type = ST_SIO;
2496521Sugen		goto got_num;
2506521Sugen	}
2516521Sugen	fatal("Bad tty name.");
2526521Sugengot_num:
2536521Sugen	attach_snp();
2546521Sugen}
2556521Sugen
2566521Sugenvoid
2576521Sugenask_dev(dev_name, msg)
2586521Sugen	char           *dev_name, *msg;
2596521Sugen{
2606521Sugen	char            buf[DEV_NAME_LEN];
2616521Sugen	int             len;
2626521Sugen
2636521Sugen	clear();
2646521Sugen	unset_tty();
2656521Sugen
2666521Sugen	if (msg)
2676521Sugen		printf("%s\n", msg);
2686521Sugen	if (dev_name)
2696521Sugen		printf("Enter device name [%s]:", dev_name);
2706521Sugen	else
2716521Sugen		printf("Enter device name:");
2726521Sugen
2736521Sugen	if (fgets(buf, DEV_NAME_LEN - 1, stdin)) {
2746521Sugen		len = strlen(buf);
2756521Sugen		if (buf[len - 1] == '\n')
2766521Sugen			buf[len - 1] = '\0';
2776521Sugen		if (buf[0] != '\0' && buf[0] != ' ')
2786521Sugen			strcpy(dev_name, buf);
2796521Sugen	}
2806521Sugen	set_tty();
2816521Sugen}
2826521Sugen
2836521Sugen
2846521Sugenvoid
2856521Sugenmain(ac, av)
2866521Sugen	int             ac;
2876521Sugen	char          **av;
2886521Sugen{
2896521Sugen	int             res, nread, b_size = MIN_SIZE;
2906521Sugen	extern int      optind;
2916521Sugen	char            ch, *buf;
2926521Sugen	fd_set          fd_s;
2936521Sugen
2946521Sugen	if (getuid() != 0)
2956521Sugen		fatal(NULL);
2966521Sugen
2976521Sugen	if (isatty(std_out))
2986521Sugen		opt_interactive = 1;
2996521Sugen	else
3006521Sugen		opt_interactive = 0;
3016521Sugen
3026521Sugen
3036521Sugen	while ((ch = getopt(ac, av, "ciot")) != EOF)
3046521Sugen		switch (ch) {
3056521Sugen		case 'c':
3066521Sugen			opt_reconn_close = 1;
3076521Sugen			break;
3086521Sugen		case 'i':
3096521Sugen			opt_interactive = 1;
3106521Sugen			break;
3116521Sugen		case 'o':
3126521Sugen			opt_reconn_oflow = 1;
3136521Sugen			break;
3146521Sugen		case 't':
3156521Sugen			opt_timestamp = 1;
3166521Sugen			break;
3176521Sugen		case '?':
3186521Sugen		default:
3196521Sugen			show_usage();
3206521Sugen			exit(1);
3216521Sugen		}
3226521Sugen
3236521Sugen	signal(SIGINT, cleanup);
3246521Sugen
3256521Sugen	setup_scr();
3266521Sugen	snp_io = open_snp();
3276521Sugen
3286521Sugen	if (*(av += optind) == NULL) {
3296521Sugen		if (opt_interactive)
3306521Sugen			ask_dev(dev_name, MSG_INIT);
3316521Sugen		else
3326521Sugen			fatal("No device name given.");
3336521Sugen	} else
3346521Sugen		strncpy(dev_name, *av, DEV_NAME_LEN);
3356521Sugen
3366521Sugen	set_dev(dev_name);
3376521Sugen
3386521Sugen	if (!(buf = (char *) malloc(b_size)))
3396521Sugen		fatal("Cannot malloc().");
3406521Sugen
3416521Sugen	FD_ZERO(&fd_s);
3426521Sugen
3436521Sugen	while (1) {
3446521Sugen		if (opt_interactive)
3456521Sugen			FD_SET(std_in, &fd_s);
3466521Sugen		FD_SET(snp_io, &fd_s);
3476521Sugen		res = select(snp_io + 1, &fd_s, NULL, NULL, NULL);
3486521Sugen		if (opt_interactive && FD_ISSET(std_in, &fd_s)) {
3496521Sugen			switch (ch = getchar()) {
3506521Sugen			case CHR_CLEAR:
3516521Sugen				clear();
3526521Sugen				break;
3536521Sugen			case CHR_SWITCH:
3546521Sugen				/* detach_snp(); */
3556521Sugen				ask_dev(dev_name, MSG_CHANGE);
3566521Sugen				set_dev(dev_name);
3576521Sugen				break;
3586521Sugen			default:
3596521Sugen			}
3606521Sugen		}
3616521Sugen		if (!FD_ISSET(snp_io, &fd_s))
3626521Sugen			continue;
3636521Sugen
3646521Sugen		if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0)
3656521Sugen			fatal("ioctl() failed.");
3666521Sugen
3676521Sugen		switch (nread) {
3686521Sugen		case SNP_OFLOW:
3696521Sugen			if (opt_reconn_oflow)
3706521Sugen				attach_snp();
3716521Sugen			else if (opt_interactive) {
3726521Sugen				ask_dev(dev_name, MSG_OFLOW);
3736521Sugen				set_dev(dev_name);
3746521Sugen			} else
3756521Sugen				cleanup();
3766521Sugen		case SNP_DETACH:
3776521Sugen		case SNP_TTYCLOSE:
3786521Sugen			if (opt_reconn_close)
3796521Sugen				attach_snp();
3806521Sugen			else if (opt_interactive) {
3816521Sugen				ask_dev(dev_name, MSG_CLOSED);
3826521Sugen				set_dev(dev_name);
3836521Sugen			} else
3846521Sugen				cleanup();
3856521Sugen		default:
3866521Sugen			if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) {
3876521Sugen				free(buf);
3886521Sugen				if (!(buf = (char *) malloc(b_size / 2)))
3896521Sugen					fatal("Cannot malloc()");
3906521Sugen				b_size = b_size / 2;
3916521Sugen			}
3926521Sugen			if (nread > b_size) {
3936521Sugen				b_size = (nread % 2) ? (nread + 1) : (nread);
3946521Sugen				free(buf);
3956521Sugen				if (!(buf = (char *) malloc(b_size)))
3966521Sugen					fatal("Cannot malloc()");
3976521Sugen			}
3986521Sugen			if (read(snp_io, buf, nread) < nread)
3996521Sugen				fatal("read failed.");
4006521Sugen			if (write(std_out, buf, nread) < nread)
4016521Sugen				fatal("write failed.");
4026521Sugen		}
4036521Sugen	}			/* While */
4046521Sugen}
405