optr.c revision 50476
1241600Sgonzo/*-
2241600Sgonzo * Copyright (c) 1980, 1988, 1993
3241600Sgonzo *	The Regents of the University of California.  All rights reserved.
4241600Sgonzo *
5241600Sgonzo * Redistribution and use in source and binary forms, with or without
6241600Sgonzo * modification, are permitted provided that the following conditions
7241600Sgonzo * are met:
8241600Sgonzo * 1. Redistributions of source code must retain the above copyright
9241600Sgonzo *    notice, this list of conditions and the following disclaimer.
10241600Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11241600Sgonzo *    notice, this list of conditions and the following disclaimer in the
12241600Sgonzo *    documentation and/or other materials provided with the distribution.
13241600Sgonzo * 3. All advertising materials mentioning features or use of this software
14241600Sgonzo *    must display the following acknowledgement:
15241600Sgonzo *	This product includes software developed by the University of
16241600Sgonzo *	California, Berkeley and its contributors.
17241600Sgonzo * 4. Neither the name of the University nor the names of its contributors
18241600Sgonzo *    may be used to endorse or promote products derived from this software
19241600Sgonzo *    without specific prior written permission.
20241600Sgonzo *
21241600Sgonzo * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22241600Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23241600Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24241600Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25241600Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26241600Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27241600Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28241600Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29241600Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30241600Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31241600Sgonzo * SUCH DAMAGE.
32241600Sgonzo */
33241600Sgonzo
34241600Sgonzo#ifndef lint
35241600Sgonzo#if 0
36241600Sgonzostatic char sccsid[] = "@(#)optr.c	8.2 (Berkeley) 1/6/94";
37241600Sgonzo#endif
38241600Sgonzostatic const char rcsid[] =
39241600Sgonzo  "$FreeBSD: head/sbin/dump/optr.c 50476 1999-08-28 00:22:10Z peter $";
40241600Sgonzo#endif /* not lint */
41241600Sgonzo
42241600Sgonzo#include <sys/param.h>
43241600Sgonzo#include <sys/wait.h>
44241600Sgonzo#include <sys/time.h>
45241600Sgonzo
46241600Sgonzo#include <errno.h>
47241600Sgonzo#include <fstab.h>
48241600Sgonzo#include <grp.h>
49241600Sgonzo#include <stdio.h>
50241600Sgonzo#include <stdlib.h>
51241600Sgonzo#include <string.h>
52241600Sgonzo#include <stdarg.h>
53241600Sgonzo#include <unistd.h>
54241600Sgonzo#include <utmp.h>
55241600Sgonzo
56241600Sgonzo#include "dump.h"
57241600Sgonzo#include "pathnames.h"
58241600Sgonzo
59241600Sgonzovoid	alarmcatch __P((/* int, int */));
60241600Sgonzoint	datesort __P((const void *, const void *));
61241600Sgonzostatic	void sendmes __P((char *, char *));
62241600Sgonzo
63241600Sgonzo/*
64241600Sgonzo *	Query the operator; This previously-fascist piece of code
65241600Sgonzo *	no longer requires an exact response.
66312399Smarius *	It is intended to protect dump aborting by inquisitive
67312399Smarius *	people banging on the console terminal to see what is
68241600Sgonzo *	happening which might cause dump to croak, destroying
69241600Sgonzo *	a large number of hours of work.
70241600Sgonzo *
71241600Sgonzo *	Every 2 minutes we reprint the message, alerting others
72241600Sgonzo *	that dump needs attention.
73241600Sgonzo */
74312399Smariusstatic	int timeout;
75241600Sgonzostatic	char *attnmessage;		/* attention message */
76241600Sgonzo
77241600Sgonzoint
78241600Sgonzoquery(question)
79241600Sgonzo	char	*question;
80241600Sgonzo{
81270885Smarius	char	replybuffer[64];
82241600Sgonzo	int	back, errcount;
83241600Sgonzo	FILE	*mytty;
84241600Sgonzo
85241600Sgonzo	if ((mytty = fopen(_PATH_TTY, "r")) == NULL)
86276469Smarius		quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno));
87276469Smarius	attnmessage = question;
88276469Smarius	timeout = 0;
89241600Sgonzo	alarmcatch();
90241600Sgonzo	back = -1;
91241600Sgonzo	errcount = 0;
92241600Sgonzo	do {
93241600Sgonzo		if (fgets(replybuffer, 63, mytty) == NULL) {
94241600Sgonzo			clearerr(mytty);
95241600Sgonzo			if (++errcount > 30)	/* XXX	ugly */
96241600Sgonzo				quit("excessive operator query failures\n");
97241600Sgonzo		} else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') {
98241600Sgonzo			back = 1;
99241600Sgonzo		} else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') {
100241600Sgonzo			back = 0;
101241600Sgonzo		} else {
102241600Sgonzo			(void) fprintf(stderr,
103241600Sgonzo			    "  DUMP: \"Yes\" or \"No\"?\n");
104241600Sgonzo			(void) fprintf(stderr,
105241600Sgonzo			    "  DUMP: %s: (\"yes\" or \"no\") ", question);
106241600Sgonzo		}
107241600Sgonzo	} while (back < 0);
108289359Sadrian
109289359Sadrian	/*
110312399Smarius	 *	Turn off the alarm, and reset the signal to trap out..
111312399Smarius	 */
112312399Smarius	(void) alarm(0);
113312399Smarius	if (signal(SIGALRM, sig) == SIG_IGN)
114312399Smarius		signal(SIGALRM, SIG_IGN);
115312399Smarius	(void) fclose(mytty);
116312399Smarius	return(back);
117312399Smarius}
118312399Smarius
119312399Smariuschar lastmsg[100];
120312399Smarius
121312399Smarius/*
122312399Smarius *	Alert the console operator, and enable the alarm clock to
123241600Sgonzo *	sleep for 2 minutes in case nobody comes to satisfy dump
124241600Sgonzo */
125241600Sgonzovoid
126241600Sgonzoalarmcatch()
127241600Sgonzo{
128241600Sgonzo	if (notify == 0) {
129241600Sgonzo		if (timeout == 0)
130241600Sgonzo			(void) fprintf(stderr,
131241600Sgonzo			    "  DUMP: %s: (\"yes\" or \"no\") ",
132241600Sgonzo			    attnmessage);
133241600Sgonzo		else
134241600Sgonzo			msgtail("\7\7");
135312399Smarius	} else {
136312399Smarius		if (timeout) {
137241600Sgonzo			msgtail("\n");
138241600Sgonzo			broadcast("");		/* just print last msg */
139270885Smarius		}
140270885Smarius		(void) fprintf(stderr,"  DUMP: %s: (\"yes\" or \"no\") ",
141270885Smarius		    attnmessage);
142241600Sgonzo	}
143241600Sgonzo	signal(SIGALRM, alarmcatch);
144241600Sgonzo	(void) alarm(120);
145241600Sgonzo	timeout = 1;
146241600Sgonzo}
147241600Sgonzo
148241600Sgonzo/*
149241600Sgonzo *	Here if an inquisitive operator interrupts the dump program
150241600Sgonzo */
151241600Sgonzovoid
152241600Sgonzointerrupt(signo)
153241600Sgonzo	int signo;
154241600Sgonzo{
155241600Sgonzo	msg("Interrupt received.\n");
156241600Sgonzo	if (query("Do you want to abort dump?"))
157241600Sgonzo		dumpabort(0);
158241600Sgonzo}
159241600Sgonzo
160241600Sgonzo/*
161241600Sgonzo *	The following variables and routines manage alerting
162241600Sgonzo *	operators to the status of dump.
163241600Sgonzo *	This works much like wall(1) does.
164241600Sgonzo */
165241600Sgonzostruct	group *gp;
166241600Sgonzo
167241600Sgonzo/*
168241600Sgonzo *	Get the names from the group entry "operator" to notify.
169241600Sgonzo */
170241600Sgonzovoid
171241600Sgonzoset_operators()
172241600Sgonzo{
173241600Sgonzo	if (!notify)		/*not going to notify*/
174241600Sgonzo		return;
175241600Sgonzo	gp = getgrnam(OPGRENT);
176241600Sgonzo	(void) endgrent();
177241600Sgonzo	if (gp == NULL) {
178241600Sgonzo		msg("No group entry for %s.\n", OPGRENT);
179241600Sgonzo		notify = 0;
180241600Sgonzo		return;
181241600Sgonzo	}
182241600Sgonzo}
183241600Sgonzo
184241600Sgonzostruct tm *localclock;
185241600Sgonzo
186241600Sgonzo/*
187241600Sgonzo *	We fork a child to do the actual broadcasting, so
188241600Sgonzo *	that the process control groups are not messed up
189241600Sgonzo */
190241600Sgonzovoid
191241600Sgonzobroadcast(message)
192241600Sgonzo	char	*message;
193241600Sgonzo{
194241600Sgonzo	time_t		clock;
195241600Sgonzo	FILE	*f_utmp;
196241600Sgonzo	struct	utmp	utmp;
197241600Sgonzo	char	**np;
198241600Sgonzo	int	pid, s;
199241600Sgonzo
200241600Sgonzo	if (!notify || gp == NULL)
201241600Sgonzo		return;
202241600Sgonzo
203241600Sgonzo	switch (pid = fork()) {
204241600Sgonzo	case -1:
205241600Sgonzo		return;
206241600Sgonzo	case 0:
207241600Sgonzo		break;
208241600Sgonzo	default:
209241600Sgonzo		while (wait(&s) != pid)
210241600Sgonzo			continue;
211241600Sgonzo		return;
212241600Sgonzo	}
213241600Sgonzo
214241600Sgonzo	clock = time((time_t *)0);
215241600Sgonzo	localclock = localtime(&clock);
216241600Sgonzo
217241600Sgonzo	if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) {
218241600Sgonzo		msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno));
219241600Sgonzo		return;
220241600Sgonzo	}
221241600Sgonzo
222241600Sgonzo	while (!feof(f_utmp)) {
223241600Sgonzo		if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1)
224241600Sgonzo			break;
225241600Sgonzo		if (utmp.ut_name[0] == 0)
226276469Smarius			continue;
227241600Sgonzo		for (np = gp->gr_mem; *np; np++) {
228276469Smarius			if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0)
229276469Smarius				continue;
230276469Smarius			/*
231276469Smarius			 *	Do not send messages to operators on dialups
232241600Sgonzo			 */
233276469Smarius			if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0)
234241600Sgonzo				continue;
235241600Sgonzo#ifdef DEBUG
236241600Sgonzo			msg("Message to %s at %s\n", *np, utmp.ut_line);
237241600Sgonzo#endif
238241600Sgonzo			sendmes(utmp.ut_line, message);
239276469Smarius		}
240241600Sgonzo	}
241241600Sgonzo	(void) fclose(f_utmp);
242276469Smarius	Exit(0);	/* the wait in this same routine will catch this */
243241600Sgonzo	/* NOTREACHED */
244241600Sgonzo}
245241600Sgonzo
246241600Sgonzostatic void
247276469Smariussendmes(tty, message)
248276469Smarius	char *tty, *message;
249276469Smarius{
250276469Smarius	char t[MAXPATHLEN], buf[BUFSIZ];
251276469Smarius	register char *cp;
252276469Smarius	int lmsg = 1;
253276469Smarius	FILE *f_tty;
254276469Smarius
255276469Smarius	(void) strcpy(t, _PATH_DEV);
256276469Smarius	(void) strncat(t, tty, sizeof t - strlen(_PATH_DEV) - 1);
257276469Smarius
258276469Smarius	if ((f_tty = fopen(t, "w")) != NULL) {
259276469Smarius		setbuf(f_tty, buf);
260276469Smarius		(void) fprintf(f_tty,
261276469Smarius		    "\n\
262276469Smarius\7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\
263241600SgonzoDUMP: NEEDS ATTENTION: ",
264241600Sgonzo		    localclock->tm_hour, localclock->tm_min);
265241600Sgonzo		for (cp = lastmsg; ; cp++) {
266241600Sgonzo			if (*cp == '\0') {
267241600Sgonzo				if (lmsg) {
268241600Sgonzo					cp = message;
269241600Sgonzo					if (*cp == '\0')
270270885Smarius						break;
271241600Sgonzo					lmsg = 0;
272241600Sgonzo				} else
273241600Sgonzo					break;
274241600Sgonzo			}
275241600Sgonzo			if (*cp == '\n')
276270885Smarius				(void) putc('\r', f_tty);
277241600Sgonzo			(void) putc(*cp, f_tty);
278241600Sgonzo		}
279241600Sgonzo		(void) fclose(f_tty);
280241600Sgonzo	}
281241600Sgonzo}
282241600Sgonzo
283241600Sgonzo/*
284241600Sgonzo *	print out an estimate of the amount of time left to do the dump
285241600Sgonzo */
286241600Sgonzo
287241600Sgonzotime_t	tschedule = 0;
288241600Sgonzo
289241600Sgonzovoid
290241600Sgonzotimeest()
291241600Sgonzo{
292270885Smarius	time_t	tnow, deltat;
293241600Sgonzo
294241600Sgonzo	(void) time((time_t *) &tnow);
295241600Sgonzo	if (tnow >= tschedule) {
296241600Sgonzo		tschedule = tnow + 300;
297241600Sgonzo		if (blockswritten < 500)
298241600Sgonzo			return;
299241600Sgonzo		deltat = tstart_writing - tnow +
300241600Sgonzo			(1.0 * (tnow - tstart_writing))
301241600Sgonzo			/ blockswritten * tapesize;
302270885Smarius		msg("%3.2f%% done, finished in %d:%02d\n",
303241600Sgonzo			(blockswritten * 100.0) / tapesize,
304241600Sgonzo			deltat / 3600, (deltat % 3600) / 60);
305241600Sgonzo	}
306241600Sgonzo}
307241600Sgonzo
308241600Sgonzovoid
309241600Sgonzo#if __STDC__
310241600Sgonzomsg(const char *fmt, ...)
311241600Sgonzo#else
312241600Sgonzomsg(fmt, va_alist)
313241600Sgonzo	char *fmt;
314241600Sgonzo	va_dcl
315241600Sgonzo#endif
316241600Sgonzo{
317241600Sgonzo	va_list ap;
318241600Sgonzo
319241600Sgonzo	(void) fprintf(stderr,"  DUMP: ");
320241600Sgonzo#ifdef TDEBUG
321241600Sgonzo	(void) fprintf(stderr, "pid=%d ", getpid());
322241600Sgonzo#endif
323241600Sgonzo#if __STDC__
324241600Sgonzo	va_start(ap, fmt);
325241600Sgonzo#else
326241600Sgonzo	va_start(ap);
327241600Sgonzo#endif
328241600Sgonzo	(void) vfprintf(stderr, fmt, ap);
329270885Smarius	(void) fflush(stdout);
330270885Smarius	(void) fflush(stderr);
331270885Smarius	(void) vsprintf(lastmsg, fmt, ap);
332270885Smarius	va_end(ap);
333270885Smarius}
334270885Smarius
335241600Sgonzovoid
336241600Sgonzo#if __STDC__
337270885Smariusmsgtail(const char *fmt, ...)
338241600Sgonzo#else
339241600Sgonzomsgtail(fmt, va_alist)
340241600Sgonzo	char *fmt;
341241600Sgonzo	va_dcl
342241600Sgonzo#endif
343241600Sgonzo{
344241600Sgonzo	va_list ap;
345270885Smarius#if __STDC__
346296135Sjhibbits	va_start(ap, fmt);
347296135Sjhibbits#else
348241600Sgonzo	va_start(ap);
349241600Sgonzo#endif
350241600Sgonzo	(void) vfprintf(stderr, fmt, ap);
351241600Sgonzo	va_end(ap);
352289359Sadrian}
353289359Sadrian
354241600Sgonzovoid
355241600Sgonzo#if __STDC__
356241600Sgonzoquit(const char *fmt, ...)
357241600Sgonzo#else
358241600Sgonzoquit(fmt, va_alist)
359241600Sgonzo	char *fmt;
360241600Sgonzo	va_dcl
361241600Sgonzo#endif
362241600Sgonzo{
363241600Sgonzo	va_list ap;
364241600Sgonzo
365241600Sgonzo	(void) fprintf(stderr,"  DUMP: ");
366241600Sgonzo#ifdef TDEBUG
367241600Sgonzo	(void) fprintf(stderr, "pid=%d ", getpid());
368241600Sgonzo#endif
369241600Sgonzo#if __STDC__
370241600Sgonzo	va_start(ap, fmt);
371241600Sgonzo#else
372241600Sgonzo	va_start(ap);
373270885Smarius#endif
374241600Sgonzo	(void) vfprintf(stderr, fmt, ap);
375241600Sgonzo	va_end(ap);
376241600Sgonzo	(void) fflush(stdout);
377241600Sgonzo	(void) fflush(stderr);
378241600Sgonzo	dumpabort(0);
379241600Sgonzo}
380241600Sgonzo
381241600Sgonzo/*
382241600Sgonzo *	Tell the operator what has to be done;
383241600Sgonzo *	we don't actually do it
384241600Sgonzo */
385270885Smarius
386270885Smariusstruct fstab *
387241600Sgonzoallocfsent(fs)
388241600Sgonzo	register struct fstab *fs;
389241600Sgonzo{
390241600Sgonzo	register struct fstab *new;
391241600Sgonzo
392241600Sgonzo	new = (struct fstab *)malloc(sizeof (*fs));
393270885Smarius	if (new == NULL ||
394241600Sgonzo	    (new->fs_file = strdup(fs->fs_file)) == NULL ||
395276469Smarius	    (new->fs_type = strdup(fs->fs_type)) == NULL ||
396276469Smarius	    (new->fs_spec = strdup(fs->fs_spec)) == NULL)
397241600Sgonzo		quit("%s\n", strerror(errno));
398241600Sgonzo	new->fs_passno = fs->fs_passno;
399241600Sgonzo	new->fs_freq = fs->fs_freq;
400241600Sgonzo	return (new);
401276469Smarius}
402276469Smarius
403276469Smariusstruct	pfstab {
404276469Smarius	struct	pfstab *pf_next;
405276469Smarius	struct	fstab *pf_fstab;
406276469Smarius};
407276469Smarius
408276469Smariusstatic	struct pfstab *table;
409276469Smarius
410276469Smariusvoid
411241600Sgonzogetfstab()
412241600Sgonzo{
413241600Sgonzo	register struct fstab *fs;
414241600Sgonzo	register struct pfstab *pf;
415241600Sgonzo
416241600Sgonzo	if (setfsent() == 0) {
417241600Sgonzo		msg("Can't open %s for dump table information: %s\n",
418241600Sgonzo		    _PATH_FSTAB, strerror(errno));
419241600Sgonzo		return;
420270885Smarius	}
421241600Sgonzo	while ((fs = getfsent()) != NULL) {
422241600Sgonzo		if (strcmp(fs->fs_type, FSTAB_RW) &&
423241600Sgonzo		    strcmp(fs->fs_type, FSTAB_RO) &&
424241600Sgonzo		    strcmp(fs->fs_type, FSTAB_RQ))
425241600Sgonzo			continue;
426241600Sgonzo		fs = allocfsent(fs);
427241600Sgonzo		if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL)
428299414Strasz			quit("%s\n", strerror(errno));
429241600Sgonzo		pf->pf_fstab = fs;
430241600Sgonzo		pf->pf_next = table;
431241600Sgonzo		table = pf;
432299414Strasz	}
433299414Strasz	(void) endfsent();
434299414Strasz}
435299414Strasz
436299414Strasz/*
437299414Strasz * Search in the fstab for a file name.
438241600Sgonzo * This file name can be either the special or the path file name.
439241600Sgonzo *
440241600Sgonzo * The entries in the fstab are the BLOCK special names, not the
441241600Sgonzo * character special names.
442241600Sgonzo * The caller of fstabsearch assures that the character device
443241600Sgonzo * is dumped (that is much faster)
444241600Sgonzo *
445241600Sgonzo * The file name can omit the leading '/'.
446241600Sgonzo */
447241600Sgonzostruct fstab *
448241600Sgonzofstabsearch(key)
449241600Sgonzo	char *key;
450241600Sgonzo{
451241600Sgonzo	register struct pfstab *pf;
452241600Sgonzo	register struct fstab *fs;
453241600Sgonzo	char *rn;
454241600Sgonzo
455241600Sgonzo	for (pf = table; pf != NULL; pf = pf->pf_next) {
456241600Sgonzo		fs = pf->pf_fstab;
457276469Smarius		if (strcmp(fs->fs_file, key) == 0 ||
458241600Sgonzo		    strcmp(fs->fs_spec, key) == 0)
459241600Sgonzo			return (fs);
460241600Sgonzo		rn = rawname(fs->fs_spec);
461241600Sgonzo		if (rn != NULL && strcmp(rn, key) == 0)
462241600Sgonzo			return (fs);
463241600Sgonzo		if (key[0] != '/') {
464241600Sgonzo			if (*fs->fs_spec == '/' &&
465241600Sgonzo			    strcmp(fs->fs_spec + 1, key) == 0)
466312399Smarius				return (fs);
467312399Smarius			if (*fs->fs_file == '/' &&
468312399Smarius			    strcmp(fs->fs_file + 1, key) == 0)
469312399Smarius				return (fs);
470312399Smarius		}
471241600Sgonzo	}
472241600Sgonzo	return (NULL);
473241600Sgonzo}
474241600Sgonzo
475241600Sgonzo/*
476241600Sgonzo *	Tell the operator what to do
477241600Sgonzo */
478241600Sgonzovoid
479241600Sgonzolastdump(arg)
480241600Sgonzo	char	arg;	/* w ==> just what to do; W ==> most recent dumps */
481241600Sgonzo{
482246128Ssbz	register int i;
483241600Sgonzo	register struct fstab *dt;
484241600Sgonzo	register struct dumpdates *dtwalk;
485241600Sgonzo	char *lastname, *date;
486241600Sgonzo	int dumpme;
487241600Sgonzo	time_t tnow;
488241600Sgonzo	struct tm *tlast;
489241600Sgonzo
490241600Sgonzo	(void) time(&tnow);
491241600Sgonzo	getfstab();		/* /etc/fstab input */
492270885Smarius	initdumptimes();	/* /etc/dumpdates input */
493270885Smarius	qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort);
494241600Sgonzo
495292180Sian	if (arg == 'w')
496297127Sian		(void) printf("Dump these file systems:\n");
497	else
498		(void) printf("Last dump(s) done (Dump '>' file systems):\n");
499	lastname = "??";
500	ITITERATE(i, dtwalk) {
501		if (strncmp(lastname, dtwalk->dd_name,
502		    sizeof(dtwalk->dd_name)) == 0)
503			continue;
504		date = (char *)ctime(&dtwalk->dd_ddate);
505		date[16] = '\0';	/* blast away seconds and year */
506		lastname = dtwalk->dd_name;
507		dt = fstabsearch(dtwalk->dd_name);
508		dumpme = (dt != NULL && dt->fs_freq != 0);
509		if (dumpme) {
510		    tlast = localtime(&dtwalk->dd_ddate);
511		    dumpme = tnow > (dtwalk->dd_ddate - (tlast->tm_hour * 3600)
512				     - (tlast->tm_min * 60) - tlast->tm_sec
513				     + (dt->fs_freq * 86400));
514		};
515		if (arg != 'w' || dumpme)
516			(void) printf(
517			    "%c %8s\t(%6s) Last dump: Level %c, Date %s\n",
518			    dumpme && (arg != 'w') ? '>' : ' ',
519			    dtwalk->dd_name,
520			    dt ? dt->fs_file : "",
521			    dtwalk->dd_level,
522			    date);
523	}
524}
525
526int
527datesort(a1, a2)
528	const void *a1, *a2;
529{
530	struct dumpdates *d1 = *(struct dumpdates **)a1;
531	struct dumpdates *d2 = *(struct dumpdates **)a2;
532	int diff;
533
534	diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name));
535	if (diff == 0)
536		return (d2->dd_ddate - d1->dd_ddate);
537	return (diff);
538}
539