view usr/src/cmd/backup/dump/dumpoptr.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line source

/*
 * Copyright (c) 1998 by Sun Microsystems, Inc.
 * All rights reserved.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved	*/

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#pragma ident	"@(#)dumpoptr.c	1.66	05/03/08 SMI"

#include <errno.h>
#include "dump.h"

static unsigned int timeout;		/* current timeout */
static char *attnmessage, *saveattn;	/* attention message */

#ifdef __STDC__
static void alarmcatch();
static int idatesort(const void *, const void *);
#else /* !__STDC__ */
static void alarmcatch();
static int idatesort();
#endif

#ifdef DEBUG
extern int xflag;
#endif

/*
 *	Query the operator; This fascist piece of code requires
 *	an exact response.
 *	It is intended to protect dump aborting by inquisitive
 *	people banging on the console terminal to see what is
 *	happening which might cause dump to croak, destroying
 *	a large number of hours of work.
 *
 *	Every time += 2 minutes we reprint the message, alerting others
 *	that dump needs attention.
 */
int
query(question)
	char	*question;
{
	int def = -1;

	while (def == -1)
		def = query_once(question, -1);
	return (def);
}

static int in_query_once;
static jmp_buf sjalarmbuf;

/* real simple check-sum */
static int
addem(s)
	char *s;
{
	int total = 0;

	if (s == (char *)NULL)
		return (total);
	while (*s)
		total += *s++;
	return (total);
}

int
query_once(question, def)
	char	*question;
	int	def;
{
	static char *lastmsg;
	static int lastmsgsum;
	int	msgsum;
	char	replybuffer[BUFSIZ];
	int	back;
	time32_t timeclockstate;
	pollfd_t pollset;
	struct sigvec sv;

	/* special hook to flush timeout cache */
	if (question == NULL) {
		lastmsg = (char *)NULL;
		lastmsgsum = 0;
		return (0);
	}

	attnmessage = question;
	/*
	 * Only reset the state if the message changed somehow
	 */
	msgsum = addem(question);
	if (lastmsg != question || lastmsgsum != msgsum) {
		timeout = 0;
		if (telapsed && tstart_writing)
			*telapsed += time((time_t *)0) - *tstart_writing;
		lastmsg = question;
		lastmsgsum = msgsum;
	}
	timeclockstate = timeclock((time_t)0);
	if (setjmp(sjalarmbuf) != 0) {
		if (def != -1) {
			if (def)
				msgtail(gettext("YES\n"));
			else
				msgtail(gettext("NO\n"));
		}
		back = def;
		goto done;
	}
	alarmcatch();
	in_query_once = 1;
	pollset.fd = -1;
	pollset.events = 0;
	pollset.revents = 0;
	if (isatty(fileno(stdin))) {
		pollset.fd = fileno(stdin);
		pollset.events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
	} else {
		dumpabort();
		/*NOTREACHED*/
	}
	for (;;) {
		if (poll(&pollset, 1, -1) < 0) {
			if (errno == EINTR)
				continue;
			perror("poll(stdin)");
			dumpabort();
			/*NOTREACHED*/
		}
		if (pollset.revents == 0)
			continue;	/* sanity check */
		if (fgets(replybuffer, sizeof (replybuffer), stdin) == NULL) {
			if (ferror(stdin)) {
				clearerr(stdin);
				continue;
			} else {
				dumpabort();
				/*NOTREACHED*/
			}
		}
		timeout = 0;
		if (strcasecmp(replybuffer, gettext("yes\n")) == 0) {
			back = 1;
			lastmsg = (char *)NULL;
			lastmsgsum = 0;
			goto done;
		} else if (strcasecmp(replybuffer, gettext("no\n")) == 0) {
			back = 0;
			lastmsg = (char *)NULL;
			lastmsgsum = 0;
			goto done;
		} else {
			msg(gettext("\"yes\" or \"no\"?\n"));
			in_query_once = 0;
			alarmcatch();
			in_query_once = 1;
		}
	}
done:
	/*
	 * Turn off the alarm, and reset the signal to trap out..
	 */
	(void) alarm(0);
	attnmessage = NULL;
	sv.sv_handler = sigAbort;
	sv.sv_flags = SA_RESTART;
	(void) sigemptyset(&sv.sa_mask);
	(void) sigvec(SIGALRM, &sv, (struct sigvec *)0);
	if (tstart_writing)
		(void) time(tstart_writing);
	(void) timeclock(timeclockstate);
	in_query_once = 0;
	return (back);
}
/*
 *	Alert the console operator, and enable the alarm clock to
 *	sleep for time += 2 minutes in case nobody comes to satisfy dump
 *	If the alarm goes off while in the query_once for loop, we just
 *	longjmp back there and return the default answer.
 */
