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