NetBSD-5.0.2/usr.bin/last/want.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: want.c,v 1.11 2007/10/05 07:27:42 lukem Exp $	*/

/*
 * Copyright (c) 1987, 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
static struct utmp *buf;

static void onintr(int);
static int want(struct utmp *, int);
static const char *gethost(struct utmp *, const char *, int);

static const char *
/*ARGSUSED*/
gethost(struct utmp *ut, const char *host, int numeric)
{
#if FIRSTVALID == 0
	return numeric ? "" : host;
#else
	if (numeric) {
		static char buf[512];
		buf[0] = '\0';
		(void)sockaddr_snprintf(buf, sizeof(buf), "%a",
		    (struct sockaddr *)&ut->ut_ss);
		return buf;
	} else
		return host;
#endif
}

#define NULTERM(what) \
	if (check ## what) \
		(void)strlcpy(what ## p = what ## buf, bp->ut_ ## what, \
		    sizeof(what ## buf)); \
	else \
		what ## p = bp->ut_ ## what
	
/*
 * wtmp --
 *	read through the wtmp file
 */
void
wtmp(const char *file, int namesz, int linesz, int hostsz, int numeric)
{
	struct utmp	*bp;		/* current structure */
	TTY	*T;			/* tty list entry */
	struct stat	stb;		/* stat of file for sz */
	off_t	offset;
	int	wfd;
	char	*ct, *crmsg;
	size_t  len = sizeof(*buf) * MAXUTMP;
	char namebuf[sizeof(bp->ut_name) + 1], *namep;
	char linebuf[sizeof(bp->ut_line) + 1], *linep;
	char hostbuf[sizeof(bp->ut_host) + 1], *hostp;
	int checkname = namesz > sizeof(bp->ut_name);
	int checkline = linesz > sizeof(bp->ut_line);
	int checkhost = hostsz > sizeof(bp->ut_host);

	if ((buf = malloc(len)) == NULL)
		err(EXIT_FAILURE, "Cannot allocate utmp buffer");

	crmsg = NULL;

	if (!strcmp(file, "-")) {
		wfd = STDIN_FILENO;
		file = "<stdin>";
	} else if ((wfd = open(file, O_RDONLY, 0)) < 0) {
		err(EXIT_FAILURE, "%s", file);
	}

	if (lseek(wfd, 0, SEEK_CUR) < 0) {
		const char *dir;
		char *tfile;
		int tempfd;
		ssize_t tlen;

		if (ESPIPE != errno) {
			err(EXIT_FAILURE, "lseek");
		}
		dir = getenv("TMPDIR");
		if (asprintf(&tfile, "%s/last.XXXXXX", dir ? dir : _PATH_TMP) == -1)
			err(EXIT_FAILURE, "asprintf");
		tempfd = mkstemp(tfile);
		if (tempfd < 0) {
			err(EXIT_FAILURE, "mkstemp");
		}
		unlink(tfile);
		for (;;) {
		   	tlen = read(wfd, buf, len);
			if (tlen < 0) {
				err(1, "%s: read", file);
			}
			if (tlen == 0) {
				break;
			}
			if (write(tempfd, buf, tlen) != tlen) {
				err(1, "%s: write", tfile);
			}
		}
		wfd = tempfd;
	}

	if (fstat(wfd, &stb) == -1)
		err(EXIT_FAILURE, "%s: fstat", file);
	if (!S_ISREG(stb.st_mode))
		errx(EXIT_FAILURE, "%s: Not a regular file", file);

	buf[FIRSTVALID].ut_timefld = time(NULL);
	(void)signal(SIGINT, onintr);
	(void)signal(SIGQUIT, onintr);

	offset = stb.st_size;
	/* Ignore trailing garbage or partial record */
	offset -= offset % (off_t) sizeof(*buf);
	
	while (offset >= (off_t) sizeof(*buf)) {
		ssize_t ret, i;
		size_t size;

		size = MIN(len, offset);
		offset -= size; /* Always a multiple of sizeof(*buf) */
		ret = pread(wfd, buf, size, offset);
		if (ret < 0) {
			err(EXIT_FAILURE, "%s: pread", file);
		} else if ((size_t) ret < size) {
			err(EXIT_FAILURE, "%s: Unexpected end of file", file);
		}

		for (i = ret / sizeof(*buf) - 1; i >= 0; i--) {
			bp = &buf[i];

			NULTERM(name);
			NULTERM(line);
			NULTERM(host);
			/*
			 * if the terminal line is '~', the machine stopped.
			 * see utmp(5) for more info.
			 */
			if (linep[0] == '~' && !linep[1]) {
				/* everybody just logged out */
				for (T = ttylist; T; T = T->next)
					T->logout = -bp->ut_timefld;
				currentout = -bp->ut_timefld;
				crmsg = strncmp(namep, "shutdown",
				    namesz) ? "crash" : "shutdown";
				if (want(bp, NO)) {
					ct = fmttime(bp->ut_timefld, fulltime);
					printf("%-*.*s  %-*.*s %-*.*s %s\n",
					    namesz, namesz, namep,
					    linesz, linesz, linep,
					    hostsz, hostsz,
					    gethost(bp, hostp, numeric), ct);
					if (maxrec != -1 && !--maxrec)
						return;
				}
				continue;
			}
			/*
			 * if the line is '{' or '|', date got set; see
			 * utmp(5) for more info.
			 */
			if ((linep[0] == '{' || linep[0] == '|') && !linep[1]) {
				if (want(bp, NO)) {
					ct = fmttime(bp->ut_timefld, fulltime);
					printf("%-*.*s  %-*.*s %-*.*s %s\n",
					    namesz, namesz, namep,
					    linesz, linesz, linep,
					    hostsz, hostsz,
					    gethost(bp, hostp, numeric),
					    ct);
					if (maxrec && !--maxrec)
						return;
				}
				continue;
			}
			/* find associated tty */
			for (T = ttylist;; T = T->next) {
				if (!T) {
					/* add new one */
					T = addtty(linep);
					break;
				}
				if (!strncmp(T->tty, linep, LINESIZE))
					break;
			}
			if (TYPE(bp) == SIGNATURE)
				continue;
			if (namep[0] && want(bp, YES)) {
				ct = fmttime(bp->ut_timefld, fulltime);
				printf("%-*.*s  %-*.*s %-*.*s %s ",
				    namesz, namesz, namep,
				    linesz, linesz, linep,
				    hostsz, hostsz,
				    gethost(bp, hostp, numeric),
				    ct);
				if (!T->logout)
					puts("  still logged in");
				else {
					time_t	delta;			/* time difference */

					if (T->logout < 0) {
						T->logout = -T->logout;
						printf("- %s", crmsg);
					}
					else
						printf("- %s",
						    fmttime(T->logout,
						    fulltime | TIMEONLY));
					delta = T->logout - bp->ut_timefld;
					if (delta < SECSPERDAY)
						printf("  (%s)\n",
						    fmttime(delta,
						    fulltime | TIMEONLY | GMT));
					else
						printf(" (%ld+%s)\n",
						    delta / SECSPERDAY,
						    fmttime(delta,
						    fulltime | TIMEONLY | GMT));
				}
				if (maxrec != -1 && !--maxrec)
					return;
			}
			T->logout = bp->ut_timefld;
		}
	}
	fulltime = 1;	/* show full time */
	crmsg = fmttime(buf[FIRSTVALID].ut_timefld, FULLTIME);
	if ((ct = strrchr(file, '/')) != NULL)
		ct++;
	printf("\n%s begins %s\n", ct ? ct : file, crmsg);
}

/*
 * want --
 *	see if want this entry
 */
static int
want(struct utmp *bp, int check)
{
	ARG *step;

	if (check) {
		/*
		 * when uucp and ftp log in over a network, the entry in
		 * the utmp file is the name plus their process id.  See
		 * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
		 */
		if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
			bp->ut_line[3] = '\0';
		else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
			bp->ut_line[4] = '\0';
	}
	if (!arglist)
		return (YES);

	for (step = arglist; step; step = step->next)
		switch(step->type) {
		case HOST_TYPE:
			if (!strncasecmp(step->name, bp->ut_host, HOSTSIZE))
				return (YES);
			break;
		case TTY_TYPE:
			if (!strncmp(step->name, bp->ut_line, LINESIZE))
				return (YES);
			break;
		case USER_TYPE:
			if (!strncmp(step->name, bp->ut_name, NAMESIZE))
				return (YES);
			break;
	}
	return (NO);
}

/*
 * onintr --
 *	on interrupt, we inform the user how far we've gotten
 */
static void
onintr(int signo)
{
	/* FIXME: None of this is allowed in a signal handler */
	printf("\ninterrupted %s\n", fmttime(buf[FIRSTVALID].ut_timefld,
	    FULLTIME));
	if (signo == SIGINT) {
		(void)raise_default_signal(signo);
		exit(EXIT_FAILURE);
	}
	(void)fflush(stdout);		/* fix required for rsh */
}