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/types.h> 17 #include <sys/fcntl.h> 18 #include <sys/filio.h> 19 #include <sys/snoop.h> 20 #include <sys/stat.h> 21 22 #include <locale.h> 23 #include <signal.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <sysexits.h> 28 #include <termcap.h> 29 #include <termios.h> 30 #include <unistd.h> 31 32 #define MSG_INIT "Snoop started." 33 #define MSG_OFLOW "Snoop stopped due to overflow. Reconnecting." 34 #define MSG_CLOSED "Snoop stopped due to tty close. Reconnecting." 35 #define MSG_CHANGE "Snoop device change by user request." 36 #define MSG_NOWRITE "Snoop device change due to write failure." 37 38 39 #define DEV_NAME_LEN 1024 /* for /dev/ttyXX++ */ 40 #define MIN_SIZE 256 41 42 #define CHR_SWITCH 24 /* Ctrl+X */ 43 #define CHR_CLEAR 23 /* Ctrl+V */ 44 45 46 int opt_reconn_close = 0; 47 int opt_reconn_oflow = 0; 48 int opt_interactive = 1; 49 int opt_timestamp = 0; 50 int opt_write = 0; 51 int opt_no_switch = 0; 52 53 char dev_name[DEV_NAME_LEN]; 54 int snp_io; 55 dev_t snp_tty; 56 int std_in = 0, std_out = 1; 57 58 59 int clear_ok = 0; 60 struct termios otty; 61 char tbuf[1024], buf[1024]; 62 63 64 void 65 clear() 66 { 67 if (clear_ok) 68 tputs(buf, 1, putchar); 69 fflush(stdout); 70 } 71 72 void 73 timestamp(buf) 74 char *buf; 75 { 76 time_t t; 77 char btmp[1024]; 78 clear(); 79 printf("\n---------------------------------------------\n"); 80 t = time(NULL); 81 strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t)); 82 printf("%s\n", btmp); 83 printf("%s\n", buf); 84 printf("---------------------------------------------\n"); 85 fflush(stdout); 86 } 87 88 void 89 set_tty() 90 { 91 struct termios ntty; 92 93 tcgetattr (std_in, &otty); 94 ntty = otty; 95 ntty.c_lflag &= ~ICANON; /* disable canonical operation */ 96 ntty.c_lflag &= ~ECHO; 97 #ifdef FLUSHO 98 ntty.c_lflag &= ~FLUSHO; 99 #endif 100 #ifdef PENDIN 101 ntty.c_lflag &= ~PENDIN; 102 #endif 103 #ifdef IEXTEN 104 ntty.c_lflag &= ~IEXTEN; 105 #endif 106 ntty.c_cc[VMIN] = 1; /* minimum of one character */ 107 ntty.c_cc[VTIME] = 0; /* timeout value */ 108 109 ntty.c_cc[VINTR] = 07; /* ^G */ 110 ntty.c_cc[VQUIT] = 07; /* ^G */ 111 tcsetattr (std_in, TCSANOW, &ntty); 112 } 113 114 void 115 unset_tty() 116 { 117 tcsetattr (std_in, TCSANOW, &otty); 118 } 119 120 121 void 122 fatal(err, buf) 123 unsigned int err; 124 char *buf; 125 { 126 unset_tty(); 127 if (buf) 128 fprintf(stderr, "Fatal: %s\n", buf); 129 exit(err); 130 } 131 132 int 133 open_snp() 134 { 135 char snp[] = {"/dev/snpX"}; 136 char c; 137 int f, mode; 138 139 if (opt_write) 140 mode = O_RDWR; 141 else 142 mode = O_RDONLY; 143 144 for (c = '0'; c <= '9'; c++) { 145 snp[8] = c; 146 if ((f = open(snp, mode)) < 0) 147 continue; 148 return f; 149 } 150 fatal(EX_OSFILE, "Cannot open snoop device."); 151 } 152 153 154 void 155 cleanup() 156 { 157 if (opt_timestamp) 158 timestamp("Logging Exited."); 159 close(snp_io); 160 unset_tty(); 161 exit(EX_OK); 162 } 163 164 165 void 166 show_usage() 167 { 168 printf("watch -[ciotnW] [tty name]\n"); 169 exit(EX_USAGE); 170 } 171 172 void 173 setup_scr() 174 { 175 char *cbuf = buf, *term; 176 if (!opt_interactive) 177 return; 178 if ((term = getenv("TERM"))) 179 if (tgetent(tbuf, term) == 1) 180 if (tgetstr("cl", &cbuf)) 181 clear_ok = 1; 182 set_tty(); 183 clear(); 184 } 185 186 187 int 188 ctoh(c) 189 char c; 190 { 191 if (c >= '0' && c <= '9') 192 return (int) (c - '0'); 193 194 if (c >= 'a' && c <= 'f') 195 return (int) (c - 'a' + 10); 196 197 fatal(EX_DATAERR, "Bad tty number."); 198 } 199 200 201 void 202 detach_snp() 203 { 204 dev_t dev; 205 206 dev = -1; 207 ioctl(snp_io, SNPSTTY, &dev); 208 } 209 210 void 211 attach_snp() 212 { 213 if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0) 214 fatal(EX_UNAVAILABLE, "Cannot attach to tty."); 215 if (opt_timestamp) 216 timestamp("Logging Started."); 217 } 218 219 220 void 221 set_dev(name) 222 char *name; 223 { 224 char buf[DEV_NAME_LEN]; 225 struct stat sb; 226 227 if (strlen(name) > 5 && !strncmp(name, "/dev/", 5)) { 228 snprintf(buf, sizeof buf, "%s", name); 229 } 230 else { 231 if (strlen(name) == 2) 232 sprintf(buf, "/dev/tty%s", name); 233 else 234 sprintf(buf, "/dev/%s", name); 235 } 236 237 if (*name == '\0' || stat(buf, &sb) < 0) 238 fatal(EX_DATAERR, "Bad device name."); 239 240 if ((sb.st_mode & S_IFMT) != S_IFCHR) 241 fatal(EX_DATAERR, "Must be a character device."); 242 243 snp_tty = sb.st_rdev; 244 attach_snp(); 245 } 246 247 void 248 ask_dev(dev_name, msg) 249 char *dev_name, *msg; 250 { 251 char buf[DEV_NAME_LEN]; 252 int len; 253 254 clear(); 255 unset_tty(); 256 257 if (msg) 258 printf("%s\n", msg); 259 if (dev_name) 260 printf("Enter device name [%s]:", dev_name); 261 else 262 printf("Enter device name:"); 263 264 if (fgets(buf, DEV_NAME_LEN - 1, stdin)) { 265 len = strlen(buf); 266 if (buf[len - 1] == '\n') 267 buf[len - 1] = '\0'; 268 if (buf[0] != '\0' && buf[0] != ' ') 269 strcpy(dev_name, buf); 270 } 271 set_tty(); 272 } 273 274 #define READB_LEN 5 275 276 void 277 main(ac, av) 278 int ac; 279 char **av; 280 { 281 int res, nread, b_size = MIN_SIZE; 282 extern int optind; 283 char ch, *buf, chb[READB_LEN]; 284 fd_set fd_s; 285 286 (void) setlocale(LC_TIME, ""); 287 288 if (isatty(std_out)) 289 opt_interactive = 1; 290 else 291 opt_interactive = 0; 292 293 294 while ((ch = getopt(ac, av, "Wciotn")) != -1) 295 switch (ch) { 296 case 'W': 297 opt_write = 1; 298 break; 299 case 'c': 300 opt_reconn_close = 1; 301 break; 302 case 'i': 303 opt_interactive = 1; 304 break; 305 case 'o': 306 opt_reconn_oflow = 1; 307 break; 308 case 't': 309 opt_timestamp = 1; 310 break; 311 case 'n': 312 opt_no_switch = 1; 313 break; 314 case '?': 315 default: 316 show_usage(); 317 exit(1); 318 } 319 320 signal(SIGINT, cleanup); 321 322 setup_scr(); 323 snp_io = open_snp(); 324 325 if (*(av += optind) == NULL) { 326 if (opt_interactive && !opt_no_switch) 327 ask_dev(dev_name, MSG_INIT); 328 else 329 fatal(EX_DATAERR, "No device name given."); 330 } else 331 strncpy(dev_name, *av, DEV_NAME_LEN); 332 333 set_dev(dev_name); 334 335 if (!(buf = (char *) malloc(b_size))) 336 fatal(EX_UNAVAILABLE, "Cannot malloc()."); 337 338 FD_ZERO(&fd_s); 339 340 while (1) { 341 if (opt_interactive) 342 FD_SET(std_in, &fd_s); 343 FD_SET(snp_io, &fd_s); 344 res = select(snp_io + 1, &fd_s, NULL, NULL, NULL); 345 if (opt_interactive && FD_ISSET(std_in, &fd_s)) { 346 347 if ((res = ioctl(std_in, FIONREAD, &nread)) != 0) 348 fatal(EX_OSERR, "ioctl() failed."); 349 if (nread > READB_LEN) 350 nread = READB_LEN; 351 if (read(std_in,chb,nread)!=nread) 352 fatal(EX_IOERR, "read (stdin) failed."); 353 354 switch (chb[0]) { 355 case CHR_CLEAR: 356 clear(); 357 break; 358 case CHR_SWITCH: 359 if (opt_no_switch) 360 break; 361 detach_snp(); 362 ask_dev(dev_name, MSG_CHANGE); 363 set_dev(dev_name); 364 break; 365 default: 366 if (opt_write) { 367 if (write(snp_io,chb,nread) != nread) { 368 detach_snp(); 369 if (opt_no_switch) 370 fatal(EX_IOERR, "Write failed."); 371 ask_dev(dev_name, MSG_NOWRITE); 372 set_dev(dev_name); 373 } 374 } 375 376 } 377 } 378 if (!FD_ISSET(snp_io, &fd_s)) 379 continue; 380 381 if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0) 382 fatal(EX_OSERR, "ioctl() failed."); 383 384 switch (nread) { 385 case SNP_OFLOW: 386 if (opt_reconn_oflow) 387 attach_snp(); 388 else if (opt_interactive && !opt_no_switch) { 389 ask_dev(dev_name, MSG_OFLOW); 390 set_dev(dev_name); 391 } else 392 cleanup(); 393 case SNP_DETACH: 394 case SNP_TTYCLOSE: 395 if (opt_reconn_close) 396 attach_snp(); 397 else if (opt_interactive && !opt_no_switch) { 398 ask_dev(dev_name, MSG_CLOSED); 399 set_dev(dev_name); 400 } else 401 cleanup(); 402 default: 403 if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) { 404 free(buf); 405 if (!(buf = (char *) malloc(b_size / 2))) 406 fatal(EX_UNAVAILABLE, "Cannot malloc()"); 407 b_size = b_size / 2; 408 } 409 if (nread > b_size) { 410 b_size = (nread % 2) ? (nread + 1) : (nread); 411 free(buf); 412 if (!(buf = (char *) malloc(b_size))) 413 fatal(EX_UNAVAILABLE, "Cannot malloc()"); 414 } 415 if (read(snp_io, buf, nread) < nread) 416 fatal(EX_IOERR, "read failed."); 417 if (write(std_out, buf, nread) < nread) 418 fatal(EX_IOERR, "write failed."); 419 } 420 } /* While */ 421 } 422 423