Index: head/usr.sbin/watch/watch.8 =================================================================== --- head/usr.sbin/watch/watch.8 (revision 6774) +++ head/usr.sbin/watch/watch.8 (revision 6775) @@ -1,77 +1,79 @@ .\" .\" @(#)watch.8 1.1 (FreeBSD) 2/17/95 .\" .Dd February 17, 1995 .Dt WATCH 8 .Os .Sh NAME .Nm watch .Nd snoop on another tty line .Sh SYNOPSYS .Nm watch -.Op Fl ciot +.Op Fl ciotW .Ar tty -.\" watch [-ciot] [] +.\" watch [-ciotW] [] .Sh DESCRIPTION .Nm Watch allows the superuser to examine all data coming through a specified tty. .Nm Watch writes to standard output. .Pp The options are as follows: .Bl -tag -width "-l nul " .It Fl c Reconnect on close. If the tty observed by .Nm watch is closed, automatically reattach to the same tty. If this option is not specified, .Nm watch will request a new tty if running in interactive mode or exit if running without a controlling tty. .It Fl i Force interactive mode. Interactive mode is a default if .Nm watch is started from a tty. If output is redirected to a file, interactive mode can still be requested by specifying this option. .It Fl o Reconnect on overflow. The behaviour of .Nm watch if the observed tty overflows is similar to the behavior if the observed tty is closed. For more info see .Xr snp 4 . .It Fl t Print the date and time when observation of a given tty is started. +.It Fl W +Allow write access to observed tty. .It Ar tty Tty may be specified as an tty-style device, such as a pseudo tty device, a virtual console, or a serial line, etc. Names may be preceded by "/dev/". .Sh OPERATION While running in interactive mode, all user input is discarded except for: .Pp .Bl -tag -width "XXXX" -compact .It Sy "" Exit .Nm watch . .It Sy "" Clear screen. .It Sy "" Change attached tty. .Sh RESTRICTIONS Only the superuser can run .Nm watch . .Sh SEE ALSO .Xr snp 4 , .Xr pty 4 , .Xr sio 4 . .Sh BUGS No terminal emulation is performed. All user output is reproduced as-is. .Sh AUTHOR Ugen J.S. Antsilevich .Sh HISTORY .Nm Watch first appeared in FreeBSD 2.1 Index: head/usr.sbin/watch/watch.c =================================================================== --- head/usr.sbin/watch/watch.c (revision 6774) +++ head/usr.sbin/watch/watch.c (revision 6775) @@ -1,362 +1,394 @@ /* * Copyright (c) 1995 Ugen J.S.Antsilevich * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * Snoop stuff. */ #include #include #include #include +#include #include #include #include #include #include #include #include #include #define MSG_INIT "Snoop started." #define MSG_OFLOW "Snoop stopped due to overflow. Reconnecting." #define MSG_CLOSED "Snoop stopped due to tty close. Reconnecting." #define MSG_CHANGE "Snoop device change by user request." +#define MSG_NOWRITE "Snoop device change due to write failure." #define DEV_NAME_LEN 1024 /* for /dev/ttyXX++ */ #define MIN_SIZE 256 #define CHR_SWITCH 24 /* Ctrl+X */ #define CHR_CLEAR 23 /* Ctrl+V */ int opt_reconn_close = 0; int opt_reconn_oflow = 0; int opt_interactive = 1; int opt_timestamp = 0; +int opt_write = 0; char dev_name[DEV_NAME_LEN]; int snp_io; dev_t snp_tty; int std_in = 0, std_out = 1; int clear_ok = 0; struct sgttyb sgo; char tbuf[1024], buf[1024]; void clear() { if (clear_ok) tputs(buf, 1, putchar); fflush(stdout); } void timestamp(buf) char *buf; { time_t t; char btmp[1024]; clear(); printf("\n---------------------------------------------\n"); t = time(NULL); strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t)); printf("%s\n", btmp); printf("%s\n", buf); printf("---------------------------------------------\n"); fflush(stdout); } void set_tty() { struct sgttyb sgn; ioctl(std_in, TIOCGETP, &sgo); /* bcopy(&sgn, &sgo, sizeof(struct sgttyb)); */ sgn = sgo; sgn.sg_flags |= CBREAK; sgn.sg_flags &= ~ECHO; ioctl(std_in, TIOCSETP, &sgn); } void unset_tty() { ioctl(std_in, TIOCSETP, &sgo); } void fatal(buf) char *buf; { unset_tty(); if (buf) fprintf(stderr, "Fatal: %s\n", buf); exit(1); } int open_snp() { - char *snp = "/dev/snpX"; + char snp[] = {"/dev/snpX"}; char c; - int f; + int f, mode; + + if (opt_write) + mode = O_RDWR; + else + mode = O_RDONLY; + for (c = '0'; c <= '9'; c++) { snp[8] = c; - if ((f = open(snp, O_RDONLY)) < 0) + if ((f = open(snp, mode)) < 0) continue; return f; } fatal("Cannot open snoop device."); } void cleanup() { if (opt_timestamp) timestamp("Logging Exited."); close(snp_io); unset_tty(); exit(0); } void show_usage() { - printf("watch -[ciot] [tty name]\n"); + printf("watch -[ciotW] [tty name]\n"); exit(1); } void setup_scr() { char *cbuf = buf, *term; if (!opt_interactive) return; if ((term = getenv("TERM"))) if (tgetent(tbuf, term) == 1) if (tgetstr("cl", &cbuf)) clear_ok = 1; clear(); set_tty(); } int ctoh(c) char c; { if (c >= '0' && c <= '9') return (int) (c - '0'); if (c >= 'a' && c <= 'f') return (int) (c - 'a' + 10); fatal("Bad tty number."); } void detach_snp() { dev_t dev; dev = -1; ioctl(snp_io, SNPSTTY, &dev); } void attach_snp() { if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0) fatal("Cannot attach to tty."); if (opt_timestamp) timestamp("Logging Started."); } void set_dev(name) char *name; { char buf[DEV_NAME_LEN]; struct stat sb; if (strlen(name) > 5 && !strncmp(name, "/dev/", 5)) - strcpy(buf, &(name[5])); - else strcpy(buf, name); + else { + if (strlen(name) == 2) + sprintf(buf, "/dev/tty%s", name); + else + sprintf(buf, "/dev/%s", name); + } - if (stat(buf, &sb) < 0) fatal("Bad device name."); snp_tty = sb.st_rdev; attach_snp(); } void ask_dev(dev_name, msg) char *dev_name, *msg; { char buf[DEV_NAME_LEN]; int len; clear(); unset_tty(); if (msg) printf("%s\n", msg); if (dev_name) printf("Enter device name [%s]:", dev_name); else printf("Enter device name:"); if (fgets(buf, DEV_NAME_LEN - 1, stdin)) { len = strlen(buf); if (buf[len - 1] == '\n') buf[len - 1] = '\0'; if (buf[0] != '\0' && buf[0] != ' ') strcpy(dev_name, buf); } set_tty(); } +#define READB_LEN 5 void main(ac, av) int ac; char **av; { int res, nread, b_size = MIN_SIZE; extern int optind; - char ch, *buf; + char ch, *buf, chb[READB_LEN]; fd_set fd_s; if (getuid() != 0) fatal(NULL); if (isatty(std_out)) opt_interactive = 1; else opt_interactive = 0; - while ((ch = getopt(ac, av, "ciot")) != EOF) + while ((ch = getopt(ac, av, "Wciot")) != EOF) switch (ch) { + case 'W': + opt_write = 1; + break; case 'c': opt_reconn_close = 1; break; case 'i': opt_interactive = 1; break; case 'o': opt_reconn_oflow = 1; break; case 't': opt_timestamp = 1; break; case '?': default: show_usage(); exit(1); } signal(SIGINT, cleanup); setup_scr(); snp_io = open_snp(); if (*(av += optind) == NULL) { if (opt_interactive) ask_dev(dev_name, MSG_INIT); else fatal("No device name given."); } else strncpy(dev_name, *av, DEV_NAME_LEN); set_dev(dev_name); if (!(buf = (char *) malloc(b_size))) fatal("Cannot malloc()."); FD_ZERO(&fd_s); while (1) { if (opt_interactive) FD_SET(std_in, &fd_s); FD_SET(snp_io, &fd_s); res = select(snp_io + 1, &fd_s, NULL, NULL, NULL); if (opt_interactive && FD_ISSET(std_in, &fd_s)) { - switch (ch = getchar()) { + + if ((res = ioctl(std_in, FIONREAD, &nread)) != 0) + fatal("ioctl() failed."); + if (nread > READB_LEN) + nread = READB_LEN; + if (read(std_in,chb,nread)!=nread) + fatal("read (stdin) failed."); + + switch (chb[0]) { case CHR_CLEAR: clear(); break; case CHR_SWITCH: - /* detach_snp(); */ + detach_snp(); ask_dev(dev_name, MSG_CHANGE); set_dev(dev_name); break; default: + if (opt_write) { + if (write(snp_io,chb,nread) != nread) { + detach_snp(); + ask_dev(dev_name, MSG_NOWRITE); + set_dev(dev_name); + } + } + } } if (!FD_ISSET(snp_io, &fd_s)) continue; if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0) fatal("ioctl() failed."); switch (nread) { case SNP_OFLOW: if (opt_reconn_oflow) attach_snp(); else if (opt_interactive) { ask_dev(dev_name, MSG_OFLOW); set_dev(dev_name); } else cleanup(); case SNP_DETACH: case SNP_TTYCLOSE: if (opt_reconn_close) attach_snp(); else if (opt_interactive) { ask_dev(dev_name, MSG_CLOSED); set_dev(dev_name); } else cleanup(); default: if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) { free(buf); if (!(buf = (char *) malloc(b_size / 2))) fatal("Cannot malloc()"); b_size = b_size / 2; } if (nread > b_size) { b_size = (nread % 2) ? (nread + 1) : (nread); free(buf); if (!(buf = (char *) malloc(b_size))) fatal("Cannot malloc()"); } if (read(snp_io, buf, nread) < nread) fatal("read failed."); if (write(std_out, buf, nread) < nread) fatal("write failed."); } } /* While */ }