lastlogin.c revision 1.8 1 /* $NetBSD: lastlogin.c,v 1.8 2003/12/16 15:40:29 wulf Exp $ */
2 /*
3 * Copyright (c) 1996 John M. Vinopal
4 * All rights reserved.
5 *
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.
20 *
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.
32 */
33
34 #include <sys/cdefs.h>
35 #ifndef lint
36 __RCSID("$NetBSD: lastlogin.c,v 1.8 2003/12/16 15:40:29 wulf Exp $");
37 #endif
38
39 #include <sys/types.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <pwd.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <time.h>
47 #include <utmp.h>
48 #include <unistd.h>
49
50 struct output {
51 char o_name[UT_NAMESIZE];
52 char o_line[UT_LINESIZE];
53 char o_host[UT_HOSTSIZE];
54 time_t o_time;
55 struct output *next;
56 };
57
58 static char *logfile = _PATH_LASTLOG;
59
60 #define SORT_NONE 0x0000
61 #define SORT_REVERSE 0x0001
62 #define SORT_TIME 0x0002
63 #define DOSORT(x) ((x) & (SORT_TIME))
64 static int sortlog = SORT_NONE;
65 static struct output *outstack = NULL;
66
67 int main __P((int, char **));
68 static int comparelog __P((const void *, const void *));
69 static void output __P((struct output *));
70 static void process_entry __P((struct passwd *, struct lastlog *));
71 static void push __P((struct output *));
72 static void sortoutput __P((struct output *));
73 static void usage __P((void));
74
75 int
76 main(argc, argv)
77 int argc;
78 char *argv[];
79 {
80 int ch, i;
81 FILE *fp;
82 struct passwd *passwd;
83 struct lastlog last;
84
85 while ((ch = getopt(argc, argv, "rt")) != -1) {
86 switch (ch) {
87 case 'r':
88 sortlog |= SORT_REVERSE;
89 break;
90 case 't':
91 sortlog |= SORT_TIME;
92 break;
93 default:
94 usage();
95 }
96 }
97 argc -= optind;
98 argv += optind;
99
100 fp = fopen(logfile, "r");
101 if (fp == NULL)
102 err(1, "%s", logfile);
103
104 setpassent(1); /* Keep passwd file pointers open */
105
106 /* Process usernames given on the command line. */
107 if (argc > 0) {
108 long offset;
109 for (i = 0; i < argc; i++) {
110 if ((passwd = getpwnam(argv[i])) == NULL) {
111 warnx("user '%s' not found", argv[i]);
112 continue;
113 }
114 /* Calculate the offset into the lastlog file. */
115 offset = (long)(passwd->pw_uid * sizeof(last));
116 if (fseek(fp, offset, SEEK_SET)) {
117 warn("fseek error");
118 continue;
119 }
120 if (fread(&last, sizeof(last), 1, fp) != 1) {
121 warnx("fread error on '%s'", passwd->pw_name);
122 clearerr(fp);
123 continue;
124 }
125 process_entry(passwd, &last);
126 }
127 }
128 /* Read all lastlog entries, looking for active ones */
129 else {
130 for (i = 0; fread(&last, sizeof(last), 1, fp) == 1; i++) {
131 if (last.ll_time == 0)
132 continue;
133 if ((passwd = getpwuid(i)) != NULL)
134 process_entry(passwd, &last);
135 }
136 if (ferror(fp))
137 warnx("fread error");
138 }
139
140 setpassent(0); /* Close passwd file pointers */
141
142 if (DOSORT(sortlog))
143 sortoutput(outstack);
144
145 fclose(fp);
146 exit(0);
147 }
148
149 static void
150 process_entry(struct passwd *p, struct lastlog *l)
151 {
152 struct output o;
153
154 strncpy(o.o_name, p->pw_name, UT_NAMESIZE);
155 strncpy(o.o_line, l->ll_line, UT_LINESIZE);
156 strncpy(o.o_host, l->ll_host, UT_HOSTSIZE);
157 o.o_time = l->ll_time;
158 o.next = NULL;
159
160 /*
161 * If we are sorting it, we need all the entries in memory so
162 * push the current entry onto a stack. Otherwise, we can just
163 * output it.
164 */
165 if (DOSORT(sortlog))
166 push(&o);
167 else
168 output(&o);
169 }
170
171 static void
172 push(struct output *o)
173 {
174 struct output *out;
175
176 out = malloc(sizeof(*out));
177 if (!out)
178 err(EXIT_FAILURE, "malloc failed");
179 memcpy(out, o, sizeof(*out));
180 out->next = NULL;
181
182 if (outstack) {
183 out->next = outstack;
184 outstack = out;
185 } else {
186 outstack = out;
187 }
188 }
189
190 static void
191 sortoutput(struct output *o)
192 {
193 struct output **outs;
194 struct output *tmpo;
195 int num;
196 int i;
197
198 /* count the number of entries to display */
199 for (num=0, tmpo = o; tmpo; tmpo=tmpo->next, num++)
200 ;
201
202 outs = malloc(sizeof(*outs) * num);
203 if (!outs)
204 err(EXIT_FAILURE, "malloc failed");
205 for (i=0, tmpo = o; i < num; tmpo=tmpo->next, i++)
206 outs[i] = tmpo;
207
208 mergesort(outs, num, sizeof(*outs), comparelog);
209
210 for (i=0; i < num; i++)
211 output(outs[i]);
212 }
213
214 static int
215 comparelog(const void *left, const void *right)
216 {
217 struct output *l = *(struct output **)left;
218 struct output *r = *(struct output **)right;
219 int order = (sortlog&SORT_REVERSE)?-1:1;
220
221 if (l->o_time < r->o_time)
222 return 1 * order;
223 if (l->o_time == r->o_time)
224 return 0;
225 return -1 * order;
226 }
227
228 /* Duplicate the output of last(1) */
229 static void
230 output(struct output *o)
231 {
232
233 printf("%-*.*s %-*.*s %-*.*s %s",
234 UT_NAMESIZE, UT_NAMESIZE, o->o_name,
235 UT_LINESIZE, UT_LINESIZE, o->o_line,
236 UT_HOSTSIZE, UT_HOSTSIZE, o->o_host,
237 (o->o_time) ? ctime(&(o->o_time)) : "Never logged in\n");
238 }
239
240 static void
241 usage()
242 {
243 fprintf(stderr, "usage: %s [-rt] [user ...]\n", getprogname());
244 exit(1);
245 }
246