122038Sdist /* 239130Smckusick * Copyright (c) 1980, 1988 Regents of the University of California. 322038Sdist * All rights reserved. The Berkeley software License Agreement 422038Sdist * specifies the terms and conditions for redistribution. 522038Sdist */ 65319Smckusic 722038Sdist #ifndef lint 8*46788Smckusick static char sccsid[] = "@(#)optr.c 5.5 (Berkeley) 02/28/91"; 946588Storek #endif /* not lint */ 1022038Sdist 111424Sroot #include "dump.h" 1246588Storek #include <sys/wait.h> 1346588Storek #include <errno.h> 1446588Storek #include <grp.h> 1546588Storek #include <varargs.h> 1639130Smckusick #include "pathnames.h" 171424Sroot 1846588Storek static void alarmcatch(); 1946588Storek static void sendmes(); 2046588Storek 211424Sroot /* 2239130Smckusick * Query the operator; This previously-fascist piece of code 2339130Smckusick * no longer requires an exact response. 241424Sroot * It is intended to protect dump aborting by inquisitive 251424Sroot * people banging on the console terminal to see what is 261424Sroot * happening which might cause dump to croak, destroying 271424Sroot * a large number of hours of work. 281424Sroot * 291424Sroot * Every 2 minutes we reprint the message, alerting others 301424Sroot * that dump needs attention. 311424Sroot */ 321424Sroot int timeout; 3321124Smckusick char *attnmessage; /* attention message */ 3446588Storek 3546588Storek int 361424Sroot query(question) 371424Sroot char *question; 381424Sroot { 391424Sroot char replybuffer[64]; 4046588Storek int back, errcount; 411424Sroot FILE *mytty; 421424Sroot 4346588Storek if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 4446588Storek quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 451424Sroot attnmessage = question; 461424Sroot timeout = 0; 471424Sroot alarmcatch(); 4846588Storek back = -1; 4946588Storek errcount = 0; 5046588Storek do { 5146588Storek if (fgets(replybuffer, 63, mytty) == NULL) { 5246588Storek clearerr(mytty); 5346588Storek if (++errcount > 30) /* XXX ugly */ 5446588Storek quit("excessive operator query failures\n"); 5539130Smckusick } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 5646588Storek back = 1; 5739130Smckusick } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 5846588Storek back = 0; 591424Sroot } else { 6046588Storek (void) fprintf(stderr, 6146588Storek " DUMP: \"Yes\" or \"No\"?\n"); 6246588Storek (void) fprintf(stderr, 6346588Storek " DUMP: %s: (\"yes\" or \"no\") ", question); 641424Sroot } 6546588Storek } while (back < 0); 6646588Storek 671424Sroot /* 681424Sroot * Turn off the alarm, and reset the signal to trap out.. 691424Sroot */ 701424Sroot alarm(0); 711424Sroot if (signal(SIGALRM, sigalrm) == SIG_IGN) 721424Sroot signal(SIGALRM, SIG_IGN); 731424Sroot fclose(mytty); 741424Sroot return(back); 751424Sroot } 7639130Smckusick 7739130Smckusick char lastmsg[100]; 7839130Smckusick 791424Sroot /* 801424Sroot * Alert the console operator, and enable the alarm clock to 811424Sroot * sleep for 2 minutes in case nobody comes to satisfy dump 821424Sroot */ 8346588Storek static void 841424Sroot alarmcatch() 851424Sroot { 8639130Smckusick if (notify == 0) { 8739130Smckusick if (timeout == 0) 8846588Storek (void) fprintf(stderr, 8946588Storek " DUMP: %s: (\"yes\" or \"no\") ", 9039130Smckusick attnmessage); 9139130Smckusick else 9239130Smckusick msgtail("\7\7"); 9339130Smckusick } else { 9439130Smckusick if (timeout) { 9539130Smckusick msgtail("\n"); 9639130Smckusick broadcast(""); /* just print last msg */ 9739130Smckusick } 9846588Storek (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", 9939130Smckusick attnmessage); 10039130Smckusick } 1011424Sroot signal(SIGALRM, alarmcatch); 1021424Sroot alarm(120); 1031424Sroot timeout = 1; 1041424Sroot } 10546588Storek 1061424Sroot /* 1071424Sroot * Here if an inquisitive operator interrupts the dump program 1081424Sroot */ 10946588Storek void 1101424Sroot interrupt() 1111424Sroot { 11221124Smckusick msg("Interrupt received.\n"); 11321124Smckusick if (query("Do you want to abort dump?")) 1141424Sroot dumpabort(); 1151424Sroot } 1161424Sroot 1171424Sroot /* 1181424Sroot * The following variables and routines manage alerting 1191424Sroot * operators to the status of dump. 1201424Sroot * This works much like wall(1) does. 1211424Sroot */ 12246588Storek struct group *gp; 1231424Sroot 1241424Sroot /* 1251424Sroot * Get the names from the group entry "operator" to notify. 1261424Sroot */ 12746588Storek void 1281424Sroot set_operators() 1291424Sroot { 1301424Sroot if (!notify) /*not going to notify*/ 1311424Sroot return; 1321424Sroot gp = getgrnam(OPGRENT); 1331424Sroot endgrent(); 13446588Storek if (gp == NULL) { 13546588Storek msg("No group entry for %s.\n", OPGRENT); 1361424Sroot notify = 0; 1371424Sroot return; 1381424Sroot } 1391424Sroot } 1401424Sroot 1411424Sroot struct tm *localtime(); 1421424Sroot struct tm *localclock; 1431424Sroot 1441424Sroot /* 1451424Sroot * We fork a child to do the actual broadcasting, so 1461424Sroot * that the process control groups are not messed up 1471424Sroot */ 14846588Storek void 1491424Sroot broadcast(message) 1501424Sroot char *message; 1511424Sroot { 1521424Sroot time_t clock; 1531424Sroot FILE *f_utmp; 1541424Sroot struct utmp utmp; 1551424Sroot int nusers; 1561424Sroot char **np; 1571424Sroot int pid, s; 1581424Sroot 15946588Storek if (!notify || gp == NULL) 16046588Storek return; 16146588Storek 1621424Sroot switch (pid = fork()) { 1631424Sroot case -1: 1641424Sroot return; 1651424Sroot case 0: 1661424Sroot break; 1671424Sroot default: 1681424Sroot while (wait(&s) != pid) 1691424Sroot continue; 1701424Sroot return; 1711424Sroot } 1721424Sroot 1731424Sroot clock = time(0); 1741424Sroot localclock = localtime(&clock); 1751424Sroot 17646588Storek if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { 17746588Storek msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); 1781424Sroot return; 1791424Sroot } 1801424Sroot 1811424Sroot nusers = 0; 18246588Storek while (!feof(f_utmp)) { 1831424Sroot if (fread(&utmp, sizeof (struct utmp), 1, f_utmp) != 1) 1841424Sroot break; 1851424Sroot if (utmp.ut_name[0] == 0) 1861424Sroot continue; 1871424Sroot nusers++; 18846588Storek for (np = gp->gr_mem; *np; np++) { 1891424Sroot if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 1901424Sroot continue; 1911424Sroot /* 1921424Sroot * Do not send messages to operators on dialups 1931424Sroot */ 1941424Sroot if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 1951424Sroot continue; 1961424Sroot #ifdef DEBUG 19746588Storek msg("Message to %s at %s\n", *np, utmp.ut_line); 19846588Storek #endif 1991424Sroot sendmes(utmp.ut_line, message); 2001424Sroot } 2011424Sroot } 20246588Storek (void) fclose(f_utmp); 2031424Sroot Exit(0); /* the wait in this same routine will catch this */ 2041424Sroot /* NOTREACHED */ 2051424Sroot } 2061424Sroot 20746588Storek static void 2081424Sroot sendmes(tty, message) 2095319Smckusic char *tty, *message; 2101424Sroot { 2111424Sroot char t[50], buf[BUFSIZ]; 2121424Sroot register char *cp; 21339130Smckusick int lmsg = 1; 2141424Sroot FILE *f_tty; 2151424Sroot 21646588Storek (void) strcpy(t, _PATH_DEV); 21746588Storek (void) strcat(t, tty); 2181424Sroot 21946588Storek if ((f_tty = fopen(t, "w")) != NULL) { 2201424Sroot setbuf(f_tty, buf); 22146588Storek (void) fprintf(f_tty, 22246588Storek "\n\ 22346588Storek \7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ 22446588Storek DUMP: NEEDS ATTENTION: ", 22546588Storek localclock->tm_hour, localclock->tm_min); 22639130Smckusick for (cp = lastmsg; ; cp++) { 22739130Smckusick if (*cp == '\0') { 22839130Smckusick if (lmsg) { 22939130Smckusick cp = message; 23039130Smckusick if (*cp == '\0') 23139130Smckusick break; 23239130Smckusick lmsg = 0; 23339130Smckusick } else 23439130Smckusick break; 23539130Smckusick } 23639130Smckusick if (*cp == '\n') 23746588Storek (void) putc('\r', f_tty); 23846588Storek (void) putc(*cp, f_tty); 2391424Sroot } 24046588Storek (void) fclose(f_tty); 2411424Sroot } 2421424Sroot } 2431424Sroot 2441424Sroot /* 2451424Sroot * print out an estimate of the amount of time left to do the dump 2461424Sroot */ 2471424Sroot 2481424Sroot time_t tschedule = 0; 2491424Sroot 25046588Storek void 2511424Sroot timeest() 2521424Sroot { 2531424Sroot time_t tnow, deltat; 2541424Sroot 2551424Sroot time (&tnow); 25646588Storek if (tnow >= tschedule) { 2571424Sroot tschedule = tnow + 300; 2581424Sroot if (blockswritten < 500) 2591424Sroot return; 2601424Sroot deltat = tstart_writing - tnow + 261*46788Smckusick (1.0 * (tnow - tstart_writing)) 262*46788Smckusick / blockswritten * tapesize; 2631424Sroot msg("%3.2f%% done, finished in %d:%02d\n", 264*46788Smckusick (blockswritten * 100.0) / tapesize, 265*46788Smckusick deltat / 3600, (deltat % 3600) / 60); 2661424Sroot } 2671424Sroot } 2681424Sroot 269*46788Smckusick /* 270*46788Smckusick * tapesize: total number of blocks estimated over all reels 271*46788Smckusick * blockswritten: blocks actually written, over all reels 272*46788Smckusick * etapes: estimated number of tapes to write 273*46788Smckusick * 274*46788Smckusick * tsize: blocks can write on this reel 275*46788Smckusick * asize: blocks written on this reel 276*46788Smckusick * tapeno: number of tapes written so far 277*46788Smckusick */ 27846588Storek int 27946588Storek blocksontape() 2801424Sroot { 2811424Sroot if (tapeno == etapes) 282*46788Smckusick return (tapesize - (etapes - 1) * tsize); 28346588Storek return (tsize); 2841424Sroot } 2851424Sroot 28646588Storek #ifdef lint 28746588Storek 28846588Storek /* VARARGS1 */ 28946588Storek void msg(fmt) char *fmt; { strcpy(lastmsg, fmt); } 29046588Storek 29146588Storek /* VARARGS1 */ 29246588Storek void msgtail(fmt) char *fmt; { fmt = fmt; } 29346588Storek 29446588Storek void quit(fmt) char *fmt; { msg(fmt); dumpabort(); } 29546588Storek 29646588Storek #else /* lint */ 29746588Storek 29846588Storek void 29946588Storek msg(va_alist) 30046588Storek va_dcl 3011424Sroot { 30246588Storek va_list ap; 30346588Storek char *fmt; 30446588Storek 30546588Storek (void) fprintf(stderr," DUMP: "); 3061424Sroot #ifdef TDEBUG 30746588Storek (void) fprintf(stderr, "pid=%d ", getpid()); 3081424Sroot #endif 30946588Storek va_start(ap); 31046588Storek fmt = va_arg(ap, char *); 31146588Storek (void) vfprintf(stderr, fmt, ap); 31246588Storek va_end(ap); 31346588Storek (void) fflush(stdout); 31446588Storek (void) fflush(stderr); 31546588Storek va_start(ap); 31646588Storek fmt = va_arg(ap, char *); 31746588Storek (void) vsprintf(lastmsg, fmt, ap); 31846588Storek va_end(ap); 3191424Sroot } 3201424Sroot 32146588Storek void 32246588Storek msgtail(va_alist) 32346588Storek va_dcl 3241424Sroot { 32546588Storek va_list ap; 32646588Storek char *fmt; 32746588Storek 32846588Storek va_start(ap); 32946588Storek fmt = va_arg(ap, char *); 33046588Storek (void) vfprintf(stderr, fmt, ap); 33146588Storek va_end(ap); 3321424Sroot } 33346588Storek 33446588Storek void 33546588Storek quit(va_alist) 33646588Storek va_dcl 33746588Storek { 33846588Storek va_list ap; 33946588Storek char *fmt; 34046588Storek 34146588Storek (void) fprintf(stderr," DUMP: "); 34246588Storek #ifdef TDEBUG 34346588Storek (void) fprintf(stderr, "pid=%d ", getpid()); 34446588Storek #endif 34546588Storek va_start(ap); 34646588Storek fmt = va_arg(ap, char *); 34746588Storek vfprintf(stderr, fmt, ap); 34846588Storek va_end(ap); 34946588Storek (void) fflush(stdout); 35046588Storek (void) fflush(stderr); 35146588Storek dumpabort(); 35246588Storek } 35346588Storek 35446588Storek #endif /* lint */ 35546588Storek 3561424Sroot /* 3571424Sroot * Tell the operator what has to be done; 3581424Sroot * we don't actually do it 3591424Sroot */ 3601424Sroot 36113047Ssam struct fstab * 36213047Ssam allocfsent(fs) 36313047Ssam register struct fstab *fs; 36413047Ssam { 36513047Ssam register struct fstab *new; 36613047Ssam 36713047Ssam new = (struct fstab *)malloc(sizeof (*fs)); 36846588Storek if (new == NULL || 36946588Storek (new->fs_file = strdup(fs->fs_file)) == NULL || 37046588Storek (new->fs_type = strdup(fs->fs_type)) == NULL || 37146588Storek (new->fs_spec = strdup(fs->fs_spec)) == NULL) 37246588Storek quit("%s\n", strerror(errno)); 37313047Ssam new->fs_passno = fs->fs_passno; 37413047Ssam new->fs_freq = fs->fs_freq; 37513047Ssam return (new); 37613047Ssam } 37713047Ssam 37813047Ssam struct pfstab { 37913047Ssam struct pfstab *pf_next; 38013047Ssam struct fstab *pf_fstab; 38113047Ssam }; 38213047Ssam 38346588Storek static struct pfstab *table; 38413047Ssam 38546588Storek void 3861424Sroot getfstab() 3871424Sroot { 38813047Ssam register struct fstab *fs; 38913047Ssam register struct pfstab *pf; 3901424Sroot 3911428Sroot if (setfsent() == 0) { 39246588Storek msg("Can't open %s for dump table information: %s\n", 39346588Storek _PATH_FSTAB, strerror(errno)); 39413047Ssam return; 3951424Sroot } 39613047Ssam while (fs = getfsent()) { 39713047Ssam if (strcmp(fs->fs_type, FSTAB_RW) && 39813047Ssam strcmp(fs->fs_type, FSTAB_RO) && 39913047Ssam strcmp(fs->fs_type, FSTAB_RQ)) 40013047Ssam continue; 40113047Ssam fs = allocfsent(fs); 40246588Storek if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 40346588Storek quit("%s\n", strerror(errno)); 40413047Ssam pf->pf_fstab = fs; 40513047Ssam pf->pf_next = table; 40613047Ssam table = pf; 40713047Ssam } 40813047Ssam endfsent(); 4091424Sroot } 4101424Sroot 4111424Sroot /* 41213047Ssam * Search in the fstab for a file name. 41313047Ssam * This file name can be either the special or the path file name. 4141424Sroot * 41513047Ssam * The entries in the fstab are the BLOCK special names, not the 41613047Ssam * character special names. 41713047Ssam * The caller of fstabsearch assures that the character device 41813047Ssam * is dumped (that is much faster) 4191424Sroot * 42013047Ssam * The file name can omit the leading '/'. 4211424Sroot */ 42213047Ssam struct fstab * 42313047Ssam fstabsearch(key) 42413047Ssam char *key; 4251424Sroot { 42613047Ssam register struct pfstab *pf; 42713047Ssam register struct fstab *fs; 42813047Ssam char *rawname(); 4291424Sroot 43046588Storek for (pf = table; pf != NULL; pf = pf->pf_next) { 43113047Ssam fs = pf->pf_fstab; 43246588Storek if (strcmp(fs->fs_file, key) == 0 || 43346588Storek strcmp(fs->fs_spec, key) == 0 || 43446588Storek strcmp(rawname(fs->fs_spec), key) == 0) 43513047Ssam return (fs); 43646588Storek if (key[0] != '/') { 43713047Ssam if (*fs->fs_spec == '/' && 43813197Sroot strcmp(fs->fs_spec + 1, key) == 0) 43913047Ssam return (fs); 44013047Ssam if (*fs->fs_file == '/' && 44113197Sroot strcmp(fs->fs_file + 1, key) == 0) 44213047Ssam return (fs); 4431424Sroot } 4441424Sroot } 44546588Storek return (NULL); 4461424Sroot } 4471424Sroot 4481424Sroot /* 4491424Sroot * Tell the operator what to do 4501424Sroot */ 45146588Storek void 4521463Sroot lastdump(arg) 45346588Storek char arg; /* w ==> just what to do; W ==> most recent dumps */ 4541424Sroot { 455*46788Smckusick register int i; 456*46788Smckusick register struct fstab *dt; 457*46788Smckusick register struct dumpdates *dtwalk; 458*46788Smckusick char *lastname, *date; 459*46788Smckusick int dumpme, datesort(); 460*46788Smckusick time_t tnow; 4611424Sroot 4621424Sroot time(&tnow); 4631424Sroot getfstab(); /* /etc/fstab input */ 464*46788Smckusick initdumptimes(); /* /etc/dumpdates input */ 465*46788Smckusick qsort(ddatev, nddates, sizeof(struct dumpdates *), datesort); 4661424Sroot 4671463Sroot if (arg == 'w') 46846588Storek (void) printf("Dump these file systems:\n"); 4691463Sroot else 47046588Storek (void) printf("Last dump(s) done (Dump '>' file systems):\n"); 4711424Sroot lastname = "??"; 472*46788Smckusick ITITERATE(i, dtwalk) { 473*46788Smckusick if (strncmp(lastname, dtwalk->dd_name, 474*46788Smckusick sizeof(dtwalk->dd_name)) == 0) 4751424Sroot continue; 476*46788Smckusick date = (char *)ctime(&dtwalk->dd_ddate); 47746588Storek date[16] = '\0'; /* blast away seconds and year */ 478*46788Smckusick lastname = dtwalk->dd_name; 479*46788Smckusick dt = fstabsearch(dtwalk->dd_name); 48046588Storek dumpme = (dt != NULL && 48146588Storek dt->fs_freq != 0 && 482*46788Smckusick dtwalk->dd_ddate < tnow - (dt->fs_freq * DAY)); 48346588Storek if (arg != 'w' || dumpme) 48446588Storek (void) printf( 48546588Storek "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 48646588Storek dumpme && (arg != 'w') ? '>' : ' ', 487*46788Smckusick dtwalk->dd_name, 48846588Storek dt ? dt->fs_file : "", 489*46788Smckusick dtwalk->dd_level, 49046588Storek date); 4911424Sroot } 4921424Sroot } 4931424Sroot 494*46788Smckusick int 495*46788Smckusick datesort(a1, a2) 49646588Storek void *a1, *a2; 4971424Sroot { 498*46788Smckusick struct dumpdates *d1 = *(struct dumpdates **)a1; 499*46788Smckusick struct dumpdates *d2 = *(struct dumpdates **)a2; 50046588Storek int diff; 5011424Sroot 502*46788Smckusick diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 5031424Sroot if (diff == 0) 504*46788Smckusick return (d2->dd_ddate - d1->dd_ddate); 505*46788Smckusick return (diff); 5061424Sroot } 5071424Sroot 50846588Storek int max(a, b) 5095319Smckusic int a, b; 5101424Sroot { 51146588Storek return (a > b ? a : b); 5121424Sroot } 51346588Storek int min(a, b) 5145319Smckusic int a, b; 5151424Sroot { 51646588Storek return (a < b ? a : b); 5171424Sroot } 518