11590Srgrimes/*
21590Srgrimes * Copyright (c) 1987, 1993, 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
4338451Sphilip * Copyright (c) 2018 Philip Paeps
51590Srgrimes *
61590Srgrimes * Redistribution and use in source and binary forms, with or without
71590Srgrimes * modification, are permitted provided that the following conditions
81590Srgrimes * are met:
91590Srgrimes * 1. Redistributions of source code must retain the above copyright
101590Srgrimes *    notice, this list of conditions and the following disclaimer.
111590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer in the
131590Srgrimes *    documentation and/or other materials provided with the distribution.
141590Srgrimes * 4. Neither the name of the University nor the names of its contributors
151590Srgrimes *    may be used to endorse or promote products derived from this software
161590Srgrimes *    without specific prior written permission.
171590Srgrimes *
181590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
191590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
201590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
211590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
221590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
231590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
241590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
251590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
261590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
271590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
281590Srgrimes * SUCH DAMAGE.
291590Srgrimes */
301590Srgrimes
311590Srgrimes#ifndef lint
3278201Sddstatic const char copyright[] =
331590Srgrimes"@(#) Copyright (c) 1987, 1993, 1994\n\
341590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
351590Srgrimes#endif /* not lint */
361590Srgrimes
371590Srgrimes#ifndef lint
3878201Sddstatic const char sccsid[] = "@(#)last.c	8.2 (Berkeley) 4/2/94";
391590Srgrimes#endif /* not lint */
4099112Sobrien#include <sys/cdefs.h>
4199112Sobrien__FBSDID("$FreeBSD: stable/11/usr.bin/last/last.c 351925 2019-09-06 05:34:31Z eugen $");
421590Srgrimes
431590Srgrimes#include <sys/param.h>
441590Srgrimes#include <sys/stat.h>
451590Srgrimes
461590Srgrimes#include <err.h>
47118077Stjr#include <errno.h>
481590Srgrimes#include <fcntl.h>
4974588Sache#include <langinfo.h>
5016438Sache#include <locale.h>
511590Srgrimes#include <paths.h>
521590Srgrimes#include <signal.h>
531590Srgrimes#include <stdio.h>
541590Srgrimes#include <stdlib.h>
551590Srgrimes#include <string.h>
561590Srgrimes#include <time.h>
57125856Sdwmalone#include <timeconv.h>
581590Srgrimes#include <unistd.h>
59202197Sed#include <utmpx.h>
6011547Sdg#include <sys/queue.h>
611590Srgrimes
62338451Sphilip#include <libxo/xo.h>
63338451Sphilip
641590Srgrimes#define	NO	0				/* false/no */
651590Srgrimes#define	YES	1				/* true/yes */
6677291Sdd#define	ATOI2(ar)	((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
671590Srgrimes
681590Srgrimestypedef struct arg {
691590Srgrimes	char	*name;				/* argument */
70285742Sed#define	REBOOT_TYPE	-1
711590Srgrimes#define	HOST_TYPE	-2
721590Srgrimes#define	TTY_TYPE	-3
731590Srgrimes#define	USER_TYPE	-4
741590Srgrimes	int	type;				/* type of arg */
751590Srgrimes	struct arg	*next;			/* linked list pointer */
761590Srgrimes} ARG;
77227168Sedstatic ARG	*arglist;			/* head of linked list */
781590Srgrimes
79240425Sedstatic SLIST_HEAD(, idtab) idlist;
8011547Sdg
81202197Sedstruct idtab {
8236062Sjb	time_t	logout;				/* log out time */
83202197Sed	char	id[sizeof ((struct utmpx *)0)->ut_id]; /* identifier */
84240425Sed	SLIST_ENTRY(idtab) list;
8511547Sdg};
861590Srgrimes
8791536Siedowsestatic const	char *crmsg;			/* cause of last reboot */
88202197Sedstatic time_t	currentout;			/* current logout value */
89202197Sedstatic long	maxrec;				/* records to display */
90230458Shrsstatic const	char *file = NULL;		/* utx.log file */
91351925Seugenstatic int	noctfix = 0;			/* locale is C or UTF-8 */
9236434Sdannystatic int	sflag = 0;			/* show delta in seconds */
9336434Sdannystatic int	width = 5;			/* show seconds in delta */
9491541Siedowsestatic int	yflag;				/* show year */
9574588Sachestatic int      d_first;
9691538Siedowsestatic int	snapfound = 0;			/* found snapshot entry? */
9777291Sddstatic time_t	snaptime;			/* if != 0, we will only
9877291Sdd						 * report users logged in
9977291Sdd						 * at this snapshot time
10077291Sdd						 */
1011590Srgrimes
102227168Sedstatic void	 addarg(int, char *);
103351925Seugenstatic const char *ctf(const char *);
104227168Sedstatic time_t	 dateconv(char *);
105227168Sedstatic void	 doentry(struct utmpx *);
106227168Sedstatic void	 hostconv(char *);
107227168Sedstatic void	 printentry(struct utmpx *, struct idtab *);
108227168Sedstatic char	*ttyconv(char *);
109227168Sedstatic int	 want(struct utmpx *);
110227168Sedstatic void	 usage(void);
111227168Sedstatic void	 wtmp(void);
1121590Srgrimes
113351925Seugenstatic const char*
114351925Seugenctf(const char *fmt) {
115351925Seugen	static char  buf[31];
116351925Seugen	const char  *src, *end;
117351925Seugen	char	    *dst;
118351925Seugen
119351925Seugen	if (noctfix)
120351925Seugen		return (fmt);
121351925Seugen
122351925Seugen	end = buf + sizeof(buf);
123351925Seugen	for (src = fmt, dst = buf; dst < end; *dst++ = *src++) {
124351925Seugen		if (*src == '\0') {
125351925Seugen			*dst = '\0';
126351925Seugen			break;
127351925Seugen		} else if (*src == '%' && *(src+1) == 's') {
128351925Seugen			*dst++ = '%';
129351925Seugen			*dst++ = 'h';
130351925Seugen			*dst++ = 's';
131351925Seugen			strlcpy(dst, src+2, end - dst);
132351925Seugen			return (buf);
133351925Seugen		}
134351925Seugen	}
135351925Seugen	return (buf);
136351925Seugen}
137351925Seugen
138227168Sedstatic void
13936434Sdannyusage(void)
14036434Sdanny{
141338451Sphilip	xo_error(
142119023Stjr"usage: last [-swy] [-d [[CC]YY][MMDD]hhmm[.SS]] [-f file] [-h host]\n"
143119023Stjr"            [-n maxrec] [-t tty] [user ...]\n");
14436434Sdanny	exit(1);
14536434Sdanny}
14636434Sdanny
1471590Srgrimesint
148102944Sdwmalonemain(int argc, char *argv[])
1491590Srgrimes{
1501590Srgrimes	int ch;
1511590Srgrimes	char *p;
1521590Srgrimes
15316438Sache	(void) setlocale(LC_TIME, "");
15474588Sache	d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
15516438Sache
156351925Seugen	(void) setlocale(LC_CTYPE, "");
157351925Seugen	p = nl_langinfo(CODESET);
158351925Seugen	if (strcmp(p, "UTF-8") == 0 || strcmp(p, "US-ASCII") == 0)
159351925Seugen		noctfix = 1;
160351925Seugen
161338451Sphilip	argc = xo_parse_args(argc, argv);
162338451Sphilip	if (argc < 0)
163338451Sphilip		exit(1);
164338451Sphilip	atexit(xo_finish_atexit);
165338451Sphilip
1661590Srgrimes	maxrec = -1;
16777291Sdd	snaptime = 0;
168118077Stjr	while ((ch = getopt(argc, argv, "0123456789d:f:h:n:st:wy")) != -1)
1691590Srgrimes		switch (ch) {
1701590Srgrimes		case '0': case '1': case '2': case '3': case '4':
1711590Srgrimes		case '5': case '6': case '7': case '8': case '9':
1721590Srgrimes			/*
1731590Srgrimes			 * kludge: last was originally designed to take
1741590Srgrimes			 * a number after a dash.
1751590Srgrimes			 */
1761590Srgrimes			if (maxrec == -1) {
177106215Smux				p = strchr(argv[optind - 1], ch);
178106215Smux				if (p == NULL)
179106215Smux					p = strchr(argv[optind], ch);
180106215Smux				maxrec = atol(p);
1811590Srgrimes				if (!maxrec)
1821590Srgrimes					exit(0);
1831590Srgrimes			}
1841590Srgrimes			break;
18577291Sdd		case 'd':
18677291Sdd			snaptime = dateconv(optarg);
18777291Sdd			break;
1881590Srgrimes		case 'f':
1891590Srgrimes			file = optarg;
1901590Srgrimes			break;
1911590Srgrimes		case 'h':
1921590Srgrimes			hostconv(optarg);
1931590Srgrimes			addarg(HOST_TYPE, optarg);
1941590Srgrimes			break;
195118077Stjr		case 'n':
196118077Stjr			errno = 0;
197118077Stjr			maxrec = strtol(optarg, &p, 10);
198118077Stjr			if (p == optarg || *p != '\0' || errno != 0 ||
199118077Stjr			    maxrec <= 0)
200338451Sphilip				xo_errx(1, "%s: bad line count", optarg);
201118077Stjr			break;
20236434Sdanny		case 's':
20336434Sdanny			sflag++;	/* Show delta as seconds */
20436434Sdanny			break;
2051590Srgrimes		case 't':
2061590Srgrimes			addarg(TTY_TYPE, ttyconv(optarg));
2071590Srgrimes			break;
20836434Sdanny		case 'w':
20936434Sdanny			width = 8;
21036434Sdanny			break;
21191541Siedowse		case 'y':
21291541Siedowse			yflag++;
21391541Siedowse			break;
2141590Srgrimes		case '?':
2151590Srgrimes		default:
21636434Sdanny			usage();
2171590Srgrimes		}
2181590Srgrimes
21936434Sdanny	if (sflag && width == 8) usage();
22036434Sdanny
2211590Srgrimes	if (argc) {
2221590Srgrimes		setlinebuf(stdout);
2231590Srgrimes		for (argv += optind; *argv; ++argv) {
224285742Sed			if (strcmp(*argv, "reboot") == 0)
225285742Sed				addarg(REBOOT_TYPE, *argv);
2261590Srgrimes#define	COMPATIBILITY
2271590Srgrimes#ifdef	COMPATIBILITY
2281590Srgrimes			/* code to allow "last p5" to work */
2291590Srgrimes			addarg(TTY_TYPE, ttyconv(*argv));
2301590Srgrimes#endif
2311590Srgrimes			addarg(USER_TYPE, *argv);
2321590Srgrimes		}
2331590Srgrimes	}
2341590Srgrimes	wtmp();
2351590Srgrimes	exit(0);
2361590Srgrimes}
2371590Srgrimes
2381590Srgrimes/*
2391590Srgrimes * wtmp --
240230458Shrs *	read through the utx.log file
2411590Srgrimes */
242227168Sedstatic void
243102944Sdwmalonewtmp(void)
2441590Srgrimes{
245202643Sed	struct utmpx *buf = NULL;
246202197Sed	struct utmpx *ut;
247202643Sed	static unsigned int amount = 0;
248202197Sed	time_t t;
24916438Sache	char ct[80];
25016438Sache	struct tm *tm;
2511590Srgrimes
252240425Sed	SLIST_INIT(&idlist);
253202197Sed	(void)time(&t);
25411547Sdg
255338451Sphilip	xo_open_container("last-information");
256338451Sphilip
257202197Sed	/* Load the last entries from the file. */
258202197Sed	if (setutxdb(UTXDB_LOG, file) != 0)
259338451Sphilip		xo_err(1, "%s", file);
260202197Sed	while ((ut = getutxent()) != NULL) {
261202643Sed		if (amount % 128 == 0) {
262202643Sed			buf = realloc(buf, (amount + 128) * sizeof *ut);
263202643Sed			if (buf == NULL)
264338451Sphilip				xo_err(1, "realloc");
265202643Sed		}
266202643Sed		memcpy(&buf[amount++], ut, sizeof *ut);
267202197Sed		if (t > ut->ut_tv.tv_sec)
268202197Sed			t = ut->ut_tv.tv_sec;
269202197Sed	}
270202197Sed	endutxent();
2711590Srgrimes
272202197Sed	/* Display them in reverse order. */
273338451Sphilip	xo_open_list("last");
274202197Sed	while (amount > 0)
275202643Sed		doentry(&buf[--amount]);
276338451Sphilip	xo_close_list("last");
277338451Sphilip	free(buf);
27885648Sdillon	tm = localtime(&t);
279230458Shrs	(void) strftime(ct, sizeof(ct), "%+", tm);
280338451Sphilip	xo_emit("\n{:utxdb/%s}", (file == NULL) ? "utx.log" : file);
281338451Sphilip	xo_attr("seconds", "%lu", (unsigned long) t);
282351925Seugen	xo_emit(ctf(" begins {:begins/%s}\n"), ct);
283338451Sphilip	xo_close_container("last-information");
2841590Srgrimes}
2851590Srgrimes
2861590Srgrimes/*
28791536Siedowse * doentry --
288230458Shrs *	process a single utx.log entry
28991536Siedowse */
290227168Sedstatic void
291202197Seddoentry(struct utmpx *bp)
29291536Siedowse{
293240425Sed	struct idtab *tt;
29491536Siedowse
295202197Sed	/* the machine stopped */
296202197Sed	if (bp->ut_type == BOOT_TIME || bp->ut_type == SHUTDOWN_TIME) {
29791536Siedowse		/* everybody just logged out */
298240425Sed		while ((tt = SLIST_FIRST(&idlist)) != NULL) {
299240425Sed			SLIST_REMOVE_HEAD(&idlist, list);
300240425Sed			free(tt);
30191536Siedowse		}
302202197Sed		currentout = -bp->ut_tv.tv_sec;
303202197Sed		crmsg = bp->ut_type != SHUTDOWN_TIME ?
30491536Siedowse		    "crash" : "shutdown";
30591536Siedowse		/*
30691536Siedowse		 * if we're in snapshot mode, we want to exit if this
30791536Siedowse		 * shutdown/reboot appears while we we are tracking the
30891536Siedowse		 * active range
30991536Siedowse		 */
31091536Siedowse		if (snaptime && snapfound)
31191536Siedowse			exit(0);
31291536Siedowse		/*
31391536Siedowse		 * don't print shutdown/reboot entries unless flagged for
31491536Siedowse		 */
31591538Siedowse		if (!snaptime && want(bp))
31691536Siedowse			printentry(bp, NULL);
31791536Siedowse		return;
31891536Siedowse	}
319202197Sed	/* date got set */
320202197Sed	if (bp->ut_type == OLD_TIME || bp->ut_type == NEW_TIME) {
32191538Siedowse		if (want(bp) && !snaptime)
32291536Siedowse			printentry(bp, NULL);
32391536Siedowse		return;
32491536Siedowse	}
325202197Sed
326202197Sed	if (bp->ut_type != USER_PROCESS && bp->ut_type != DEAD_PROCESS)
327202197Sed		return;
328202197Sed
329202197Sed	/* find associated identifier */
330240425Sed	SLIST_FOREACH(tt, &idlist, list)
331202197Sed	    if (!memcmp(tt->id, bp->ut_id, sizeof bp->ut_id))
33291536Siedowse		    break;
33391536Siedowse
33491536Siedowse	if (tt == NULL) {
33591536Siedowse		/* add new one */
336202197Sed		tt = malloc(sizeof(struct idtab));
33791536Siedowse		if (tt == NULL)
338338451Sphilip			xo_errx(1, "malloc failure");
33991536Siedowse		tt->logout = currentout;
340202197Sed		memcpy(tt->id, bp->ut_id, sizeof bp->ut_id);
341240425Sed		SLIST_INSERT_HEAD(&idlist, tt, list);
34291536Siedowse	}
34391536Siedowse
34491536Siedowse	/*
34591536Siedowse	 * print record if not in snapshot mode and wanted
34691536Siedowse	 * or in snapshot mode and in snapshot range
34791536Siedowse	 */
348202197Sed	if (bp->ut_type == USER_PROCESS && (want(bp) ||
349202197Sed	    (bp->ut_tv.tv_sec < snaptime &&
35091536Siedowse	    (tt->logout > snaptime || tt->logout < 1)))) {
35191536Siedowse		snapfound = 1;
35291536Siedowse		printentry(bp, tt);
35391536Siedowse	}
354202197Sed	tt->logout = bp->ut_tv.tv_sec;
35591536Siedowse}
35691536Siedowse
35791536Siedowse/*
35891536Siedowse * printentry --
35991536Siedowse *	output an entry
36091536Siedowse *
36191536Siedowse * If `tt' is non-NULL, use it and `crmsg' to print the logout time or
36291536Siedowse * logout type (crash/shutdown) as appropriate.
36391536Siedowse */
364227168Sedstatic void
365202197Sedprintentry(struct utmpx *bp, struct idtab *tt)
36691536Siedowse{
36791536Siedowse	char ct[80];
36891536Siedowse	struct tm *tm;
36991536Siedowse	time_t	delta;				/* time difference */
37091536Siedowse	time_t	t;
37191536Siedowse
37291538Siedowse	if (maxrec != -1 && !maxrec--)
37391538Siedowse		exit(0);
374338451Sphilip	xo_open_instance("last");
375202197Sed	t = bp->ut_tv.tv_sec;
37691536Siedowse	tm = localtime(&t);
37791541Siedowse	(void) strftime(ct, sizeof(ct), d_first ?
37891541Siedowse	    (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") :
37991541Siedowse	    (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm);
380202197Sed	switch (bp->ut_type) {
381202197Sed	case BOOT_TIME:
382338451Sphilip		xo_emit("{:user/%-42s/%s}", "boot time");
383202197Sed		break;
384202197Sed	case SHUTDOWN_TIME:
385338451Sphilip		xo_emit("{:user/%-42s/%s}", "shutdown time");
386202197Sed		break;
387202197Sed	case OLD_TIME:
388338451Sphilip		xo_emit("{:user/%-42s/%s}", "old time");
389202197Sed		break;
390202197Sed	case NEW_TIME:
391338451Sphilip		xo_emit("{:user/%-42s/%s}", "new time");
392202197Sed		break;
393202197Sed	case USER_PROCESS:
394338451Sphilip		xo_emit("{:user/%-10s/%s} {:tty/%-8s/%s} {:from/%-22.22s/%s}",
395202197Sed		    bp->ut_user, bp->ut_line, bp->ut_host);
396202197Sed		break;
397202197Sed	}
398338451Sphilip	xo_attr("seconds", "%lu", (unsigned long)t);
399351925Seugen	xo_emit(ctf(" {:login-time/%s%c/%s}"), ct, tt == NULL ? '\n' : ' ');
40091536Siedowse	if (tt == NULL)
401338451Sphilip		goto end;
40291536Siedowse	if (!tt->logout) {
403338451Sphilip		xo_emit("  {:logout-time/still logged in}\n");
404338451Sphilip		goto end;
40591536Siedowse	}
40691536Siedowse	if (tt->logout < 0) {
40791536Siedowse		tt->logout = -tt->logout;
408338451Sphilip		xo_emit("- {:logout-reason/%s}", crmsg);
40991536Siedowse	} else {
41091536Siedowse		tm = localtime(&tt->logout);
41191536Siedowse		(void) strftime(ct, sizeof(ct), "%R", tm);
412338451Sphilip		xo_attr("seconds", "%lu", (unsigned long)tt->logout);
413351925Seugen		xo_emit(ctf("- {:logout-time/%s}"), ct);
41491536Siedowse	}
415202197Sed	delta = tt->logout - bp->ut_tv.tv_sec;
416338451Sphilip	xo_attr("seconds", "%ld", (long)delta);
41791536Siedowse	if (sflag) {
418338451Sphilip		xo_emit("  ({:session-length/%8ld})\n", (long)delta);
41991536Siedowse	} else {
42091536Siedowse		tm = gmtime(&delta);
42191536Siedowse		(void) strftime(ct, sizeof(ct), width >= 8 ? "%T" : "%R", tm);
42291536Siedowse		if (delta < 86400)
423351925Seugen			xo_emit(ctf("  ({:session-length/%s})\n"), ct);
42491536Siedowse		else
425351925Seugen			xo_emit(ctf(" ({:session-length/%ld+%s})\n"),
426338451Sphilip			    (long)delta / 86400, ct);
42791536Siedowse	}
428338451Sphilip
429338451Sphilipend:
430338451Sphilip	xo_close_instance("last");
43191536Siedowse}
43291536Siedowse
43391536Siedowse/*
4341590Srgrimes * want --
4351590Srgrimes *	see if want this entry
4361590Srgrimes */
437227168Sedstatic int
438202197Sedwant(struct utmpx *bp)
4391590Srgrimes{
4401590Srgrimes	ARG *step;
4411590Srgrimes
44277291Sdd	if (snaptime)
44377291Sdd		return (NO);
44477291Sdd
4451590Srgrimes	if (!arglist)
4461590Srgrimes		return (YES);
4471590Srgrimes
4481590Srgrimes	for (step = arglist; step; step = step->next)
4491590Srgrimes		switch(step->type) {
450285742Sed		case REBOOT_TYPE:
451285742Sed			if (bp->ut_type == BOOT_TIME ||
452285742Sed			    bp->ut_type == SHUTDOWN_TIME)
453285742Sed				return (YES);
454285742Sed			break;
4551590Srgrimes		case HOST_TYPE:
456202197Sed			if (!strcasecmp(step->name, bp->ut_host))
4571590Srgrimes				return (YES);
4581590Srgrimes			break;
4591590Srgrimes		case TTY_TYPE:
460202197Sed			if (!strcmp(step->name, bp->ut_line))
4611590Srgrimes				return (YES);
4621590Srgrimes			break;
4631590Srgrimes		case USER_TYPE:
464202197Sed			if (!strcmp(step->name, bp->ut_user))
4651590Srgrimes				return (YES);
4661590Srgrimes			break;
46791536Siedowse		}
4681590Srgrimes	return (NO);
4691590Srgrimes}
4701590Srgrimes
4711590Srgrimes/*
4721590Srgrimes * addarg --
4731590Srgrimes *	add an entry to a linked list of arguments
4741590Srgrimes */
475227168Sedstatic void
476102944Sdwmaloneaddarg(int type, char *arg)
4771590Srgrimes{
4781590Srgrimes	ARG *cur;
4791590Srgrimes
48096785Sjmallett	if ((cur = malloc(sizeof(ARG))) == NULL)
481338451Sphilip		xo_errx(1, "malloc failure");
4821590Srgrimes	cur->next = arglist;
4831590Srgrimes	cur->type = type;
4841590Srgrimes	cur->name = arg;
4851590Srgrimes	arglist = cur;
4861590Srgrimes}
4871590Srgrimes
4881590Srgrimes/*
4891590Srgrimes * hostconv --
4901590Srgrimes *	convert the hostname to search pattern; if the supplied host name
4911590Srgrimes *	has a domain attached that is the same as the current domain, rip
4921590Srgrimes *	off the domain suffix since that's what login(1) does.
4931590Srgrimes */
494227168Sedstatic void
495102944Sdwmalonehostconv(char *arg)
4961590Srgrimes{
4971590Srgrimes	static int first = 1;
4981590Srgrimes	static char *hostdot, name[MAXHOSTNAMELEN];
4991590Srgrimes	char *argdot;
5001590Srgrimes
5011590Srgrimes	if (!(argdot = strchr(arg, '.')))
5021590Srgrimes		return;
5031590Srgrimes	if (first) {
5041590Srgrimes		first = 0;
5051590Srgrimes		if (gethostname(name, sizeof(name)))
506338451Sphilip			xo_err(1, "gethostname");
5071590Srgrimes		hostdot = strchr(name, '.');
5081590Srgrimes	}
5091590Srgrimes	if (hostdot && !strcasecmp(hostdot, argdot))
5101590Srgrimes		*argdot = '\0';
5111590Srgrimes}
5121590Srgrimes
5131590Srgrimes/*
5141590Srgrimes * ttyconv --
5151590Srgrimes *	convert tty to correct name.
5161590Srgrimes */
517227168Sedstatic char *
518102944Sdwmalonettyconv(char *arg)
5191590Srgrimes{
5201590Srgrimes	char *mval;
5211590Srgrimes
5221590Srgrimes	/*
5231590Srgrimes	 * kludge -- we assume that all tty's end with
5241590Srgrimes	 * a two character suffix.
5251590Srgrimes	 */
5261590Srgrimes	if (strlen(arg) == 2) {
5271590Srgrimes		/* either 6 for "ttyxx" or 8 for "console" */
52896785Sjmallett		if ((mval = malloc(8)) == NULL)
529338451Sphilip			xo_errx(1, "malloc failure");
5301590Srgrimes		if (!strcmp(arg, "co"))
5311590Srgrimes			(void)strcpy(mval, "console");
5321590Srgrimes		else {
5331590Srgrimes			(void)strcpy(mval, "tty");
5341590Srgrimes			(void)strcpy(mval + 3, arg);
5351590Srgrimes		}
5361590Srgrimes		return (mval);
5371590Srgrimes	}
5381590Srgrimes	if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
5391590Srgrimes		return (arg + 5);
5401590Srgrimes	return (arg);
5411590Srgrimes}
5421590Srgrimes
5431590Srgrimes/*
54477291Sdd * dateconv --
54577291Sdd * 	Convert the snapshot time in command line given in the format
54677291Sdd * 	[[CC]YY]MMDDhhmm[.SS]] to a time_t.
54777291Sdd * 	Derived from atime_arg1() in usr.bin/touch/touch.c
54877291Sdd */
549227168Sedstatic time_t
550102944Sdwmalonedateconv(char *arg)
55177291Sdd{
55277291Sdd        time_t timet;
55377291Sdd        struct tm *t;
55477291Sdd        int yearset;
55577291Sdd        char *p;
55677291Sdd
55777291Sdd        /* Start with the current time. */
55877291Sdd        if (time(&timet) < 0)
559338451Sphilip                xo_err(1, "time");
56077291Sdd        if ((t = localtime(&timet)) == NULL)
561338451Sphilip                xo_err(1, "localtime");
56277291Sdd
56377291Sdd        /* [[CC]YY]MMDDhhmm[.SS] */
56477291Sdd        if ((p = strchr(arg, '.')) == NULL)
56577291Sdd                t->tm_sec = 0; 		/* Seconds defaults to 0. */
56677291Sdd        else {
56777291Sdd                if (strlen(p + 1) != 2)
56877291Sdd                        goto terr;
56977291Sdd                *p++ = '\0';
57077291Sdd                t->tm_sec = ATOI2(p);
57177291Sdd        }
57277291Sdd
57377291Sdd        yearset = 0;
57477291Sdd        switch (strlen(arg)) {
57577291Sdd        case 12:                	/* CCYYMMDDhhmm */
57677291Sdd                t->tm_year = ATOI2(arg);
57777291Sdd                t->tm_year *= 100;
57877291Sdd                yearset = 1;
579133332Sdwmalone                /* FALLTHROUGH */
58077291Sdd        case 10:                	/* YYMMDDhhmm */
58177291Sdd                if (yearset) {
58277291Sdd                        yearset = ATOI2(arg);
58377291Sdd                        t->tm_year += yearset;
58477291Sdd                } else {
58577291Sdd                        yearset = ATOI2(arg);
58677291Sdd                        if (yearset < 69)
58777291Sdd                                t->tm_year = yearset + 2000;
58877291Sdd                        else
58977291Sdd                                t->tm_year = yearset + 1900;
59077291Sdd                }
59177291Sdd                t->tm_year -= 1900;     /* Convert to UNIX time. */
59277291Sdd                /* FALLTHROUGH */
59377291Sdd        case 8:				/* MMDDhhmm */
59477291Sdd                t->tm_mon = ATOI2(arg);
59577291Sdd                --t->tm_mon;    	/* Convert from 01-12 to 00-11 */
59677291Sdd                t->tm_mday = ATOI2(arg);
59777291Sdd                t->tm_hour = ATOI2(arg);
59877291Sdd                t->tm_min = ATOI2(arg);
59977291Sdd                break;
60077291Sdd        case 4:				/* hhmm */
60177291Sdd                t->tm_hour = ATOI2(arg);
60277291Sdd                t->tm_min = ATOI2(arg);
60377291Sdd                break;
60477291Sdd        default:
60577291Sdd                goto terr;
60677291Sdd        }
60777291Sdd        t->tm_isdst = -1;       	/* Figure out DST. */
60877291Sdd        timet = mktime(t);
60977291Sdd        if (timet == -1)
610338451Sphilipterr:           xo_errx(1,
61177291Sdd        "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
61277291Sdd        return timet;
61377291Sdd}
614