watch.c revision 98140
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 1630773Scharnier#ifndef lint 1730773Scharnierstatic const char rcsid[] = 1850479Speter "$FreeBSD: head/usr.sbin/watch/watch.c 98140 2002-06-12 04:28:15Z imp $"; 1930773Scharnier#endif /* not lint */ 2030773Scharnier 2179759Sdd#include <sys/param.h> 2224035Sbde#include <sys/fcntl.h> 2324035Sbde#include <sys/filio.h> 2424035Sbde#include <sys/snoop.h> 2524035Sbde#include <sys/stat.h> 2683249Sdd#include <sys/linker.h> 2783249Sdd#include <sys/module.h> 2824035Sbde 2930773Scharnier#include <err.h> 3024035Sbde#include <locale.h> 3169793Sobrien#include <paths.h> 3224035Sbde#include <signal.h> 336521Sugen#include <stdio.h> 346521Sugen#include <stdlib.h> 356775Sugen#include <string.h> 3624035Sbde#include <sysexits.h> 379895Sache#include <termcap.h> 3823837Sroberto#include <termios.h> 3991229Sbde#include <time.h> 4024035Sbde#include <unistd.h> 416521Sugen 426521Sugen#define MSG_INIT "Snoop started." 436713Spst#define MSG_OFLOW "Snoop stopped due to overflow. Reconnecting." 446713Spst#define MSG_CLOSED "Snoop stopped due to tty close. Reconnecting." 456521Sugen#define MSG_CHANGE "Snoop device change by user request." 466775Sugen#define MSG_NOWRITE "Snoop device change due to write failure." 476521Sugen 486521Sugen 496713Spst#define DEV_NAME_LEN 1024 /* for /dev/ttyXX++ */ 506521Sugen#define MIN_SIZE 256 516521Sugen 526521Sugen#define CHR_SWITCH 24 /* Ctrl+X */ 536521Sugen#define CHR_CLEAR 23 /* Ctrl+V */ 546521Sugen 5598140Simpstatic void clear(void); 5698140Simpstatic void timestamp(const char *); 5798140Simpstatic void set_tty(void); 5898140Simpstatic void unset_tty(void); 5998140Simpstatic void fatal(int, const char *); 6098140Simpstatic int open_snp(void); 6198140Simpstatic void cleanup(int); 6298140Simpstatic void usage(void) __dead2; 6398140Simpstatic void setup_scr(void); 6498140Simpstatic void attach_snp(void); 6598140Simpstatic void detach_snp(void); 6698140Simpstatic void set_dev(const char *); 6798140Simpstatic void ask_dev(char *, const char *); 686521Sugen 696521Sugenint opt_reconn_close = 0; 706521Sugenint opt_reconn_oflow = 0; 716521Sugenint opt_interactive = 1; 726521Sugenint opt_timestamp = 0; 736775Sugenint opt_write = 0; 7420085Sjkhint opt_no_switch = 0; 7586858Sddconst char *opt_snpdev; 766521Sugen 776521Sugenchar dev_name[DEV_NAME_LEN]; 786521Sugenint snp_io; 796713Spstdev_t snp_tty; 806521Sugenint std_in = 0, std_out = 1; 816521Sugen 826521Sugen 836521Sugenint clear_ok = 0; 8423837Srobertostruct termios otty; 8579759Sddchar tbuf[1024], gbuf[1024]; 866521Sugen 876521Sugen 8879759Sddstatic void 8998140Simpclear(void) 906521Sugen{ 916521Sugen if (clear_ok) 9279759Sdd tputs(gbuf, 1, putchar); 936521Sugen fflush(stdout); 946521Sugen} 956521Sugen 9679759Sddstatic void 9798140Simptimestamp(const char *buf) 986521Sugen{ 996521Sugen time_t t; 1006521Sugen char btmp[1024]; 1016521Sugen clear(); 1026521Sugen printf("\n---------------------------------------------\n"); 1036521Sugen t = time(NULL); 1046521Sugen strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t)); 1056521Sugen printf("%s\n", btmp); 1066521Sugen printf("%s\n", buf); 1076521Sugen printf("---------------------------------------------\n"); 1086521Sugen fflush(stdout); 1096521Sugen} 1106521Sugen 11179759Sddstatic void 11298140Simpset_tty(void) 1136521Sugen{ 11423837Sroberto struct termios ntty; 11512887Sjkh 11623837Sroberto tcgetattr (std_in, &otty); 11723837Sroberto ntty = otty; 11823837Sroberto ntty.c_lflag &= ~ICANON; /* disable canonical operation */ 11923837Sroberto ntty.c_lflag &= ~ECHO; 12023837Sroberto#ifdef FLUSHO 12123837Sroberto ntty.c_lflag &= ~FLUSHO; 12223837Sroberto#endif 12323837Sroberto#ifdef PENDIN 12423837Sroberto ntty.c_lflag &= ~PENDIN; 12523837Sroberto#endif 12623837Sroberto#ifdef IEXTEN 12723837Sroberto ntty.c_lflag &= ~IEXTEN; 12823837Sroberto#endif 12923837Sroberto ntty.c_cc[VMIN] = 1; /* minimum of one character */ 13023837Sroberto ntty.c_cc[VTIME] = 0; /* timeout value */ 13123837Sroberto 13223837Sroberto ntty.c_cc[VINTR] = 07; /* ^G */ 13323837Sroberto ntty.c_cc[VQUIT] = 07; /* ^G */ 13423837Sroberto tcsetattr (std_in, TCSANOW, &ntty); 1356521Sugen} 1366521Sugen 13779759Sddstatic void 13898140Simpunset_tty(void) 1396521Sugen{ 14023837Sroberto tcsetattr (std_in, TCSANOW, &otty); 1416521Sugen} 1426521Sugen 1436521Sugen 14479759Sddstatic void 14598140Simpfatal(int error, const char *buf) 1466521Sugen{ 1476521Sugen unset_tty(); 1486521Sugen if (buf) 14979759Sdd errx(error, "fatal: %s", buf); 15030773Scharnier else 15179759Sdd exit(error); 1526521Sugen} 1536521Sugen 15479759Sddstatic int 15598140Simpopen_snp(void) 1566521Sugen{ 15769793Sobrien char snp[] = {_PATH_DEV "snpX"}; 1586521Sugen char c; 1596775Sugen int f, mode; 1606775Sugen 1616775Sugen if (opt_write) 1626775Sugen mode = O_RDWR; 1636775Sugen else 1646775Sugen mode = O_RDONLY; 1656775Sugen 16686858Sdd if (opt_snpdev == NULL) 16786858Sdd for (c = '0'; c <= '9'; c++) { 16886858Sdd snp[8] = c; 16986858Sdd if ((f = open(snp, mode)) < 0) 17086858Sdd continue; 17186858Sdd return f; 17286858Sdd } 17386858Sdd else 17486858Sdd if ((f = open(opt_snpdev, mode)) != -1) 17586858Sdd return (f); 17630773Scharnier fatal(EX_OSFILE, "cannot open snoop device"); 17730773Scharnier return (0); 1786521Sugen} 1796521Sugen 1806521Sugen 18179759Sddstatic void 18298140Simpcleanup(int signo __unused) 1836521Sugen{ 1846521Sugen if (opt_timestamp) 1856521Sugen timestamp("Logging Exited."); 1866521Sugen close(snp_io); 1876521Sugen unset_tty(); 18823837Sroberto exit(EX_OK); 1896521Sugen} 1906521Sugen 1916521Sugen 19230773Scharnierstatic void 19398140Simpusage(void) 1946521Sugen{ 19530773Scharnier fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n"); 19623837Sroberto exit(EX_USAGE); 1976521Sugen} 1986521Sugen 19979759Sddstatic void 20098140Simpsetup_scr(void) 2016521Sugen{ 20279759Sdd char *cbuf = gbuf, *term; 2036521Sugen if (!opt_interactive) 2046521Sugen return; 2056521Sugen if ((term = getenv("TERM"))) 2066521Sugen if (tgetent(tbuf, term) == 1) 2076521Sugen if (tgetstr("cl", &cbuf)) 2086521Sugen clear_ok = 1; 2099895Sache set_tty(); 2106521Sugen clear(); 2116521Sugen} 2126521Sugen 21379759Sddstatic void 21498140Simpdetach_snp(void) 2156521Sugen{ 2166713Spst dev_t dev; 2176713Spst 21879759Sdd dev = NODEV; 2196713Spst ioctl(snp_io, SNPSTTY, &dev); 2206521Sugen} 2216521Sugen 22279759Sddstatic void 22398140Simpattach_snp(void) 2246521Sugen{ 2256521Sugen if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0) 22630773Scharnier fatal(EX_UNAVAILABLE, "cannot attach to tty"); 2276521Sugen if (opt_timestamp) 2286521Sugen timestamp("Logging Started."); 2296521Sugen} 2306521Sugen 2316521Sugen 23279759Sddstatic void 23398140Simpset_dev(const char *name) 2346521Sugen{ 2356713Spst char buf[DEV_NAME_LEN]; 2366713Spst struct stat sb; 2376521Sugen 23869793Sobrien if (strlen(name) > 5 && !strncmp(name, _PATH_DEV, sizeof _PATH_DEV - 1)) { 23923837Sroberto snprintf(buf, sizeof buf, "%s", name); 24098140Simp } else { 2416775Sugen if (strlen(name) == 2) 24269793Sobrien sprintf(buf, "%s%s", _PATH_TTY, name); 2436775Sugen else 24469793Sobrien sprintf(buf, "%s%s", _PATH_DEV, name); 2456775Sugen } 2466521Sugen 24723837Sroberto if (*name == '\0' || stat(buf, &sb) < 0) 24830773Scharnier fatal(EX_DATAERR, "bad device name"); 2496521Sugen 25023837Sroberto if ((sb.st_mode & S_IFMT) != S_IFCHR) 25130773Scharnier fatal(EX_DATAERR, "must be a character device"); 25223837Sroberto 2536713Spst snp_tty = sb.st_rdev; 2546521Sugen attach_snp(); 2556521Sugen} 2566521Sugen 2576521Sugenvoid 25898140Simpask_dev(char *dbuf, const char *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); 26879759Sdd if (dbuf) 26979759Sdd printf("Enter device name [%s]:", dbuf); 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] != ' ') 27879759Sdd strcpy(dbuf, buf); 2796521Sugen } 2806521Sugen set_tty(); 2816521Sugen} 2826521Sugen 2836775Sugen#define READB_LEN 5 2846521Sugen 28551287Speterint 28698140Simpmain(int ac, char *av[]) 2876521Sugen{ 28879759Sdd int res, idata, rv; 28979759Sdd size_t nread, b_size = MIN_SIZE; 2906775Sugen char ch, *buf, chb[READB_LEN]; 2916521Sugen fd_set fd_s; 2926521Sugen 29311828Sache (void) setlocale(LC_TIME, ""); 29411828Sache 2956521Sugen if (isatty(std_out)) 2966521Sugen opt_interactive = 1; 2976521Sugen else 2986521Sugen opt_interactive = 0; 2996521Sugen 3006521Sugen 30186858Sdd while ((ch = getopt(ac, av, "Wciotnf:")) != -1) 3026521Sugen switch (ch) { 3036775Sugen case 'W': 3046775Sugen opt_write = 1; 3056775Sugen break; 3066521Sugen case 'c': 3076521Sugen opt_reconn_close = 1; 3086521Sugen break; 3096521Sugen case 'i': 3106521Sugen opt_interactive = 1; 3116521Sugen break; 3126521Sugen case 'o': 3136521Sugen opt_reconn_oflow = 1; 3146521Sugen break; 3156521Sugen case 't': 3166521Sugen opt_timestamp = 1; 3176521Sugen break; 31820085Sjkh case 'n': 31920085Sjkh opt_no_switch = 1; 32020085Sjkh break; 32186858Sdd case 'f': 32286858Sdd opt_snpdev = optarg; 32386858Sdd break; 3246521Sugen case '?': 3256521Sugen default: 32630773Scharnier usage(); 3276521Sugen } 3286521Sugen 32983249Sdd if (modfind("snp") == -1) 33083249Sdd if (kldload("snp") == -1 || modfind("snp") == -1) 33183249Sdd warn("snp module not available"); 33283249Sdd 3336521Sugen signal(SIGINT, cleanup); 3346521Sugen 3356521Sugen setup_scr(); 3366521Sugen snp_io = open_snp(); 3376521Sugen 3386521Sugen if (*(av += optind) == NULL) { 33920085Sjkh if (opt_interactive && !opt_no_switch) 3406521Sugen ask_dev(dev_name, MSG_INIT); 3416521Sugen else 34230773Scharnier fatal(EX_DATAERR, "no device name given"); 3436521Sugen } else 3446521Sugen strncpy(dev_name, *av, DEV_NAME_LEN); 3456521Sugen 3466521Sugen set_dev(dev_name); 3476521Sugen 3486521Sugen if (!(buf = (char *) malloc(b_size))) 34930773Scharnier fatal(EX_UNAVAILABLE, "malloc failed"); 3506521Sugen 3516521Sugen FD_ZERO(&fd_s); 3526521Sugen 3536521Sugen while (1) { 3546521Sugen if (opt_interactive) 3556521Sugen FD_SET(std_in, &fd_s); 3566521Sugen FD_SET(snp_io, &fd_s); 3576521Sugen res = select(snp_io + 1, &fd_s, NULL, NULL, NULL); 3586521Sugen if (opt_interactive && FD_ISSET(std_in, &fd_s)) { 3596775Sugen 3606775Sugen if ((res = ioctl(std_in, FIONREAD, &nread)) != 0) 36130773Scharnier fatal(EX_OSERR, "ioctl(FIONREAD)"); 3626775Sugen if (nread > READB_LEN) 3636775Sugen nread = READB_LEN; 36479759Sdd rv = read(std_in, chb, nread); 36579759Sdd if (rv == -1 || (unsigned)rv != nread) 36630773Scharnier fatal(EX_IOERR, "read (stdin) failed"); 3676775Sugen 3686775Sugen switch (chb[0]) { 3696521Sugen case CHR_CLEAR: 3706521Sugen clear(); 3716521Sugen break; 3726521Sugen case CHR_SWITCH: 37386169Srwatson if (!opt_no_switch) { 37486169Srwatson detach_snp(); 37586169Srwatson ask_dev(dev_name, MSG_CHANGE); 37686169Srwatson set_dev(dev_name); 37720085Sjkh break; 37886169Srwatson } 3796521Sugen default: 3806775Sugen if (opt_write) { 38179759Sdd rv = write(snp_io, chb, nread); 38279759Sdd if (rv == -1 || (unsigned)rv != nread) { 3836775Sugen detach_snp(); 38420085Sjkh if (opt_no_switch) 38598140Simp fatal(EX_IOERR, 38698140Simp "write failed"); 3876775Sugen ask_dev(dev_name, MSG_NOWRITE); 3886775Sugen set_dev(dev_name); 3896775Sugen } 3906775Sugen } 3918857Srgrimes 3926521Sugen } 3936521Sugen } 3946521Sugen if (!FD_ISSET(snp_io, &fd_s)) 3956521Sugen continue; 3966521Sugen 39779759Sdd if ((res = ioctl(snp_io, FIONREAD, &idata)) != 0) 39830773Scharnier fatal(EX_OSERR, "ioctl(FIONREAD)"); 3996521Sugen 40079759Sdd switch (idata) { 4016521Sugen case SNP_OFLOW: 4026521Sugen if (opt_reconn_oflow) 4036521Sugen attach_snp(); 40420085Sjkh else if (opt_interactive && !opt_no_switch) { 4056521Sugen ask_dev(dev_name, MSG_OFLOW); 4066521Sugen set_dev(dev_name); 4076521Sugen } else 40879759Sdd cleanup(-1); 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); 4186521Sugen default: 41979759Sdd nread = (unsigned)idata; 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); 43379759Sdd if (rv == -1 || (unsigned)rv != nread) 43430773Scharnier fatal(EX_IOERR, "read failed"); 43579759Sdd rv = write(std_out, buf, nread); 43679759Sdd if (rv == -1 || (unsigned)rv != nread) 43730773Scharnier fatal(EX_IOERR, "write failed"); 4386521Sugen } 4396521Sugen } /* While */ 44056489Scharnier return(0); 4416521Sugen} 44223837Sroberto 443