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