xref: /netbsd/usr.bin/last/last.c (revision 6550d01e)
1 /*	$NetBSD: last.c,v 1.34 2010/06/05 03:24:01 dholland Exp $	*/
2 
3 /*
4  * Copyright (c) 1987, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)last.c	8.2 (Berkeley) 4/2/94";
41 #endif
42 __RCSID("$NetBSD: last.c,v 1.34 2010/06/05 03:24:01 dholland Exp $");
43 #endif /* not lint */
44 
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 
48 #include <err.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <paths.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <time.h>
56 #include <tzfile.h>
57 #include <unistd.h>
58 #include <arpa/inet.h>
59 #ifdef SUPPORT_UTMPX
60 #include <utmpx.h>
61 #endif
62 #ifdef SUPPORT_UTMP
63 #include <utmp.h>
64 #endif
65 #include <util.h>
66 
67 #ifndef UT_NAMESIZE
68 #define UT_NAMESIZE 8
69 #define UT_LINESIZE 8
70 #define UT_HOSTSIZE 16
71 #endif
72 #ifndef SIGNATURE
73 #define SIGNATURE -1
74 #endif
75 
76 
77 
78 #define	NO	0			/* false/no */
79 #define	YES	1			/* true/yes */
80 
81 #define	TBUFLEN	30			/* length of time string buffer */
82 #define	TFMT	"%a %b %d %R"		/* strftime format string */
83 #define	LTFMT	"%a %b %d %Y %T"	/* strftime long format string */
84 #define	TFMTS	"%R"			/* strftime format string - time only */
85 #define	LTFMTS	"%T"			/* strftime long format string - " */
86 
87 /* fmttime() flags */
88 #define	FULLTIME	0x1		/* show year, seconds */
89 #define	TIMEONLY	0x2		/* show time only, not date */
90 #define	GMT		0x4		/* show time at GMT, for offsets only */
91 
92 #define MAXUTMP		1024
93 
94 typedef struct arg {
95 	const char	*name;		/* argument */
96 #define	HOST_TYPE	-2
97 #define	TTY_TYPE	-3
98 #define	USER_TYPE	-4
99 	int		type;		/* type of arg */
100 	struct arg	*next;		/* linked list pointer */
101 } ARG;
102 static ARG	*arglist;		/* head of linked list */
103 
104 typedef struct ttytab {
105 	time_t	logout;			/* log out time */
106 	char	tty[128];		/* terminal name */
107 	struct ttytab	*next;		/* linked list pointer */
108 } TTY;
109 static TTY	*ttylist;		/* head of linked list */
110 
111 static time_t	currentout;		/* current logout value */
112 static long	maxrec;			/* records to display */
113 static int	fulltime = 0;		/* Display seconds? */
114 static int	xflag;			/* Assume file is wtmpx format */
115 
116 int	 main(int, char *[]);
117 
118 static void	 addarg(int, const char *);
119 static TTY	*addtty(const char *);
120 static void	 hostconv(char *);
121 static const char *ttyconv(char *);
122 #ifdef SUPPORT_UTMPX
123 static void	 wtmpx(const char *, int, int, int, int);
124 #endif
125 #ifdef SUPPORT_UTMP
126 static void	 wtmp(const char *, int, int, int, int);
127 #endif
128 static char	*fmttime(time_t, int);
129 static void	 usage(void);
130 
131 static
132 void usage(void)
133 {
134 	(void)fprintf(stderr, "usage: %s [-#%s] [-nTx] [-f file]"
135 	    " [-H hostsize] [-h host] [-L linesize]\n"
136 	    "\t    [-N namesize] [-t tty] [user ...]\n", getprogname(),
137 #ifdef NOTYET_SUPPORT_UTMPX
138 	    "w"
139 #else
140 	    ""
141 #endif
142 	);
143 	exit(EXIT_FAILURE);
144 }
145 
146 int
147 main(int argc, char *argv[])
148 {
149 	int ch;
150 	char *p;
151 	const char *file = NULL;
152 	int namesize = UT_NAMESIZE;
153 	int linesize = UT_LINESIZE;
154 	int hostsize = UT_HOSTSIZE;
155 	int numeric = 0;
156 
157 	maxrec = -1;
158 
159 	while ((ch = getopt(argc, argv, "0123456789f:H:h:L:nN:Tt:x")) != -1)
160 		switch (ch) {
161 		case '0': case '1': case '2': case '3': case '4':
162 		case '5': case '6': case '7': case '8': case '9':
163 			/*
164 			 * kludge: last was originally designed to take
165 			 * a number after a dash.
166 			 */
167 			if (maxrec == -1) {
168 				p = argv[optind - 1];
169 				if (p[0] == '-' && p[1] == ch && !p[2])
170 					maxrec = atol(++p);
171 				else if (optind < argc)
172 					maxrec = atol(argv[optind] + 1);
173 				else
174 					usage();
175 				if (!maxrec)
176 					return 0;
177 			}
178 			break;
179 		case 'f':
180 			file = optarg;
181 			if ('\0' == file[0])
182 				usage();
183 			break;
184 		case 'H':
185 			hostsize = atoi(optarg);
186 			if (hostsize < 1)
187 				usage();
188 			break;
189 		case 'h':
190 			hostconv(optarg);
191 			addarg(HOST_TYPE, optarg);
192 			break;
193 		case 'L':
194 			linesize = atoi(optarg);
195 			if (linesize < 1)
196 				usage();
197 			break;
198 		case 'N':
199 			namesize = atoi(optarg);
200 			if (namesize < 1)
201 				usage();
202 			break;
203 		case 'n':
204 			numeric = 1;
205 			break;
206 		case 'T':
207 			fulltime = 1;
208 			break;
209 		case 't':
210 			addarg(TTY_TYPE, ttyconv(optarg));
211 			break;
212 		case 'x':
213 			xflag = 1;
214 			break;
215 		case '?':
216 		default:
217 			usage();
218 		}
219 
220 	if (argc) {
221 		setlinebuf(stdout);
222 		for (argv += optind; *argv; ++argv) {
223 #define	COMPATIBILITY
224 #ifdef	COMPATIBILITY
225 			/* code to allow "last p5" to work */
226 			addarg(TTY_TYPE, ttyconv(*argv));
227 #endif
228 			addarg(USER_TYPE, *argv);
229 		}
230 	}
231 	if (file == NULL) {
232 #ifdef SUPPORT_UTMPX
233 		if (access(_PATH_WTMPX, R_OK) == 0)
234 			file = _PATH_WTMPX;
235 		else
236 #endif
237 #ifdef SUPPORT_UTMP
238 		if (access(_PATH_WTMP, R_OK) == 0)
239 			file = _PATH_WTMP;
240 #endif
241 		if (file == NULL)
242 #if defined(SUPPORT_UTMPX) && defined(SUPPORT_UTMP)
243 			errx(EXIT_FAILURE, "Cannot access `%s' or `%s'", _PATH_WTMPX,
244 			    _PATH_WTMP);
245 #elif defined(SUPPORT_UTMPX)
246 			errx(EXIT_FAILURE, "Cannot access `%s'", _PATH_WTMPX);
247 #elif defined(SUPPORT_UTMP)
248 			errx(EXIT_FAILURE, "Cannot access `%s'", _PATH_WTMP);
249 #else
250 			errx(EXIT_FAILURE, "No utmp or utmpx support compiled in.");
251 #endif
252 	}
253 #if defined(SUPPORT_UTMPX) && defined(SUPPORT_UTMP)
254 	if (file[strlen(file) - 1] == 'x' || xflag)
255 		wtmpx(file, namesize, linesize, hostsize, numeric);
256 	else
257 		wtmp(file, namesize, linesize, hostsize, numeric);
258 #elif defined(SUPPORT_UTMPX)
259 	wtmpx(file, namesize, linesize, hostsize, numeric);
260 #elif defined(SUPPORT_UTMP)
261 	wtmp(file, namesize, linesize, hostsize, numeric);
262 #else
263 	errx(EXIT_FAILURE, "No utmp or utmpx support compiled in.");
264 #endif
265 	exit(EXIT_SUCCESS);
266 }
267 
268 
269 /*
270  * addarg --
271  *	add an entry to a linked list of arguments
272  */
273 static void
274 addarg(int type, const char *arg)
275 {
276 	ARG *cur;
277 
278 	if (!(cur = (ARG *)malloc(sizeof(ARG))))
279 		err(EXIT_FAILURE, "malloc failure");
280 	cur->next = arglist;
281 	cur->type = type;
282 	cur->name = arg;
283 	arglist = cur;
284 }
285 
286 /*
287  * addtty --
288  *	add an entry to a linked list of ttys
289  */
290 static TTY *
291 addtty(const char *tty)
292 {
293 	TTY *cur;
294 
295 	if (!(cur = (TTY *)malloc(sizeof(TTY))))
296 		err(EXIT_FAILURE, "malloc failure");
297 	cur->next = ttylist;
298 	cur->logout = currentout;
299 	memmove(cur->tty, tty, sizeof(cur->tty));
300 	return (ttylist = cur);
301 }
302 
303 /*
304  * hostconv --
305  *	convert the hostname to search pattern; if the supplied host name
306  *	has a domain attached that is the same as the current domain, rip
307  *	off the domain suffix since that's what login(1) does.
308  */
309 static void
310 hostconv(char *arg)
311 {
312 	static int first = 1;
313 	static char *hostdot, name[MAXHOSTNAMELEN + 1];
314 	char *argdot;
315 
316 	if (!(argdot = strchr(arg, '.')))
317 		return;
318 	if (first) {
319 		first = 0;
320 		if (gethostname(name, sizeof(name)))
321 			err(EXIT_FAILURE, "gethostname");
322 		name[sizeof(name) - 1] = '\0';
323 		hostdot = strchr(name, '.');
324 	}
325 	if (hostdot && !strcasecmp(hostdot, argdot))
326 		*argdot = '\0';
327 }
328 
329 /*
330  * ttyconv --
331  *	convert tty to correct name.
332  */
333 static const char *
334 ttyconv(char *arg)
335 {
336 	char *mval;
337 
338 	if (!strcmp(arg, "co"))
339 		return ("console");
340 	/*
341 	 * kludge -- we assume that all tty's end with
342 	 * a two character suffix.
343 	 */
344 	if (strlen(arg) == 2) {
345 		if (asprintf(&mval, "tty%s", arg) == -1)
346 			err(EXIT_FAILURE, "malloc failure");
347 		return (mval);
348 	}
349 	if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
350 		return (&arg[sizeof(_PATH_DEV) - 1]);
351 	return (arg);
352 }
353 
354 /*
355  * fmttime --
356  *	return pointer to (static) formatted time string.
357  */
358 static char *
359 fmttime(time_t t, int flags)
360 {
361 	struct tm *tm;
362 	static char tbuf[TBUFLEN];
363 
364 	tm = (flags & GMT) ? gmtime(&t) : localtime(&t);
365 	if (tm == NULL) {
366 		strcpy(tbuf, "????");
367 		return tbuf;
368 	}
369 	strftime(tbuf, sizeof(tbuf),
370 	    (flags & TIMEONLY)
371 	     ? (flags & FULLTIME ? LTFMTS : TFMTS)
372 	     : (flags & FULLTIME ? LTFMT : TFMT),
373 	    tm);
374 	return (tbuf);
375 }
376 
377 #ifdef SUPPORT_UTMP
378 #define TYPE(a)	0
379 #define NAMESIZE UT_NAMESIZE
380 #define LINESIZE UT_LINESIZE
381 #define HOSTSIZE UT_HOSTSIZE
382 #define ut_timefld ut_time
383 #define FIRSTVALID 0
384 #include "want.c"
385 #undef TYPE /*(a)*/
386 #undef NAMESIZE
387 #undef LINESIZE
388 #undef HOSTSIZE
389 #undef ut_timefld
390 #undef FIRSTVALID
391 #endif
392 
393 #ifdef SUPPORT_UTMPX
394 #define utmp utmpx
395 #define want wantx
396 #define wtmp wtmpx
397 #define gethost gethostx
398 #define buf bufx
399 #define onintr onintrx
400 #define TYPE(a) (a)->ut_type
401 #define NAMESIZE UTX_USERSIZE
402 #define LINESIZE UTX_LINESIZE
403 #define HOSTSIZE UTX_HOSTSIZE
404 #define ut_timefld ut_xtime
405 #define FIRSTVALID 1
406 #include "want.c"
407 #endif
408