optr.c revision 1.23 1 /* $NetBSD: optr.c,v 1.23 2001/12/23 12:29: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.23 2001/12/23 12:29: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 #include <sys/ucred.h>
49 #include <sys/mount.h>
50
51 #include <errno.h>
52 #include <fstab.h>
53 #include <grp.h>
54 #include <signal.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <stdarg.h>
59 #include <time.h>
60 #include <tzfile.h>
61 #include <unistd.h>
62 #include <utmp.h>
63
64 #include "dump.h"
65 #include "pathnames.h"
66
67 void alarmcatch(int);
68 struct fstab *allocfsent(struct fstab *);
69 int datesort(const void *, const void *);
70 static void sendmes(char *, char *);
71 extern gid_t egid;
72 extern char *time_string;
73 extern char default_time_string[];
74
75 static void do_timestamp(time_t, 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[200];
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 struct utmp utmp;
219 time_t now;
220 FILE *f_utmp;
221 char **np;
222 int pid, s;
223
224 if (!notify || gp == NULL)
225 return;
226
227 /* Restore 'tty' privs for the child's use only. */
228 setegid(egid);
229 switch (pid = fork()) {
230 case -1:
231 setegid(getgid());
232 return;
233 case 0:
234 break;
235 default:
236 setegid(getgid());
237 while (wait(&s) != pid)
238 continue;
239 return;
240 }
241
242 now = time((time_t *)0);
243 localclock = localtime(&now);
244
245 if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) {
246 msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno));
247 return;
248 }
249
250 while (!feof(f_utmp)) {
251 if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1)
252 break;
253 if (utmp.ut_name[0] == 0)
254 continue;
255 for (np = gp->gr_mem; *np; np++) {
256 if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0)
257 continue;
258 /*
259 * Do not send messages to operators on dialups
260 */
261 if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0)
262 continue;
263 #ifdef DEBUG
264 msg("Message to %s at %s\n", *np, utmp.ut_line);
265 #endif
266 sendmes(utmp.ut_line, message);
267 }
268 }
269 (void) fclose(f_utmp);
270 Exit(0); /* the wait in this same routine will catch this */
271 /* NOTREACHED */
272 }
273
274 static void
275 sendmes(char *tty, char *message)
276 {
277 char t[50], buf[BUFSIZ];
278 char *cp;
279 int lmsg = 1;
280 FILE *f_tty;
281
282 (void)strncpy(t, _PATH_DEV, sizeof(t) - 1);
283 (void)strncat(t, tty, sizeof(t) - sizeof(_PATH_DEV) - 1);
284 t[sizeof(t) - 1] = '\0';
285
286 if ((f_tty = fopen(t, "w")) != NULL) {
287 setbuf(f_tty, buf);
288 (void) fprintf(f_tty,
289 "\n\
290 \7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\
291 DUMP: NEEDS ATTENTION: ",
292 localclock->tm_hour, localclock->tm_min);
293 for (cp = lastmsg; ; cp++) {
294 if (*cp == '\0') {
295 if (lmsg) {
296 cp = message;
297 if (*cp == '\0')
298 break;
299 lmsg = 0;
300 } else
301 break;
302 }
303 if (*cp == '\n')
304 (void) putc('\r', f_tty);
305 (void) putc(*cp, f_tty);
306 }
307 (void) fclose(f_tty);
308 }
309 }
310
311 /*
312 * print out the timestamp string to stderr.
313 */
314 #define STAMP_LENGTH 80
315
316 static void
317 do_timestamp(time_t thistime, char *message)
318 {
319 struct tm tm_time;
320 char then[STAMP_LENGTH + 1];
321
322 (void) localtime_r(&thistime, &tm_time);
323 if (strftime(then, STAMP_LENGTH, time_string, &tm_time) == 0) {
324 time_string = default_time_string;
325 strftime(then, STAMP_LENGTH, time_string, &tm_time);
326 fprintf(stderr,
327 "DUMP: ERROR: TIMEFORMAT too long, reverting to default\n");
328 }
329
330 fprintf(stderr, message, then);
331 }
332
333
334 /*
335 * print out an estimate of the amount of time left to do the dump
336 */
337
338 time_t tschedule = 0;
339
340 void
341 timeest(void)
342 {
343 time_t tnow, deltat;
344
345 (void) time((time_t *) &tnow);
346 if (tnow >= tschedule) {
347 tschedule = tnow + 300;
348 if (blockswritten < 500)
349 return;
350 deltat = tstart_writing - tnow +
351 (1.0 * (tnow - tstart_writing))
352 / blockswritten * tapesize;
353
354 msg("%3.2f%% done, finished in %ld:%02ld",
355 (blockswritten * 100.0) / tapesize,
356 (long)(deltat / 3600), (long)((deltat % 3600) / 60));
357
358 if (timestamp == 1)
359 do_timestamp(tnow + deltat, " (at %s)");
360
361 fprintf(stderr, "\n");
362 }
363 }
364
365 void
366 msg(const char *fmt, ...)
367 {
368 time_t tnow;
369 va_list ap;
370
371 fprintf(stderr, " ");
372 if (timestamp == 1) {
373 (void) time((time_t *) &tnow);
374 do_timestamp(tnow, "[%s] ");
375 }
376
377 (void) fprintf(stderr,"DUMP: ");
378 #ifdef TDEBUG
379 (void) fprintf(stderr, "pid=%d ", getpid());
380 #endif
381 va_start(ap, fmt);
382 (void) vsnprintf(lastmsg, sizeof lastmsg, fmt, ap);
383 fputs(lastmsg, stderr);
384 (void) fflush(stdout);
385 (void) fflush(stderr);
386 va_end(ap);
387 }
388
389 void
390 msgtail(const char *fmt, ...)
391 {
392 va_list ap;
393
394 va_start(ap, fmt);
395 (void) vfprintf(stderr, fmt, ap);
396 va_end(ap);
397 }
398
399 void
400 quit(const char *fmt, ...)
401 {
402 va_list ap;
403
404 (void) fprintf(stderr," DUMP: ");
405 #ifdef TDEBUG
406 (void) fprintf(stderr, "pid=%d ", getpid());
407 #endif
408 va_start(ap, fmt);
409 (void) vfprintf(stderr, fmt, ap);
410 va_end(ap);
411 (void) fflush(stdout);
412 (void) fflush(stderr);
413 dumpabort(0);
414 }
415
416 /*
417 * Tell the operator what has to be done;
418 * we don't actually do it
419 */
420
421 struct fstab *
422 allocfsent(struct fstab *fs)
423 {
424 struct fstab *new;
425
426 new = (struct fstab *)xmalloc(sizeof (*fs));
427 new->fs_file = xstrdup(fs->fs_file);
428 new->fs_type = xstrdup(fs->fs_type);
429 new->fs_spec = xstrdup(fs->fs_spec);
430 new->fs_passno = fs->fs_passno;
431 new->fs_freq = fs->fs_freq;
432 return (new);
433 }
434
435 struct pfstab {
436 struct pfstab *pf_next;
437 struct fstab *pf_fstab;
438 };
439
440 static struct pfstab *table;
441
442 void
443 getfstab(void)
444 {
445 struct fstab *fs;
446 struct pfstab *pf;
447
448 if (setfsent() == 0) {
449 msg("Can't open %s for dump table information: %s\n",
450 _PATH_FSTAB, strerror(errno));
451 return;
452 }
453 while ((fs = getfsent()) != NULL) {
454 if (strcmp(fs->fs_type, FSTAB_RW) &&
455 strcmp(fs->fs_type, FSTAB_RO) &&
456 strcmp(fs->fs_type, FSTAB_RQ))
457 continue;
458 if (strcmp(fs->fs_vfstype, "ufs") &&
459 strcmp(fs->fs_vfstype, "ffs"))
460 continue;
461 fs = allocfsent(fs);
462 pf = (struct pfstab *)xmalloc(sizeof (*pf));
463 pf->pf_fstab = fs;
464 pf->pf_next = table;
465 table = pf;
466 }
467 (void) endfsent();
468 }
469
470 /*
471 * Search in the fstab for a file name.
472 * This file name can be either the special or the path file name.
473 *
474 * The entries in the fstab are the BLOCK special names, not the
475 * character special names.
476 * The caller of fstabsearch assures that the character device
477 * is dumped (that is much faster)
478 *
479 * The file name can omit the leading '/'.
480 */
481 struct fstab *
482 fstabsearch(const char *key)
483 {
484 struct pfstab *pf;
485 struct fstab *fs;
486 char *rn;
487
488 for (pf = table; pf != NULL; pf = pf->pf_next) {
489 fs = pf->pf_fstab;
490 if (strcmp(fs->fs_file, key) == 0 ||
491 strcmp(fs->fs_spec, key) == 0)
492 return (fs);
493 rn = rawname(fs->fs_spec);
494 if (rn != NULL && strcmp(rn, key) == 0)
495 return (fs);
496 if (key[0] != '/') {
497 if (*fs->fs_spec == '/' &&
498 strcmp(fs->fs_spec + 1, key) == 0)
499 return (fs);
500 if (*fs->fs_file == '/' &&
501 strcmp(fs->fs_file + 1, key) == 0)
502 return (fs);
503 }
504 }
505 return (NULL);
506 }
507
508 /*
509 * Search in the mounted file list for a file name.
510 * This file name can be either the special or the path file name.
511 *
512 * The entries in the list are the BLOCK special names, not the
513 * character special names.
514 * The caller of mntinfosearch assures that the character device
515 * is dumped (that is much faster)
516 */
517 struct statfs *
518 mntinfosearch(const char *key)
519 {
520 int i, mntbufc;
521 struct statfs *mntbuf, *fs;
522 char *rn;
523
524 if ((mntbufc = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
525 quit("Can't get mount list: %s", strerror(errno));
526 for (fs = mntbuf, i = 0; i < mntbufc; i++, fs++) {
527 if (strcmp(fs->f_fstypename, "ufs") != 0 &&
528 strcmp(fs->f_fstypename, "ffs") != 0)
529 continue;
530 if (strcmp(fs->f_mntonname, key) == 0 ||
531 strcmp(fs->f_mntfromname, key) == 0)
532 return (fs);
533 rn = rawname(fs->f_mntfromname);
534 if (rn != NULL && strcmp(rn, key) == 0)
535 return (fs);
536 }
537 return (NULL);
538 }
539
540
541 /*
542 * Tell the operator what to do.
543 * arg: w ==> just what to do; W ==> most recent dumps
544 */
545 void
546 lastdump(char arg)
547 {
548 int i;
549 struct fstab *dt;
550 struct dumpdates *dtwalk;
551 char *lastname, *date;
552 int dumpme;
553 time_t tnow;
554
555 (void) time(&tnow);
556 getfstab(); /* /etc/fstab input */
557 initdumptimes(); /* /etc/dumpdates input */
558 qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort);
559
560 if (arg == 'w')
561 (void) printf("Dump these file systems:\n");
562 else
563 (void) printf("Last dump(s) done (Dump '>' file systems):\n");
564 lastname = "??";
565 ITITERATE(i, dtwalk) {
566 if (strncmp(lastname, dtwalk->dd_name,
567 sizeof(dtwalk->dd_name)) == 0)
568 continue;
569 date = (char *)ctime(&dtwalk->dd_ddate);
570 date[24] = '\0';
571 strcpy(date + 16, date + 19); /* blast away seconds */
572 lastname = dtwalk->dd_name;
573 dt = fstabsearch(dtwalk->dd_name);
574 dumpme = (dt != NULL &&
575 dt->fs_freq != 0 &&
576 dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY));
577 if (arg != 'w' || dumpme)
578 (void) printf(
579 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n",
580 dumpme && (arg != 'w') ? '>' : ' ',
581 dtwalk->dd_name,
582 dt ? dt->fs_file : "",
583 dtwalk->dd_level,
584 date);
585 }
586 }
587
588 int
589 datesort(const void *a1, const void *a2)
590 {
591 struct dumpdates *d1 = *(struct dumpdates **)a1;
592 struct dumpdates *d2 = *(struct dumpdates **)a2;
593 int diff;
594
595 diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name));
596 if (diff == 0)
597 return (d2->dd_ddate - d1->dd_ddate);
598 return (diff);
599 }
600