watch.c revision 51287
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 51287 1999-09-15 01:58:44Z peter $"; 1930773Scharnier#endif /* not lint */ 2030773Scharnier 2124035Sbde#include <sys/types.h> 2224035Sbde#include <sys/fcntl.h> 2324035Sbde#include <sys/filio.h> 2424035Sbde#include <sys/snoop.h> 2524035Sbde#include <sys/stat.h> 2624035Sbde 2730773Scharnier#include <err.h> 2824035Sbde#include <locale.h> 2924035Sbde#include <signal.h> 306521Sugen#include <stdio.h> 316521Sugen#include <stdlib.h> 326775Sugen#include <string.h> 3324035Sbde#include <sysexits.h> 349895Sache#include <termcap.h> 3523837Sroberto#include <termios.h> 3624035Sbde#include <unistd.h> 376521Sugen 386521Sugen#define MSG_INIT "Snoop started." 396713Spst#define MSG_OFLOW "Snoop stopped due to overflow. Reconnecting." 406713Spst#define MSG_CLOSED "Snoop stopped due to tty close. Reconnecting." 416521Sugen#define MSG_CHANGE "Snoop device change by user request." 426775Sugen#define MSG_NOWRITE "Snoop device change due to write failure." 436521Sugen 446521Sugen 456713Spst#define DEV_NAME_LEN 1024 /* for /dev/ttyXX++ */ 466521Sugen#define MIN_SIZE 256 476521Sugen 486521Sugen#define CHR_SWITCH 24 /* Ctrl+X */ 496521Sugen#define CHR_CLEAR 23 /* Ctrl+V */ 506521Sugen 516521Sugen 526521Sugenint opt_reconn_close = 0; 536521Sugenint opt_reconn_oflow = 0; 546521Sugenint opt_interactive = 1; 556521Sugenint opt_timestamp = 0; 566775Sugenint opt_write = 0; 5720085Sjkhint opt_no_switch = 0; 586521Sugen 596521Sugenchar dev_name[DEV_NAME_LEN]; 606521Sugenint snp_io; 616713Spstdev_t snp_tty; 626521Sugenint std_in = 0, std_out = 1; 636521Sugen 646521Sugen 656521Sugenint clear_ok = 0; 6623837Srobertostruct termios otty; 676521Sugenchar tbuf[1024], buf[1024]; 686521Sugen 696521Sugen 706521Sugenvoid 716521Sugenclear() 726521Sugen{ 736521Sugen if (clear_ok) 746521Sugen tputs(buf, 1, putchar); 756521Sugen fflush(stdout); 766521Sugen} 776521Sugen 786521Sugenvoid 796521Sugentimestamp(buf) 806521Sugen char *buf; 816521Sugen{ 826521Sugen time_t t; 836521Sugen char btmp[1024]; 846521Sugen clear(); 856521Sugen printf("\n---------------------------------------------\n"); 866521Sugen t = time(NULL); 876521Sugen strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t)); 886521Sugen printf("%s\n", btmp); 896521Sugen printf("%s\n", buf); 906521Sugen printf("---------------------------------------------\n"); 916521Sugen fflush(stdout); 926521Sugen} 936521Sugen 946521Sugenvoid 956521Sugenset_tty() 966521Sugen{ 9723837Sroberto struct termios ntty; 9812887Sjkh 9923837Sroberto tcgetattr (std_in, &otty); 10023837Sroberto ntty = otty; 10123837Sroberto ntty.c_lflag &= ~ICANON; /* disable canonical operation */ 10223837Sroberto ntty.c_lflag &= ~ECHO; 10323837Sroberto#ifdef FLUSHO 10423837Sroberto ntty.c_lflag &= ~FLUSHO; 10523837Sroberto#endif 10623837Sroberto#ifdef PENDIN 10723837Sroberto ntty.c_lflag &= ~PENDIN; 10823837Sroberto#endif 10923837Sroberto#ifdef IEXTEN 11023837Sroberto ntty.c_lflag &= ~IEXTEN; 11123837Sroberto#endif 11223837Sroberto ntty.c_cc[VMIN] = 1; /* minimum of one character */ 11323837Sroberto ntty.c_cc[VTIME] = 0; /* timeout value */ 11423837Sroberto 11523837Sroberto ntty.c_cc[VINTR] = 07; /* ^G */ 11623837Sroberto ntty.c_cc[VQUIT] = 07; /* ^G */ 11723837Sroberto tcsetattr (std_in, TCSANOW, &ntty); 1186521Sugen} 1196521Sugen 1206521Sugenvoid 1216521Sugenunset_tty() 1226521Sugen{ 12323837Sroberto tcsetattr (std_in, TCSANOW, &otty); 1246521Sugen} 1256521Sugen 1266521Sugen 1276521Sugenvoid 12823837Srobertofatal(err, buf) 12923837Sroberto unsigned int err; 1306521Sugen char *buf; 1316521Sugen{ 1326521Sugen unset_tty(); 1336521Sugen if (buf) 13430773Scharnier errx(err, "fatal: %s", buf); 13530773Scharnier else 13630773Scharnier exit(err); 1376521Sugen} 1386521Sugen 1396521Sugenint 1406521Sugenopen_snp() 1416521Sugen{ 1426775Sugen char snp[] = {"/dev/snpX"}; 1436521Sugen char c; 1446775Sugen int f, mode; 1456775Sugen 1466775Sugen if (opt_write) 1476775Sugen mode = O_RDWR; 1486775Sugen else 1496775Sugen mode = O_RDONLY; 1506775Sugen 1516521Sugen for (c = '0'; c <= '9'; c++) { 1526521Sugen snp[8] = c; 1536775Sugen if ((f = open(snp, mode)) < 0) 1546521Sugen continue; 1556521Sugen return f; 1566521Sugen } 15730773Scharnier fatal(EX_OSFILE, "cannot open snoop device"); 15830773Scharnier return (0); 1596521Sugen} 1606521Sugen 1616521Sugen 1626521Sugenvoid 1636521Sugencleanup() 1646521Sugen{ 1656521Sugen if (opt_timestamp) 1666521Sugen timestamp("Logging Exited."); 1676521Sugen close(snp_io); 1686521Sugen unset_tty(); 16923837Sroberto exit(EX_OK); 1706521Sugen} 1716521Sugen 1726521Sugen 17330773Scharnierstatic void 17430773Scharnierusage() 1756521Sugen{ 17630773Scharnier fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n"); 17723837Sroberto exit(EX_USAGE); 1786521Sugen} 1796521Sugen 1806521Sugenvoid 1816521Sugensetup_scr() 1826521Sugen{ 1836521Sugen char *cbuf = buf, *term; 1846521Sugen if (!opt_interactive) 1856521Sugen return; 1866521Sugen if ((term = getenv("TERM"))) 1876521Sugen if (tgetent(tbuf, term) == 1) 1886521Sugen if (tgetstr("cl", &cbuf)) 1896521Sugen clear_ok = 1; 1909895Sache set_tty(); 1916521Sugen clear(); 1926521Sugen} 1936521Sugen 1946521Sugen 1956521Sugenint 1966521Sugenctoh(c) 1976521Sugen char c; 1986521Sugen{ 1996521Sugen if (c >= '0' && c <= '9') 2006521Sugen return (int) (c - '0'); 2016521Sugen 2026521Sugen if (c >= 'a' && c <= 'f') 2036521Sugen return (int) (c - 'a' + 10); 2046521Sugen 20530773Scharnier fatal(EX_DATAERR, "bad tty number"); 20630773Scharnier return (0); 2076521Sugen} 2086521Sugen 2096521Sugen 2106521Sugenvoid 2116521Sugendetach_snp() 2126521Sugen{ 2136713Spst dev_t dev; 2146713Spst 2156713Spst dev = -1; 2166713Spst ioctl(snp_io, SNPSTTY, &dev); 2176521Sugen} 2186521Sugen 2196521Sugenvoid 2206521Sugenattach_snp() 2216521Sugen{ 2226521Sugen if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0) 22330773Scharnier fatal(EX_UNAVAILABLE, "cannot attach to tty"); 2246521Sugen if (opt_timestamp) 2256521Sugen timestamp("Logging Started."); 2266521Sugen} 2276521Sugen 2286521Sugen 2296521Sugenvoid 2306521Sugenset_dev(name) 2316521Sugen char *name; 2326521Sugen{ 2336713Spst char buf[DEV_NAME_LEN]; 2346713Spst struct stat sb; 2356521Sugen 23623837Sroberto if (strlen(name) > 5 && !strncmp(name, "/dev/", 5)) { 23723837Sroberto snprintf(buf, sizeof buf, "%s", name); 23823837Sroberto } 2396775Sugen else { 2406775Sugen if (strlen(name) == 2) 2416775Sugen sprintf(buf, "/dev/tty%s", name); 2426775Sugen else 2436775Sugen sprintf(buf, "/dev/%s", name); 2446775Sugen } 2456521Sugen 24623837Sroberto if (*name == '\0' || stat(buf, &sb) < 0) 24730773Scharnier fatal(EX_DATAERR, "bad device name"); 2486521Sugen 24923837Sroberto if ((sb.st_mode & S_IFMT) != S_IFCHR) 25030773Scharnier fatal(EX_DATAERR, "must be a character device"); 25123837Sroberto 2526713Spst snp_tty = sb.st_rdev; 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 2836775Sugen#define READB_LEN 5 2846521Sugen 28551287Speterint 2866521Sugenmain(ac, av) 2876521Sugen int ac; 2886521Sugen char **av; 2896521Sugen{ 2906521Sugen int res, nread, b_size = MIN_SIZE; 2916521Sugen extern int optind; 2926775Sugen char ch, *buf, chb[READB_LEN]; 2936521Sugen fd_set fd_s; 2946521Sugen 29511828Sache (void) setlocale(LC_TIME, ""); 29611828Sache 2976521Sugen if (isatty(std_out)) 2986521Sugen opt_interactive = 1; 2996521Sugen else 3006521Sugen opt_interactive = 0; 3016521Sugen 3026521Sugen 30324428Simp while ((ch = getopt(ac, av, "Wciotn")) != -1) 3046521Sugen switch (ch) { 3056775Sugen case 'W': 3066775Sugen opt_write = 1; 3076775Sugen break; 3086521Sugen case 'c': 3096521Sugen opt_reconn_close = 1; 3106521Sugen break; 3116521Sugen case 'i': 3126521Sugen opt_interactive = 1; 3136521Sugen break; 3146521Sugen case 'o': 3156521Sugen opt_reconn_oflow = 1; 3166521Sugen break; 3176521Sugen case 't': 3186521Sugen opt_timestamp = 1; 3196521Sugen break; 32020085Sjkh case 'n': 32120085Sjkh opt_no_switch = 1; 32220085Sjkh break; 3236521Sugen case '?': 3246521Sugen default: 32530773Scharnier usage(); 3266521Sugen } 3276521Sugen 3286521Sugen signal(SIGINT, cleanup); 3296521Sugen 3306521Sugen setup_scr(); 3316521Sugen snp_io = open_snp(); 3326521Sugen 3336521Sugen if (*(av += optind) == NULL) { 33420085Sjkh if (opt_interactive && !opt_no_switch) 3356521Sugen ask_dev(dev_name, MSG_INIT); 3366521Sugen else 33730773Scharnier fatal(EX_DATAERR, "no device name given"); 3386521Sugen } else 3396521Sugen strncpy(dev_name, *av, DEV_NAME_LEN); 3406521Sugen 3416521Sugen set_dev(dev_name); 3426521Sugen 3436521Sugen if (!(buf = (char *) malloc(b_size))) 34430773Scharnier fatal(EX_UNAVAILABLE, "malloc failed"); 3456521Sugen 3466521Sugen FD_ZERO(&fd_s); 3476521Sugen 3486521Sugen while (1) { 3496521Sugen if (opt_interactive) 3506521Sugen FD_SET(std_in, &fd_s); 3516521Sugen FD_SET(snp_io, &fd_s); 3526521Sugen res = select(snp_io + 1, &fd_s, NULL, NULL, NULL); 3536521Sugen if (opt_interactive && FD_ISSET(std_in, &fd_s)) { 3546775Sugen 3556775Sugen if ((res = ioctl(std_in, FIONREAD, &nread)) != 0) 35630773Scharnier fatal(EX_OSERR, "ioctl(FIONREAD)"); 3576775Sugen if (nread > READB_LEN) 3586775Sugen nread = READB_LEN; 3596775Sugen if (read(std_in,chb,nread)!=nread) 36030773Scharnier fatal(EX_IOERR, "read (stdin) failed"); 3616775Sugen 3626775Sugen switch (chb[0]) { 3636521Sugen case CHR_CLEAR: 3646521Sugen clear(); 3656521Sugen break; 3666521Sugen case CHR_SWITCH: 36720085Sjkh if (opt_no_switch) 36820085Sjkh break; 3696775Sugen detach_snp(); 3706521Sugen ask_dev(dev_name, MSG_CHANGE); 3716521Sugen set_dev(dev_name); 3726521Sugen break; 3736521Sugen default: 3746775Sugen if (opt_write) { 3756775Sugen if (write(snp_io,chb,nread) != nread) { 3766775Sugen detach_snp(); 37720085Sjkh if (opt_no_switch) 37830773Scharnier fatal(EX_IOERR, "write failed"); 3796775Sugen ask_dev(dev_name, MSG_NOWRITE); 3806775Sugen set_dev(dev_name); 3816775Sugen } 3826775Sugen } 3838857Srgrimes 3846521Sugen } 3856521Sugen } 3866521Sugen if (!FD_ISSET(snp_io, &fd_s)) 3876521Sugen continue; 3886521Sugen 3896521Sugen if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0) 39030773Scharnier fatal(EX_OSERR, "ioctl(FIONREAD)"); 3916521Sugen 3926521Sugen switch (nread) { 3936521Sugen case SNP_OFLOW: 3946521Sugen if (opt_reconn_oflow) 3956521Sugen attach_snp(); 39620085Sjkh else if (opt_interactive && !opt_no_switch) { 3976521Sugen ask_dev(dev_name, MSG_OFLOW); 3986521Sugen set_dev(dev_name); 3996521Sugen } else 4006521Sugen cleanup(); 4016521Sugen case SNP_DETACH: 4026521Sugen case SNP_TTYCLOSE: 4036521Sugen if (opt_reconn_close) 4046521Sugen attach_snp(); 40520085Sjkh else if (opt_interactive && !opt_no_switch) { 4066521Sugen ask_dev(dev_name, MSG_CLOSED); 4076521Sugen set_dev(dev_name); 4086521Sugen } else 4096521Sugen cleanup(); 4106521Sugen default: 4116521Sugen if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) { 4126521Sugen free(buf); 4136521Sugen if (!(buf = (char *) malloc(b_size / 2))) 41430773Scharnier fatal(EX_UNAVAILABLE, "malloc failed"); 4156521Sugen b_size = b_size / 2; 4166521Sugen } 4176521Sugen if (nread > b_size) { 4186521Sugen b_size = (nread % 2) ? (nread + 1) : (nread); 4196521Sugen free(buf); 4206521Sugen if (!(buf = (char *) malloc(b_size))) 42130773Scharnier fatal(EX_UNAVAILABLE, "malloc failed"); 4226521Sugen } 4236521Sugen if (read(snp_io, buf, nread) < nread) 42430773Scharnier fatal(EX_IOERR, "read failed"); 4256521Sugen if (write(std_out, buf, nread) < nread) 42630773Scharnier fatal(EX_IOERR, "write failed"); 4276521Sugen } 4286521Sugen } /* While */ 4296521Sugen} 43023837Sroberto 431