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/param.h> 17 #include <sys/fcntl.h> 18 #include <sys/filio.h> 19 #include <sys/snoop.h> 20 #include <sys/stat.h> 21 #include <sys/linker.h> 22 #include <sys/module.h> 23 24 #include <err.h> 25 #include <errno.h> 26 #include <locale.h> 27 #include <paths.h> 28 #include <signal.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <sysexits.h> 33 #include <termcap.h> 34 #include <termios.h> 35 #include <time.h> 36 #include <unistd.h> 37 38 #define MSG_INIT "Snoop started." 39 #define MSG_OFLOW "Snoop stopped due to overflow. Reconnecting." 40 #define MSG_CLOSED "Snoop stopped due to tty close. Reconnecting." 41 #define MSG_CHANGE "Snoop device change by user request." 42 #define MSG_NOWRITE "Snoop device change due to write failure." 43 44 #define DEV_NAME_LEN 1024 /* for /dev/ttyXX++ */ 45 #define MIN_SIZE 256 46 47 #define CHR_SWITCH 24 /* Ctrl+X */ 48 #define CHR_CLEAR 23 /* Ctrl+V */ 49 50 static void clear(void); 51 static void timestamp(const char *); 52 static void set_tty(void); 53 static void unset_tty(void); 54 static void fatal(int, const char *); 55 static int open_snp(void); 56 static void cleanup(int); 57 static void usage(void) __dead2; 58 static void setup_scr(void); 59 static void attach_snp(void); 60 static void detach_snp(void); 61 static void set_dev(const char *); 62 static void ask_dev(char *, const char *); 63 64 int opt_reconn_close = 0; 65 int opt_reconn_oflow = 0; 66 int opt_interactive = 1; 67 int opt_timestamp = 0; 68 int opt_write = 0; 69 int opt_no_switch = 0; 70 const char *opt_snpdev; 71 72 char dev_name[DEV_NAME_LEN]; 73 int snp_io; 74 int std_in = 0, std_out = 1; 75 76 int clear_ok = 0; 77 struct termios otty; 78 char tbuf[1024], gbuf[1024]; 79 80 static void 81 clear(void) 82 { 83 84 if (clear_ok) 85 tputs(gbuf, 1, putchar); 86 fflush(stdout); 87 } 88 89 static void 90 timestamp(const char *buf) 91 { 92 time_t t; 93 char btmp[1024]; 94 95 clear(); 96 printf("\n---------------------------------------------\n"); 97 t = time(NULL); 98 strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t)); 99 printf("%s\n", btmp); 100 printf("%s\n", buf); 101 printf("---------------------------------------------\n"); 102 fflush(stdout); 103 } 104 105 static void 106 set_tty(void) 107 { 108 struct termios ntty; 109 110 ntty = otty; 111 ntty.c_lflag &= ~ICANON; /* disable canonical operation */ 112 ntty.c_lflag &= ~ECHO; 113 #ifdef FLUSHO 114 ntty.c_lflag &= ~FLUSHO; 115 #endif 116 #ifdef PENDIN 117 ntty.c_lflag &= ~PENDIN; 118 #endif 119 #ifdef IEXTEN 120 ntty.c_lflag &= ~IEXTEN; 121 #endif 122 ntty.c_cc[VMIN] = 1; /* minimum of one character */ 123 ntty.c_cc[VTIME] = 0; /* timeout value */ 124 125 ntty.c_cc[VINTR] = 07; /* ^G */ 126 ntty.c_cc[VQUIT] = 07; /* ^G */ 127 tcsetattr(std_in, TCSANOW, &ntty); 128 } 129 130 static void 131 unset_tty(void) 132 { 133 134 tcsetattr(std_in, TCSANOW, &otty); 135 } 136 137 static void 138 fatal(int error, const char *buf) 139 { 140 141 unset_tty(); 142 if (buf) 143 errx(error, "fatal: %s", buf); 144 else 145 exit(error); 146 } 147 148 static int 149 open_snp(void) 150 { 151 int f, mode; 152 153 if (opt_write) 154 mode = O_RDWR; 155 else 156 mode = O_RDONLY; 157 158 if (opt_snpdev == NULL) 159 f = open(_PATH_DEV "snp", mode); 160 else 161 f = open(opt_snpdev, mode); 162 if (f == -1) 163 fatal(EX_OSFILE, "cannot open snoop device"); 164 165 return (f); 166 } 167 168 static void 169 cleanup(int signo __unused) 170 { 171 172 if (opt_timestamp) 173 timestamp("Logging Exited."); 174 close(snp_io); 175 unset_tty(); 176 exit(EX_OK); 177 } 178 179 static void 180 usage(void) 181 { 182 183 fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n"); 184 exit(EX_USAGE); 185 } 186 187 static void 188 setup_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 202 static void 203 detach_snp(void) 204 { 205 int fd; 206 207 fd = -1; 208 ioctl(snp_io, SNPSTTY, &fd); 209 } 210 211 static void 212 attach_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 226 static void 227 set_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 strlcpy(dev_name, buf, sizeof(dev_name)); 248 249 attach_snp(); 250 } 251 252 void 253 ask_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 280 int 281 main(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 tcgetattr(std_in, &otty); 324 325 if (modfind("snp") == -1) 326 if (kldload("snp") == -1 || modfind("snp") == -1) 327 warn("snp module not available"); 328 329 signal(SIGINT, cleanup); 330 331 snp_io = open_snp(); 332 setup_scr(); 333 334 if (*(av += optind) == NULL) { 335 if (opt_interactive && !opt_no_switch) 336 ask_dev(dev_name, MSG_INIT); 337 else 338 fatal(EX_DATAERR, "no device name given"); 339 } else 340 strlcpy(dev_name, *av, sizeof(dev_name)); 341 342 set_dev(dev_name); 343 344 if (!(buf = (char *) malloc(b_size))) 345 fatal(EX_UNAVAILABLE, "malloc failed"); 346 347 FD_ZERO(&fd_s); 348 349 for (;;) { 350 if (opt_interactive) 351 FD_SET(std_in, &fd_s); 352 FD_SET(snp_io, &fd_s); 353 res = select(snp_io + 1, &fd_s, NULL, NULL, NULL); 354 if (opt_interactive && FD_ISSET(std_in, &fd_s)) { 355 356 if ((res = ioctl(std_in, FIONREAD, &nread)) != 0) 357 fatal(EX_OSERR, "ioctl(FIONREAD)"); 358 if (nread > READB_LEN) 359 nread = READB_LEN; 360 rv = read(std_in, chb, nread); 361 if (rv == -1 || rv != nread) 362 fatal(EX_IOERR, "read (stdin) failed"); 363 364 switch (chb[0]) { 365 case CHR_CLEAR: 366 clear(); 367 break; 368 case CHR_SWITCH: 369 if (!opt_no_switch) { 370 detach_snp(); 371 ask_dev(dev_name, MSG_CHANGE); 372 set_dev(dev_name); 373 break; 374 } 375 default: 376 if (opt_write) { 377 rv = write(snp_io, chb, nread); 378 if (rv == -1 || rv != nread) { 379 detach_snp(); 380 if (opt_no_switch) 381 fatal(EX_IOERR, 382 "write failed"); 383 ask_dev(dev_name, MSG_NOWRITE); 384 set_dev(dev_name); 385 } 386 } 387 388 } 389 } 390 if (!FD_ISSET(snp_io, &fd_s)) 391 continue; 392 393 if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0) 394 fatal(EX_OSERR, "ioctl(FIONREAD)"); 395 396 switch (nread) { 397 case SNP_OFLOW: 398 if (opt_reconn_oflow) 399 attach_snp(); 400 else if (opt_interactive && !opt_no_switch) { 401 ask_dev(dev_name, MSG_OFLOW); 402 set_dev(dev_name); 403 } else 404 cleanup(-1); 405 break; 406 case SNP_DETACH: 407 case SNP_TTYCLOSE: 408 if (opt_reconn_close) 409 attach_snp(); 410 else if (opt_interactive && !opt_no_switch) { 411 ask_dev(dev_name, MSG_CLOSED); 412 set_dev(dev_name); 413 } else 414 cleanup(-1); 415 break; 416 default: 417 if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) { 418 free(buf); 419 if (!(buf = (char *) malloc(b_size / 2))) 420 fatal(EX_UNAVAILABLE, "malloc failed"); 421 b_size = b_size / 2; 422 } 423 if (nread > b_size) { 424 b_size = (nread % 2) ? (nread + 1) : (nread); 425 free(buf); 426 if (!(buf = (char *) malloc(b_size))) 427 fatal(EX_UNAVAILABLE, "malloc failed"); 428 } 429 rv = read(snp_io, buf, nread); 430 if (rv == -1 || rv != nread) 431 fatal(EX_IOERR, "read failed"); 432 rv = write(std_out, buf, nread); 433 if (rv == -1 || rv != nread) 434 fatal(EX_IOERR, "write failed"); 435 } 436 } /* While */ 437 return(0); 438 } 439