xref: /original-bsd/sbin/dump/optr.c (revision d830e70e)
1 /*-
2  * Copyright (c) 1980, 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)optr.c	5.12 (Berkeley) 07/29/91";
10 #endif /* not lint */
11 
12 #ifdef sunos
13 #include <stdio.h>
14 #include <ctype.h>
15 #include <sys/param.h>
16 #include <sys/wait.h>
17 #include <sys/stat.h>
18 #include <sys/time.h>
19 #include <sys/dir.h>
20 #else
21 #include <sys/param.h>
22 #include <sys/wait.h>
23 #include <stdio.h>
24 #endif
25 #include <signal.h>
26 #include <time.h>
27 #include <fstab.h>
28 #include <grp.h>
29 #include <utmp.h>
30 #include <tzfile.h>
31 #include <errno.h>
32 #ifdef __STDC__
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <stdarg.h>
37 #else
38 #include <varargs.h>
39 #endif
40 #include "dump.h"
41 #include "pathnames.h"
42 
43 static void alarmcatch();
44 static void sendmes();
45 
46 /*
47  *	Query the operator; This previously-fascist piece of code
48  *	no longer requires an exact response.
49  *	It is intended to protect dump aborting by inquisitive
50  *	people banging on the console terminal to see what is
51  *	happening which might cause dump to croak, destroying
52  *	a large number of hours of work.
53  *
54  *	Every 2 minutes we reprint the message, alerting others
55  *	that dump needs attention.
56  */
57 int	timeout;
58 char	*attnmessage;		/* attention message */
59 
60 int
61 query(question)
62 	char	*question;
63 {
64 	char	replybuffer[64];
65 	int	back, errcount;
66 	FILE	*mytty;
67 
68 	if ((mytty = fopen(_PATH_TTY, "r")) == NULL)
69 		quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno));
70 	attnmessage = question;
71 	timeout = 0;
72 	alarmcatch();
73 	back = -1;
74 	errcount = 0;
75 	do {
76 		if (fgets(replybuffer, 63, mytty) == NULL) {
77 			clearerr(mytty);
78 			if (++errcount > 30)	/* XXX	ugly */
79 				quit("excessive operator query failures\n");
80 		} else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') {
81 			back = 1;
82 		} else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') {
83 			back = 0;
84 		} else {
85 			(void) fprintf(stderr,
86 			    "  DUMP: \"Yes\" or \"No\"?\n");
87 			(void) fprintf(stderr,
88 			    "  DUMP: %s: (\"yes\" or \"no\") ", question);
89 		}
90 	} while (back < 0);
91 
92 	/*
93 	 *	Turn off the alarm, and reset the signal to trap out..
94 	 */
95 	alarm(0);
96 	if (signal(SIGALRM, sigalrm) == SIG_IGN)
97 		signal(SIGALRM, SIG_IGN);
98 	fclose(mytty);
99 	return(back);
100 }
101 
102 char lastmsg[100];
103 
104 /*
105  *	Alert the console operator, and enable the alarm clock to
106  *	sleep for 2 minutes in case nobody comes to satisfy dump
107  */
108 static void
109 alarmcatch()
110 {
111 	if (notify == 0) {
112 		if (timeout == 0)
113 			(void) fprintf(stderr,
114 			    "  DUMP: %s: (\"yes\" or \"no\") ",
115 			    attnmessage);
116 		else
117 			msgtail("\7\7");
118 	} else {
119 		if (timeout) {
120 			msgtail("\n");
121 			broadcast("");		/* just print last msg */
122 		}
123 		(void) fprintf(stderr,"  DUMP: %s: (\"yes\" or \"no\") ",
124 		    attnmessage);
125 	}
126 	signal(SIGALRM, alarmcatch);
127 	alarm(120);
128 	timeout = 1;
129 }
130 
131 /*
132  *	Here if an inquisitive operator interrupts the dump program
133  */
134 void
135 interrupt()
136 {
137 	msg("Interrupt received.\n");
138 	if (query("Do you want to abort dump?"))
139 		dumpabort();
140 }
141 
142 /*
143  *	The following variables and routines manage alerting
144  *	operators to the status of dump.
145  *	This works much like wall(1) does.
146  */
147 struct	group *gp;
148 
149 /*
150  *	Get the names from the group entry "operator" to notify.
151  */
152 void
153 set_operators()
154 {
155 	if (!notify)		/*not going to notify*/
156 		return;
157 	gp = getgrnam(OPGRENT);
158 	endgrent();
159 	if (gp == NULL) {
160 		msg("No group entry for %s.\n", OPGRENT);
161 		notify = 0;
162 		return;
163 	}
164 }
165 
166 struct tm *localtime();
167 struct tm *localclock;
168 
169 /*
170  *	We fork a child to do the actual broadcasting, so
171  *	that the process control groups are not messed up
172  */
173 void
174 broadcast(message)
175 	char	*message;
176 {
177 	time_t		clock;
178 	FILE	*f_utmp;
179 	struct	utmp	utmp;
180 	int	nusers;
181 	char	**np;
182 	int	pid, s;
183 
184 	if (!notify || gp == NULL)
185 		return;
186 
187 	switch (pid = fork()) {
188 	case -1:
189 		return;
190 	case 0:
191 		break;
192 	default:
193 		while (wait(&s) != pid)
194 			continue;
195 		return;
196 	}
197 
198 	clock = time(0);
199 	localclock = localtime(&clock);
200 
201 	if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) {
202 		msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno));
203 		return;
204 	}
205 
206 	nusers = 0;
207 	while (!feof(f_utmp)) {
208 		if (fread(&utmp, sizeof (struct utmp), 1, f_utmp) != 1)
209 			break;
210 		if (utmp.ut_name[0] == 0)
211 			continue;
212 		nusers++;
213 		for (np = gp->gr_mem; *np; np++) {
214 			if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0)
215 				continue;
216 			/*
217 			 *	Do not send messages to operators on dialups
218 			 */
219 			if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0)
220 				continue;
221 #ifdef DEBUG
222 			msg("Message to %s at %s\n", *np, utmp.ut_line);
223 #endif
224 			sendmes(utmp.ut_line, message);
225 		}
226 	}
227 	(void) fclose(f_utmp);
228 	Exit(0);	/* the wait in this same routine will catch this */
229 	/* NOTREACHED */
230 }
231 
232 static void
233 sendmes(tty, message)
234 	char *tty, *message;
235 {
236 	char t[50], buf[BUFSIZ];
237 	register char *cp;
238 	int lmsg = 1;
239 	FILE *f_tty;
240 
241 	(void) strcpy(t, _PATH_DEV);
242 	(void) strcat(t, tty);
243 
244 	if ((f_tty = fopen(t, "w")) != NULL) {
245 		setbuf(f_tty, buf);
246 		(void) fprintf(f_tty,
247 		    "\n\
248 \7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\
249 DUMP: NEEDS ATTENTION: ",
250 		    localclock->tm_hour, localclock->tm_min);
251 		for (cp = lastmsg; ; cp++) {
252 			if (*cp == '\0') {
253 				if (lmsg) {
254 					cp = message;
255 					if (*cp == '\0')
256 						break;
257 					lmsg = 0;
258 				} else
259 					break;
260 			}
261 			if (*cp == '\n')
262 				(void) putc('\r', f_tty);
263 			(void) putc(*cp, f_tty);
264 		}
265 		(void) fclose(f_tty);
266 	}
267 }
268 
269 /*
270  *	print out an estimate of the amount of time left to do the dump
271  */
272 
273 time_t	tschedule = 0;
274 
275 void
276 timeest()
277 {
278 	time_t	tnow, deltat;
279 
280 	time (&tnow);
281 	if (tnow >= tschedule) {
282 		tschedule = tnow + 300;
283 		if (blockswritten < 500)
284 			return;
285 		deltat = tstart_writing - tnow +
286 			(1.0 * (tnow - tstart_writing))
287 			/ blockswritten * tapesize;
288 		msg("%3.2f%% done, finished in %d:%02d\n",
289 			(blockswritten * 100.0) / tapesize,
290 			deltat / 3600, (deltat % 3600) / 60);
291 	}
292 }
293 
294 /*
295  *	tapesize: total number of blocks estimated over all reels
296  *	blockswritten:	blocks actually written, over all reels
297  *	etapes:	estimated number of tapes to write
298  *
299  *	tsize:	blocks can write on this reel
300  *	asize:	blocks written on this reel
301  *	tapeno:	number of tapes written so far
302  */
303 int
304 blocksontape()
305 {
306 	if (tapeno == etapes)
307 		return (tapesize - (etapes - 1) * tsize);
308 	return (tsize);
309 }
310 
311 void
312 #if __STDC__
313 msg(const char *fmt, ...)
314 #else
315 msg(fmt, va_alist)
316 	char *fmt;
317 	va_dcl
318 #endif
319 {
320 	va_list ap;
321 
322 	(void) fprintf(stderr,"  DUMP: ");
323 #ifdef TDEBUG
324 	(void) fprintf(stderr, "pid=%d ", getpid());
325 #endif
326 #if __STDC__
327 	va_start(ap, fmt);
328 #else
329 	va_start(ap);
330 #endif
331 	(void) vfprintf(stderr, fmt, ap);
332 	(void) fflush(stdout);
333 	(void) fflush(stderr);
334 	(void) vsprintf(lastmsg, fmt, ap);
335 	va_end(ap);
336 }
337 
338 void
339 #if __STDC__
340 msgtail(const char *fmt, ...)
341 #else
342 msgtail(fmt, va_alist)
343 	char *fmt;
344 	va_dcl
345 #endif
346 {
347 	va_list ap;
348 #if __STDC__
349 	va_start(ap, fmt);
350 #else
351 	va_start(ap);
352 #endif
353 	(void) vfprintf(stderr, fmt, ap);
354 	va_end(ap);
355 }
356 
357 void
358 #if __STDC__
359 quit(const char *fmt, ...)
360 #else
361 quit(fmt, va_alist)
362 	char *fmt;
363 	va_dcl
364 #endif
365 {
366 	va_list ap;
367 
368 	(void) fprintf(stderr,"  DUMP: ");
369 #ifdef TDEBUG
370 	(void) fprintf(stderr, "pid=%d ", getpid());
371 #endif
372 #if __STDC__
373 	va_start(ap, fmt);
374 #else
375 	va_start(ap);
376 #endif
377 	vfprintf(stderr, fmt, ap);
378 	va_end(ap);
379 	(void) fflush(stdout);
380 	(void) fflush(stderr);
381 	dumpabort();
382 }
383 
384 /*
385  *	Tell the operator what has to be done;
386  *	we don't actually do it
387  */
388 
389 struct fstab *
390 allocfsent(fs)
391 	register struct fstab *fs;
392 {
393 	register struct fstab *new;
394 
395 	new = (struct fstab *)malloc(sizeof (*fs));
396 	if (new == NULL ||
397 	    (new->fs_file = strdup(fs->fs_file)) == NULL ||
398 	    (new->fs_type = strdup(fs->fs_type)) == NULL ||
399 	    (new->fs_spec = strdup(fs->fs_spec)) == NULL)
400 		quit("%s\n", strerror(errno));
401 	new->fs_passno = fs->fs_passno;
402 	new->fs_freq = fs->fs_freq;
403 	return (new);
404 }
405 
406 struct	pfstab {
407 	struct	pfstab *pf_next;
408 	struct	fstab *pf_fstab;
409 };
410 
411 static	struct pfstab *table;
412 
413 void
414 getfstab()
415 {
416 	register struct fstab *fs;
417 	register struct pfstab *pf;
418 
419 	if (setfsent() == 0) {
420 		msg("Can't open %s for dump table information: %s\n",
421 		    _PATH_FSTAB, strerror(errno));
422 		return;
423 	}
424 	while (fs = getfsent()) {
425 		if (strcmp(fs->fs_type, FSTAB_RW) &&
426 		    strcmp(fs->fs_type, FSTAB_RO) &&
427 		    strcmp(fs->fs_type, FSTAB_RQ))
428 			continue;
429 		fs = allocfsent(fs);
430 		if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL)
431 			quit("%s\n", strerror(errno));
432 		pf->pf_fstab = fs;
433 		pf->pf_next = table;
434 		table = pf;
435 	}
436 	endfsent();
437 }
438 
439 /*
440  * Search in the fstab for a file name.
441  * This file name can be either the special or the path file name.
442  *
443  * The entries in the fstab are the BLOCK special names, not the
444  * character special names.
445  * The caller of fstabsearch assures that the character device
446  * is dumped (that is much faster)
447  *
448  * The file name can omit the leading '/'.
449  */
450 struct fstab *
451 fstabsearch(key)
452 	char *key;
453 {
454 	register struct pfstab *pf;
455 	register struct fstab *fs;
456 	char *rawname();
457 
458 	for (pf = table; pf != NULL; pf = pf->pf_next) {
459 		fs = pf->pf_fstab;
460 		if (strcmp(fs->fs_file, key) == 0 ||
461 		    strcmp(fs->fs_spec, key) == 0 ||
462 		    strcmp(rawname(fs->fs_spec), key) == 0)
463 			return (fs);
464 		if (key[0] != '/') {
465 			if (*fs->fs_spec == '/' &&
466 			    strcmp(fs->fs_spec + 1, key) == 0)
467 				return (fs);
468 			if (*fs->fs_file == '/' &&
469 			    strcmp(fs->fs_file + 1, key) == 0)
470 				return (fs);
471 		}
472 	}
473 	return (NULL);
474 }
475 
476 /*
477  *	Tell the operator what to do
478  */
479 void
480 lastdump(arg)
481 	char	arg;	/* w ==> just what to do; W ==> most recent dumps */
482 {
483 	register int i;
484 	register struct fstab *dt;
485 	register struct dumpdates *dtwalk;
486 	char *lastname, *date;
487 	int dumpme, datesort();
488 	time_t tnow;
489 
490 	time(&tnow);
491 	getfstab();		/* /etc/fstab input */
492 	initdumptimes();	/* /etc/dumpdates input */
493 	qsort(ddatev, nddates, sizeof(struct dumpdates *), datesort);
494 
495 	if (arg == 'w')
496 		(void) printf("Dump these file systems:\n");
497 	else
498 		(void) printf("Last dump(s) done (Dump '>' file systems):\n");
499 	lastname = "??";
500 	ITITERATE(i, dtwalk) {
501 		if (strncmp(lastname, dtwalk->dd_name,
502 		    sizeof(dtwalk->dd_name)) == 0)
503 			continue;
504 		date = (char *)ctime(&dtwalk->dd_ddate);
505 		date[16] = '\0';	/* blast away seconds and year */
506 		lastname = dtwalk->dd_name;
507 		dt = fstabsearch(dtwalk->dd_name);
508 		dumpme = (dt != NULL &&
509 		    dt->fs_freq != 0 &&
510 		    dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY));
511 		if (arg != 'w' || dumpme)
512 			(void) printf(
513 			    "%c %8s\t(%6s) Last dump: Level %c, Date %s\n",
514 			    dumpme && (arg != 'w') ? '>' : ' ',
515 			    dtwalk->dd_name,
516 			    dt ? dt->fs_file : "",
517 			    dtwalk->dd_level,
518 			    date);
519 	}
520 }
521 
522 int
523 datesort(a1, a2)
524 	void *a1, *a2;
525 {
526 	struct dumpdates *d1 = *(struct dumpdates **)a1;
527 	struct dumpdates *d2 = *(struct dumpdates **)a2;
528 	int diff;
529 
530 	diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name));
531 	if (diff == 0)
532 		return (d2->dd_ddate - d1->dd_ddate);
533 	return (diff);
534 }
535 
536 int max(a, b)
537 	int a, b;
538 {
539 	return (a > b ? a : b);
540 }
541 int min(a, b)
542 	int a, b;
543 {
544 	return (a < b ? a : b);
545 }
546