xref: /csrg-svn/sbin/dump/optr.c (revision 39130)
122038Sdist /*
2*39130Smckusick  * 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*39130Smckusick static char sccsid[] = "@(#)optr.c	5.2 (Berkeley) 09/12/89";
922038Sdist #endif not lint
1022038Sdist 
111424Sroot #include "dump.h"
12*39130Smckusick #include "pathnames.h"
131424Sroot 
141424Sroot /*
151424Sroot  *	This is from /usr/include/grp.h
161424Sroot  *	That defined struct group, which conflicts
171424Sroot  *	with the struct group defined in param.h
181424Sroot  */
191424Sroot struct	Group { /* see getgrent(3) */
201424Sroot 	char	*gr_name;
211424Sroot 	char	*gr_passwd;
221424Sroot 	int	gr_gid;
231424Sroot 	char	**gr_mem;
241424Sroot };
251424Sroot struct	Group *getgrnam();
261424Sroot /*
27*39130Smckusick  *	Query the operator; This previously-fascist piece of code
28*39130Smckusick  *	no longer requires an exact response.
291424Sroot  *	It is intended to protect dump aborting by inquisitive
301424Sroot  *	people banging on the console terminal to see what is
311424Sroot  *	happening which might cause dump to croak, destroying
321424Sroot  *	a large number of hours of work.
331424Sroot  *
341424Sroot  *	Every 2 minutes we reprint the message, alerting others
351424Sroot  *	that dump needs attention.
361424Sroot  */
371424Sroot int	timeout;
3821124Smckusick char	*attnmessage;		/* attention message */
391424Sroot query(question)
401424Sroot 	char	*question;
411424Sroot {
421424Sroot 	char	replybuffer[64];
431424Sroot 	int	back;
441424Sroot 	FILE	*mytty;
451424Sroot 
46*39130Smckusick 	if ( (mytty = fopen(_PATH_TTY, "r")) == NULL){
47*39130Smckusick 		msg("fopen on %s fails\n", _PATH_TTY);
481424Sroot 		abort();
491424Sroot 	}
501424Sroot 	attnmessage = question;
511424Sroot 	timeout = 0;
521424Sroot 	alarmcatch();
531424Sroot 	for(;;){
541424Sroot 		if ( fgets(replybuffer, 63, mytty) == NULL){
551424Sroot 			if (ferror(mytty)){
561424Sroot 				clearerr(mytty);
571424Sroot 				continue;
581424Sroot 			}
59*39130Smckusick 		} else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') {
601424Sroot 				back = 1;
611424Sroot 				goto done;
62*39130Smckusick 		} else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') {
631424Sroot 				back = 0;
641424Sroot 				goto done;
651424Sroot 		} else {
66*39130Smckusick 			fprintf(stderr, "  DUMP: \"Yes\" or \"No\"?\n");
67*39130Smckusick 			fprintf(stderr,"  DUMP: %s: (\"yes\" or \"no\") ",
68*39130Smckusick 			    question);
691424Sroot 		}
701424Sroot 	}
711424Sroot     done:
721424Sroot 	/*
731424Sroot 	 *	Turn off the alarm, and reset the signal to trap out..
741424Sroot 	 */
751424Sroot 	alarm(0);
761424Sroot 	if (signal(SIGALRM, sigalrm) == SIG_IGN)
771424Sroot 		signal(SIGALRM, SIG_IGN);
781424Sroot 	fclose(mytty);
791424Sroot 	return(back);
801424Sroot }
81*39130Smckusick 
82*39130Smckusick char lastmsg[100];
83*39130Smckusick 
841424Sroot /*
851424Sroot  *	Alert the console operator, and enable the alarm clock to
861424Sroot  *	sleep for 2 minutes in case nobody comes to satisfy dump
871424Sroot  */
881424Sroot alarmcatch()
891424Sroot {
90*39130Smckusick 	if (notify == 0) {
91*39130Smckusick 		if (timeout == 0)
92*39130Smckusick 			fprintf(stderr,"  DUMP: %s: (\"yes\" or \"no\") ",
93*39130Smckusick 			    attnmessage);
94*39130Smckusick 		else
95*39130Smckusick 			msgtail("\7\7");
96*39130Smckusick 	} else {
97*39130Smckusick 		if (timeout) {
98*39130Smckusick 			msgtail("\n");
99*39130Smckusick 			broadcast("");		/* just print last msg */
100*39130Smckusick 		}
101*39130Smckusick 		fprintf(stderr,"  DUMP: %s: (\"yes\" or \"no\") ",
102*39130Smckusick 		    attnmessage);
103*39130Smckusick 	}
1041424Sroot 	signal(SIGALRM, alarmcatch);
1051424Sroot 	alarm(120);
1061424Sroot 	timeout = 1;
1071424Sroot }
1081424Sroot /*
1091424Sroot  *	Here if an inquisitive operator interrupts the dump program
1101424Sroot  */
1111424Sroot interrupt()
1121424Sroot {
11321124Smckusick 	msg("Interrupt received.\n");
11421124Smckusick 	if (query("Do you want to abort dump?"))
1151424Sroot 		dumpabort();
1161424Sroot }
1171424Sroot 
1181424Sroot /*
1191424Sroot  *	The following variables and routines manage alerting
1201424Sroot  *	operators to the status of dump.
1211424Sroot  *	This works much like wall(1) does.
1221424Sroot  */
1231424Sroot struct	Group *gp;
1241424Sroot 
1251424Sroot /*
1261424Sroot  *	Get the names from the group entry "operator" to notify.
1271424Sroot  */
1281424Sroot set_operators()
1291424Sroot {
1301424Sroot 	if (!notify)		/*not going to notify*/
1311424Sroot 		return;
1321424Sroot 	gp = getgrnam(OPGRENT);
1331424Sroot 	endgrent();
1341424Sroot 	if (gp == (struct Group *)0){
135*39130Smckusick 		msg("No group entry for %s.\n",
1361424Sroot 			OPGRENT);
1371424Sroot 		notify = 0;
1381424Sroot 		return;
1391424Sroot 	}
1401424Sroot }
1411424Sroot 
1421424Sroot struct tm *localtime();
1431424Sroot struct tm *localclock;
1441424Sroot 
1451424Sroot /*
1461424Sroot  *	We fork a child to do the actual broadcasting, so
1471424Sroot  *	that the process control groups are not messed up
1481424Sroot  */
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 
1591424Sroot 	switch (pid = fork()) {
1601424Sroot 	case -1:
1611424Sroot 		return;
1621424Sroot 	case 0:
1631424Sroot 		break;
1641424Sroot 	default:
1651424Sroot 		while (wait(&s) != pid)
1661424Sroot 			continue;
1671424Sroot 		return;
1681424Sroot 	}
1691424Sroot 
1701424Sroot 	if (!notify || gp == 0)
1711925Swnj 		exit(0);
1721424Sroot 	clock = time(0);
1731424Sroot 	localclock = localtime(&clock);
1741424Sroot 
175*39130Smckusick 	if((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) {
176*39130Smckusick 		msg("Cannot open %s\n", _PATH_UTMP);
1771424Sroot 		return;
1781424Sroot 	}
1791424Sroot 
1801424Sroot 	nusers = 0;
1811424Sroot 	while (!feof(f_utmp)){
1821424Sroot 		if (fread(&utmp, sizeof (struct utmp), 1, f_utmp) != 1)
1831424Sroot 			break;
1841424Sroot 		if (utmp.ut_name[0] == 0)
1851424Sroot 			continue;
1861424Sroot 		nusers++;
1871424Sroot 		for (np = gp->gr_mem; *np; np++){
1881424Sroot 			if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0)
1891424Sroot 				continue;
1901424Sroot 			/*
1911424Sroot 			 *	Do not send messages to operators on dialups
1921424Sroot 			 */
1931424Sroot 			if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0)
1941424Sroot 				continue;
1951424Sroot #ifdef DEBUG
1961424Sroot 			msg("Message to %s at %s\n",
1971424Sroot 				utmp.ut_name, utmp.ut_line);
1981424Sroot #endif DEBUG
1991424Sroot 			sendmes(utmp.ut_line, message);
2001424Sroot 		}
2011424Sroot 	}
2021424Sroot 	fclose(f_utmp);
2031424Sroot 	Exit(0);	/* the wait in this same routine will catch this */
2041424Sroot 	/* NOTREACHED */
2051424Sroot }
2061424Sroot 
2071424Sroot sendmes(tty, message)
2085319Smckusic 	char *tty, *message;
2091424Sroot {
2101424Sroot 	char t[50], buf[BUFSIZ];
2111424Sroot 	register char *cp;
212*39130Smckusick 	int lmsg = 1;
2131424Sroot 	FILE *f_tty;
2141424Sroot 
215*39130Smckusick 	strcpy(t, _PATH_DEV);
2161424Sroot 	strcat(t, tty);
2171424Sroot 
2181424Sroot 	if((f_tty = fopen(t, "w")) != NULL) {
2191424Sroot 		setbuf(f_tty, buf);
220*39130Smckusick 		fprintf(f_tty, "\n\07\07\07Message from the dump program to all operators at %d:%02d ...\r\n\n  DUMP: NEEDS ATTENTION: "
2211424Sroot 		       ,localclock->tm_hour
2221424Sroot 		       ,localclock->tm_min);
223*39130Smckusick 		for (cp = lastmsg; ; cp++) {
224*39130Smckusick 			if (*cp == '\0') {
225*39130Smckusick 				if (lmsg) {
226*39130Smckusick 					cp = message;
227*39130Smckusick 					if (*cp == '\0')
228*39130Smckusick 						break;
229*39130Smckusick 					lmsg = 0;
230*39130Smckusick 				} else
231*39130Smckusick 					break;
232*39130Smckusick 			}
233*39130Smckusick 			if (*cp == '\n')
2341424Sroot 				putc('\r', f_tty);
235*39130Smckusick 			putc(*cp, f_tty);
2361424Sroot 		}
2371424Sroot 		fclose(f_tty);
2381424Sroot 	}
2391424Sroot }
2401424Sroot 
2411424Sroot /*
2421424Sroot  *	print out an estimate of the amount of time left to do the dump
2431424Sroot  */
2441424Sroot 
2451424Sroot time_t	tschedule = 0;
2461424Sroot 
2471424Sroot timeest()
2481424Sroot {
2491424Sroot 	time_t	tnow, deltat;
2501424Sroot 
2511424Sroot 	time (&tnow);
2521424Sroot 	if (tnow >= tschedule){
2531424Sroot 		tschedule = tnow + 300;
2541424Sroot 		if (blockswritten < 500)
2551424Sroot 			return;
2561424Sroot 		deltat = tstart_writing - tnow +
2571424Sroot 			(((1.0*(tnow - tstart_writing))/blockswritten) * esize);
2581424Sroot 		msg("%3.2f%% done, finished in %d:%02d\n",
2591424Sroot 			(blockswritten*100.0)/esize,
2601424Sroot 			deltat/3600, (deltat%3600)/60);
2611424Sroot 	}
2621424Sroot }
2631424Sroot 
2641424Sroot int blocksontape()
2651424Sroot {
2661424Sroot 	/*
2671424Sroot 	 *	esize: total number of blocks estimated over all reels
2681424Sroot 	 *	blockswritten:	blocks actually written, over all reels
2691424Sroot 	 *	etapes:	estimated number of tapes to write
2701424Sroot 	 *
2711424Sroot 	 *	tsize:	blocks can write on this reel
2721424Sroot 	 *	asize:	blocks written on this reel
2731424Sroot 	 *	tapeno:	number of tapes written so far
2741424Sroot 	 */
2751424Sroot 	if (tapeno == etapes)
2761424Sroot 		return(esize - (etapes - 1)*tsize);
2771424Sroot 	return(tsize);
2781424Sroot }
2791424Sroot 
2801424Sroot 	/* VARARGS1 */
2811424Sroot 	/* ARGSUSED */
2821424Sroot msg(fmt, a1, a2, a3, a4, a5)
2831424Sroot 	char	*fmt;
2845319Smckusic 	int	a1, a2, a3, a4, a5;
2851424Sroot {
2861424Sroot 	fprintf(stderr,"  DUMP: ");
2871424Sroot #ifdef TDEBUG
2881424Sroot 	fprintf(stderr,"pid=%d ", getpid());
2891424Sroot #endif
2901424Sroot 	fprintf(stderr, fmt, a1, a2, a3, a4, a5);
2911424Sroot 	fflush(stdout);
2921424Sroot 	fflush(stderr);
293*39130Smckusick 	sprintf(lastmsg, fmt, a1, a2, a3, a4, a5);
2941424Sroot }
2951424Sroot 
2961424Sroot 	/* VARARGS1 */
2971424Sroot 	/* ARGSUSED */
2981424Sroot msgtail(fmt, a1, a2, a3, a4, a5)
2991424Sroot 	char	*fmt;
3005319Smckusic 	int	a1, a2, a3, a4, a5;
3011424Sroot {
3021424Sroot 	fprintf(stderr, fmt, a1, a2, a3, a4, a5);
3031424Sroot }
3041424Sroot /*
3051424Sroot  *	Tell the operator what has to be done;
3061424Sroot  *	we don't actually do it
3071424Sroot  */
3081424Sroot 
30913047Ssam struct fstab *
31013047Ssam allocfsent(fs)
31113047Ssam 	register struct fstab *fs;
31213047Ssam {
31313047Ssam 	register struct fstab *new;
31413047Ssam 	register char *cp;
31513047Ssam 	char *malloc();
31613047Ssam 
31713047Ssam 	new = (struct fstab *)malloc(sizeof (*fs));
31813047Ssam 	cp = malloc(strlen(fs->fs_file) + 1);
31913047Ssam 	strcpy(cp, fs->fs_file);
32013047Ssam 	new->fs_file = cp;
32113047Ssam 	cp = malloc(strlen(fs->fs_type) + 1);
32213047Ssam 	strcpy(cp, fs->fs_type);
32313047Ssam 	new->fs_type = cp;
32413047Ssam 	cp = malloc(strlen(fs->fs_spec) + 1);
32513047Ssam 	strcpy(cp, fs->fs_spec);
32613047Ssam 	new->fs_spec = cp;
32713047Ssam 	new->fs_passno = fs->fs_passno;
32813047Ssam 	new->fs_freq = fs->fs_freq;
32913047Ssam 	return (new);
33013047Ssam }
33113047Ssam 
33213047Ssam struct	pfstab {
33313047Ssam 	struct	pfstab *pf_next;
33413047Ssam 	struct	fstab *pf_fstab;
33513047Ssam };
33613047Ssam 
33713047Ssam static	struct pfstab *table = NULL;
33813047Ssam 
3391424Sroot getfstab()
3401424Sroot {
34113047Ssam 	register struct fstab *fs;
34213047Ssam 	register struct pfstab *pf;
3431424Sroot 
3441428Sroot 	if (setfsent() == 0) {
345*39130Smckusick 		msg("Can't open %s for dump table information.\n", _PATH_FSTAB);
34613047Ssam 		return;
3471424Sroot 	}
34813047Ssam 	while (fs = getfsent()) {
34913047Ssam 		if (strcmp(fs->fs_type, FSTAB_RW) &&
35013047Ssam 		    strcmp(fs->fs_type, FSTAB_RO) &&
35113047Ssam 		    strcmp(fs->fs_type, FSTAB_RQ))
35213047Ssam 			continue;
35313047Ssam 		fs = allocfsent(fs);
35413047Ssam 		pf = (struct pfstab *)malloc(sizeof (*pf));
35513047Ssam 		pf->pf_fstab = fs;
35613047Ssam 		pf->pf_next = table;
35713047Ssam 		table = pf;
35813047Ssam 	}
35913047Ssam 	endfsent();
3601424Sroot }
3611424Sroot 
3621424Sroot /*
36313047Ssam  * Search in the fstab for a file name.
36413047Ssam  * This file name can be either the special or the path file name.
3651424Sroot  *
36613047Ssam  * The entries in the fstab are the BLOCK special names, not the
36713047Ssam  * character special names.
36813047Ssam  * The caller of fstabsearch assures that the character device
36913047Ssam  * is dumped (that is much faster)
3701424Sroot  *
37113047Ssam  * The file name can omit the leading '/'.
3721424Sroot  */
37313047Ssam struct fstab *
37413047Ssam fstabsearch(key)
37513047Ssam 	char *key;
3761424Sroot {
37713047Ssam 	register struct pfstab *pf;
37813047Ssam 	register struct fstab *fs;
37913047Ssam 	char *rawname();
3801424Sroot 
38113047Ssam 	if (table == NULL)
38213047Ssam 		return ((struct fstab *)0);
38313047Ssam 	for (pf = table; pf; pf = pf->pf_next) {
38413047Ssam 		fs = pf->pf_fstab;
38513197Sroot 		if (strcmp(fs->fs_file, key) == 0)
38613047Ssam 			return (fs);
38713197Sroot 		if (strcmp(fs->fs_spec, key) == 0)
38813047Ssam 			return (fs);
38913197Sroot 		if (strcmp(rawname(fs->fs_spec), key) == 0)
39013047Ssam 			return (fs);
3911424Sroot 		if (key[0] != '/'){
39213047Ssam 			if (*fs->fs_spec == '/' &&
39313197Sroot 			    strcmp(fs->fs_spec + 1, key) == 0)
39413047Ssam 				return (fs);
39513047Ssam 			if (*fs->fs_file == '/' &&
39613197Sroot 			    strcmp(fs->fs_file + 1, key) == 0)
39713047Ssam 				return (fs);
3981424Sroot 		}
3991424Sroot 	}
40013047Ssam 	return (0);
4011424Sroot }
4021424Sroot 
4031424Sroot /*
4041424Sroot  *	Tell the operator what to do
4051424Sroot  */
4061463Sroot lastdump(arg)
4071463Sroot 	char	arg;		/* w ==> just what to do; W ==> most recent dumps */
4081424Sroot {
4091424Sroot 			char	*lastname;
4101424Sroot 			char	*date;
4111424Sroot 	register	int	i;
4121424Sroot 			time_t	tnow;
4131424Sroot 	register	struct	fstab	*dt;
4141424Sroot 			int	dumpme;
4151424Sroot 	register	struct	idates	*itwalk;
4161424Sroot 
4171424Sroot 	int	idatesort();
4181424Sroot 
4191424Sroot 	time(&tnow);
4201424Sroot 	getfstab();		/* /etc/fstab input */
4211424Sroot 	inititimes();		/* /etc/dumpdates input */
4221424Sroot 	qsort(idatev, nidates, sizeof(struct idates *), idatesort);
4231424Sroot 
4241463Sroot 	if (arg == 'w')
4251463Sroot 		fprintf(stdout, "Dump these file systems:\n");
4261463Sroot 	else
4271463Sroot 		fprintf(stdout, "Last dump(s) done (Dump '>' file systems):\n");
4281424Sroot 	lastname = "??";
4291424Sroot 	ITITERATE(i, itwalk){
4301424Sroot 		if (strncmp(lastname, itwalk->id_name, sizeof(itwalk->id_name)) == 0)
4311424Sroot 			continue;
4321424Sroot 		date = (char *)ctime(&itwalk->id_ddate);
4331424Sroot 		date[16] = '\0';		/* blast away seconds and year */
4341424Sroot 		lastname = itwalk->id_name;
4351424Sroot 		dt = fstabsearch(itwalk->id_name);
4361424Sroot 		dumpme = (  (dt != 0)
4371424Sroot 			 && (dt->fs_freq != 0)
4381424Sroot 			 && (itwalk->id_ddate < tnow - (dt->fs_freq*DAY)));
4391463Sroot 		if ( (arg != 'w') || dumpme)
4401463Sroot 		  fprintf(stdout,"%c %8s\t(%6s) Last dump: Level %c, Date %s\n",
4411463Sroot 			dumpme && (arg != 'w') ? '>' : ' ',
4421424Sroot 			itwalk->id_name,
44310910Ssam 			dt ? dt->fs_file : "",
4441424Sroot 			itwalk->id_incno,
4451424Sroot 			date
4461424Sroot 		    );
4471424Sroot 	}
4481424Sroot }
4491424Sroot 
4501424Sroot int	idatesort(p1, p2)
4511424Sroot 	struct	idates	**p1, **p2;
4521424Sroot {
4531424Sroot 	int	diff;
4541424Sroot 
4551424Sroot 	diff = strncmp((*p1)->id_name, (*p2)->id_name, sizeof((*p1)->id_name));
4561424Sroot 	if (diff == 0)
4571424Sroot 		return ((*p2)->id_ddate - (*p1)->id_ddate);
4581424Sroot 	else
4591424Sroot 		return (diff);
4601424Sroot }
4611424Sroot 
4621424Sroot int max(a,b)
4635319Smckusic 	int a, b;
4641424Sroot {
4651424Sroot 	return(a>b?a:b);
4661424Sroot }
4671424Sroot int min(a,b)
4685319Smckusic 	int a, b;
4691424Sroot {
4701424Sroot 	return(a<b?a:b);
4711424Sroot }
472