1 /* 2 * Copyright (c) 1980, 1988 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7 #ifndef lint 8 static char sccsid[] = "@(#)optr.c 5.4 (Berkeley) 02/23/91"; 9 #endif /* not lint */ 10 11 #include "dump.h" 12 #include <sys/wait.h> 13 #include <errno.h> 14 #include <grp.h> 15 #include <varargs.h> 16 #include "pathnames.h" 17 18 static void alarmcatch(); 19 static void sendmes(); 20 21 /* 22 * Query the operator; This previously-fascist piece of code 23 * no longer requires an exact response. 24 * It is intended to protect dump aborting by inquisitive 25 * people banging on the console terminal to see what is 26 * happening which might cause dump to croak, destroying 27 * a large number of hours of work. 28 * 29 * Every 2 minutes we reprint the message, alerting others 30 * that dump needs attention. 31 */ 32 int timeout; 33 char *attnmessage; /* attention message */ 34 35 int 36 query(question) 37 char *question; 38 { 39 char replybuffer[64]; 40 int back, errcount; 41 FILE *mytty; 42 43 if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 44 quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 45 attnmessage = question; 46 timeout = 0; 47 alarmcatch(); 48 back = -1; 49 errcount = 0; 50 do { 51 if (fgets(replybuffer, 63, mytty) == NULL) { 52 clearerr(mytty); 53 if (++errcount > 30) /* XXX ugly */ 54 quit("excessive operator query failures\n"); 55 } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 56 back = 1; 57 } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 58 back = 0; 59 } else { 60 (void) fprintf(stderr, 61 " DUMP: \"Yes\" or \"No\"?\n"); 62 (void) fprintf(stderr, 63 " DUMP: %s: (\"yes\" or \"no\") ", question); 64 } 65 } while (back < 0); 66 67 /* 68 * Turn off the alarm, and reset the signal to trap out.. 69 */ 70 alarm(0); 71 if (signal(SIGALRM, sigalrm) == SIG_IGN) 72 signal(SIGALRM, SIG_IGN); 73 fclose(mytty); 74 return(back); 75 } 76 77 char lastmsg[100]; 78 79 /* 80 * Alert the console operator, and enable the alarm clock to 81 * sleep for 2 minutes in case nobody comes to satisfy dump 82 */ 83 static void 84 alarmcatch() 85 { 86 if (notify == 0) { 87 if (timeout == 0) 88 (void) fprintf(stderr, 89 " DUMP: %s: (\"yes\" or \"no\") ", 90 attnmessage); 91 else 92 msgtail("\7\7"); 93 } else { 94 if (timeout) { 95 msgtail("\n"); 96 broadcast(""); /* just print last msg */ 97 } 98 (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", 99 attnmessage); 100 } 101 signal(SIGALRM, alarmcatch); 102 alarm(120); 103 timeout = 1; 104 } 105 106 /* 107 * Here if an inquisitive operator interrupts the dump program 108 */ 109 void 110 interrupt() 111 { 112 msg("Interrupt received.\n"); 113 if (query("Do you want to abort dump?")) 114 dumpabort(); 115 } 116 117 /* 118 * The following variables and routines manage alerting 119 * operators to the status of dump. 120 * This works much like wall(1) does. 121 */ 122 struct group *gp; 123 124 /* 125 * Get the names from the group entry "operator" to notify. 126 */ 127 void 128 set_operators() 129 { 130 if (!notify) /*not going to notify*/ 131 return; 132 gp = getgrnam(OPGRENT); 133 endgrent(); 134 if (gp == NULL) { 135 msg("No group entry for %s.\n", OPGRENT); 136 notify = 0; 137 return; 138 } 139 } 140 141 struct tm *localtime(); 142 struct tm *localclock; 143 144 /* 145 * We fork a child to do the actual broadcasting, so 146 * that the process control groups are not messed up 147 */ 148 void 149 broadcast(message) 150 char *message; 151 { 152 time_t clock; 153 FILE *f_utmp; 154 struct utmp utmp; 155 int nusers; 156 char **np; 157 int pid, s; 158 159 if (!notify || gp == NULL) 160 return; 161 162 switch (pid = fork()) { 163 case -1: 164 return; 165 case 0: 166 break; 167 default: 168 while (wait(&s) != pid) 169 continue; 170 return; 171 } 172 173 clock = time(0); 174 localclock = localtime(&clock); 175 176 if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { 177 msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); 178 return; 179 } 180 181 nusers = 0; 182 while (!feof(f_utmp)) { 183 if (fread(&utmp, sizeof (struct utmp), 1, f_utmp) != 1) 184 break; 185 if (utmp.ut_name[0] == 0) 186 continue; 187 nusers++; 188 for (np = gp->gr_mem; *np; np++) { 189 if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 190 continue; 191 /* 192 * Do not send messages to operators on dialups 193 */ 194 if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 195 continue; 196 #ifdef DEBUG 197 msg("Message to %s at %s\n", *np, utmp.ut_line); 198 #endif 199 sendmes(utmp.ut_line, message); 200 } 201 } 202 (void) fclose(f_utmp); 203 Exit(0); /* the wait in this same routine will catch this */ 204 /* NOTREACHED */ 205 } 206 207 static void 208 sendmes(tty, message) 209 char *tty, *message; 210 { 211 char t[50], buf[BUFSIZ]; 212 register char *cp; 213 int lmsg = 1; 214 FILE *f_tty; 215 216 (void) strcpy(t, _PATH_DEV); 217 (void) strcat(t, tty); 218 219 if ((f_tty = fopen(t, "w")) != NULL) { 220 setbuf(f_tty, buf); 221 (void) fprintf(f_tty, 222 "\n\ 223 \7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ 224 DUMP: NEEDS ATTENTION: ", 225 localclock->tm_hour, localclock->tm_min); 226 for (cp = lastmsg; ; cp++) { 227 if (*cp == '\0') { 228 if (lmsg) { 229 cp = message; 230 if (*cp == '\0') 231 break; 232 lmsg = 0; 233 } else 234 break; 235 } 236 if (*cp == '\n') 237 (void) putc('\r', f_tty); 238 (void) putc(*cp, f_tty); 239 } 240 (void) fclose(f_tty); 241 } 242 } 243 244 /* 245 * print out an estimate of the amount of time left to do the dump 246 */ 247 248 time_t tschedule = 0; 249 250 void 251 timeest() 252 { 253 time_t tnow, deltat; 254 255 time (&tnow); 256 if (tnow >= tschedule) { 257 tschedule = tnow + 300; 258 if (blockswritten < 500) 259 return; 260 deltat = tstart_writing - tnow + 261 (((1.0*(tnow - tstart_writing))/blockswritten) * esize); 262 msg("%3.2f%% done, finished in %d:%02d\n", 263 (blockswritten*100.0)/esize, 264 deltat/3600, (deltat%3600)/60); 265 } 266 } 267 268 int 269 blocksontape() 270 { 271 /* 272 * esize: total number of blocks estimated over all reels 273 * blockswritten: blocks actually written, over all reels 274 * etapes: estimated number of tapes to write 275 * 276 * tsize: blocks can write on this reel 277 * asize: blocks written on this reel 278 * tapeno: number of tapes written so far 279 */ 280 if (tapeno == etapes) 281 return (esize - (etapes - 1)*tsize); 282 return (tsize); 283 } 284 285 #ifdef lint 286 287 /* VARARGS1 */ 288 void msg(fmt) char *fmt; { strcpy(lastmsg, fmt); } 289 290 /* VARARGS1 */ 291 void msgtail(fmt) char *fmt; { fmt = fmt; } 292 293 void quit(fmt) char *fmt; { msg(fmt); dumpabort(); } 294 295 #else /* lint */ 296 297 void 298 msg(va_alist) 299 va_dcl 300 { 301 va_list ap; 302 char *fmt; 303 304 (void) fprintf(stderr," DUMP: "); 305 #ifdef TDEBUG 306 (void) fprintf(stderr, "pid=%d ", getpid()); 307 #endif 308 va_start(ap); 309 fmt = va_arg(ap, char *); 310 (void) vfprintf(stderr, fmt, ap); 311 va_end(ap); 312 (void) fflush(stdout); 313 (void) fflush(stderr); 314 va_start(ap); 315 fmt = va_arg(ap, char *); 316 (void) vsprintf(lastmsg, fmt, ap); 317 va_end(ap); 318 } 319 320 void 321 msgtail(va_alist) 322 va_dcl 323 { 324 va_list ap; 325 char *fmt; 326 327 va_start(ap); 328 fmt = va_arg(ap, char *); 329 (void) vfprintf(stderr, fmt, ap); 330 va_end(ap); 331 } 332 333 void 334 quit(va_alist) 335 va_dcl 336 { 337 va_list ap; 338 char *fmt; 339 340 (void) fprintf(stderr," DUMP: "); 341 #ifdef TDEBUG 342 (void) fprintf(stderr, "pid=%d ", getpid()); 343 #endif 344 va_start(ap); 345 fmt = va_arg(ap, char *); 346 vfprintf(stderr, fmt, ap); 347 va_end(ap); 348 (void) fflush(stdout); 349 (void) fflush(stderr); 350 dumpabort(); 351 } 352 353 #endif /* lint */ 354 355 /* 356 * Tell the operator what has to be done; 357 * we don't actually do it 358 */ 359 360 struct fstab * 361 allocfsent(fs) 362 register struct fstab *fs; 363 { 364 register struct fstab *new; 365 366 new = (struct fstab *)malloc(sizeof (*fs)); 367 if (new == NULL || 368 (new->fs_file = strdup(fs->fs_file)) == NULL || 369 (new->fs_type = strdup(fs->fs_type)) == NULL || 370 (new->fs_spec = strdup(fs->fs_spec)) == NULL) 371 quit("%s\n", strerror(errno)); 372 new->fs_passno = fs->fs_passno; 373 new->fs_freq = fs->fs_freq; 374 return (new); 375 } 376 377 struct pfstab { 378 struct pfstab *pf_next; 379 struct fstab *pf_fstab; 380 }; 381 382 static struct pfstab *table; 383 384 void 385 getfstab() 386 { 387 register struct fstab *fs; 388 register struct pfstab *pf; 389 390 if (setfsent() == 0) { 391 msg("Can't open %s for dump table information: %s\n", 392 _PATH_FSTAB, strerror(errno)); 393 return; 394 } 395 while (fs = getfsent()) { 396 if (strcmp(fs->fs_type, FSTAB_RW) && 397 strcmp(fs->fs_type, FSTAB_RO) && 398 strcmp(fs->fs_type, FSTAB_RQ)) 399 continue; 400 fs = allocfsent(fs); 401 if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 402 quit("%s\n", strerror(errno)); 403 pf->pf_fstab = fs; 404 pf->pf_next = table; 405 table = pf; 406 } 407 endfsent(); 408 } 409 410 /* 411 * Search in the fstab for a file name. 412 * This file name can be either the special or the path file name. 413 * 414 * The entries in the fstab are the BLOCK special names, not the 415 * character special names. 416 * The caller of fstabsearch assures that the character device 417 * is dumped (that is much faster) 418 * 419 * The file name can omit the leading '/'. 420 */ 421 struct fstab * 422 fstabsearch(key) 423 char *key; 424 { 425 register struct pfstab *pf; 426 register struct fstab *fs; 427 char *rawname(); 428 429 for (pf = table; pf != NULL; pf = pf->pf_next) { 430 fs = pf->pf_fstab; 431 if (strcmp(fs->fs_file, key) == 0 || 432 strcmp(fs->fs_spec, key) == 0 || 433 strcmp(rawname(fs->fs_spec), key) == 0) 434 return (fs); 435 if (key[0] != '/') { 436 if (*fs->fs_spec == '/' && 437 strcmp(fs->fs_spec + 1, key) == 0) 438 return (fs); 439 if (*fs->fs_file == '/' && 440 strcmp(fs->fs_file + 1, key) == 0) 441 return (fs); 442 } 443 } 444 return (NULL); 445 } 446 447 /* 448 * Tell the operator what to do 449 */ 450 void 451 lastdump(arg) 452 char arg; /* w ==> just what to do; W ==> most recent dumps */ 453 { 454 char *lastname; 455 char *date; 456 register int i; 457 time_t tnow; 458 register struct fstab *dt; 459 int dumpme; 460 register struct idates *itwalk; 461 462 int idatesort(); 463 464 time(&tnow); 465 getfstab(); /* /etc/fstab input */ 466 inititimes(); /* /etc/dumpdates input */ 467 qsort(idatev, nidates, sizeof(struct idates *), idatesort); 468 469 if (arg == 'w') 470 (void) printf("Dump these file systems:\n"); 471 else 472 (void) printf("Last dump(s) done (Dump '>' file systems):\n"); 473 lastname = "??"; 474 ITITERATE(i, itwalk) { 475 if (strncmp(lastname, itwalk->id_name, sizeof(itwalk->id_name)) == 0) 476 continue; 477 date = (char *)ctime(&itwalk->id_ddate); 478 date[16] = '\0'; /* blast away seconds and year */ 479 lastname = itwalk->id_name; 480 dt = fstabsearch(itwalk->id_name); 481 dumpme = (dt != NULL && 482 dt->fs_freq != 0 && 483 itwalk->id_ddate < tnow - (dt->fs_freq * DAY)); 484 if (arg != 'w' || dumpme) 485 (void) printf( 486 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 487 dumpme && (arg != 'w') ? '>' : ' ', 488 itwalk->id_name, 489 dt ? dt->fs_file : "", 490 itwalk->id_incno, 491 date); 492 } 493 } 494 495 int idatesort(a1, a2) 496 void *a1, *a2; 497 { 498 struct idates *d1 = *(struct idates **)a1, *d2 = *(struct idates **)a2; 499 int diff; 500 501 diff = strncmp(d1->id_name, d2->id_name, sizeof(d1->id_name)); 502 if (diff == 0) 503 return (d2->id_ddate - d1->id_ddate); 504 else 505 return (diff); 506 } 507 508 int max(a, b) 509 int a, b; 510 { 511 return (a > b ? a : b); 512 } 513 int min(a, b) 514 int a, b; 515 { 516 return (a < b ? a : b); 517 } 518