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$"); 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 54 static void clear(void); 55 static void timestamp(const char *); 56 static void set_tty(void); 57 static void unset_tty(void); 58 static void fatal(int, const char *); 59 static int open_snp(void); 60 static void cleanup(int); 61 static void usage(void) __dead2; 62 static void setup_scr(void); 63 static void attach_snp(void); 64 static void detach_snp(void); 65 static void set_dev(const char *); 66 static void ask_dev(char *, const char *); 67 68 int opt_reconn_close = 0; 69 int opt_reconn_oflow = 0; 70 int opt_interactive = 1; 71 int opt_timestamp = 0; 72 int opt_write = 0; 73 int opt_no_switch = 0; 74 const char *opt_snpdev; 75 76 char dev_name[DEV_NAME_LEN]; 77 int snp_io; 78 int std_in = 0, std_out = 1; 79 80 81 int clear_ok = 0; 82 struct termios otty; 83 char tbuf[1024], gbuf[1024]; 84 85 86 static void 87 clear(void) 88 { 89 if (clear_ok) 90 tputs(gbuf, 1, putchar); 91 fflush(stdout); 92 } 93 94 static void 95 timestamp(const char *buf) 96 { 97 time_t t; 98 char btmp[1024]; 99 clear(); 100 printf("\n---------------------------------------------\n"); 101 t = time(NULL); 102 strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t)); 103 printf("%s\n", btmp); 104 printf("%s\n", buf); 105 printf("---------------------------------------------\n"); 106 fflush(stdout); 107 } 108 109 static void 110 set_tty(void) 111 { 112 struct termios ntty; 113 114 tcgetattr (std_in, &otty); 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 135 static void 136 unset_tty(void) 137 { 138 tcsetattr (std_in, TCSANOW, &otty); 139 } 140 141 142 static void 143 fatal(int error, const char *buf) 144 { 145 unset_tty(); 146 if (buf) 147 errx(error, "fatal: %s", buf); 148 else 149 exit(error); 150 } 151 152 static int 153 open_snp(void) 154 { 155 char snp[] = {_PATH_DEV "snpX"}; 156 char c; 157 int f, mode, pos; 158 159 pos = strlen(snp) - 1; 160 if (opt_write) 161 mode = O_RDWR; 162 else 163 mode = O_RDONLY; 164 165 if (opt_snpdev == NULL) 166 for (c = '0'; c <= '9'; c++) { 167 snp[pos] = c; 168 if ((f = open(snp, mode)) < 0) { 169 if (errno == EBUSY) 170 continue; 171 err(1, "open %s", snp); 172 } 173 return f; 174 } 175 else 176 if ((f = open(opt_snpdev, mode)) != -1) 177 return (f); 178 fatal(EX_OSFILE, "cannot open snoop device"); 179 return (0); 180 } 181 182 183 static void 184 cleanup(int signo __unused) 185 { 186 if (opt_timestamp) 187 timestamp("Logging Exited."); 188 close(snp_io); 189 unset_tty(); 190 exit(EX_OK); 191 } 192 193 194 static void 195 usage(void) 196 { 197 fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n"); 198 exit(EX_USAGE); 199 } 200 201 static void 202 setup_scr(void) 203 { 204 char *cbuf = gbuf, *term; 205 if (!opt_interactive) 206 return; 207 if ((term = getenv("TERM"))) 208 if (tgetent(tbuf, term) == 1) 209 if (tgetstr("cl", &cbuf)) 210 clear_ok = 1; 211 set_tty(); 212 clear(); 213 } 214 215 static void 216 detach_snp(void) 217 { 218 int fd; 219 220 fd = -1; 221 ioctl(snp_io, SNPSTTY, &fd); 222 } 223 224 static void 225 attach_snp(void) 226 { 227 int snp_tty; 228 229 snp_tty = open(dev_name, O_RDONLY | O_NONBLOCK); 230 if (snp_tty < 0) 231 fatal(EX_DATAERR, "can't open device"); 232 if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0) 233 fatal(EX_UNAVAILABLE, "cannot attach to tty"); 234 close(snp_tty); 235 if (opt_timestamp) 236 timestamp("Logging Started."); 237 } 238 239 240 static void 241 set_dev(const char *name) 242 { 243 char buf[DEV_NAME_LEN]; 244 struct stat sb; 245 246 if (strlen(name) > 5 && !strncmp(name, _PATH_DEV, sizeof _PATH_DEV - 1)) { 247 snprintf(buf, sizeof buf, "%s", name); 248 } else { 249 if (strlen(name) == 2) 250 sprintf(buf, "%s%s", _PATH_TTY, name); 251 else 252 sprintf(buf, "%s%s", _PATH_DEV, name); 253 } 254 255 if (*name == '\0' || stat(buf, &sb) < 0) 256 fatal(EX_DATAERR, "bad device name"); 257 258 if ((sb.st_mode & S_IFMT) != S_IFCHR) 259 fatal(EX_DATAERR, "must be a character device"); 260 261 strncpy(dev_name, buf, DEV_NAME_LEN); 262 263 attach_snp(); 264 } 265 266 void 267 ask_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 294 int 295 main(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