static void
#ifdef __STDC__
alarmcatch(void)
#else
alarmcatch()
#endif
{
	struct sigvec sv;

	if (in_query_once) {
		longjmp(sjalarmbuf, 1);
	}
	if (timeout) {
		msgtail("\n");
	}

	timeout += 120;
	msg(gettext("NEEDS ATTENTION: %s"), attnmessage);
	sv.sv_handler = alarmcatch;
	sv.sv_flags = SA_RESTART;
	(void) sigemptyset(&sv.sa_mask);
	(void) sigvec(SIGALRM, &sv, (struct sigvec *)0);
	(void) alarm(timeout);
}

/*
 *	Here if an inquisitive operator interrupts the dump program
 */
/*ARGSUSED*/
void
interrupt(sig)
	int	sig;
{
	if (!saveattn) {
		saveattn = attnmessage;
	}
	msg(gettext("Interrupt received.\n"));
	if (query(gettext(
	    "Do you want to abort dump?: (\"yes\" or \"no\") "))) {
		dumpabort();
		/*NOTREACHED*/
	}
	if (saveattn) {
		attnmessage = saveattn;
		saveattn = NULL;
		alarmcatch();
	}
}

/*
 *	We use wall(1) to do the actual broadcasting, so
 *	that we don't have to worry about duplicated code
 *	only getting fixed in one place.  This also saves
 *	us from having to worry about process groups,
 *	controlling terminals, and the like.
 */
void
broadcast(message)
	char	*message;
{
	time_t	clock;
	pid_t	pid;
	int	saverr;
	int	fildes[2];
	FILE	*wall;
	struct tm *localclock;

	if (!notify)
		return;

	if (pipe(fildes) < 0) {
		saverr = errno;
		msg(gettext("pipe: %s\n"), strerror(saverr));
		return;
	}

	switch (pid = fork()) {
	case -1:
		return;
	case 0:
		close(fildes[0]);
		if (dup2(fildes[1], 0) < 0) {
			saverr = errno;
			msg(gettext("dup2: %s\n"), strerror(saverr));
			exit(1);
		}
		execl("/usr/sbin/wall", "wall", "-g", OPGRENT, (char *)NULL);
		saverr = errno;
		msg(gettext("execl: %s\n"), strerror(saverr));
		exit(1);
	default:
		break;		/* parent */
	}

	close(fildes[1]);
	wall = fdopen(fildes[0], "r+");
	if (wall == (FILE *)NULL) {
		saverr = errno;
		msg(gettext("fdopen: %s\n"), strerror(saverr));
		return;
	}

	clock = time((time_t *)0);
	localclock = localtime(&clock);

	(void) fprintf(wall, gettext(
"\n\007\007\007Message from the dump program to all operators at \
%d:%02d ...\n\n%s"),
	    localclock->tm_hour, localclock->tm_min, message);
	fclose(wall);

	while (wait((int *)0) != pid) {
		continue;
		/*LINTED [empty loop body]*/
	}
}

/*
 *	print out an estimate of the amount of time left to do the dump
 */
#define	EST_SEC	600			/* every 10 minutes */
void
timeest(force, blkswritten)
	int force;
	int blkswritten;
{
	time_t tnow, deltat;
	char *msgp;

	if (tschedule == NULL)
		return;
	if (*tschedule == 0)
		*tschedule = time((time_t *)0) + EST_SEC;
	(void) time(&tnow);
	if ((force || tnow >= *tschedule) && blkswritten) {
		*tschedule = tnow + EST_SEC;
		if (!force && blkswritten < 50 * ntrec)
			return;
		deltat = (*telapsed + (tnow - *tstart_writing))
				* ((double)esize / blkswritten - 1.0);
		msgp = gettext("%3.2f%% done, finished in %d:%02d\n");
		msg(msgp, (blkswritten*100.0)/esize,
			deltat/3600, (deltat%3600)/60);
	}
}

