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 "snpXXX"}; 156 int f, mode, pos, c; 157 158 pos = strlen(snp) - 3; 159 if (opt_write) 160 mode = O_RDWR; 161 else 162 mode = O_RDONLY; 163 164 if (opt_snpdev == NULL) 165 for (c = 0; c <= 999; c++) { 166 snprintf(snp+pos, 4, "%d", c); 167 if ((f = open(snp, mode)) < 0) { 168 if (errno == EBUSY) 169 continue; 170 err(1, "open %s", snp); 171 } 172 return f; 173 } 174 else 175 if ((f = open(opt_snpdev, mode)) != -1) 176 return (f); 177 fatal(EX_OSFILE, "cannot open snoop device"); 178 return (0); 179 } 180 181 182 static void 183 cleanup(int signo __unused) 184 { 185 if (opt_timestamp) 186 timestamp("Logging Exited."); 187 close(snp_io); 188 unset_tty(); 189 exit(EX_OK); 190 } 191 192 193 static void 194 usage(void) 195 { 196 fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n"); 197 exit(EX_USAGE); 198 } 199 200 static void 201 setup_scr(void) 202 { 203 char *cbuf = gbuf, *term; 204 if (!opt_interactive) 205 return; 206 if ((term = getenv("TERM"))) 207 if (tgetent(tbuf, term) == 1) 208 if (tgetstr("cl", &cbuf)) 209 clear_ok = 1; 210 set_tty(); 211 clear(); 212 } 213 214 static void 215 detach_snp(void) 216 { 217 int fd; 218 219 fd = -1; 220 ioctl(snp_io, SNPSTTY, &fd); 221 } 222 223 static void 224 attach_snp(void) 225 { 226 int snp_tty; 227 228 snp_tty = open(dev_name, O_RDONLY | O_NONBLOCK); 229 if (snp_tty < 0) 230 fatal(EX_DATAERR, "can't open device"); 231 if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0) 232 fatal(EX_UNAVAILABLE, "cannot attach to tty"); 233 close(snp_tty); 234 if (opt_timestamp) 235 timestamp("Logging Started."); 236 } 237 238 239 static void 240 set_dev(const char *name) 241 { 242 char buf[DEV_NAME_LEN]; 243 struct stat sb; 244 245 if (strlen(name) > 5 && !strncmp(name, _PATH_DEV, sizeof _PATH_DEV - 1)) { 246 snprintf(buf, sizeof buf, "%s", name); 247 } else { 248 if (strlen(name) == 2) 249 sprintf(buf, "%s%s", _PATH_TTY, name); 250 else 251 sprintf(buf, "%s%s", _PATH_DEV, name); 252 } 253 254 if (*name == '\0' || stat(buf, &sb) < 0) 255 fatal(EX_DATAERR, "bad device name"); 256 257 if ((sb.st_mode & S_IFMT) != S_IFCHR) 258 fatal(EX_DATAERR, "must be a character device"); 259 260 strncpy(dev_name, buf, DEV_NAME_LEN); 261 262 attach_snp(); 263 } 264 265 void 266 ask_dev(char *dbuf, const char *msg) 267 { 268 char buf[DEV_NAME_LEN]; 269 int len; 270 271 clear(); 272 unset_tty(); 273 274 if (msg) 275 printf("%s\n", msg); 276 if (dbuf) 277 printf("Enter device name [%s]:", dbuf); 278 else 279 printf("Enter device name:"); 280 281 if (fgets(buf, DEV_NAME_LEN - 1, stdin)) { 282 len = strlen(buf); 283 if (buf[len - 1] == '\n') 284 buf[len - 1] = '\0'; 285 if (buf[0] != '\0' && buf[0] != ' ') 286 strcpy(dbuf, buf); 287 } 288 set_tty(); 289 } 290 291 #define READB_LEN 5 292 293 int 294 main(int ac, char *av[]) 295 { 296 int ch, res, rv, nread; 297 size_t b_size = MIN_SIZE; 298 char *buf, chb[READB_LEN]; 299 fd_set fd_s; 300 301 (void) setlocale(LC_TIME, ""); 302 303 if (isatty(std_out)) 304 opt_interactive = 1; 305 else 306 opt_interactive = 0; 307 308 309 while ((ch = getopt(ac, av, "Wciotnf:")) != -1) 310 switch (ch) { 311 case 'W': 312 opt_write = 1; 313 break; 314 case 'c': 315 opt_reconn_close = 1; 316 break; 317 case 'i': 318 opt_interactive = 1; 319 break; 320 case 'o': 321 opt_reconn_oflow = 1; 322 break; 323 case 't': 324 opt_timestamp = 1; 325 break; 326 case 'n': 327 opt_no_switch = 1; 328 break; 329 case 'f': 330 opt_snpdev = optarg; 331 break; 332 case '?': 333 default: 334 usage(); 335 } 336 337 if (modfind("snp") == -1) 338 if (kldload("snp") == -1 || modfind("snp") == -1) 339 warn("snp module not available"); 340 341 signal(SIGINT, cleanup); 342 343 snp_io = open_snp(); 344 setup_scr(); 345 346 if (*(av += optind) == NULL) { 347 if (opt_interactive && !opt_no_switch) 348 ask_dev(dev_name, MSG_INIT); 349 else 350 fatal(EX_DATAERR, "no device name given"); 351 } else 352 strncpy(dev_name, *av, DEV_NAME_LEN); 353 354 set_dev(dev_name); 355 356 if (!(buf = (char *) malloc(b_size))) 357 fatal(EX_UNAVAILABLE, "malloc failed"); 358 359 FD_ZERO(&fd_s); 360 361 while (1) { 362 if (opt_interactive) 363 FD_SET(std_in, &fd_s); 364 FD_SET(snp_io, &fd_s); 365 res = select(snp_io + 1, &fd_s, NULL, NULL, NULL); 366 if (opt_interactive && FD_ISSET(std_in, &fd_s)) { 367 368 if ((res = ioctl(std_in, FIONREAD, &nread)) != 0) 369 fatal(EX_OSERR, "ioctl(FIONREAD)"); 370 if (nread > READB_LEN) 371 nread = READB_LEN; 372 rv = read(std_in, chb, nread); 373 if (rv == -1 || rv != nread) 374 fatal(EX_IOERR, "read (stdin) failed"); 375 376 switch (chb[0]) { 377 case CHR_CLEAR: 378 clear(); 379 break; 380 case CHR_SWITCH: 381 if (!opt_no_switch) { 382 detach_snp(); 383 ask_dev(dev_name, MSG_CHANGE); 384 set_dev(dev_name); 385 break; 386 } 387 default: 388 if (opt_write) { 389 rv = write(snp_io, chb, nread); 390 if (rv == -1 || rv != nread) { 391 detach_snp(); 392 if (opt_no_switch) 393 fatal(EX_IOERR, 394 "write failed"); 395 ask_dev(dev_name, MSG_NOWRITE); 396 set_dev(dev_name); 397 } 398 } 399 400 } 401 } 402 if (!FD_ISSET(snp_io, &fd_s)) 403 continue; 404 405 if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0) 406 fatal(EX_OSERR, "ioctl(FIONREAD)"); 407 408 switch (nread) { 409 case SNP_OFLOW: 410 if (opt_reconn_oflow) 411 attach_snp(); 412 else if (opt_interactive && !opt_no_switch) { 413 ask_dev(dev_name, MSG_OFLOW); 414 set_dev(dev_name); 415 } else 416 cleanup(-1); 417 break; 418 case SNP_DETACH: 419 case SNP_TTYCLOSE: 420 if (opt_reconn_close) 421 attach_snp(); 422 else if (opt_interactive && !opt_no_switch) { 423 ask_dev(dev_name, MSG_CLOSED); 424 set_dev(dev_name); 425 } else 426 cleanup(-1); 427 break; 428 default: 429 if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) { 430 free(buf); 431 if (!(buf = (char *) malloc(b_size / 2))) 432 fatal(EX_UNAVAILABLE, "malloc failed"); 433 b_size = b_size / 2; 434 } 435 if (nread > b_size) { 436 b_size = (nread % 2) ? (nread + 1) : (nread); 437 free(buf); 438 if (!(buf = (char *) malloc(b_size))) 439 fatal(EX_UNAVAILABLE, "malloc failed"); 440 } 441 rv = read(snp_io, buf, nread); 442 if (rv == -1 || rv != nread) 443 fatal(EX_IOERR, "read failed"); 444 rv = write(std_out, buf, nread); 445 if (rv == -1 || rv != nread) 446 fatal(EX_IOERR, "write failed"); 447 } 448 } /* While */ 449 return(0); 450 } 451 452