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