optr.c revision 120323
1/*-
2 * Copyright (c) 1980, 1988, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35#if 0
36static char sccsid[] = "@(#)optr.c	8.2 (Berkeley) 1/6/94";
37#endif
38static const char rcsid[] =
39  "$FreeBSD: head/sbin/dump/optr.c 120323 2003-09-21 22:14:49Z ps $";
40#endif /* not lint */
41
42#include <sys/param.h>
43#include <sys/queue.h>
44#include <sys/wait.h>
45#include <sys/time.h>
46
47#include <ufs/ufs/dinode.h>
48
49#include <errno.h>
50#include <fstab.h>
51#include <grp.h>
52#include <limits.h>
53#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
56#include <stdarg.h>
57#include <signal.h>
58#include <unistd.h>
59
60#include "dump.h"
61#include "pathnames.h"
62
63void	alarmcatch(int);
64int	datesort(const void *, const void *);
65
66/*
67 *	Query the operator; This previously-fascist piece of code
68 *	no longer requires an exact response.
69 *	It is intended to protect dump aborting by inquisitive
70 *	people banging on the console terminal to see what is
71 *	happening which might cause dump to croak, destroying
72 *	a large number of hours of work.
73 *
74 *	Every 2 minutes we reprint the message, alerting others
75 *	that dump needs attention.
76 */
77static	int timeout;
78static	const char *attnmessage;		/* attention message */
79
80int
81query(const char *question)
82{
83	char	replybuffer[64];
84	int	back, errcount;
85	FILE	*mytty;
86
87	if ((mytty = fopen(_PATH_TTY, "r")) == NULL)
88		quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno));
89	attnmessage = question;
90	timeout = 0;
91	alarmcatch(0);
92	back = -1;
93	errcount = 0;
94	do {
95		if (fgets(replybuffer, 63, mytty) == NULL) {
96			clearerr(mytty);
97			if (++errcount > 30)	/* XXX	ugly */
98				quit("excessive operator query failures\n");
99		} else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') {
100			back = 1;
101		} else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') {
102			back = 0;
103		} else {
104			(void) fprintf(stderr,
105			    "  DUMP: \"Yes\" or \"No\"?\n");
106			(void) fprintf(stderr,
107			    "  DUMP: %s: (\"yes\" or \"no\") ", question);
108		}
109	} while (back < 0);
110
111	/*
112	 *	Turn off the alarm, and reset the signal to trap out..
113	 */
114	(void) alarm(0);
115	if (signal(SIGALRM, sig) == SIG_IGN)
116		signal(SIGALRM, SIG_IGN);
117	(void) fclose(mytty);
118	return(back);
119}
120
121char lastmsg[BUFSIZ];
122
123/*
124 *	Alert the console operator, and enable the alarm clock to
125 *	sleep for 2 minutes in case nobody comes to satisfy dump
126 */
127void
128alarmcatch(int sig __unused)
129{
130	if (notify == 0) {
131		if (timeout == 0)
132			(void) fprintf(stderr,
133			    "  DUMP: %s: (\"yes\" or \"no\") ",
134			    attnmessage);
135		else
136			msgtail("\a\a");
137	} else {
138		if (timeout) {
139			msgtail("\n");
140			broadcast("");		/* just print last msg */
141		}
142		(void) fprintf(stderr,"  DUMP: %s: (\"yes\" or \"no\") ",
143		    attnmessage);
144	}
145	signal(SIGALRM, alarmcatch);
146	(void) alarm(120);
147	timeout = 1;
148}
149
150/*
151 *	Here if an inquisitive operator interrupts the dump program
152 */
153void
154interrupt(int signo __unused)
155{
156	msg("Interrupt received.\n");
157	if (query("Do you want to abort dump?"))
158		dumpabort(0);
159}
160
161/*
162 *	We now use wall(1) to do the actual broadcasting.
163 */
164void
165broadcast(const char *message)
166{
167	FILE *fp;
168	char buf[sizeof(_PATH_WALL) + sizeof(OPGRENT) + 3];
169
170	if (!notify)
171		return;
172
173	snprintf(buf, sizeof(buf), "%s -g %s", _PATH_WALL, OPGRENT);
174	if ((fp = popen(buf, "w")) == NULL)
175		return;
176
177	(void) fputs("\a\a\aMessage from the dump program to all operators\n\nDUMP: NEEDS ATTENTION: ", fp);
178	if (lastmsg[0])
179		(void) fputs(lastmsg, fp);
180	if (message[0])
181		(void) fputs(message, fp);
182
183	(void) pclose(fp);
184}
185
186/*
187 *	Print out an estimate of the amount of time left to do the dump
188 */
189
190time_t	tschedule = 0;
191
192void
193timeest(void)
194{
195	double percent;
196	time_t	tnow;
197	int deltat, hours, mins;
198
199	(void) time(&tnow);
200	deltat = (blockswritten == 0) ? 0 : tstart_writing - tnow +
201	    (double)(tnow - tstart_writing) / blockswritten * tapesize;
202	percent = (blockswritten * 100.0) / tapesize;
203	hours = deltat / 3600;
204	mins = (deltat % 3600) / 60;
205
206	setproctitle("%s: pass %d: %3.2f%% done, finished in %d:%02d",
207	    disk, passno, percent, hours, mins);
208	if (tnow >= tschedule) {
209		tschedule = tnow + 300;
210		if (blockswritten < 500)
211			return;
212		msg("%3.2f%% done, finished in %d:%02d\n", percent, hours,
213		    mins);
214	}
215}
216
217/*
218 * Schedule a printout of the estimate in the next call to timeest().
219 */
220void
221infosch(int signal __unused)
222{
223	tschedule = 0;
224}
225
226void
227msg(const char *fmt, ...)
228{
229	va_list ap;
230	va_list ap2;
231
232	(void) fprintf(stderr,"  DUMP: ");
233#ifdef TDEBUG
234	(void) fprintf(stderr, "pid=%d ", getpid());
235#endif
236	va_start(ap, fmt);
237	va_copy(ap2, ap);
238	(void) vfprintf(stderr, fmt, ap);
239	(void) fflush(stdout);
240	(void) fflush(stderr);
241	(void) vsnprintf(lastmsg, sizeof(lastmsg), fmt, ap2);
242	va_end(ap);
243	va_end(ap2);
244}
245
246void
247msgtail(const char *fmt, ...)
248{
249	va_list ap;
250	va_start(ap, fmt);
251	(void) vfprintf(stderr, fmt, ap);
252	va_end(ap);
253}
254
255void
256quit(const char *fmt, ...)
257{
258	va_list ap;
259
260	(void) fprintf(stderr,"  DUMP: ");
261#ifdef TDEBUG
262	(void) fprintf(stderr, "pid=%d ", getpid());
263#endif
264	va_start(ap, fmt);
265	(void) vfprintf(stderr, fmt, ap);
266	va_end(ap);
267	(void) fflush(stdout);
268	(void) fflush(stderr);
269	dumpabort(0);
270}
271
272/*
273 *	Tell the operator what has to be done;
274 *	we don't actually do it
275 */
276
277struct fstab *
278allocfsent(const struct fstab *fs)
279{
280	struct fstab *new;
281
282	new = (struct fstab *)malloc(sizeof (*fs));
283	if (new == NULL ||
284	    (new->fs_file = strdup(fs->fs_file)) == NULL ||
285	    (new->fs_type = strdup(fs->fs_type)) == NULL ||
286	    (new->fs_spec = strdup(fs->fs_spec)) == NULL)
287		quit("%s\n", strerror(errno));
288	new->fs_passno = fs->fs_passno;
289	new->fs_freq = fs->fs_freq;
290	return (new);
291}
292
293struct	pfstab {
294	SLIST_ENTRY(pfstab) pf_list;
295	struct	fstab *pf_fstab;
296};
297
298static	SLIST_HEAD(, pfstab) table;
299
300void
301dump_getfstab(void)
302{
303	struct fstab *fs;
304	struct pfstab *pf;
305
306	if (setfsent() == 0) {
307		msg("Can't open %s for dump table information: %s\n",
308		    _PATH_FSTAB, strerror(errno));
309		return;
310	}
311	while ((fs = getfsent()) != NULL) {
312		if (strcmp(fs->fs_type, FSTAB_RW) &&
313		    strcmp(fs->fs_type, FSTAB_RO) &&
314		    strcmp(fs->fs_type, FSTAB_RQ))
315			continue;
316		fs = allocfsent(fs);
317		if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL)
318			quit("%s\n", strerror(errno));
319		pf->pf_fstab = fs;
320		SLIST_INSERT_HEAD(&table, pf, pf_list);
321	}
322	(void) endfsent();
323}
324
325/*
326 * Search in the fstab for a file name.
327 * This file name can be either the special or the path file name.
328 *
329 * The file name can omit the leading '/'.
330 */
331struct fstab *
332fstabsearch(const char *key)
333{
334	struct pfstab *pf;
335	struct fstab *fs;
336	char *rn;
337
338	SLIST_FOREACH(pf, &table, pf_list) {
339		fs = pf->pf_fstab;
340		if (strcmp(fs->fs_file, key) == 0 ||
341		    strcmp(fs->fs_spec, key) == 0)
342			return (fs);
343		rn = rawname(fs->fs_spec);
344		if (rn != NULL && strcmp(rn, key) == 0)
345			return (fs);
346		if (key[0] != '/') {
347			if (*fs->fs_spec == '/' &&
348			    strcmp(fs->fs_spec + 1, key) == 0)
349				return (fs);
350			if (*fs->fs_file == '/' &&
351			    strcmp(fs->fs_file + 1, key) == 0)
352				return (fs);
353		}
354	}
355	return (NULL);
356}
357
358/*
359 *	Tell the operator what to do
360 */
361void
362lastdump(int arg)	/* w ==> just what to do; W ==> most recent dumps */
363{
364	int i;
365	struct fstab *dt;
366	struct dumpdates *dtwalk;
367	char *lastname, *date;
368	int dumpme;
369	time_t tnow;
370	struct tm *tlast;
371
372	(void) time(&tnow);
373	dump_getfstab();	/* /etc/fstab input */
374	initdumptimes();	/* /etc/dumpdates input */
375	qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort);
376
377	if (arg == 'w')
378		(void) printf("Dump these file systems:\n");
379	else
380		(void) printf("Last dump(s) done (Dump '>' file systems):\n");
381	lastname = "??";
382	ITITERATE(i, dtwalk) {
383		if (strncmp(lastname, dtwalk->dd_name,
384		    sizeof(dtwalk->dd_name)) == 0)
385			continue;
386		date = (char *)ctime(&dtwalk->dd_ddate);
387		date[16] = '\0';	/* blast away seconds and year */
388		lastname = dtwalk->dd_name;
389		dt = fstabsearch(dtwalk->dd_name);
390		dumpme = (dt != NULL && dt->fs_freq != 0);
391		if (dumpme) {
392		    tlast = localtime(&dtwalk->dd_ddate);
393		    dumpme = tnow > (dtwalk->dd_ddate - (tlast->tm_hour * 3600)
394				     - (tlast->tm_min * 60) - tlast->tm_sec
395				     + (dt->fs_freq * 86400));
396		};
397		if (arg != 'w' || dumpme)
398			(void) printf(
399			    "%c %8s\t(%6s) Last dump: Level %c, Date %s\n",
400			    dumpme && (arg != 'w') ? '>' : ' ',
401			    dtwalk->dd_name,
402			    dt ? dt->fs_file : "",
403			    dtwalk->dd_level,
404			    date);
405	}
406}
407
408int
409datesort(const void *a1, const void *a2)
410{
411	struct dumpdates *d1 = *(struct dumpdates **)a1;
412	struct dumpdates *d2 = *(struct dumpdates **)a2;
413	int diff;
414
415	diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name));
416	if (diff == 0)
417		return (d2->dd_ddate - d1->dd_ddate);
418	return (diff);
419}
420