who/utmpentry: Clean up a bit.
[dragonfly.git] / usr.bin / who / utmpentry.c
1 /*      $NetBSD: utmpentry.c,v 1.16 2008/10/28 14:01:46 christos Exp $  */
2
3 /*-
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 #include <sys/stat.h>
34
35 #include <err.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40
41 #ifdef SUPPORT_UTMP
42 #include <utmp.h>
43 #endif
44 #ifdef SUPPORT_UTMPX
45 #include <utmpx.h>
46 #endif
47
48 #include "utmpentry.h"
49
50 /* Operations on timespecs. */
51 #define timespecclear(tsp)      (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)
52 #define timespecisset(tsp)      ((tsp)->tv_sec || (tsp)->tv_nsec)
53 #define timespeccmp(tsp, usp, cmp)                                      \
54         (((tsp)->tv_sec == (usp)->tv_sec) ?                             \
55             ((tsp)->tv_nsec cmp (usp)->tv_nsec) :                       \
56             ((tsp)->tv_sec cmp (usp)->tv_sec))
57 #define timespecadd(tsp, usp, vsp)                                      \
58         do {                                                            \
59                 (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec;          \
60                 (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec;       \
61                 if ((vsp)->tv_nsec >= 1000000000L) {                    \
62                         (vsp)->tv_sec++;                                \
63                         (vsp)->tv_nsec -= 1000000000L;                  \
64                 }                                                       \
65         } while (/* CONSTCOND */ 0)
66 #define timespecsub(tsp, usp, vsp)                                      \
67         do {                                                            \
68                 (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec;          \
69                 (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec;       \
70                 if ((vsp)->tv_nsec < 0) {                               \
71                         (vsp)->tv_sec--;                                \
72                         (vsp)->tv_nsec += 1000000000L;                  \
73                 }                                                       \
74         } while (/* CONSTCOND */ 0)
75 #define timespec2ns(x) (((uint64_t)(x)->tv_sec) * 1000000000L + (x)->tv_nsec)
76
77
78 #define COMPILE_ASSERT(x)       _Static_assert(x, "assertion failed")
79
80
81 #ifdef SUPPORT_UTMP
82 static void getentry(struct utmpentry *, struct utmp *);
83 static struct timespec utmptime = {0, 0};
84 #endif
85 #ifdef SUPPORT_UTMPX
86 static void getentryx(struct utmpentry *, struct utmpx *);
87 static struct timespec utmpxtime = {0, 0};
88 #endif
89 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
90 static int setup(const char *);
91 static void adjust_size(struct utmpentry *e);
92 #endif
93
94 int maxname = 8, maxline = 8, maxhost = 16;
95 int etype = 1 << USER_PROCESS;
96 static int numutmp = 0;
97 static struct utmpentry *ehead;
98
99 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
100 static void
101 adjust_size(struct utmpentry *e)
102 {
103         int max;
104
105         if ((max = strlen(e->name)) > maxname)
106                 maxname = max;
107         if ((max = strlen(e->line)) > maxline)
108                 maxline = max;
109         if ((max = strlen(e->host)) > maxhost)
110                 maxhost = max;
111 }
112
113 static int
114 setup(const char *fname)
115 {
116         int what = 3;
117         struct stat st;
118         const char *sfname;
119
120         if (fname == NULL) {
121 #ifdef SUPPORT_UTMPX
122                 setutxent();
123 #endif
124 #ifdef SUPPORT_UTMP
125                 setutent();
126 #endif
127         } else {
128                 size_t len = strlen(fname);
129                 if (len == 0)
130                         errx(1, "Filename cannot be 0 length.");
131                 what = fname[len - 1] == 'x' ? 1 : 2;
132                 if (what == 1) {
133 #ifdef SUPPORT_UTMPX
134                         if (utmpxname(fname) == 0)
135                                 warnx("Cannot set utmpx file to `%s'",
136                                     fname);
137 #else
138                         warnx("utmpx support not compiled in");
139 #endif
140                 } else {
141 #ifdef SUPPORT_UTMP
142                         if (utmpname(fname) == 0)
143                                 warnx("Cannot set utmp file to `%s'",
144                                     fname);
145 #else
146                         warnx("utmp support not compiled in");
147 #endif
148                 }
149         }
150 #ifdef SUPPORT_UTMPX
151         if (what & 1) {
152                 sfname = fname ? fname : _PATH_UTMPX;
153                 if (stat(sfname, &st) == -1) {
154                         warn("Cannot stat `%s'", sfname);
155                         what &= ~1;
156                 } else {
157                         if (timespeccmp(&st.st_mtimespec, &utmpxtime, >))
158                             utmpxtime = st.st_mtimespec;
159                         else
160                             what &= ~1;
161                 }
162         }
163 #endif
164 #ifdef SUPPORT_UTMP
165         if (what & 2) {
166                 sfname = fname ? fname : _PATH_UTMP;
167                 if (stat(sfname, &st) == -1) {
168                         warn("Cannot stat `%s'", sfname);
169                         what &= ~2;
170                 } else {
171                         if (timespeccmp(&st.st_mtimespec, &utmptime, >))
172                                 utmptime = st.st_mtimespec;
173                         else
174                                 what &= ~2;
175                 }
176         }
177 #endif
178         return what;
179 }
180 #endif
181
182 void
183 endutentries(void)
184 {
185         struct utmpentry *ep;
186
187 #ifdef SUPPORT_UTMP
188         timespecclear(&utmptime);
189 #endif
190 #ifdef SUPPORT_UTMPX
191         timespecclear(&utmpxtime);
192 #endif
193         ep = ehead;
194         while (ep) {
195                 struct utmpentry *sep = ep;
196                 ep = ep->next;
197                 free(sep);
198         }
199         ehead = NULL;
200         numutmp = 0;
201 }
202
203 int
204 getutentries(const char *fname, struct utmpentry **epp)
205 {
206 #ifdef SUPPORT_UTMPX
207         struct utmpx *utx;
208 #endif
209 #ifdef SUPPORT_UTMP
210         struct utmp *ut;
211 #endif
212 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
213         struct utmpentry *ep;
214         int what = setup(fname);
215         struct utmpentry **nextp = &ehead;
216         switch (what) {
217         case 0:
218                 /* No updates */
219                 *epp = ehead;
220                 return numutmp;
221         default:
222                 /* Need to re-scan */
223                 ehead = NULL;
224                 numutmp = 0;
225         }
226 #endif
227
228 #ifdef SUPPORT_UTMPX
229         while ((what & 1) && (utx = getutxent()) != NULL) {
230                 if (fname == NULL && ((1 << utx->ut_type) & etype) == 0) {
231                         continue;
232                 }
233                 if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) {
234                         warn(NULL);
235                         return 0;
236                 }
237                 getentryx(ep, utx);
238                 *nextp = ep;
239                 nextp = &(ep->next);
240         }
241 #endif
242
243 #ifdef SUPPORT_UTMP
244         if ((etype & (1 << USER_PROCESS)) != 0) {
245                 while ((what & 2) && (ut = getutent()) != NULL) {
246                         if (fname == NULL && (*ut->ut_name == '\0' ||
247                             *ut->ut_line == '\0'))
248                                 continue;
249                         /* Don't process entries that we have utmpx for */
250                         for (ep = ehead; ep != NULL; ep = ep->next) {
251                                 if (strncmp(ep->line, ut->ut_line,
252                                     sizeof(ut->ut_line)) == 0)
253                                         break;
254                         }
255                         if (ep != NULL)
256                                 continue;
257                         if ((ep = calloc(1, sizeof(*ep))) == NULL) {
258                                 warn(NULL);
259                                 return 0;
260                         }
261                         getentry(ep, ut);
262                         *nextp = ep;
263                         nextp = &(ep->next);
264                 }
265         }
266 #endif
267         numutmp = 0;
268 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
269         if (ehead != NULL) {
270                 struct utmpentry *from = ehead, *save;
271                 
272                 ehead = NULL;
273                 while (from != NULL) {
274                         for (nextp = &ehead;
275                             (*nextp) && strcmp(from->line, (*nextp)->line) > 0;
276                             nextp = &(*nextp)->next)
277                                 continue;
278                         save = from;
279                         from = from->next;
280                         save->next = *nextp;
281                         *nextp = save;
282                         numutmp++;
283                 }
284         }
285         *epp = ehead;
286         return numutmp;
287 #else
288         *epp = NULL;
289         return 0;
290 #endif
291 }
292
293 #ifdef SUPPORT_UTMP
294 static void
295 getentry(struct utmpentry *e, struct utmp *up)
296 {
297 #if 1
298         COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
299         COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
300         COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
301 #endif
302
303         /*
304          * e has just been calloc'd. We don't need to clear it or
305          * append null-terminators, because its length is strictly
306          * greater than the source string. Use strncpy to _read_
307          * up->ut_* because they may not be terminated. For this
308          * reason we use the size of the _source_ as the length
309          * argument.
310          */
311
312         snprintf(e->name, sizeof(e->name), "%.*s",
313                  (int)sizeof(up->ut_name), up->ut_name);
314         snprintf(e->line, sizeof(e->line), "%.*s",
315                  (int)sizeof(up->ut_line), up->ut_line);
316         snprintf(e->host, sizeof(e->host), "%.*s",
317                  (int)sizeof(up->ut_host), up->ut_host);
318
319         e->tv.tv_sec = up->ut_time;
320         e->tv.tv_usec = 0;
321         e->pid = 0;
322         e->term = 0;
323         e->exit = 0;
324         e->sess = 0;
325         e->type = USER_PROCESS;
326         adjust_size(e);
327 }
328 #endif
329
330 #ifdef SUPPORT_UTMPX
331 static void
332 getentryx(struct utmpentry *e, struct utmpx *up)
333 {
334         COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
335         COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
336         COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
337
338         /*
339          * e has just been calloc'd. We don't need to clear it or
340          * append null-terminators, because its length is strictly
341          * greater than the source string. Use strncpy to _read_
342          * up->ut_* because they may not be terminated. For this
343          * reason we use the size of the _source_ as the length
344          * argument.
345          */
346         snprintf(e->name, sizeof(e->name), "%.*s",
347                  (int)sizeof(up->ut_name), up->ut_name);
348         snprintf(e->line, sizeof(e->line), "%.*s",
349                  (int)sizeof(up->ut_line), up->ut_line);
350         snprintf(e->host, sizeof(e->host), "%.*s",
351                  (int)sizeof(up->ut_host), up->ut_host);
352
353         e->tv = up->ut_tv;
354         e->pid = up->ut_pid;
355         e->term = up->ut_exit.e_termination;
356         e->exit = up->ut_exit.e_exit;
357         e->sess = up->ut_session;
358         e->type = up->ut_type;
359         adjust_size(e);
360 }
361 #endif