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