1*47082Smckusick /*- 2*47082Smckusick * Copyright (c) 1980, 1988 The Regents of the University of California. 3*47082Smckusick * All rights reserved. 4*47082Smckusick * 5*47082Smckusick * %sccs.include.redist.c% 622038Sdist */ 75319Smckusic 822038Sdist #ifndef lint 9*47082Smckusick static char sccsid[] = "@(#)optr.c 5.8 (Berkeley) 03/07/91"; 1046588Storek #endif /* not lint */ 1122038Sdist 1246795Sbostic #include <sys/param.h> 1346588Storek #include <sys/wait.h> 1446795Sbostic #include <ufs/dir.h> 1546795Sbostic #include <signal.h> 1646795Sbostic #include <time.h> 1746795Sbostic #include <fstab.h> 1846588Storek #include <grp.h> 1946588Storek #include <varargs.h> 2046795Sbostic #include <utmp.h> 2146816Sbostic #include <tzfile.h> 2246795Sbostic #include <errno.h> 2346795Sbostic #include <stdio.h> 2446795Sbostic #ifdef __STDC__ 2546795Sbostic #include <unistd.h> 2646795Sbostic #include <stdlib.h> 2746795Sbostic #include <string.h> 2846795Sbostic #endif 2946795Sbostic #include "dump.h" 3039130Smckusick #include "pathnames.h" 311424Sroot 3246588Storek static void alarmcatch(); 3346588Storek static void sendmes(); 3446588Storek 351424Sroot /* 3639130Smckusick * Query the operator; This previously-fascist piece of code 3739130Smckusick * no longer requires an exact response. 381424Sroot * It is intended to protect dump aborting by inquisitive 391424Sroot * people banging on the console terminal to see what is 401424Sroot * happening which might cause dump to croak, destroying 411424Sroot * a large number of hours of work. 421424Sroot * 431424Sroot * Every 2 minutes we reprint the message, alerting others 441424Sroot * that dump needs attention. 451424Sroot */ 461424Sroot int timeout; 4721124Smckusick char *attnmessage; /* attention message */ 4846588Storek 4946588Storek int 501424Sroot query(question) 511424Sroot char *question; 521424Sroot { 531424Sroot char replybuffer[64]; 5446588Storek int back, errcount; 551424Sroot FILE *mytty; 561424Sroot 5746588Storek if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 5846588Storek quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 591424Sroot attnmessage = question; 601424Sroot timeout = 0; 611424Sroot alarmcatch(); 6246588Storek back = -1; 6346588Storek errcount = 0; 6446588Storek do { 6546588Storek if (fgets(replybuffer, 63, mytty) == NULL) { 6646588Storek clearerr(mytty); 6746588Storek if (++errcount > 30) /* XXX ugly */ 6846588Storek quit("excessive operator query failures\n"); 6939130Smckusick } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 7046588Storek back = 1; 7139130Smckusick } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 7246588Storek back = 0; 731424Sroot } else { 7446588Storek (void) fprintf(stderr, 7546588Storek " DUMP: \"Yes\" or \"No\"?\n"); 7646588Storek (void) fprintf(stderr, 7746588Storek " DUMP: %s: (\"yes\" or \"no\") ", question); 781424Sroot } 7946588Storek } while (back < 0); 8046588Storek 811424Sroot /* 821424Sroot * Turn off the alarm, and reset the signal to trap out.. 831424Sroot */ 841424Sroot alarm(0); 851424Sroot if (signal(SIGALRM, sigalrm) == SIG_IGN) 861424Sroot signal(SIGALRM, SIG_IGN); 871424Sroot fclose(mytty); 881424Sroot return(back); 891424Sroot } 9039130Smckusick 9139130Smckusick char lastmsg[100]; 9239130Smckusick 931424Sroot /* 941424Sroot * Alert the console operator, and enable the alarm clock to 951424Sroot * sleep for 2 minutes in case nobody comes to satisfy dump 961424Sroot */ 9746588Storek static void 981424Sroot alarmcatch() 991424Sroot { 10039130Smckusick if (notify == 0) { 10139130Smckusick if (timeout == 0) 10246588Storek (void) fprintf(stderr, 10346588Storek " DUMP: %s: (\"yes\" or \"no\") ", 10439130Smckusick attnmessage); 10539130Smckusick else 10639130Smckusick msgtail("\7\7"); 10739130Smckusick } else { 10839130Smckusick if (timeout) { 10939130Smckusick msgtail("\n"); 11039130Smckusick broadcast(""); /* just print last msg */ 11139130Smckusick } 11246588Storek (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", 11339130Smckusick attnmessage); 11439130Smckusick } 1151424Sroot signal(SIGALRM, alarmcatch); 1161424Sroot alarm(120); 1171424Sroot timeout = 1; 1181424Sroot } 11946588Storek 1201424Sroot /* 1211424Sroot * Here if an inquisitive operator interrupts the dump program 1221424Sroot */ 12346588Storek void 1241424Sroot interrupt() 1251424Sroot { 12621124Smckusick msg("Interrupt received.\n"); 12721124Smckusick if (query("Do you want to abort dump?")) 1281424Sroot dumpabort(); 1291424Sroot } 1301424Sroot 1311424Sroot /* 1321424Sroot * The following variables and routines manage alerting 1331424Sroot * operators to the status of dump. 1341424Sroot * This works much like wall(1) does. 1351424Sroot */ 13646588Storek struct group *gp; 1371424Sroot 1381424Sroot /* 1391424Sroot * Get the names from the group entry "operator" to notify. 1401424Sroot */ 14146588Storek void 1421424Sroot set_operators() 1431424Sroot { 1441424Sroot if (!notify) /*not going to notify*/ 1451424Sroot return; 1461424Sroot gp = getgrnam(OPGRENT); 1471424Sroot endgrent(); 14846588Storek if (gp == NULL) { 14946588Storek msg("No group entry for %s.\n", OPGRENT); 1501424Sroot notify = 0; 1511424Sroot return; 1521424Sroot } 1531424Sroot } 1541424Sroot 1551424Sroot struct tm *localtime(); 1561424Sroot struct tm *localclock; 1571424Sroot 1581424Sroot /* 1591424Sroot * We fork a child to do the actual broadcasting, so 1601424Sroot * that the process control groups are not messed up 1611424Sroot */ 16246588Storek void 1631424Sroot broadcast(message) 1641424Sroot char *message; 1651424Sroot { 1661424Sroot time_t clock; 1671424Sroot FILE *f_utmp; 1681424Sroot struct utmp utmp; 1691424Sroot int nusers; 1701424Sroot char **np; 1711424Sroot int pid, s; 1721424Sroot 17346588Storek if (!notify || gp == NULL) 17446588Storek return; 17546588Storek 1761424Sroot switch (pid = fork()) { 1771424Sroot case -1: 1781424Sroot return; 1791424Sroot case 0: 1801424Sroot break; 1811424Sroot default: 1821424Sroot while (wait(&s) != pid) 1831424Sroot continue; 1841424Sroot return; 1851424Sroot } 1861424Sroot 1871424Sroot clock = time(0); 1881424Sroot localclock = localtime(&clock); 1891424Sroot 19046588Storek if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { 19146588Storek msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); 1921424Sroot return; 1931424Sroot } 1941424Sroot 1951424Sroot nusers = 0; 19646588Storek while (!feof(f_utmp)) { 1971424Sroot if (fread(&utmp, sizeof (struct utmp), 1, f_utmp) != 1) 1981424Sroot break; 1991424Sroot if (utmp.ut_name[0] == 0) 2001424Sroot continue; 2011424Sroot nusers++; 20246588Storek for (np = gp->gr_mem; *np; np++) { 2031424Sroot if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 2041424Sroot continue; 2051424Sroot /* 2061424Sroot * Do not send messages to operators on dialups 2071424Sroot */ 2081424Sroot if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 2091424Sroot continue; 2101424Sroot #ifdef DEBUG 21146588Storek msg("Message to %s at %s\n", *np, utmp.ut_line); 21246588Storek #endif 2131424Sroot sendmes(utmp.ut_line, message); 2141424Sroot } 2151424Sroot } 21646588Storek (void) fclose(f_utmp); 2171424Sroot Exit(0); /* the wait in this same routine will catch this */ 2181424Sroot /* NOTREACHED */ 2191424Sroot } 2201424Sroot 22146588Storek static void 2221424Sroot sendmes(tty, message) 2235319Smckusic char *tty, *message; 2241424Sroot { 2251424Sroot char t[50], buf[BUFSIZ]; 2261424Sroot register char *cp; 22739130Smckusick int lmsg = 1; 2281424Sroot FILE *f_tty; 2291424Sroot 23046588Storek (void) strcpy(t, _PATH_DEV); 23146588Storek (void) strcat(t, tty); 2321424Sroot 23346588Storek if ((f_tty = fopen(t, "w")) != NULL) { 2341424Sroot setbuf(f_tty, buf); 23546588Storek (void) fprintf(f_tty, 23646588Storek "\n\ 23746588Storek \7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ 23846588Storek DUMP: NEEDS ATTENTION: ", 23946588Storek localclock->tm_hour, localclock->tm_min); 24039130Smckusick for (cp = lastmsg; ; cp++) { 24139130Smckusick if (*cp == '\0') { 24239130Smckusick if (lmsg) { 24339130Smckusick cp = message; 24439130Smckusick if (*cp == '\0') 24539130Smckusick break; 24639130Smckusick lmsg = 0; 24739130Smckusick } else 24839130Smckusick break; 24939130Smckusick } 25039130Smckusick if (*cp == '\n') 25146588Storek (void) putc('\r', f_tty); 25246588Storek (void) putc(*cp, f_tty); 2531424Sroot } 25446588Storek (void) fclose(f_tty); 2551424Sroot } 2561424Sroot } 2571424Sroot 2581424Sroot /* 2591424Sroot * print out an estimate of the amount of time left to do the dump 2601424Sroot */ 2611424Sroot 2621424Sroot time_t tschedule = 0; 2631424Sroot 26446588Storek void 2651424Sroot timeest() 2661424Sroot { 2671424Sroot time_t tnow, deltat; 2681424Sroot 2691424Sroot time (&tnow); 27046588Storek if (tnow >= tschedule) { 2711424Sroot tschedule = tnow + 300; 2721424Sroot if (blockswritten < 500) 2731424Sroot return; 2741424Sroot deltat = tstart_writing - tnow + 27546788Smckusick (1.0 * (tnow - tstart_writing)) 27646788Smckusick / blockswritten * tapesize; 2771424Sroot msg("%3.2f%% done, finished in %d:%02d\n", 27846788Smckusick (blockswritten * 100.0) / tapesize, 27946788Smckusick deltat / 3600, (deltat % 3600) / 60); 2801424Sroot } 2811424Sroot } 2821424Sroot 28346788Smckusick /* 28446788Smckusick * tapesize: total number of blocks estimated over all reels 28546788Smckusick * blockswritten: blocks actually written, over all reels 28646788Smckusick * etapes: estimated number of tapes to write 28746788Smckusick * 28846788Smckusick * tsize: blocks can write on this reel 28946788Smckusick * asize: blocks written on this reel 29046788Smckusick * tapeno: number of tapes written so far 29146788Smckusick */ 29246588Storek int 29346588Storek blocksontape() 2941424Sroot { 2951424Sroot if (tapeno == etapes) 29646788Smckusick return (tapesize - (etapes - 1) * tsize); 29746588Storek return (tsize); 2981424Sroot } 2991424Sroot 30046588Storek #ifdef lint 30146588Storek 30246588Storek /* VARARGS1 */ 30346588Storek void msg(fmt) char *fmt; { strcpy(lastmsg, fmt); } 30446588Storek 30546588Storek /* VARARGS1 */ 30646588Storek void msgtail(fmt) char *fmt; { fmt = fmt; } 30746588Storek 30846588Storek void quit(fmt) char *fmt; { msg(fmt); dumpabort(); } 30946588Storek 31046588Storek #else /* lint */ 31146588Storek 31246588Storek void 31346588Storek msg(va_alist) 31446588Storek va_dcl 3151424Sroot { 31646588Storek va_list ap; 31746588Storek char *fmt; 31846588Storek 31946588Storek (void) fprintf(stderr," DUMP: "); 3201424Sroot #ifdef TDEBUG 32146588Storek (void) fprintf(stderr, "pid=%d ", getpid()); 3221424Sroot #endif 32346588Storek va_start(ap); 32446588Storek fmt = va_arg(ap, char *); 32546588Storek (void) vfprintf(stderr, fmt, ap); 32646588Storek va_end(ap); 32746588Storek (void) fflush(stdout); 32846588Storek (void) fflush(stderr); 32946588Storek va_start(ap); 33046588Storek fmt = va_arg(ap, char *); 33146588Storek (void) vsprintf(lastmsg, fmt, ap); 33246588Storek va_end(ap); 3331424Sroot } 3341424Sroot 33546588Storek void 33646588Storek msgtail(va_alist) 33746588Storek va_dcl 3381424Sroot { 33946588Storek va_list ap; 34046588Storek char *fmt; 34146588Storek 34246588Storek va_start(ap); 34346588Storek fmt = va_arg(ap, char *); 34446588Storek (void) vfprintf(stderr, fmt, ap); 34546588Storek va_end(ap); 3461424Sroot } 34746588Storek 34846588Storek void 34946588Storek quit(va_alist) 35046588Storek va_dcl 35146588Storek { 35246588Storek va_list ap; 35346588Storek char *fmt; 35446588Storek 35546588Storek (void) fprintf(stderr," DUMP: "); 35646588Storek #ifdef TDEBUG 35746588Storek (void) fprintf(stderr, "pid=%d ", getpid()); 35846588Storek #endif 35946588Storek va_start(ap); 36046588Storek fmt = va_arg(ap, char *); 36146588Storek vfprintf(stderr, fmt, ap); 36246588Storek va_end(ap); 36346588Storek (void) fflush(stdout); 36446588Storek (void) fflush(stderr); 36546588Storek dumpabort(); 36646588Storek } 36746588Storek 36846588Storek #endif /* lint */ 36946588Storek 3701424Sroot /* 3711424Sroot * Tell the operator what has to be done; 3721424Sroot * we don't actually do it 3731424Sroot */ 3741424Sroot 37513047Ssam struct fstab * 37613047Ssam allocfsent(fs) 37713047Ssam register struct fstab *fs; 37813047Ssam { 37913047Ssam register struct fstab *new; 38013047Ssam 38113047Ssam new = (struct fstab *)malloc(sizeof (*fs)); 38246588Storek if (new == NULL || 38346588Storek (new->fs_file = strdup(fs->fs_file)) == NULL || 38446588Storek (new->fs_type = strdup(fs->fs_type)) == NULL || 38546588Storek (new->fs_spec = strdup(fs->fs_spec)) == NULL) 38646588Storek quit("%s\n", strerror(errno)); 38713047Ssam new->fs_passno = fs->fs_passno; 38813047Ssam new->fs_freq = fs->fs_freq; 38913047Ssam return (new); 39013047Ssam } 39113047Ssam 39213047Ssam struct pfstab { 39313047Ssam struct pfstab *pf_next; 39413047Ssam struct fstab *pf_fstab; 39513047Ssam }; 39613047Ssam 39746588Storek static struct pfstab *table; 39813047Ssam 39946588Storek void 4001424Sroot getfstab() 4011424Sroot { 40213047Ssam register struct fstab *fs; 40313047Ssam register struct pfstab *pf; 4041424Sroot 4051428Sroot if (setfsent() == 0) { 40646588Storek msg("Can't open %s for dump table information: %s\n", 40746588Storek _PATH_FSTAB, strerror(errno)); 40813047Ssam return; 4091424Sroot } 41013047Ssam while (fs = getfsent()) { 41113047Ssam if (strcmp(fs->fs_type, FSTAB_RW) && 41213047Ssam strcmp(fs->fs_type, FSTAB_RO) && 41313047Ssam strcmp(fs->fs_type, FSTAB_RQ)) 41413047Ssam continue; 41513047Ssam fs = allocfsent(fs); 41646588Storek if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 41746588Storek quit("%s\n", strerror(errno)); 41813047Ssam pf->pf_fstab = fs; 41913047Ssam pf->pf_next = table; 42013047Ssam table = pf; 42113047Ssam } 42213047Ssam endfsent(); 4231424Sroot } 4241424Sroot 4251424Sroot /* 42613047Ssam * Search in the fstab for a file name. 42713047Ssam * This file name can be either the special or the path file name. 4281424Sroot * 42913047Ssam * The entries in the fstab are the BLOCK special names, not the 43013047Ssam * character special names. 43113047Ssam * The caller of fstabsearch assures that the character device 43213047Ssam * is dumped (that is much faster) 4331424Sroot * 43413047Ssam * The file name can omit the leading '/'. 4351424Sroot */ 43613047Ssam struct fstab * 43713047Ssam fstabsearch(key) 43813047Ssam char *key; 4391424Sroot { 44013047Ssam register struct pfstab *pf; 44113047Ssam register struct fstab *fs; 44213047Ssam char *rawname(); 4431424Sroot 44446588Storek for (pf = table; pf != NULL; pf = pf->pf_next) { 44513047Ssam fs = pf->pf_fstab; 44646588Storek if (strcmp(fs->fs_file, key) == 0 || 44746588Storek strcmp(fs->fs_spec, key) == 0 || 44846588Storek strcmp(rawname(fs->fs_spec), key) == 0) 44913047Ssam return (fs); 45046588Storek if (key[0] != '/') { 45113047Ssam if (*fs->fs_spec == '/' && 45213197Sroot strcmp(fs->fs_spec + 1, key) == 0) 45313047Ssam return (fs); 45413047Ssam if (*fs->fs_file == '/' && 45513197Sroot strcmp(fs->fs_file + 1, key) == 0) 45613047Ssam return (fs); 4571424Sroot } 4581424Sroot } 45946588Storek return (NULL); 4601424Sroot } 4611424Sroot 4621424Sroot /* 4631424Sroot * Tell the operator what to do 4641424Sroot */ 46546588Storek void 4661463Sroot lastdump(arg) 46746588Storek char arg; /* w ==> just what to do; W ==> most recent dumps */ 4681424Sroot { 46946788Smckusick register int i; 47046788Smckusick register struct fstab *dt; 47146788Smckusick register struct dumpdates *dtwalk; 47246788Smckusick char *lastname, *date; 47346788Smckusick int dumpme, datesort(); 47446788Smckusick time_t tnow; 4751424Sroot 4761424Sroot time(&tnow); 4771424Sroot getfstab(); /* /etc/fstab input */ 47846788Smckusick initdumptimes(); /* /etc/dumpdates input */ 47946788Smckusick qsort(ddatev, nddates, sizeof(struct dumpdates *), datesort); 4801424Sroot 4811463Sroot if (arg == 'w') 48246588Storek (void) printf("Dump these file systems:\n"); 4831463Sroot else 48446588Storek (void) printf("Last dump(s) done (Dump '>' file systems):\n"); 4851424Sroot lastname = "??"; 48646788Smckusick ITITERATE(i, dtwalk) { 48746788Smckusick if (strncmp(lastname, dtwalk->dd_name, 48846788Smckusick sizeof(dtwalk->dd_name)) == 0) 4891424Sroot continue; 49046788Smckusick date = (char *)ctime(&dtwalk->dd_ddate); 49146588Storek date[16] = '\0'; /* blast away seconds and year */ 49246788Smckusick lastname = dtwalk->dd_name; 49346788Smckusick dt = fstabsearch(dtwalk->dd_name); 49446588Storek dumpme = (dt != NULL && 49546588Storek dt->fs_freq != 0 && 49646816Sbostic dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY)); 49746588Storek if (arg != 'w' || dumpme) 49846588Storek (void) printf( 49946588Storek "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 50046588Storek dumpme && (arg != 'w') ? '>' : ' ', 50146788Smckusick dtwalk->dd_name, 50246588Storek dt ? dt->fs_file : "", 50346788Smckusick dtwalk->dd_level, 50446588Storek date); 5051424Sroot } 5061424Sroot } 5071424Sroot 50846788Smckusick int 50946788Smckusick datesort(a1, a2) 51046588Storek void *a1, *a2; 5111424Sroot { 51246788Smckusick struct dumpdates *d1 = *(struct dumpdates **)a1; 51346788Smckusick struct dumpdates *d2 = *(struct dumpdates **)a2; 51446588Storek int diff; 5151424Sroot 51646788Smckusick diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 5171424Sroot if (diff == 0) 51846788Smckusick return (d2->dd_ddate - d1->dd_ddate); 51946788Smckusick return (diff); 5201424Sroot } 5211424Sroot 52246588Storek int max(a, b) 5235319Smckusic int a, b; 5241424Sroot { 52546588Storek return (a > b ? a : b); 5261424Sroot } 52746588Storek int min(a, b) 5285319Smckusic int a, b; 5291424Sroot { 53046588Storek return (a < b ? a : b); 5311424Sroot } 532