xref: /illumos-gate/usr/src/cmd/backup/dump/dumpoptr.c (revision dd72704bd9e794056c558153663c739e2012d721)
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();
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
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
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
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();
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();
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
180 alarmcatch(void)
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
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();
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
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
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
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
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
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
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