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