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 * $FreeBSD: src/usr.sbin/watch/watch.c,v 1.18.2.3 2002/08/17 00:59:03 mikeh Exp $ 16 * $DragonFly: src/usr.sbin/watch/watch.c,v 1.2 2003/06/17 04:30:04 dillon Exp $ 17 */ 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 <locale.h> 29 #include <paths.h> 30 #include <signal.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <sysexits.h> 35 #include <termcap.h> 36 #include <termios.h> 37 #include <time.h> 38 #include <unistd.h> 39 40 #define MSG_INIT "Snoop started." 41 #define MSG_OFLOW "Snoop stopped due to overflow. Reconnecting." 42 #define MSG_CLOSED "Snoop stopped due to tty close. Reconnecting." 43 #define MSG_CHANGE "Snoop device change by user request." 44 #define MSG_NOWRITE "Snoop device change due to write failure." 45 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 __P((void)); 54 static void timestamp __P((const char *)); 55 static void set_tty __P((void)); 56 static void unset_tty __P((void)); 57 static void fatal __P((int, const char *)); 58 static int open_snp __P((void)); 59 static void cleanup __P((int)); 60 static void usage __P((void)); 61 static void setup_scr __P((void)); 62 static void attach_snp __P((void)); 63 static void detach_snp __P((void)); 64 static void set_dev __P((const char *)); 65 static void ask_dev __P((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 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 if (opt_snpdev == NULL) 168 for (c = '0'; c <= '9'; c++) { 169 snp[8] = c; 170 if ((f = open(snp, mode)) < 0) 171 continue; 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(signo) 184 int signo __unused; 185 { 186 if (opt_timestamp) 187 timestamp("Logging Exited."); 188 close(snp_io); 189 unset_tty(); 190 exit(EX_OK); 191 } 192 193 194 static void 195 usage() 196 { 197 fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n"); 198 exit(EX_USAGE); 199 } 200 201 static void 202 setup_scr() 203 { 204 char *cbuf = gbuf, *term; 205 if (!opt_interactive) 206 return; 207 if ((term = getenv("TERM"))) 208 if (tgetent(tbuf, term) == 1) 209 if (tgetstr("cl", &cbuf)) 210 clear_ok = 1; 211 set_tty(); 212 clear(); 213 } 214 215 static void 216 detach_snp() 217 { 218 dev_t dev; 219 220 dev = NODEV; 221 ioctl(snp_io, SNPSTTY, &dev); 222 } 223 224 static void 225 attach_snp() 226 { 227 if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0) 228 fatal(EX_UNAVAILABLE, "cannot attach to tty"); 229 if (opt_timestamp) 230 timestamp("Logging Started."); 231 } 232 233 234 static void 235 set_dev(name) 236 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 } 244 else { 245 if (strlen(name) == 2) 246 sprintf(buf, "%s%s", _PATH_TTY, name); 247 else 248 sprintf(buf, "%s%s", _PATH_DEV, name); 249 } 250 251 if (*name == '\0' || stat(buf, &sb) < 0) 252 fatal(EX_DATAERR, "bad device name"); 253 254 if ((sb.st_mode & S_IFMT) != S_IFCHR) 255 fatal(EX_DATAERR, "must be a character device"); 256 257 snp_tty = sb.st_rdev; 258 attach_snp(); 259 } 260 261 void 262 ask_dev(dbuf, msg) 263 char *dbuf; 264 const char *msg; 265 { 266 char buf[DEV_NAME_LEN]; 267 int len; 268 269 clear(); 270 unset_tty(); 271 272 if (msg) 273 printf("%s\n", msg); 274 if (dbuf) 275 printf("Enter device name [%s]:", dbuf); 276 else 277 printf("Enter device name:"); 278 279 if (fgets(buf, DEV_NAME_LEN - 1, stdin)) { 280 len = strlen(buf); 281 if (buf[len - 1] == '\n') 282 buf[len - 1] = '\0'; 283 if (buf[0] != '\0' && buf[0] != ' ') 284 strcpy(dbuf, buf); 285 } 286 set_tty(); 287 } 288 289 #define READB_LEN 5 290 291 int 292 main(ac, av) 293 int ac; 294 char **av; 295 { 296 int res, idata, rv; 297 size_t nread, b_size = MIN_SIZE; 298 char ch, *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 setup_scr(); 344 snp_io = open_snp(); 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 || (unsigned)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 || (unsigned)rv != nread) { 391 detach_snp(); 392 if (opt_no_switch) 393 fatal(EX_IOERR, "write failed"); 394 ask_dev(dev_name, MSG_NOWRITE); 395 set_dev(dev_name); 396 } 397 } 398 399 } 400 } 401 if (!FD_ISSET(snp_io, &fd_s)) 402 continue; 403 404 if ((res = ioctl(snp_io, FIONREAD, &idata)) != 0) 405 fatal(EX_OSERR, "ioctl(FIONREAD)"); 406 407 switch (idata) { 408 case SNP_OFLOW: 409 if (opt_reconn_oflow) 410 attach_snp(); 411 else if (opt_interactive && !opt_no_switch) { 412 ask_dev(dev_name, MSG_OFLOW); 413 set_dev(dev_name); 414 } else 415 cleanup(-1); 416 break; 417 case SNP_DETACH: 418 case SNP_TTYCLOSE: 419 if (opt_reconn_close) 420 attach_snp(); 421 else if (opt_interactive && !opt_no_switch) { 422 ask_dev(dev_name, MSG_CLOSED); 423 set_dev(dev_name); 424 } else 425 cleanup(-1); 426 break; 427 default: 428 nread = (unsigned)idata; 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 || (unsigned)rv != nread) 443 fatal(EX_IOERR, "read failed"); 444 rv = write(std_out, buf, nread); 445 if (rv == -1 || (unsigned)rv != nread) 446 fatal(EX_IOERR, "write failed"); 447 } 448 } /* While */ 449 return(0); 450 } 451 452