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