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