1 /* $NetBSD: lastlogin.c,v 1.13 2005/04/09 02:13:20 atatat Exp $ */
3 * Copyright (c) 1996 John M. Vinopal
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
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
34 #include <sys/cdefs.h>
36 __RCSID("$NetBSD: lastlogin.c,v 1.13 2005/04/09 02:13:20 atatat Exp $");
39 #include <sys/types.h>
49 #include <arpa/inet.h>
50 #include <sys/socket.h>
62 struct sockaddr_storage o_ss
;
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
*);
85 static void process_entry(struct passwd
*, struct lastlog
*);
86 static void dolastlog(const char *, int, char *[]);
89 static void process_entryx(struct passwd
*, struct lastlogx
*);
90 static void dolastlogx(const char *, int, char *[]);
92 static void push(struct output
*);
93 static const char *gethost(struct output
*);
94 static void sortoutput(struct output
*);
95 static void usage(void);
102 const char *logfile
=
103 #if defined(SUPPORT_UTMPX)
105 #elif defined(SUPPORT_UTMP)
108 #error "either SUPPORT_UTMP or SUPPORT_UTMPX must be defined"
113 while ((ch
= getopt(argc
, argv
, "f:H:L:nN:rt")) != -1) {
116 hostlen
= atoi(optarg
);
122 linelen
= atoi(optarg
);
128 namelen
= atoi(optarg
);
131 sortlog
|= SORT_REVERSE
;
134 sortlog
|= SORT_TIME
;
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
);
152 #if defined(SUPPORT_UTMP)
153 dolastlog(logfile
, argc
, argv
);
156 setpassent(0); /* Close passwd file pointers */
158 if (outstack
&& DOSORT(sortlog
))
159 sortoutput(outstack
);
166 dolastlog(const char *logfile
, int argc
, char **argv
)
169 FILE *fp
= fopen(logfile
, "r");
170 struct passwd
*passwd
;
174 err(1, "%s", logfile
);
176 /* Process usernames given on the command line. */
179 for (i
= 0; i
< argc
; i
++) {
180 if ((passwd
= getpwnam(argv
[i
])) == NULL
) {
181 warnx("user '%s' not found", argv
[i
]);
184 /* Calculate the offset into the lastlog file. */
185 offset
= passwd
->pw_uid
* sizeof(l
);
186 if (fseeko(fp
, offset
, SEEK_SET
)) {
190 if (fread(&l
, sizeof(l
), 1, fp
) != 1) {
191 warnx("fread error on '%s'", passwd
->pw_name
);
195 process_entry(passwd
, &l
);
198 /* Read all lastlog entries, looking for active ones */
200 for (i
= 0; fread(&l
, sizeof(l
), 1, fp
) == 1; i
++) {
203 if ((passwd
= getpwuid(i
)) == NULL
) {
204 static struct passwd p
;
206 snprintf(n
, sizeof(n
), "(%d)", i
);
211 process_entry(passwd
, &l
);
214 warnx("fread error");
221 process_entry(struct passwd
*p
, struct lastlog
*l
)
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
;
230 (void)memset(&o
.o_ss
, 0, sizeof(o
.o_ss
));
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
247 dolastlogx(const char *logfile
, int argc
, char **argv
)
250 DB
*db
= dbopen(logfile
, O_RDONLY
|O_SHLOCK
, 0, DB_HASH
, NULL
);
253 struct passwd
*passwd
;
256 err(1, "%s", logfile
);
259 for (i
= 0; i
< argc
; i
++) {
260 if ((passwd
= getpwnam(argv
[i
])) == NULL
) {
261 warnx("User `%s' not found", argv
[i
]);
264 key
.data
= &passwd
->pw_uid
;
265 key
.size
= sizeof(passwd
->pw_uid
);
267 switch ((*db
->get
)(db
, &key
, &data
, 0)) {
271 warnx("User `%s' not found", passwd
->pw_name
);
274 warn("Error looking up `%s'", passwd
->pw_name
);
280 if (data
.size
!= sizeof(l
)) {
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 */
291 switch ((*db
->seq
)(db
, &key
, &data
, R_FIRST
)) {
295 warnx("No entries found");
299 warn("Error seeking to first entry");
309 if (key
.size
!= sizeof(uid
) || data
.size
!= sizeof(l
)) {
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",
320 (void)memcpy(&l
, data
.data
, sizeof(l
));
321 process_entryx(passwd
, &l
);
322 } while ((i
= (*db
->seq
)(db
, &key
, &data
, R_NEXT
)) == 0);
328 warn("Error seeking to last entry");
340 process_entryx(struct passwd
*p
, struct lastlogx
*l
)
345 (void)snprintf(o
.o_name
, sizeof(o
.o_name
), "%d", p
->pw_uid
);
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
));
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
367 push(struct output
*o
)
371 out
= malloc(sizeof(*out
));
373 err(EXIT_FAILURE
, "malloc failed");
374 (void)memcpy(out
, o
, sizeof(*out
));
378 out
->next
= outstack
;
386 sortoutput(struct output
*o
)
388 struct output
**outs
;
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
);
399 err(EXIT_FAILURE
, "malloc failed");
400 for (i
=0, tmpo
= o
; i
< num
; tmpo
=tmpo
->next
, i
++)
403 mergesort(outs
, num
, sizeof(*outs
), comparelog
);
405 for (i
=0; i
< num
; i
++)
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
)
418 if (l
->o_tv
.tv_sec
== r
->o_tv
.tv_sec
)
424 gethost(struct output
*o
)
429 static char buf
[512];
431 (void)sockaddr_snprintf(buf
, sizeof(buf
), "%a",
432 (struct sockaddr
*)&o
->o_ss
);
437 /* Duplicate the output of last(1) */
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");
452 (void)fprintf(stderr
, "Usage: %s [-nrt] [-f <filename>] "
453 "[-H <hostlen>] [-L <linelen>] [-N <namelen>] [user ...]\n",