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