1 /*
2 * Copyright (c) 1998 by Sun Microsystems, Inc.
3 * All rights reserved.
4 */
5
6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
8
9 /*
10 * Copyright (c) 1980 Regents of the University of California.
11 * All rights reserved. The Berkeley software License Agreement
12 * specifies the terms and conditions for redistribution.
13 */
14
15 #include <errno.h>
16 #include "dump.h"
17
18 time_t *tschedule;
19 static unsigned int timeout; /* current timeout */
20 static char *attnmessage, *saveattn; /* attention message */
21
22 static void alarmcatch(int);
23 static int idatesort(const void *, const void *);
24
25 #ifdef DEBUG
26 extern int xflag;
27 #endif
28
29 /*
30 * Query the operator; This fascist piece of code requires
31 * an exact response.
32 * It is intended to protect dump aborting by inquisitive
33 * people banging on the console terminal to see what is
34 * happening which might cause dump to croak, destroying
35 * a large number of hours of work.
36 *
37 * Every time += 2 minutes we reprint the message, alerting others
38 * that dump needs attention.
39 */
40 int
query(char * question)41 query(char *question)
42 {
43 int def = -1;
44
45 while (def == -1)
46 def = query_once(question, -1);
47 return (def);
48 }
49
50 static int in_query_once;
51 static jmp_buf sjalarmbuf;
52
53 /* real simple check-sum */
54 static int
addem(char * s)55 addem(char *s)
56 {
57 int total = 0;
58
59 if (s == (char *)NULL)
60 return (total);
61 while (*s)
62 total += *s++;
63 return (total);
64 }
65
66 int
query_once(char * question,int def)67 query_once(char *question, int def)
68 {
69 static char *lastmsg;
70 static int lastmsgsum;
71 int msgsum;
72 char replybuffer[BUFSIZ];
73 int back;
74 time32_t timeclockstate;
75 pollfd_t pollset;
76 struct sigvec sv;
77
78 /* special hook to flush timeout cache */
79 if (question == NULL) {
80 lastmsg = (char *)NULL;
81 lastmsgsum = 0;
82 return (0);
83 }
84
85 attnmessage = question;
86 /*
87 * Only reset the state if the message changed somehow
88 */
89 msgsum = addem(question);
90 if (lastmsg != question || lastmsgsum != msgsum) {
91 timeout = 0;
92 if (telapsed && tstart_writing)
93 *telapsed += time((time_t *)0) - *tstart_writing;
94 lastmsg = question;
95 lastmsgsum = msgsum;
96 }
97 timeclockstate = timeclock((time_t)0);
98 if (setjmp(sjalarmbuf) != 0) {
99 if (def != -1) {
100 if (def)
101 msgtail(gettext("YES\n"));
102 else
103 msgtail(gettext("NO\n"));
104 }
105 back = def;
106 goto done;
107 }
108 alarmcatch(SIGALRM);
109 in_query_once = 1;
110 pollset.fd = -1;
111 pollset.events = 0;
112 pollset.revents = 0;
113 if (isatty(fileno(stdin))) {
114 pollset.fd = fileno(stdin);
115 pollset.events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
116 } else {
117 dumpabort();
118 /*NOTREACHED*/
119 }
120 for (;;) {
121 if (poll(&pollset, 1, -1) < 0) {
122 if (errno == EINTR)
123 continue;
124 perror("poll(stdin)");
125 dumpabort();
126 /*NOTREACHED*/
127 }
128 if (pollset.revents == 0)
129 continue; /* sanity check */
130 if (fgets(replybuffer, sizeof (replybuffer), stdin) == NULL) {
131 if (ferror(stdin)) {
132 clearerr(stdin);
133 continue;
134 } else {
135 dumpabort();
136 /*NOTREACHED*/
137 }
138 }
139 timeout = 0;
140 if (strcasecmp(replybuffer, gettext("yes\n")) == 0) {
141 back = 1;
142 lastmsg = (char *)NULL;
143 lastmsgsum = 0;
144 goto done;
145 } else if (strcasecmp(replybuffer, gettext("no\n")) == 0) {
146 back = 0;
147 lastmsg = (char *)NULL;
148 lastmsgsum = 0;
149 goto done;
150 } else {
151 msg(gettext("\"yes\" or \"no\"?\n"));
152 in_query_once = 0;
153 alarmcatch(SIGALRM);
154 in_query_once = 1;
155 }
156 }
157 done:
158 /*
159 * Turn off the alarm, and reset the signal to trap out..
160 */
161 (void) alarm(0);
162 attnmessage = NULL;
163 sv.sv_handler = sigAbort;
164 sv.sv_flags = SA_RESTART;
165 (void) sigemptyset(&sv.sa_mask);
166 (void) sigvec(SIGALRM, &sv, (struct sigvec *)0);
167 if (tstart_writing)
168 (void) time(tstart_writing);
169 (void) timeclock(timeclockstate);
170 in_query_once = 0;
171 return (back);
172 }
173 /*
174 * Alert the console operator, and enable the alarm clock to
175 * sleep for time += 2 minutes in case nobody comes to satisfy dump
176 * If the alarm goes off while in the query_once for loop, we just
177 * longjmp back there and return the default answer.
178 */
179 static void
alarmcatch(int signal __unused)180 alarmcatch(int signal __unused)
181 {
182 struct sigvec sv;
183
184 if (in_query_once) {
185 longjmp(sjalarmbuf, 1);
186 }
187 if (timeout) {
188 msgtail("\n");
189 }
190
191 timeout += 120;
192 msg(gettext("NEEDS ATTENTION: %s"), attnmessage);
193 sv.sv_handler = alarmcatch;
194 sv.sv_flags = SA_RESTART;
195 (void) sigemptyset(&sv.sa_mask);
196 (void) sigvec(SIGALRM, &sv, (struct sigvec *)0);
197 (void) alarm(timeout);
198 }
199
200 /*
201 * Here if an inquisitive operator interrupts the dump program
202 */
203 /*ARGSUSED*/
204 void
interrupt(int sig)205 interrupt(int sig)
206 {
207 if (!saveattn) {
208 saveattn = attnmessage;
209 }
210 msg(gettext("Interrupt received.\n"));
211 if (query(gettext(
212 "Do you want to abort dump?: (\"yes\" or \"no\") "))) {
213 dumpabort();
214 /*NOTREACHED*/
215 }
216 if (saveattn) {
217 attnmessage = saveattn;
218 saveattn = NULL;
219 alarmcatch(SIGALRM);
220 }
221 }
222
223 /*
224 * We use wall(1) to do the actual broadcasting, so
225 * that we don't have to worry about duplicated code
226 * only getting fixed in one place. This also saves
227 * us from having to worry about process groups,
228 * controlling terminals, and the like.
229 */
230 void
broadcast(char * message)231 broadcast(char *message)
232 {
233 time_t clock;
234 pid_t pid;
235 int saverr;
236 int fildes[2];
237 FILE *wall;
238 struct tm *localclock;
239
240 if (!notify)
241 return;
242
243 if (pipe(fildes) < 0) {
244 saverr = errno;
245 msg(gettext("pipe: %s\n"), strerror(saverr));
246 return;
247 }
248
249 switch (pid = fork()) {
250 case -1:
251 return;
252 case 0:
253 close(fildes[0]);
254 if (dup2(fildes[1], 0) < 0) {
255 saverr = errno;
256 msg(gettext("dup2: %s\n"), strerror(saverr));
257 exit(1);
258 }
259 execl("/usr/sbin/wall", "wall", "-g", OPGRENT, (char *)NULL);
260 saverr = errno;
261 msg(gettext("execl: %s\n"), strerror(saverr));
262 exit(1);
263 default:
264 break; /* parent */
265 }
266
267 close(fildes[1]);
268 wall = fdopen(fildes[0], "r+");
269 if (wall == (FILE *)NULL) {
270 saverr = errno;
271 msg(gettext("fdopen: %s\n"), strerror(saverr));
272 return;
273 }
274
275 clock = time((time_t *)0);
276 localclock = localtime(&clock);
277
278 (void) fprintf(wall, gettext(
279 "\n\007\007\007Message from the dump program to all operators at \
280 %d:%02d ...\n\n%s"),
281 localclock->tm_hour, localclock->tm_min, message);
282 fclose(wall);
283
284 while (wait((int *)0) != pid) {
285 continue;
286 /*LINTED [empty loop body]*/
287 }
288 }
289
290 /*
291 * print out an estimate of the amount of time left to do the dump
292 */
293 #define EST_SEC 600 /* every 10 minutes */
294 void
timeest(int force,int blkswritten)295 timeest(int force, int blkswritten)
296 {
297 time_t tnow, deltat;
298 char *msgp;
299
300 if (tschedule == NULL)
301 return;
302 if (*tschedule == 0)
303 *tschedule = time((time_t *)0) + EST_SEC;
304 (void) time(&tnow);
305 if ((force || tnow >= *tschedule) && blkswritten) {
306 *tschedule = tnow + EST_SEC;
307 if (!force && blkswritten < 50 * ntrec)
308 return;
309 deltat = (*telapsed + (tnow - *tstart_writing))
310 * ((double)esize / blkswritten - 1.0);
311 msgp = gettext("%3.2f%% done, finished in %d:%02d\n");
312 msg(msgp, (blkswritten*100.0)/esize,
313 deltat/3600, (deltat%3600)/60);
314 }
315 }
316
317 #include <stdarg.h>
318
319 /* VARARGS1 */
320 void
msg(const char * fmt,...)321 msg(const char *fmt, ...)
322 {
323 char buf[1024], *cp;
324 size_t size;
325 va_list args;
326
327 va_start(args, fmt);
328 (void) strcpy(buf, " DUMP: ");
329 cp = &buf[strlen(buf)];
330 #ifdef TDEBUG
331 (void) sprintf(cp, "pid=%d ", getpid());
332 cp = &buf[strlen(buf)];
333 #endif
334 /* don't need -1, vsnprintf does it right */
335 /* LINTED pointer arithmetic result fits in size_t */
336 size = ((size_t)sizeof (buf)) - (size_t)(cp - buf);
337 (void) vsnprintf(cp, size, fmt, args);
338 (void) fputs(buf, stderr);
339 (void) fflush(stdout);
340 (void) fflush(stderr);
341 va_end(args);
342 }
343
344 /* VARARGS1 */
345 void
msgtail(const char * fmt,...)346 msgtail(const char *fmt, ...)
347 {
348 va_list args;
349
350 va_start(args, fmt);
351 (void) vfprintf(stderr, fmt, args);
352 va_end(args);
353 }
354
355 #define MINUTES(x) ((x) * 60)
356
357 /*
358 * Tell the operator what has to be done;
359 * we don't actually do it
360 */
361 void
lastdump(int arg)362 lastdump(int arg) /* w ==> just what to do; W ==> most recent dumps */
363 {
364 char *lastname;
365 char *date;
366 int i;
367 time_t tnow, ddate;
368 struct mntent *dt;
369 int dumpme = 0;
370 struct idates *itwalk;
371
372 (void) time(&tnow);
373 mnttabread(); /* /etc/fstab input */
374 inititimes(); /* /etc/dumpdates input */
375
376 /* Don't use msg(), this isn't a tell-the-world kind of thing */
377 if (arg == 'w')
378 (void) fprintf(stdout, gettext("Dump these file systems:\n"));
379 else
380 (void) fprintf(stdout, gettext(
381 "Last dump(s) done (Dump '>' file systems):\n"));
382
383 if (idatev != NULL) {
384 qsort((char *)idatev, nidates, sizeof (*idatev), idatesort);
385 lastname = "??";
386 ITITERATE(i, itwalk) {
387 if (strncmp(lastname, itwalk->id_name,
388 sizeof (itwalk->id_name)) == 0)
389 continue;
390 /* must be ctime(), per ufsdump(5) */
391 ddate = itwalk->id_ddate;
392 date = (char *)ctime(&ddate);
393 date[16] = '\0'; /* blow away seconds and year */
394 lastname = itwalk->id_name;
395 dt = mnttabsearch(itwalk->id_name, 0);
396 if ((time_t)(itwalk->id_ddate) < (tnow - DAY)) {
397 dumpme = 1;
398 }
399
400 if ((arg == 'w') && dumpme) {
401 /*
402 * Handle the w option: print out file systems
403 * which haven't been backed up within a day.
404 */
405 (void) printf(gettext("%8s\t(%6s)\n"),
406 itwalk->id_name, dt ? dt->mnt_dir : "");
407 }
408 if (arg == 'W') {
409 /*
410 * Handle the W option: print out ALL
411 * filesystems including recent dump dates and
412 * dump levels. Mark the backup-needing
413 * filesystems with a >.
414 */
415 (void) printf(gettext(
416 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n"),
417 dumpme ? '>' : ' ',
418 itwalk->id_name,
419 dt ? dt->mnt_dir : "",
420 (uchar_t)itwalk->id_incno,
421 date);
422 }
423 dumpme = 0;
424 }
425 }
426 }
427
428 static int
idatesort(const void * v1,const void * v2)429 idatesort(const void *v1, const void *v2)
430 {
431 struct idates **p1 = (struct idates **)v1;
432 struct idates **p2 = (struct idates **)v2;
433 int diff;
434
435 diff = strcoll((*p1)->id_name, (*p2)->id_name);
436 if (diff == 0) {
437 /*
438 * Time may eventually become unsigned, so can't
439 * rely on subtraction to give a useful result.
440 * Note that we are sorting dates into reverse
441 * order, so that we will report based on the
442 * most-recent record for a particular filesystem.
443 */
444 if ((*p1)->id_ddate > (*p2)->id_ddate)
445 diff = -1;
446 else if ((*p1)->id_ddate < (*p2)->id_ddate)
447 diff = 1;
448 }
449 return (diff);
450 }
451