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