No empty .Rs/.Re
[netbsd-mini2440.git] / usr.sbin / lastlogin / lastlogin.c
blobacccdda32a826d3642cfba01d434d8d2a0ff1523
1 /* $NetBSD: lastlogin.c,v 1.13 2005/04/09 02:13:20 atatat Exp $ */
2 /*
3 * Copyright (c) 1996 John M. Vinopal
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed for the NetBSD Project
17 * by John M. Vinopal.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include <sys/cdefs.h>
35 #ifndef lint
36 __RCSID("$NetBSD: lastlogin.c,v 1.13 2005/04/09 02:13:20 atatat Exp $");
37 #endif
39 #include <sys/types.h>
40 #include <err.h>
41 #include <db.h>
42 #include <errno.h>
43 #include <pwd.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <fcntl.h>
48 #include <time.h>
49 #include <arpa/inet.h>
50 #include <sys/socket.h>
51 #ifdef SUPPORT_UTMP
52 #include <utmp.h>
53 #endif
54 #ifdef SUPPORT_UTMPX
55 #include <utmpx.h>
56 #endif
57 #include <unistd.h>
58 #include <util.h>
60 struct output {
61 struct timeval o_tv;
62 struct sockaddr_storage o_ss;
63 char o_name[64];
64 char o_line[64];
65 char o_host[256];
66 struct output *next;
69 #define SORT_NONE 0x0000
70 #define SORT_REVERSE 0x0001
71 #define SORT_TIME 0x0002
72 #define DOSORT(x) ((x) & (SORT_TIME))
73 static int sortlog = SORT_NONE;
74 static struct output *outstack = NULL;
76 static int numeric = 0;
77 static size_t namelen = UT_NAMESIZE;
78 static size_t linelen = UT_LINESIZE;
79 static size_t hostlen = UT_HOSTSIZE;
81 int main(int, char **);
82 static int comparelog(const void *, const void *);
83 static void output(struct output *);
84 #ifdef SUPPORT_UTMP
85 static void process_entry(struct passwd *, struct lastlog *);
86 static void dolastlog(const char *, int, char *[]);
87 #endif
88 #ifdef SUPPORT_UTMPX
89 static void process_entryx(struct passwd *, struct lastlogx *);
90 static void dolastlogx(const char *, int, char *[]);
91 #endif
92 static void push(struct output *);
93 static const char *gethost(struct output *);
94 static void sortoutput(struct output *);
95 static void usage(void);
97 int
98 main(argc, argv)
99 int argc;
100 char *argv[];
102 const char *logfile =
103 #if defined(SUPPORT_UTMPX)
104 _PATH_LASTLOGX;
105 #elif defined(SUPPORT_UTMP)
106 _PATH_LASTLOG;
107 #else
108 #error "either SUPPORT_UTMP or SUPPORT_UTMPX must be defined"
109 #endif
110 int ch;
111 size_t len;
113 while ((ch = getopt(argc, argv, "f:H:L:nN:rt")) != -1) {
114 switch (ch) {
115 case 'H':
116 hostlen = atoi(optarg);
117 break;
118 case 'f':
119 logfile = optarg;
120 break;
121 case 'L':
122 linelen = atoi(optarg);
123 break;
124 case 'n':
125 numeric++;
126 break;
127 case 'N':
128 namelen = atoi(optarg);
129 break;
130 case 'r':
131 sortlog |= SORT_REVERSE;
132 break;
133 case 't':
134 sortlog |= SORT_TIME;
135 break;
136 default:
137 usage();
140 argc -= optind;
141 argv += optind;
143 len = strlen(logfile);
145 setpassent(1); /* Keep passwd file pointers open */
147 #if defined(SUPPORT_UTMPX)
148 if (len > 0 && logfile[len - 1] == 'x')
149 dolastlogx(logfile, argc, argv);
150 else
151 #endif
152 #if defined(SUPPORT_UTMP)
153 dolastlog(logfile, argc, argv);
154 #endif
156 setpassent(0); /* Close passwd file pointers */
158 if (outstack && DOSORT(sortlog))
159 sortoutput(outstack);
161 return 0;
164 #ifdef SUPPORT_UTMP
165 static void
166 dolastlog(const char *logfile, int argc, char **argv)
168 int i;
169 FILE *fp = fopen(logfile, "r");
170 struct passwd *passwd;
171 struct lastlog l;
173 if (fp == NULL)
174 err(1, "%s", logfile);
176 /* Process usernames given on the command line. */
177 if (argc > 0) {
178 off_t offset;
179 for (i = 0; i < argc; i++) {
180 if ((passwd = getpwnam(argv[i])) == NULL) {
181 warnx("user '%s' not found", argv[i]);
182 continue;
184 /* Calculate the offset into the lastlog file. */
185 offset = passwd->pw_uid * sizeof(l);
186 if (fseeko(fp, offset, SEEK_SET)) {
187 warn("fseek error");
188 continue;
190 if (fread(&l, sizeof(l), 1, fp) != 1) {
191 warnx("fread error on '%s'", passwd->pw_name);
192 clearerr(fp);
193 continue;
195 process_entry(passwd, &l);
198 /* Read all lastlog entries, looking for active ones */
199 else {
200 for (i = 0; fread(&l, sizeof(l), 1, fp) == 1; i++) {
201 if (l.ll_time == 0)
202 continue;
203 if ((passwd = getpwuid(i)) == NULL) {
204 static struct passwd p;
205 static char n[32];
206 snprintf(n, sizeof(n), "(%d)", i);
207 p.pw_uid = i;
208 p.pw_name = n;
209 passwd = &p;
211 process_entry(passwd, &l);
213 if (ferror(fp))
214 warnx("fread error");
217 (void)fclose(fp);
220 static void
221 process_entry(struct passwd *p, struct lastlog *l)
223 struct output o;
225 (void)strlcpy(o.o_name, p->pw_name, sizeof(o.o_name));
226 (void)strlcpy(o.o_line, l->ll_line, sizeof(l->ll_line));
227 (void)strlcpy(o.o_host, l->ll_host, sizeof(l->ll_host));
228 o.o_tv.tv_sec = l->ll_time;
229 o.o_tv.tv_usec = 0;
230 (void)memset(&o.o_ss, 0, sizeof(o.o_ss));
231 o.next = NULL;
234 * If we are sorting it, we need all the entries in memory so
235 * push the current entry onto a stack. Otherwise, we can just
236 * output it.
238 if (DOSORT(sortlog))
239 push(&o);
240 else
241 output(&o);
243 #endif
245 #ifdef SUPPORT_UTMPX
246 static void
247 dolastlogx(const char *logfile, int argc, char **argv)
249 int i = 0;
250 DB *db = dbopen(logfile, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL);
251 DBT key, data;
252 struct lastlogx l;
253 struct passwd *passwd;
255 if (db == NULL)
256 err(1, "%s", logfile);
258 if (argc > 0) {
259 for (i = 0; i < argc; i++) {
260 if ((passwd = getpwnam(argv[i])) == NULL) {
261 warnx("User `%s' not found", argv[i]);
262 continue;
264 key.data = &passwd->pw_uid;
265 key.size = sizeof(passwd->pw_uid);
267 switch ((*db->get)(db, &key, &data, 0)) {
268 case 0:
269 break;
270 case 1:
271 warnx("User `%s' not found", passwd->pw_name);
272 continue;
273 case -1:
274 warn("Error looking up `%s'", passwd->pw_name);
275 continue;
276 default:
277 abort();
280 if (data.size != sizeof(l)) {
281 errno = EFTYPE;
282 err(1, "%s", logfile);
284 (void)memcpy(&l, data.data, sizeof(l));
286 process_entryx(passwd, &l);
289 /* Read all lastlog entries, looking for active ones */
290 else {
291 switch ((*db->seq)(db, &key, &data, R_FIRST)) {
292 case 0:
293 break;
294 case 1:
295 warnx("No entries found");
296 (*db->close)(db);
297 return;
298 case -1:
299 warn("Error seeking to first entry");
300 (*db->close)(db);
301 return;
302 default:
303 abort();
306 do {
307 uid_t uid;
309 if (key.size != sizeof(uid) || data.size != sizeof(l)) {
310 errno = EFTYPE;
311 err(1, "%s", logfile);
313 (void)memcpy(&uid, key.data, sizeof(uid));
315 if ((passwd = getpwuid(uid)) == NULL) {
316 warnx("Cannot find user for uid %lu",
317 (unsigned long)uid);
318 continue;
320 (void)memcpy(&l, data.data, sizeof(l));
321 process_entryx(passwd, &l);
322 } while ((i = (*db->seq)(db, &key, &data, R_NEXT)) == 0);
324 switch (i) {
325 case 1:
326 break;
327 case -1:
328 warn("Error seeking to last entry");
329 break;
330 case 0:
331 default:
332 abort();
336 (*db->close)(db);
339 static void
340 process_entryx(struct passwd *p, struct lastlogx *l)
342 struct output o;
344 if (numeric > 1)
345 (void)snprintf(o.o_name, sizeof(o.o_name), "%d", p->pw_uid);
346 else
347 (void)strlcpy(o.o_name, p->pw_name, sizeof(o.o_name));
348 (void)strlcpy(o.o_line, l->ll_line, sizeof(l->ll_line));
349 (void)strlcpy(o.o_host, l->ll_host, sizeof(l->ll_host));
350 (void)memcpy(&o.o_ss, &l->ll_ss, sizeof(o.o_ss));
351 o.o_tv = l->ll_tv;
352 o.next = NULL;
355 * If we are sorting it, we need all the entries in memory so
356 * push the current entry onto a stack. Otherwise, we can just
357 * output it.
359 if (DOSORT(sortlog))
360 push(&o);
361 else
362 output(&o);
364 #endif
366 static void
367 push(struct output *o)
369 struct output *out;
371 out = malloc(sizeof(*out));
372 if (!out)
373 err(EXIT_FAILURE, "malloc failed");
374 (void)memcpy(out, o, sizeof(*out));
375 out->next = NULL;
377 if (outstack) {
378 out->next = outstack;
379 outstack = out;
380 } else {
381 outstack = out;
385 static void
386 sortoutput(struct output *o)
388 struct output **outs;
389 struct output *tmpo;
390 int num;
391 int i;
393 /* count the number of entries to display */
394 for (num=0, tmpo = o; tmpo; tmpo=tmpo->next, num++)
397 outs = malloc(sizeof(*outs) * num);
398 if (!outs)
399 err(EXIT_FAILURE, "malloc failed");
400 for (i=0, tmpo = o; i < num; tmpo=tmpo->next, i++)
401 outs[i] = tmpo;
403 mergesort(outs, num, sizeof(*outs), comparelog);
405 for (i=0; i < num; i++)
406 output(outs[i]);
409 static int
410 comparelog(const void *left, const void *right)
412 const struct output *l = *(const struct output * const *)left;
413 const struct output *r = *(const struct output * const *)right;
414 int order = (sortlog&SORT_REVERSE)?-1:1;
416 if (l->o_tv.tv_sec < r->o_tv.tv_sec)
417 return 1 * order;
418 if (l->o_tv.tv_sec == r->o_tv.tv_sec)
419 return 0;
420 return -1 * order;
423 static const char *
424 gethost(struct output *o)
426 if (!numeric)
427 return o->o_host;
428 else {
429 static char buf[512];
430 buf[0] = '\0';
431 (void)sockaddr_snprintf(buf, sizeof(buf), "%a",
432 (struct sockaddr *)&o->o_ss);
433 return buf;
437 /* Duplicate the output of last(1) */
438 static void
439 output(struct output *o)
441 time_t t = (time_t)o->o_tv.tv_sec;
442 printf("%-*.*s %-*.*s %-*.*s %s",
443 (int)namelen, (int)namelen, o->o_name,
444 (int)linelen, (int)linelen, o->o_line,
445 (int)hostlen, (int)hostlen, gethost(o),
446 t ? ctime(&t) : "Never logged in\n");
449 static void
450 usage(void)
452 (void)fprintf(stderr, "Usage: %s [-nrt] [-f <filename>] "
453 "[-H <hostlen>] [-L <linelen>] [-N <namelen>] [user ...]\n",
454 getprogname());
455 exit(1);