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