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 #ifndef lint 17 static const char rcsid[] = 18 "$FreeBSD$"; 19 #endif /* not lint */ 20 21 #include <sys/param.h> 22 #include <sys/fcntl.h> 23 #include <sys/filio.h> 24 #include <sys/snoop.h> 25 #include <sys/stat.h> 26 #include <sys/linker.h> 27 #include <sys/module.h> 28 29 #include <err.h> 30 #include <locale.h> 31 #include <paths.h> 32 #include <signal.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <sysexits.h> 37 #include <termcap.h> 38 #include <termios.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 __P((void)); 55 static void timestamp __P((const char *)); 56 static void set_tty __P((void)); 57 static void unset_tty __P((void)); 58 static void fatal __P((int, const char *)); 59 static int open_snp __P((void)); 60 static void cleanup __P((int)); 61 static void usage __P((void)); 62 static void setup_scr __P((void)); 63 static void attach_snp __P((void)); 64 static void detach_snp __P((void)); 65 static void set_dev __P((const char *)); 66 static void ask_dev __P((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 75 char dev_name[DEV_NAME_LEN]; 76 int snp_io; 77 dev_t snp_tty; 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() 88 { 89 if (clear_ok) 90 tputs(gbuf, 1, putchar); 91 fflush(stdout); 92 } 93 94 static void 95 timestamp(buf) 96 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() 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() 138 { 139 tcsetattr (std_in, TCSANOW, &otty); 140 } 141 142 143 static void 144 fatal(error, buf) 145 int error; 146 const char *buf; 147 { 148 unset_tty(); 149 if (buf) 150 errx(error, "fatal: %s", buf); 151 else 152 exit(error); 153 } 154 155 static int 156 open_snp() 157 { 158 char snp[] = {_PATH_DEV "snpX"}; 159 char c; 160 int f, mode; 161 162 if (opt_write) 163 mode = O_RDWR; 164 else 165 mode = O_RDONLY; 166 167 for (c = '0'; c <= '9'; c++) { 168 snp[8] = c; 169 if ((f = open(snp, mode)) < 0) 170 continue; 171 return f; 172 } 173 fatal(EX_OSFILE, "cannot open snoop device"); 174 return (0); 175 } 176 177 178 static void 179 cleanup(signo) 180 int signo __unused; 181 { 182 if (opt_timestamp) 183 timestamp("Logging Exited."); 184 close(snp_io); 185 unset_tty(); 186 exit(EX_OK); 187 } 188 189 190 static void 191 usage() 192 { 193 fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n"); 194 exit(EX_USAGE); 195 } 196 197 static void 198 setup_scr() 199 { 200 char *cbuf = gbuf, *term; 201 if (!opt_interactive) 202 return; 203 if ((term = getenv("TERM"))) 204 if (tgetent(tbuf, term) == 1) 205 if (tgetstr("cl", &cbuf)) 206 clear_ok = 1; 207 set_tty(); 208 clear(); 209 } 210 211 static void 212 detach_snp() 213 { 214 dev_t dev; 215 216 dev = NODEV; 217 ioctl(snp_io, SNPSTTY, &dev); 218 } 219 220 static void 221 attach_snp() 222 { 223 if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0) 224 fatal(EX_UNAVAILABLE, "cannot attach to tty"); 225 if (opt_timestamp) 226 timestamp("Logging Started."); 227 } 228 229 230 static void 231 set_dev(name) 232 const char *name; 233 { 234 char buf[DEV_NAME_LEN]; 235 struct stat sb; 236 237 if (strlen(name) > 5 && !strncmp(name, _PATH_DEV, sizeof _PATH_DEV - 1)) { 238 snprintf(buf, sizeof buf, "%s", name); 239 } 240 else { 241 if (strlen(name) == 2) 242 sprintf(buf, "%s%s", _PATH_TTY, name); 243 else 244 sprintf(buf, "%s%s", _PATH_DEV, name); 245 } 246 247 if (*name == '\0' || stat(buf, &sb) < 0) 248 fatal(EX_DATAERR, "bad device name"); 249 250 if ((sb.st_mode & S_IFMT) != S_IFCHR) 251 fatal(EX_DATAERR, "must be a character device"); 252 253 snp_tty = sb.st_rdev; 254 attach_snp(); 255 } 256 257 void 258 ask_dev(dbuf, msg) 259 char *dbuf; 260 const char *msg; 261 { 262 char buf[DEV_NAME_LEN]; 263 int len; 264 265 clear(); 266 unset_tty(); 267 268 if (msg) 269 printf("%s\n", msg); 270 if (dbuf) 271 printf("Enter device name [%s]:", dbuf); 272 else 273 printf("Enter device name:"); 274 275 if (fgets(buf, DEV_NAME_LEN - 1, stdin)) { 276 len = strlen(buf); 277 if (buf[len - 1] == '\n') 278 buf[len - 1] = '\0'; 279 if (buf[0] != '\0' && buf[0] != ' ') 280 strcpy(dbuf, buf); 281 } 282 set_tty(); 283 } 284 285 #define READB_LEN 5 286 287 int 288 main(ac, av) 289 int ac; 290 char **av; 291 { 292 int res, idata, rv; 293 size_t nread, b_size = MIN_SIZE; 294 char ch, *buf, chb[READB_LEN]; 295 fd_set fd_s; 296 297 (void) setlocale(LC_TIME, ""); 298 299 if (isatty(std_out)) 300 opt_interactive = 1; 301 else 302 opt_interactive = 0; 303 304 305 while ((ch = getopt(ac, av, "Wciotn")) != -1) 306 switch (ch) { 307 case 'W': 308 opt_write = 1; 309 break; 310 case 'c': 311 opt_reconn_close = 1; 312 break; 313 case 'i': 314 opt_interactive = 1; 315 break; 316 case 'o': 317 opt_reconn_oflow = 1; 318 break; 319 case 't': 320 opt_timestamp = 1; 321 break; 322 case 'n': 323 opt_no_switch = 1; 324 break; 325 case '?': 326 default: 327 usage(); 328 } 329 330 if (modfind("snp") == -1) 331 if (kldload("snp") == -1 || modfind("snp") == -1) 332 warn("snp module not available"); 333 334 signal(SIGINT, cleanup); 335 336 setup_scr(); 337 snp_io = open_snp(); 338 339 if (*(av += optind) == NULL) { 340 if (opt_interactive && !opt_no_switch) 341 ask_dev(dev_name, MSG_INIT); 342 else 343 fatal(EX_DATAERR, "no device name given"); 344 } else 345 strncpy(dev_name, *av, DEV_NAME_LEN); 346 347 set_dev(dev_name); 348 349 if (!(buf = (char *) malloc(b_size))) 350 fatal(EX_UNAVAILABLE, "malloc failed"); 351 352 FD_ZERO(&fd_s); 353 354 while (1) { 355 if (opt_interactive) 356 FD_SET(std_in, &fd_s); 357 FD_SET(snp_io, &fd_s); 358 res = select(snp_io + 1, &fd_s, NULL, NULL, NULL); 359 if (opt_interactive && FD_ISSET(std_in, &fd_s)) { 360 361 if ((res = ioctl(std_in, FIONREAD, &nread)) != 0) 362 fatal(EX_OSERR, "ioctl(FIONREAD)"); 363 if (nread > READB_LEN) 364 nread = READB_LEN; 365 rv = read(std_in, chb, nread); 366 if (rv == -1 || (unsigned)rv != nread) 367 fatal(EX_IOERR, "read (stdin) failed"); 368 369 switch (chb[0]) { 370 case CHR_CLEAR: 371 clear(); 372 break; 373 case CHR_SWITCH: 374 if (opt_no_switch) 375 break; 376 detach_snp(); 377 ask_dev(dev_name, MSG_CHANGE); 378 set_dev(dev_name); 379 break; 380 default: 381 if (opt_write) { 382 rv = write(snp_io, chb, nread); 383 if (rv == -1 || (unsigned)rv != nread) { 384 detach_snp(); 385 if (opt_no_switch) 386 fatal(EX_IOERR, "write failed"); 387 ask_dev(dev_name, MSG_NOWRITE); 388 set_dev(dev_name); 389 } 390 } 391 392 } 393 } 394 if (!FD_ISSET(snp_io, &fd_s)) 395 continue; 396 397 if ((res = ioctl(snp_io, FIONREAD, &idata)) != 0) 398 fatal(EX_OSERR, "ioctl(FIONREAD)"); 399 400 switch (idata) { 401 case SNP_OFLOW: 402 if (opt_reconn_oflow) 403 attach_snp(); 404 else if (opt_interactive && !opt_no_switch) { 405 ask_dev(dev_name, MSG_OFLOW); 406 set_dev(dev_name); 407 } else 408 cleanup(-1); 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 default: 419 nread = (unsigned)idata; 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 || (unsigned)rv != nread) 434 fatal(EX_IOERR, "read failed"); 435 rv = write(std_out, buf, nread); 436 if (rv == -1 || (unsigned)rv != nread) 437 fatal(EX_IOERR, "write failed"); 438 } 439 } /* While */ 440 return(0); 441 } 442 443