#include <stdarg.h>

/* VARARGS1 */
void
msg(const char *fmt, ...)
{
	char buf[1024], *cp;
	size_t size;
	va_list args;

	va_start(args, fmt);
	(void) strcpy(buf, "  DUMP: ");
	cp = &buf[strlen(buf)];
#ifdef TDEBUG
	(void) sprintf(cp, "pid=%d ", getpid());
	cp = &buf[strlen(buf)];
#endif
	/* don't need -1, vsnprintf does it right */
	/* LINTED pointer arithmetic result fits in size_t */
	size = ((size_t)sizeof (buf)) - (size_t)(cp - buf);
	(void) vsnprintf(cp, size, fmt, args);
	(void) fputs(buf, stderr);
	(void) fflush(stdout);
	(void) fflush(stderr);
	va_end(args);
}

/* VARARGS1 */
void
msgtail(const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	(void) vfprintf(stderr, fmt, args);
	va_end(args);
}

#define	MINUTES(x)	((x) * 60)

/*
 *	Tell the operator what has to be done;
 *	we don't actually do it
 */
void
lastdump(arg)		/* w ==> just what to do; W ==> most recent dumps */
	int	arg;
{
	char *lastname;
	char *date;
	int i;
	time_t tnow, ddate;
	struct mntent *dt;
	int dumpme = 0;
	struct idates *itwalk;

	(void) time(&tnow);
	mnttabread();		/* /etc/fstab input */
	inititimes();		/* /etc/dumpdates input */

	/* Don't use msg(), this isn't a tell-the-world kind of thing */
	if (arg == 'w')
		(void) fprintf(stdout, gettext("Dump these file systems:\n"));
	else
		(void) fprintf(stdout, gettext(
		    "Last dump(s) done (Dump '>' file systems):\n"));

	if (idatev != NULL) {
		qsort((char *)idatev, nidates, sizeof (*idatev), idatesort);
		lastname = "??";
		ITITERATE(i, itwalk) {
			if (strncmp(lastname, itwalk->id_name,
			    sizeof (itwalk->id_name)) == 0)
				continue;
			/* must be ctime(), per ufsdump(4) */
			ddate = itwalk->id_ddate;
			date = (char *)ctime(&ddate);
			date[16] = '\0';	/* blow away seconds and year */
			lastname = itwalk->id_name;
			dt = mnttabsearch(itwalk->id_name, 0);
			if ((time_t)(itwalk->id_ddate) < (tnow - DAY)) {
				dumpme = 1;
			}

			if ((arg == 'w') && dumpme) {
				/*
				 * Handle the w option: print out file systems
				 * which haven't been backed up within a day.
				 */
				(void) printf(gettext("%8s\t(%6s)\n"),
				    itwalk->id_name, dt ? dt->mnt_dir : "");
			}
			if (arg == 'W') {
				/*
				 * Handle the W option: print out ALL
				 * filesystems including recent dump dates and
				 * dump levels.  Mark the backup-needing
				 * filesystems with a >.
				 */
				(void) printf(gettext(
			    "%c %8s\t(%6s) Last dump: Level %c, Date %s\n"),
				    dumpme ? '>' : ' ',
				    itwalk->id_name,
				    dt ? dt->mnt_dir : "",
				    (uchar_t)itwalk->id_incno,
				    date);
			}
			dumpme = 0;
		}
	}
}

static int
idatesort(v1, v2)
#ifdef __STDC__
	const void *v1;
	const void *v2;
#else
	void *v1;
	void *v2;
#endif
{
	struct idates **p1 = (struct idates **)v1;
	struct idates **p2 = (struct idates **)v2;
	int diff;

	diff = strcoll((*p1)->id_name, (*p2)->id_name);
	if (diff == 0) {
		/*
		 * Time may eventually become unsigned, so can't
		 * rely on subtraction to give a useful result.
		 * Note that we are sorting dates into reverse
		 * order, so that we will report based on the
		 * most-recent record for a particular filesystem.
		 */
		if ((*p1)->id_ddate > (*p2)->id_ddate)
			diff = -1;
		else if ((*p1)->id_ddate < (*p2)->id_ddate)
			diff = 1;
	}
	return (diff);
}