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*46795Sbostic static char sccsid[] = "@(#)optr.c 5.6 (Berkeley) 02/28/91"; 946588Storek #endif /* not lint */ 1022038Sdist 11*46795Sbostic #include <sys/param.h> 1246588Storek #include <sys/wait.h> 13*46795Sbostic #include <ufs/dir.h> 14*46795Sbostic #include <signal.h> 15*46795Sbostic #include <time.h> 16*46795Sbostic #include <fstab.h> 1746588Storek #include <grp.h> 1846588Storek #include <varargs.h> 19*46795Sbostic #include <utmp.h> 20*46795Sbostic #include <errno.h> 21*46795Sbostic #include <stdio.h> 22*46795Sbostic #ifdef __STDC__ 23*46795Sbostic #include <unistd.h> 24*46795Sbostic #include <stdlib.h> 25*46795Sbostic #include <string.h> 26*46795Sbostic #endif 27*46795Sbostic #include "dump.h" 2839130Smckusick #include "pathnames.h" 291424Sroot 3046588Storek static void alarmcatch(); 3146588Storek static void sendmes(); 3246588Storek 331424Sroot /* 3439130Smckusick * Query the operator; This previously-fascist piece of code 3539130Smckusick * no longer requires an exact response. 361424Sroot * It is intended to protect dump aborting by inquisitive 371424Sroot * people banging on the console terminal to see what is 381424Sroot * happening which might cause dump to croak, destroying 391424Sroot * a large number of hours of work. 401424Sroot * 411424Sroot * Every 2 minutes we reprint the message, alerting others 421424Sroot * that dump needs attention. 431424Sroot */ 441424Sroot int timeout; 4521124Smckusick char *attnmessage; /* attention message */ 4646588Storek 4746588Storek int 481424Sroot query(question) 491424Sroot char *question; 501424Sroot { 511424Sroot char replybuffer[64]; 5246588Storek int back, errcount; 531424Sroot FILE *mytty; 541424Sroot 5546588Storek if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 5646588Storek quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 571424Sroot attnmessage = question; 581424Sroot timeout = 0; 591424Sroot alarmcatch(); 6046588Storek back = -1; 6146588Storek errcount = 0; 6246588Storek do { 6346588Storek if (fgets(replybuffer, 63, mytty) == NULL) { 6446588Storek clearerr(mytty); 6546588Storek if (++errcount > 30) /* XXX ugly */ 6646588Storek quit("excessive operator query failures\n"); 6739130Smckusick } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 6846588Storek back = 1; 6939130Smckusick } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 7046588Storek back = 0; 711424Sroot } else { 7246588Storek (void) fprintf(stderr, 7346588Storek " DUMP: \"Yes\" or \"No\"?\n"); 7446588Storek (void) fprintf(stderr, 7546588Storek " DUMP: %s: (\"yes\" or \"no\") ", question); 761424Sroot } 7746588Storek } while (back < 0); 7846588Storek 791424Sroot /* 801424Sroot * Turn off the alarm, and reset the signal to trap out.. 811424Sroot */ 821424Sroot alarm(0); 831424Sroot if (signal(SIGALRM, sigalrm) == SIG_IGN) 841424Sroot signal(SIGALRM, SIG_IGN); 851424Sroot fclose(mytty); 861424Sroot return(back); 871424Sroot } 8839130Smckusick 8939130Smckusick char lastmsg[100]; 9039130Smckusick 911424Sroot /* 921424Sroot * Alert the console operator, and enable the alarm clock to 931424Sroot * sleep for 2 minutes in case nobody comes to satisfy dump 941424Sroot */ 9546588Storek static void 961424Sroot alarmcatch() 971424Sroot { 9839130Smckusick if (notify == 0) { 9939130Smckusick if (timeout == 0) 10046588Storek (void) fprintf(stderr, 10146588Storek " DUMP: %s: (\"yes\" or \"no\") ", 10239130Smckusick attnmessage); 10339130Smckusick else 10439130Smckusick msgtail("\7\7"); 10539130Smckusick } else { 10639130Smckusick if (timeout) { 10739130Smckusick msgtail("\n"); 10839130Smckusick broadcast(""); /* just print last msg */ 10939130Smckusick } 11046588Storek (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", 11139130Smckusick attnmessage); 11239130Smckusick } 1131424Sroot signal(SIGALRM, alarmcatch); 1141424Sroot alarm(120); 1151424Sroot timeout = 1; 1161424Sroot } 11746588Storek 1181424Sroot /* 1191424Sroot * Here if an inquisitive operator interrupts the dump program 1201424Sroot */ 12146588Storek void 1221424Sroot interrupt() 1231424Sroot { 12421124Smckusick msg("Interrupt received.\n"); 12521124Smckusick if (query("Do you want to abort dump?")) 1261424Sroot dumpabort(); 1271424Sroot } 1281424Sroot 1291424Sroot /* 1301424Sroot * The following variables and routines manage alerting 1311424Sroot * operators to the status of dump. 1321424Sroot * This works much like wall(1) does. 1331424Sroot */ 13446588Storek struct group *gp; 1351424Sroot 1361424Sroot /* 1371424Sroot * Get the names from the group entry "operator" to notify. 1381424Sroot */ 13946588Storek void 1401424Sroot set_operators() 1411424Sroot { 1421424Sroot if (!notify) /*not going to notify*/ 1431424Sroot return; 1441424Sroot gp = getgrnam(OPGRENT); 1451424Sroot endgrent(); 14646588Storek if (gp == NULL) { 14746588Storek msg("No group entry for %s.\n", OPGRENT); 1481424Sroot notify = 0; 1491424Sroot return; 1501424Sroot } 1511424Sroot } 1521424Sroot 1531424Sroot struct tm *localtime(); 1541424Sroot struct tm *localclock; 1551424Sroot 1561424Sroot /* 1571424Sroot * We fork a child to do the actual broadcasting, so 1581424Sroot * that the process control groups are not messed up 1591424Sroot */ 16046588Storek void 1611424Sroot broadcast(message) 1621424Sroot char *message; 1631424Sroot { 1641424Sroot time_t clock; 1651424Sroot FILE *f_utmp; 1661424Sroot struct utmp utmp; 1671424Sroot int nusers; 1681424Sroot char **np; 1691424Sroot int pid, s; 1701424Sroot 17146588Storek if (!notify || gp == NULL) 17246588Storek return; 17346588Storek 1741424Sroot switch (pid = fork()) { 1751424Sroot case -1: 1761424Sroot return; 1771424Sroot case 0: 1781424Sroot break; 1791424Sroot default: 1801424Sroot while (wait(&s) != pid) 1811424Sroot continue; 1821424Sroot return; 1831424Sroot } 1841424Sroot 1851424Sroot clock = time(0); 1861424Sroot localclock = localtime(&clock); 1871424Sroot 18846588Storek if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { 18946588Storek msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); 1901424Sroot return; 1911424Sroot } 1921424Sroot 1931424Sroot nusers = 0; 19446588Storek while (!feof(f_utmp)) { 1951424Sroot if (fread(&utmp, sizeof (struct utmp), 1, f_utmp) != 1) 1961424Sroot break; 1971424Sroot if (utmp.ut_name[0] == 0) 1981424Sroot continue; 1991424Sroot nusers++; 20046588Storek for (np = gp->gr_mem; *np; np++) { 2011424Sroot if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 2021424Sroot continue; 2031424Sroot /* 2041424Sroot * Do not send messages to operators on dialups 2051424Sroot */ 2061424Sroot if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 2071424Sroot continue; 2081424Sroot #ifdef DEBUG 20946588Storek msg("Message to %s at %s\n", *np, utmp.ut_line); 21046588Storek #endif 2111424Sroot sendmes(utmp.ut_line, message); 2121424Sroot } 2131424Sroot } 21446588Storek (void) fclose(f_utmp); 2151424Sroot Exit(0); /* the wait in this same routine will catch this */ 2161424Sroot /* NOTREACHED */ 2171424Sroot } 2181424Sroot 21946588Storek static void 2201424Sroot sendmes(tty, message) 2215319Smckusic char *tty, *message; 2221424Sroot { 2231424Sroot char t[50], buf[BUFSIZ]; 2241424Sroot register char *cp; 22539130Smckusick int lmsg = 1; 2261424Sroot FILE *f_tty; 2271424Sroot 22846588Storek (void) strcpy(t, _PATH_DEV); 22946588Storek (void) strcat(t, tty); 2301424Sroot 23146588Storek if ((f_tty = fopen(t, "w")) != NULL) { 2321424Sroot setbuf(f_tty, buf); 23346588Storek (void) fprintf(f_tty, 23446588Storek "\n\ 23546588Storek \7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ 23646588Storek DUMP: NEEDS ATTENTION: ", 23746588Storek localclock->tm_hour, localclock->tm_min); 23839130Smckusick for (cp = lastmsg; ; cp++) { 23939130Smckusick if (*cp == '\0') { 24039130Smckusick if (lmsg) { 24139130Smckusick cp = message; 24239130Smckusick if (*cp == '\0') 24339130Smckusick break; 24439130Smckusick lmsg = 0; 24539130Smckusick } else 24639130Smckusick break; 24739130Smckusick } 24839130Smckusick if (*cp == '\n') 24946588Storek (void) putc('\r', f_tty); 25046588Storek (void) putc(*cp, f_tty); 2511424Sroot } 25246588Storek (void) fclose(f_tty); 2531424Sroot } 2541424Sroot } 2551424Sroot 2561424Sroot /* 2571424Sroot * print out an estimate of the amount of time left to do the dump 2581424Sroot */ 2591424Sroot 2601424Sroot time_t tschedule = 0; 2611424Sroot 26246588Storek void 2631424Sroot timeest() 2641424Sroot { 2651424Sroot time_t tnow, deltat; 2661424Sroot 2671424Sroot time (&tnow); 26846588Storek if (tnow >= tschedule) { 2691424Sroot tschedule = tnow + 300; 2701424Sroot if (blockswritten < 500) 2711424Sroot return; 2721424Sroot deltat = tstart_writing - tnow + 27346788Smckusick (1.0 * (tnow - tstart_writing)) 27446788Smckusick / blockswritten * tapesize; 2751424Sroot msg("%3.2f%% done, finished in %d:%02d\n", 27646788Smckusick (blockswritten * 100.0) / tapesize, 27746788Smckusick deltat / 3600, (deltat % 3600) / 60); 2781424Sroot } 2791424Sroot } 2801424Sroot 28146788Smckusick /* 28246788Smckusick * tapesize: total number of blocks estimated over all reels 28346788Smckusick * blockswritten: blocks actually written, over all reels 28446788Smckusick * etapes: estimated number of tapes to write 28546788Smckusick * 28646788Smckusick * tsize: blocks can write on this reel 28746788Smckusick * asize: blocks written on this reel 28846788Smckusick * tapeno: number of tapes written so far 28946788Smckusick */ 29046588Storek int 29146588Storek blocksontape() 2921424Sroot { 2931424Sroot if (tapeno == etapes) 29446788Smckusick return (tapesize - (etapes - 1) * tsize); 29546588Storek return (tsize); 2961424Sroot } 2971424Sroot 29846588Storek #ifdef lint 29946588Storek 30046588Storek /* VARARGS1 */ 30146588Storek void msg(fmt) char *fmt; { strcpy(lastmsg, fmt); } 30246588Storek 30346588Storek /* VARARGS1 */ 30446588Storek void msgtail(fmt) char *fmt; { fmt = fmt; } 30546588Storek 30646588Storek void quit(fmt) char *fmt; { msg(fmt); dumpabort(); } 30746588Storek 30846588Storek #else /* lint */ 30946588Storek 31046588Storek void 31146588Storek msg(va_alist) 31246588Storek va_dcl 3131424Sroot { 31446588Storek va_list ap; 31546588Storek char *fmt; 31646588Storek 31746588Storek (void) fprintf(stderr," DUMP: "); 3181424Sroot #ifdef TDEBUG 31946588Storek (void) fprintf(stderr, "pid=%d ", getpid()); 3201424Sroot #endif 32146588Storek va_start(ap); 32246588Storek fmt = va_arg(ap, char *); 32346588Storek (void) vfprintf(stderr, fmt, ap); 32446588Storek va_end(ap); 32546588Storek (void) fflush(stdout); 32646588Storek (void) fflush(stderr); 32746588Storek va_start(ap); 32846588Storek fmt = va_arg(ap, char *); 32946588Storek (void) vsprintf(lastmsg, fmt, ap); 33046588Storek va_end(ap); 3311424Sroot } 3321424Sroot 33346588Storek void 33446588Storek msgtail(va_alist) 33546588Storek va_dcl 3361424Sroot { 33746588Storek va_list ap; 33846588Storek char *fmt; 33946588Storek 34046588Storek va_start(ap); 34146588Storek fmt = va_arg(ap, char *); 34246588Storek (void) vfprintf(stderr, fmt, ap); 34346588Storek va_end(ap); 3441424Sroot } 34546588Storek 34646588Storek void 34746588Storek quit(va_alist) 34846588Storek va_dcl 34946588Storek { 35046588Storek va_list ap; 35146588Storek char *fmt; 35246588Storek 35346588Storek (void) fprintf(stderr," DUMP: "); 35446588Storek #ifdef TDEBUG 35546588Storek (void) fprintf(stderr, "pid=%d ", getpid()); 35646588Storek #endif 35746588Storek va_start(ap); 35846588Storek fmt = va_arg(ap, char *); 35946588Storek vfprintf(stderr, fmt, ap); 36046588Storek va_end(ap); 36146588Storek (void) fflush(stdout); 36246588Storek (void) fflush(stderr); 36346588Storek dumpabort(); 36446588Storek } 36546588Storek 36646588Storek #endif /* lint */ 36746588Storek 3681424Sroot /* 3691424Sroot * Tell the operator what has to be done; 3701424Sroot * we don't actually do it 3711424Sroot */ 3721424Sroot 37313047Ssam struct fstab * 37413047Ssam allocfsent(fs) 37513047Ssam register struct fstab *fs; 37613047Ssam { 37713047Ssam register struct fstab *new; 37813047Ssam 37913047Ssam new = (struct fstab *)malloc(sizeof (*fs)); 38046588Storek if (new == NULL || 38146588Storek (new->fs_file = strdup(fs->fs_file)) == NULL || 38246588Storek (new->fs_type = strdup(fs->fs_type)) == NULL || 38346588Storek (new->fs_spec = strdup(fs->fs_spec)) == NULL) 38446588Storek quit("%s\n", strerror(errno)); 38513047Ssam new->fs_passno = fs->fs_passno; 38613047Ssam new->fs_freq = fs->fs_freq; 38713047Ssam return (new); 38813047Ssam } 38913047Ssam 39013047Ssam struct pfstab { 39113047Ssam struct pfstab *pf_next; 39213047Ssam struct fstab *pf_fstab; 39313047Ssam }; 39413047Ssam 39546588Storek static struct pfstab *table; 39613047Ssam 39746588Storek void 3981424Sroot getfstab() 3991424Sroot { 40013047Ssam register struct fstab *fs; 40113047Ssam register struct pfstab *pf; 4021424Sroot 4031428Sroot if (setfsent() == 0) { 40446588Storek msg("Can't open %s for dump table information: %s\n", 40546588Storek _PATH_FSTAB, strerror(errno)); 40613047Ssam return; 4071424Sroot } 40813047Ssam while (fs = getfsent()) { 40913047Ssam if (strcmp(fs->fs_type, FSTAB_RW) && 41013047Ssam strcmp(fs->fs_type, FSTAB_RO) && 41113047Ssam strcmp(fs->fs_type, FSTAB_RQ)) 41213047Ssam continue; 41313047Ssam fs = allocfsent(fs); 41446588Storek if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 41546588Storek quit("%s\n", strerror(errno)); 41613047Ssam pf->pf_fstab = fs; 41713047Ssam pf->pf_next = table; 41813047Ssam table = pf; 41913047Ssam } 42013047Ssam endfsent(); 4211424Sroot } 4221424Sroot 4231424Sroot /* 42413047Ssam * Search in the fstab for a file name. 42513047Ssam * This file name can be either the special or the path file name. 4261424Sroot * 42713047Ssam * The entries in the fstab are the BLOCK special names, not the 42813047Ssam * character special names. 42913047Ssam * The caller of fstabsearch assures that the character device 43013047Ssam * is dumped (that is much faster) 4311424Sroot * 43213047Ssam * The file name can omit the leading '/'. 4331424Sroot */ 43413047Ssam struct fstab * 43513047Ssam fstabsearch(key) 43613047Ssam char *key; 4371424Sroot { 43813047Ssam register struct pfstab *pf; 43913047Ssam register struct fstab *fs; 44013047Ssam char *rawname(); 4411424Sroot 44246588Storek for (pf = table; pf != NULL; pf = pf->pf_next) { 44313047Ssam fs = pf->pf_fstab; 44446588Storek if (strcmp(fs->fs_file, key) == 0 || 44546588Storek strcmp(fs->fs_spec, key) == 0 || 44646588Storek strcmp(rawname(fs->fs_spec), key) == 0) 44713047Ssam return (fs); 44846588Storek if (key[0] != '/') { 44913047Ssam if (*fs->fs_spec == '/' && 45013197Sroot strcmp(fs->fs_spec + 1, key) == 0) 45113047Ssam return (fs); 45213047Ssam if (*fs->fs_file == '/' && 45313197Sroot strcmp(fs->fs_file + 1, key) == 0) 45413047Ssam return (fs); 4551424Sroot } 4561424Sroot } 45746588Storek return (NULL); 4581424Sroot } 4591424Sroot 4601424Sroot /* 4611424Sroot * Tell the operator what to do 4621424Sroot */ 46346588Storek void 4641463Sroot lastdump(arg) 46546588Storek char arg; /* w ==> just what to do; W ==> most recent dumps */ 4661424Sroot { 46746788Smckusick register int i; 46846788Smckusick register struct fstab *dt; 46946788Smckusick register struct dumpdates *dtwalk; 47046788Smckusick char *lastname, *date; 47146788Smckusick int dumpme, datesort(); 47246788Smckusick time_t tnow; 4731424Sroot 4741424Sroot time(&tnow); 4751424Sroot getfstab(); /* /etc/fstab input */ 47646788Smckusick initdumptimes(); /* /etc/dumpdates input */ 47746788Smckusick qsort(ddatev, nddates, sizeof(struct dumpdates *), datesort); 4781424Sroot 4791463Sroot if (arg == 'w') 48046588Storek (void) printf("Dump these file systems:\n"); 4811463Sroot else 48246588Storek (void) printf("Last dump(s) done (Dump '>' file systems):\n"); 4831424Sroot lastname = "??"; 48446788Smckusick ITITERATE(i, dtwalk) { 48546788Smckusick if (strncmp(lastname, dtwalk->dd_name, 48646788Smckusick sizeof(dtwalk->dd_name)) == 0) 4871424Sroot continue; 48846788Smckusick date = (char *)ctime(&dtwalk->dd_ddate); 48946588Storek date[16] = '\0'; /* blast away seconds and year */ 49046788Smckusick lastname = dtwalk->dd_name; 49146788Smckusick dt = fstabsearch(dtwalk->dd_name); 49246588Storek dumpme = (dt != NULL && 49346588Storek dt->fs_freq != 0 && 49446788Smckusick dtwalk->dd_ddate < tnow - (dt->fs_freq * DAY)); 49546588Storek if (arg != 'w' || dumpme) 49646588Storek (void) printf( 49746588Storek "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 49846588Storek dumpme && (arg != 'w') ? '>' : ' ', 49946788Smckusick dtwalk->dd_name, 50046588Storek dt ? dt->fs_file : "", 50146788Smckusick dtwalk->dd_level, 50246588Storek date); 5031424Sroot } 5041424Sroot } 5051424Sroot 50646788Smckusick int 50746788Smckusick datesort(a1, a2) 50846588Storek void *a1, *a2; 5091424Sroot { 51046788Smckusick struct dumpdates *d1 = *(struct dumpdates **)a1; 51146788Smckusick struct dumpdates *d2 = *(struct dumpdates **)a2; 51246588Storek int diff; 5131424Sroot 51446788Smckusick diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 5151424Sroot if (diff == 0) 51646788Smckusick return (d2->dd_ddate - d1->dd_ddate); 51746788Smckusick return (diff); 5181424Sroot } 5191424Sroot 52046588Storek int max(a, b) 5215319Smckusic int a, b; 5221424Sroot { 52346588Storek return (a > b ? a : b); 5241424Sroot } 52546588Storek int min(a, b) 5265319Smckusic int a, b; 5271424Sroot { 52846588Storek return (a < b ? a : b); 5291424Sroot } 530