watch.c revision 232780
1/* 2 * Copyright (c) 1995 Ugen J.S.Antsilevich 3 * 4 * Redistribution and use in source forms, with and without modification, 5 * are permitted provided that this entire comment appears intact. 6 * 7 * Redistribution in binary form may occur without any restrictions. 8 * Obviously, it would be nice if you gave credit where credit is due 9 * but requiring it would be too onerous. 10 * 11 * This software is provided ``AS IS'' without any warranties of any kind. 12 * 13 * Snoop stuff. 14 */ 15 16#include <sys/cdefs.h> 17__FBSDID("$FreeBSD: head/usr.sbin/watch/watch.c 232780 2012-03-10 14:35:09Z ed $"); 18 19#include <sys/param.h> 20#include <sys/fcntl.h> 21#include <sys/filio.h> 22#include <sys/snoop.h> 23#include <sys/stat.h> 24#include <sys/linker.h> 25#include <sys/module.h> 26 27#include <err.h> 28#include <errno.h> 29#include <locale.h> 30#include <paths.h> 31#include <signal.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <sysexits.h> 36#include <termcap.h> 37#include <termios.h> 38#include <time.h> 39#include <unistd.h> 40 41#define MSG_INIT "Snoop started." 42#define MSG_OFLOW "Snoop stopped due to overflow. Reconnecting." 43#define MSG_CLOSED "Snoop stopped due to tty close. Reconnecting." 44#define MSG_CHANGE "Snoop device change by user request." 45#define MSG_NOWRITE "Snoop device change due to write failure." 46 47#define DEV_NAME_LEN 1024 /* for /dev/ttyXX++ */ 48#define MIN_SIZE 256 49 50#define CHR_SWITCH 24 /* Ctrl+X */ 51#define CHR_CLEAR 23 /* Ctrl+V */ 52 53static void clear(void); 54static void timestamp(const char *); 55static void set_tty(void); 56static void unset_tty(void); 57static void fatal(int, const char *); 58static int open_snp(void); 59static void cleanup(int); 60static void usage(void) __dead2; 61static void setup_scr(void); 62static void attach_snp(void); 63static void detach_snp(void); 64static void set_dev(const char *); 65static void ask_dev(char *, const char *); 66 67int opt_reconn_close = 0; 68int opt_reconn_oflow = 0; 69int opt_interactive = 1; 70int opt_timestamp = 0; 71int opt_write = 0; 72int opt_no_switch = 0; 73const char *opt_snpdev; 74 75char dev_name[DEV_NAME_LEN]; 76int snp_io; 77int std_in = 0, std_out = 1; 78 79int clear_ok = 0; 80struct termios otty; 81char tbuf[1024], gbuf[1024]; 82 83static void 84clear(void) 85{ 86 87 if (clear_ok) 88 tputs(gbuf, 1, putchar); 89 fflush(stdout); 90} 91 92static void 93timestamp(const char *buf) 94{ 95 time_t t; 96 char btmp[1024]; 97 98 clear(); 99 printf("\n---------------------------------------------\n"); 100 t = time(NULL); 101 strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t)); 102 printf("%s\n", btmp); 103 printf("%s\n", buf); 104 printf("---------------------------------------------\n"); 105 fflush(stdout); 106} 107 108static void 109set_tty(void) 110{ 111 struct termios ntty; 112 113 tcgetattr (std_in, &otty); 114 ntty = otty; 115 ntty.c_lflag &= ~ICANON; /* disable canonical operation */ 116 ntty.c_lflag &= ~ECHO; 117#ifdef FLUSHO 118 ntty.c_lflag &= ~FLUSHO; 119#endif 120#ifdef PENDIN 121 ntty.c_lflag &= ~PENDIN; 122#endif 123#ifdef IEXTEN 124 ntty.c_lflag &= ~IEXTEN; 125#endif 126 ntty.c_cc[VMIN] = 1; /* minimum of one character */ 127 ntty.c_cc[VTIME] = 0; /* timeout value */ 128 129 ntty.c_cc[VINTR] = 07; /* ^G */ 130 ntty.c_cc[VQUIT] = 07; /* ^G */ 131 tcsetattr (std_in, TCSANOW, &ntty); 132} 133 134static void 135unset_tty(void) 136{ 137 tcsetattr (std_in, TCSANOW, &otty); 138} 139 140static void 141fatal(int error, const char *buf) 142{ 143 unset_tty(); 144 if (buf) 145 errx(error, "fatal: %s", buf); 146 else 147 exit(error); 148} 149 150static int 151open_snp(void) 152{ 153 int f, mode; 154 155 if (opt_write) 156 mode = O_RDWR; 157 else 158 mode = O_RDONLY; 159 160 if (opt_snpdev == NULL) 161 f = open(_PATH_DEV "snp", mode); 162 else 163 f = open(opt_snpdev, mode); 164 if (f == -1) 165 fatal(EX_OSFILE, "cannot open snoop device"); 166 167 return (f); 168} 169 170static void 171cleanup(int signo __unused) 172{ 173 if (opt_timestamp) 174 timestamp("Logging Exited."); 175 close(snp_io); 176 unset_tty(); 177 exit(EX_OK); 178} 179 180static void 181usage(void) 182{ 183 fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n"); 184 exit(EX_USAGE); 185} 186 187static void 188setup_scr(void) 189{ 190 char *cbuf = gbuf, *term; 191 192 if (!opt_interactive) 193 return; 194 if ((term = getenv("TERM"))) 195 if (tgetent(tbuf, term) == 1) 196 if (tgetstr("cl", &cbuf)) 197 clear_ok = 1; 198 set_tty(); 199 clear(); 200} 201 202static void 203detach_snp(void) 204{ 205 int fd; 206 207 fd = -1; 208 ioctl(snp_io, SNPSTTY, &fd); 209} 210 211static void 212attach_snp(void) 213{ 214 int snp_tty; 215 216 snp_tty = open(dev_name, O_RDONLY | O_NONBLOCK); 217 if (snp_tty < 0) 218 fatal(EX_DATAERR, "can't open device"); 219 if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0) 220 fatal(EX_UNAVAILABLE, "cannot attach to tty"); 221 close(snp_tty); 222 if (opt_timestamp) 223 timestamp("Logging Started."); 224} 225 226static void 227set_dev(const char *name) 228{ 229 char buf[DEV_NAME_LEN]; 230 struct stat sb; 231 232 if (strlen(name) > 5 && !strncmp(name, _PATH_DEV, sizeof _PATH_DEV - 1)) { 233 snprintf(buf, sizeof buf, "%s", name); 234 } else { 235 if (strlen(name) == 2) 236 sprintf(buf, "%s%s", _PATH_TTY, name); 237 else 238 sprintf(buf, "%s%s", _PATH_DEV, name); 239 } 240 241 if (*name == '\0' || stat(buf, &sb) < 0) 242 fatal(EX_DATAERR, "bad device name"); 243 244 if ((sb.st_mode & S_IFMT) != S_IFCHR) 245 fatal(EX_DATAERR, "must be a character device"); 246 247 strncpy(dev_name, buf, DEV_NAME_LEN); 248 249 attach_snp(); 250} 251 252void 253ask_dev(char *dbuf, const char *msg) 254{ 255 char buf[DEV_NAME_LEN]; 256 int len; 257 258 clear(); 259 unset_tty(); 260 261 if (msg) 262 printf("%s\n", msg); 263 if (dbuf) 264 printf("Enter device name [%s]:", dbuf); 265 else 266 printf("Enter device name:"); 267 268 if (fgets(buf, DEV_NAME_LEN - 1, stdin)) { 269 len = strlen(buf); 270 if (buf[len - 1] == '\n') 271 buf[len - 1] = '\0'; 272 if (buf[0] != '\0' && buf[0] != ' ') 273 strcpy(dbuf, buf); 274 } 275 set_tty(); 276} 277 278#define READB_LEN 5 279 280int 281main(int ac, char *av[]) 282{ 283 int ch, res, rv, nread; 284 size_t b_size = MIN_SIZE; 285 char *buf, chb[READB_LEN]; 286 fd_set fd_s; 287 288 (void) setlocale(LC_TIME, ""); 289 290 if (isatty(std_out)) 291 opt_interactive = 1; 292 else 293 opt_interactive = 0; 294 295 while ((ch = getopt(ac, av, "Wciotnf:")) != -1) 296 switch (ch) { 297 case 'W': 298 opt_write = 1; 299 break; 300 case 'c': 301 opt_reconn_close = 1; 302 break; 303 case 'i': 304 opt_interactive = 1; 305 break; 306 case 'o': 307 opt_reconn_oflow = 1; 308 break; 309 case 't': 310 opt_timestamp = 1; 311 break; 312 case 'n': 313 opt_no_switch = 1; 314 break; 315 case 'f': 316 opt_snpdev = optarg; 317 break; 318 case '?': 319 default: 320 usage(); 321 } 322 323 if (modfind("snp") == -1) 324 if (kldload("snp") == -1 || modfind("snp") == -1) 325 warn("snp module not available"); 326 327 signal(SIGINT, cleanup); 328 329 snp_io = open_snp(); 330 setup_scr(); 331 332 if (*(av += optind) == NULL) { 333 if (opt_interactive && !opt_no_switch) 334 ask_dev(dev_name, MSG_INIT); 335 else 336 fatal(EX_DATAERR, "no device name given"); 337 } else 338 strncpy(dev_name, *av, DEV_NAME_LEN); 339 340 set_dev(dev_name); 341 342 if (!(buf = (char *) malloc(b_size))) 343 fatal(EX_UNAVAILABLE, "malloc failed"); 344 345 FD_ZERO(&fd_s); 346 347 while (1) { 348 if (opt_interactive) 349 FD_SET(std_in, &fd_s); 350 FD_SET(snp_io, &fd_s); 351 res = select(snp_io + 1, &fd_s, NULL, NULL, NULL); 352 if (opt_interactive && FD_ISSET(std_in, &fd_s)) { 353 354 if ((res = ioctl(std_in, FIONREAD, &nread)) != 0) 355 fatal(EX_OSERR, "ioctl(FIONREAD)"); 356 if (nread > READB_LEN) 357 nread = READB_LEN; 358 rv = read(std_in, chb, nread); 359 if (rv == -1 || rv != nread) 360 fatal(EX_IOERR, "read (stdin) failed"); 361 362 switch (chb[0]) { 363 case CHR_CLEAR: 364 clear(); 365 break; 366 case CHR_SWITCH: 367 if (!opt_no_switch) { 368 detach_snp(); 369 ask_dev(dev_name, MSG_CHANGE); 370 set_dev(dev_name); 371 break; 372 } 373 default: 374 if (opt_write) { 375 rv = write(snp_io, chb, nread); 376 if (rv == -1 || rv != nread) { 377 detach_snp(); 378 if (opt_no_switch) 379 fatal(EX_IOERR, 380 "write failed"); 381 ask_dev(dev_name, MSG_NOWRITE); 382 set_dev(dev_name); 383 } 384 } 385 386 } 387 } 388 if (!FD_ISSET(snp_io, &fd_s)) 389 continue; 390 391 if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0) 392 fatal(EX_OSERR, "ioctl(FIONREAD)"); 393 394 switch (nread) { 395 case SNP_OFLOW: 396 if (opt_reconn_oflow) 397 attach_snp(); 398 else if (opt_interactive && !opt_no_switch) { 399 ask_dev(dev_name, MSG_OFLOW); 400 set_dev(dev_name); 401 } else 402 cleanup(-1); 403 break; 404 case SNP_DETACH: 405 case SNP_TTYCLOSE: 406 if (opt_reconn_close) 407 attach_snp(); 408 else if (opt_interactive && !opt_no_switch) { 409 ask_dev(dev_name, MSG_CLOSED); 410 set_dev(dev_name); 411 } else 412 cleanup(-1); 413 break; 414 default: 415 if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) { 416 free(buf); 417 if (!(buf = (char *) malloc(b_size / 2))) 418 fatal(EX_UNAVAILABLE, "malloc failed"); 419 b_size = b_size / 2; 420 } 421 if (nread > b_size) { 422 b_size = (nread % 2) ? (nread + 1) : (nread); 423 free(buf); 424 if (!(buf = (char *) malloc(b_size))) 425 fatal(EX_UNAVAILABLE, "malloc failed"); 426 } 427 rv = read(snp_io, buf, nread); 428 if (rv == -1 || rv != nread) 429 fatal(EX_IOERR, "read failed"); 430 rv = write(std_out, buf, nread); 431 if (rv == -1 || rv != nread) 432 fatal(EX_IOERR, "write failed"); 433 } 434 } /* While */ 435 return(0); 436} 437