optr.c revision 1558
1131702Smbr/*-
2131702Smbr * Copyright (c) 1980, 1988, 1993
3131702Smbr *	The Regents of the University of California.  All rights reserved.
4131702Smbr *
5131702Smbr * Redistribution and use in source and binary forms, with or without
6131702Smbr * modification, are permitted provided that the following conditions
7131702Smbr * are met:
8131702Smbr * 1. Redistributions of source code must retain the above copyright
9131702Smbr *    notice, this list of conditions and the following disclaimer.
10131702Smbr * 2. Redistributions in binary form must reproduce the above copyright
11131702Smbr *    notice, this list of conditions and the following disclaimer in the
12131702Smbr *    documentation and/or other materials provided with the distribution.
13131702Smbr * 3. All advertising materials mentioning features or use of this software
14131702Smbr *    must display the following acknowledgement:
15131702Smbr *	This product includes software developed by the University of
16131702Smbr *	California, Berkeley and its contributors.
17131702Smbr * 4. Neither the name of the University nor the names of its contributors
18131702Smbr *    may be used to endorse or promote products derived from this software
19131702Smbr *    without specific prior written permission.
20131702Smbr *
21131702Smbr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22131702Smbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23131702Smbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24131702Smbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25131702Smbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26131702Smbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27131702Smbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28131702Smbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29131702Smbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30131702Smbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31131702Smbr * SUCH DAMAGE.
32131702Smbr */
33131702Smbr
34131702Smbr#ifndef lint
35131702Smbrstatic char sccsid[] = "@(#)optr.c	8.2 (Berkeley) 1/6/94";
36131702Smbr#endif /* not lint */
37131702Smbr
38131702Smbr#include <sys/param.h>
39131702Smbr#include <sys/wait.h>
40131702Smbr#include <sys/time.h>
41131702Smbr
42131702Smbr#include <errno.h>
43131702Smbr#include <fstab.h>
44131702Smbr#include <grp.h>
45131702Smbr#include <signal.h>
46131702Smbr#include <stdio.h>
47131702Smbr#ifdef __STDC__
48131702Smbr#include <stdlib.h>
49131702Smbr#include <string.h>
50131702Smbr#include <stdarg.h>
51131702Smbr#endif
52131702Smbr#include <tzfile.h>
53131702Smbr#ifdef __STDC__
54131702Smbr#include <unistd.h>
55131702Smbr#endif
56131702Smbr#include <utmp.h>
57131702Smbr#ifndef __STDC__
58131702Smbr#include <varargs.h>
59131702Smbr#endif
60131702Smbr
61#include "dump.h"
62#include "pathnames.h"
63
64void	alarmcatch __P((/* int, int */));
65int	datesort __P((const void *, const void *));
66static	void sendmes __P((char *, char *));
67
68/*
69 *	Query the operator; This previously-fascist piece of code
70 *	no longer requires an exact response.
71 *	It is intended to protect dump aborting by inquisitive
72 *	people banging on the console terminal to see what is
73 *	happening which might cause dump to croak, destroying
74 *	a large number of hours of work.
75 *
76 *	Every 2 minutes we reprint the message, alerting others
77 *	that dump needs attention.
78 */
79static	int timeout;
80static	char *attnmessage;		/* attention message */
81
82int
83query(question)
84	char	*question;
85{
86	char	replybuffer[64];
87	int	back, errcount;
88	FILE	*mytty;
89
90	if ((mytty = fopen(_PATH_TTY, "r")) == NULL)
91		quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno));
92	attnmessage = question;
93	timeout = 0;
94	alarmcatch();
95	back = -1;
96	errcount = 0;
97	do {
98		if (fgets(replybuffer, 63, mytty) == NULL) {
99			clearerr(mytty);
100			if (++errcount > 30)	/* XXX	ugly */
101				quit("excessive operator query failures\n");
102		} else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') {
103			back = 1;
104		} else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') {
105			back = 0;
106		} else {
107			(void) fprintf(stderr,
108			    "  DUMP: \"Yes\" or \"No\"?\n");
109			(void) fprintf(stderr,
110			    "  DUMP: %s: (\"yes\" or \"no\") ", question);
111		}
112	} while (back < 0);
113
114	/*
115	 *	Turn off the alarm, and reset the signal to trap out..
116	 */
117	(void) alarm(0);
118	if (signal(SIGALRM, sig) == SIG_IGN)
119		signal(SIGALRM, SIG_IGN);
120	(void) fclose(mytty);
121	return(back);
122}
123
124char lastmsg[100];
125
126/*
127 *	Alert the console operator, and enable the alarm clock to
128 *	sleep for 2 minutes in case nobody comes to satisfy dump
129 */
130void
131alarmcatch()
132{
133	if (notify == 0) {
134		if (timeout == 0)
135			(void) fprintf(stderr,
136			    "  DUMP: %s: (\"yes\" or \"no\") ",
137			    attnmessage);
138		else
139			msgtail("\7\7");
140	} else {
141		if (timeout) {
142			msgtail("\n");
143			broadcast("");		/* just print last msg */
144		}
145		(void) fprintf(stderr,"  DUMP: %s: (\"yes\" or \"no\") ",
146		    attnmessage);
147	}
148	signal(SIGALRM, alarmcatch);
149	(void) alarm(120);
150	timeout = 1;
151}
152
153/*
154 *	Here if an inquisitive operator interrupts the dump program
155 */
156void
157interrupt(signo)
158	int signo;
159{
160	msg("Interrupt received.\n");
161	if (query("Do you want to abort dump?"))
162		dumpabort(0);
163}
164
165/*
166 *	The following variables and routines manage alerting
167 *	operators to the status of dump.
168 *	This works much like wall(1) does.
169 */
170struct	group *gp;
171
172/*
173 *	Get the names from the group entry "operator" to notify.
174 */
175void
176set_operators()
177{
178	if (!notify)		/*not going to notify*/
179		return;
180	gp = getgrnam(OPGRENT);
181	(void) endgrent();
182	if (gp == NULL) {
183		msg("No group entry for %s.\n", OPGRENT);
184		notify = 0;
185		return;
186	}
187}
188
189struct tm *localclock;
190
191/*
192 *	We fork a child to do the actual broadcasting, so
193 *	that the process control groups are not messed up
194 */
195void
196broadcast(message)
197	char	*message;
198{
199	time_t		clock;
200	FILE	*f_utmp;
201	struct	utmp	utmp;
202	char	**np;
203	int	pid, s;
204
205	if (!notify || gp == NULL)
206		return;
207
208	switch (pid = fork()) {
209	case -1:
210		return;
211	case 0:
212		break;
213	default:
214		while (wait(&s) != pid)
215			continue;
216		return;
217	}
218
219	clock = time((time_t *)0);
220	localclock = localtime(&clock);
221
222	if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) {
223		msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno));
224		return;
225	}
226
227	while (!feof(f_utmp)) {
228		if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1)
229			break;
230		if (utmp.ut_name[0] == 0)
231			continue;
232		for (np = gp->gr_mem; *np; np++) {
233			if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0)
234				continue;
235			/*
236			 *	Do not send messages to operators on dialups
237			 */
238			if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0)
239				continue;
240#ifdef DEBUG
241			msg("Message to %s at %s\n", *np, utmp.ut_line);
242#endif
243			sendmes(utmp.ut_line, message);
244		}
245	}
246	(void) fclose(f_utmp);
247	Exit(0);	/* the wait in this same routine will catch this */
248	/* NOTREACHED */
249}
250
251static void
252sendmes(tty, message)
253	char *tty, *message;
254{
255	char t[50], buf[BUFSIZ];
256	register char *cp;
257	int lmsg = 1;
258	FILE *f_tty;
259
260	(void) strcpy(t, _PATH_DEV);
261	(void) strcat(t, tty);
262
263	if ((f_tty = fopen(t, "w")) != NULL) {
264		setbuf(f_tty, buf);
265		(void) fprintf(f_tty,
266		    "\n\
267\7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\
268DUMP: NEEDS ATTENTION: ",
269		    localclock->tm_hour, localclock->tm_min);
270		for (cp = lastmsg; ; cp++) {
271			if (*cp == '\0') {
272				if (lmsg) {
273					cp = message;
274					if (*cp == '\0')
275						break;
276					lmsg = 0;
277				} else
278					break;
279			}
280			if (*cp == '\n')
281				(void) putc('\r', f_tty);
282			(void) putc(*cp, f_tty);
283		}
284		(void) fclose(f_tty);
285	}
286}
287
288/*
289 *	print out an estimate of the amount of time left to do the dump
290 */
291
292time_t	tschedule = 0;
293
294void
295timeest()
296{
297	time_t	tnow, deltat;
298
299	(void) time((time_t *) &tnow);
300	if (tnow >= tschedule) {
301		tschedule = tnow + 300;
302		if (blockswritten < 500)
303			return;
304		deltat = tstart_writing - tnow +
305			(1.0 * (tnow - tstart_writing))
306			/ blockswritten * tapesize;
307		msg("%3.2f%% done, finished in %d:%02d\n",
308			(blockswritten * 100.0) / tapesize,
309			deltat / 3600, (deltat % 3600) / 60);
310	}
311}
312
313void
314#if __STDC__
315msg(const char *fmt, ...)
316#else
317msg(fmt, va_alist)
318	char *fmt;
319	va_dcl
320#endif
321{
322	va_list ap;
323
324	(void) fprintf(stderr,"  DUMP: ");
325#ifdef TDEBUG
326	(void) fprintf(stderr, "pid=%d ", getpid());
327#endif
328#if __STDC__
329	va_start(ap, fmt);
330#else
331	va_start(ap);
332#endif
333	(void) vfprintf(stderr, fmt, ap);
334	(void) fflush(stdout);
335	(void) fflush(stderr);
336	(void) vsprintf(lastmsg, fmt, ap);
337	va_end(ap);
338}
339
340void
341#if __STDC__
342msgtail(const char *fmt, ...)
343#else
344msgtail(fmt, va_alist)
345	char *fmt;
346	va_dcl
347#endif
348{
349	va_list ap;
350#if __STDC__
351	va_start(ap, fmt);
352#else
353	va_start(ap);
354#endif
355	(void) vfprintf(stderr, fmt, ap);
356	va_end(ap);
357}
358
359void
360#if __STDC__
361quit(const char *fmt, ...)
362#else
363quit(fmt, va_alist)
364	char *fmt;
365	va_dcl
366#endif
367{
368	va_list ap;
369
370	(void) fprintf(stderr,"  DUMP: ");
371#ifdef TDEBUG
372	(void) fprintf(stderr, "pid=%d ", getpid());
373#endif
374#if __STDC__
375	va_start(ap, fmt);
376#else
377	va_start(ap);
378#endif
379	(void) vfprintf(stderr, fmt, ap);
380	va_end(ap);
381	(void) fflush(stdout);
382	(void) fflush(stderr);
383	dumpabort(0);
384}
385
386/*
387 *	Tell the operator what has to be done;
388 *	we don't actually do it
389 */
390
391struct fstab *
392allocfsent(fs)
393	register struct fstab *fs;
394{
395	register struct fstab *new;
396
397	new = (struct fstab *)malloc(sizeof (*fs));
398	if (new == NULL ||
399	    (new->fs_file = strdup(fs->fs_file)) == NULL ||
400	    (new->fs_type = strdup(fs->fs_type)) == NULL ||
401	    (new->fs_spec = strdup(fs->fs_spec)) == NULL)
402		quit("%s\n", strerror(errno));
403	new->fs_passno = fs->fs_passno;
404	new->fs_freq = fs->fs_freq;
405	return (new);
406}
407
408struct	pfstab {
409	struct	pfstab *pf_next;
410	struct	fstab *pf_fstab;
411};
412
413static	struct pfstab *table;
414
415void
416getfstab()
417{
418	register struct fstab *fs;
419	register struct pfstab *pf;
420
421	if (setfsent() == 0) {
422		msg("Can't open %s for dump table information: %s\n",
423		    _PATH_FSTAB, strerror(errno));
424		return;
425	}
426	while ((fs = getfsent()) != NULL) {
427		if (strcmp(fs->fs_type, FSTAB_RW) &&
428		    strcmp(fs->fs_type, FSTAB_RO) &&
429		    strcmp(fs->fs_type, FSTAB_RQ))
430			continue;
431		fs = allocfsent(fs);
432		if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL)
433			quit("%s\n", strerror(errno));
434		pf->pf_fstab = fs;
435		pf->pf_next = table;
436		table = pf;
437	}
438	(void) endfsent();
439}
440
441/*
442 * Search in the fstab for a file name.
443 * This file name can be either the special or the path file name.
444 *
445 * The entries in the fstab are the BLOCK special names, not the
446 * character special names.
447 * The caller of fstabsearch assures that the character device
448 * is dumped (that is much faster)
449 *
450 * The file name can omit the leading '/'.
451 */
452struct fstab *
453fstabsearch(key)
454	char *key;
455{
456	register struct pfstab *pf;
457	register struct fstab *fs;
458	char *rn;
459
460	for (pf = table; pf != NULL; pf = pf->pf_next) {
461		fs = pf->pf_fstab;
462		if (strcmp(fs->fs_file, key) == 0 ||
463		    strcmp(fs->fs_spec, key) == 0)
464			return (fs);
465		rn = rawname(fs->fs_spec);
466		if (rn != NULL && strcmp(rn, key) == 0)
467			return (fs);
468		if (key[0] != '/') {
469			if (*fs->fs_spec == '/' &&
470			    strcmp(fs->fs_spec + 1, key) == 0)
471				return (fs);
472			if (*fs->fs_file == '/' &&
473			    strcmp(fs->fs_file + 1, key) == 0)
474				return (fs);
475		}
476	}
477	return (NULL);
478}
479
480/*
481 *	Tell the operator what to do
482 */
483void
484lastdump(arg)
485	char	arg;	/* w ==> just what to do; W ==> most recent dumps */
486{
487	register int i;
488	register struct fstab *dt;
489	register struct dumpdates *dtwalk;
490	char *lastname, *date;
491	int dumpme;
492	time_t tnow;
493
494	(void) time(&tnow);
495	getfstab();		/* /etc/fstab input */
496	initdumptimes();	/* /etc/dumpdates input */
497	qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort);
498
499	if (arg == 'w')
500		(void) printf("Dump these file systems:\n");
501	else
502		(void) printf("Last dump(s) done (Dump '>' file systems):\n");
503	lastname = "??";
504	ITITERATE(i, dtwalk) {
505		if (strncmp(lastname, dtwalk->dd_name,
506		    sizeof(dtwalk->dd_name)) == 0)
507			continue;
508		date = (char *)ctime(&dtwalk->dd_ddate);
509		date[16] = '\0';	/* blast away seconds and year */
510		lastname = dtwalk->dd_name;
511		dt = fstabsearch(dtwalk->dd_name);
512		dumpme = (dt != NULL &&
513		    dt->fs_freq != 0 &&
514		    dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY));
515		if (arg != 'w' || dumpme)
516			(void) printf(
517			    "%c %8s\t(%6s) Last dump: Level %c, Date %s\n",
518			    dumpme && (arg != 'w') ? '>' : ' ',
519			    dtwalk->dd_name,
520			    dt ? dt->fs_file : "",
521			    dtwalk->dd_level,
522			    date);
523	}
524}
525
526int
527datesort(a1, a2)
528	const void *a1, *a2;
529{
530	struct dumpdates *d1 = *(struct dumpdates **)a1;
531	struct dumpdates *d2 = *(struct dumpdates **)a2;
532	int diff;
533
534	diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name));
535	if (diff == 0)
536		return (d2->dd_ddate - d1->dd_ddate);
537	return (diff);
538}
539