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