a6427fbea3a111f85bd25a5dce1bb5166c66d802
[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 #include <utmpx.h>
41
42 #include "utmpentry.h"
43
44 /* Operations on timespecs. */
45 #define timespecclear(tsp)      (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)
46 #define timespecisset(tsp)      ((tsp)->tv_sec || (tsp)->tv_nsec)
47 #define timespeccmp(tsp, usp, cmp)                                      \
48         (((tsp)->tv_sec == (usp)->tv_sec) ?                             \
49             ((tsp)->tv_nsec cmp (usp)->tv_nsec) :                       \
50             ((tsp)->tv_sec cmp (usp)->tv_sec))
51 #define timespecadd(tsp, usp, vsp)                                      \
52         do {                                                            \
53                 (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec;          \
54                 (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec;       \
55                 if ((vsp)->tv_nsec >= 1000000000L) {                    \
56                         (vsp)->tv_sec++;                                \
57                         (vsp)->tv_nsec -= 1000000000L;                  \
58                 }                                                       \
59         } while (/* CONSTCOND */ 0)
60 #define timespecsub(tsp, usp, vsp)                                      \
61         do {                                                            \
62                 (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec;          \
63                 (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec;       \
64                 if ((vsp)->tv_nsec < 0) {                               \
65                         (vsp)->tv_sec--;                                \
66                         (vsp)->tv_nsec += 1000000000L;                  \
67                 }                                                       \
68         } while (/* CONSTCOND */ 0)
69 #define timespec2ns(x) (((uint64_t)(x)->tv_sec) * 1000000000L + (x)->tv_nsec)
70
71
72 #define COMPILE_ASSERT(x)       _Static_assert(x, "assertion failed")
73
74
75 static void getentryx(struct utmpentry *, struct utmpx *);
76 static struct timespec utmpxtime = {0, 0};
77 static int setup(const char *);
78 static void adjust_size(struct utmpentry *e);
79
80 int maxname = 8, maxline = 8, maxhost = 16;
81 int etype = 1 << USER_PROCESS;
82 static int numutmp = 0;
83 static struct utmpentry *ehead;
84
85 static void
86 adjust_size(struct utmpentry *e)
87 {
88         int max;
89
90         if ((max = strlen(e->name)) > maxname)
91                 maxname = max;
92         if ((max = strlen(e->line)) > maxline)
93                 maxline = max;
94         if ((max = strlen(e->host)) > maxhost)
95                 maxhost = max;
96 }
97
98 static int
99 setup(const char *fname)
100 {
101         int what = 3;
102         struct stat st;
103         const char *sfname;
104
105         if (fname == NULL) {
106                 setutxent();
107         } else {
108                 size_t len = strlen(fname);
109                 if (len == 0)
110                         errx(1, "Filename cannot be 0 length.");
111                 what = fname[len - 1] == 'x' ? 1 : 2;
112                 if (what == 1) {
113                         if (utmpxname(fname) == 0)
114                                 warnx("Cannot set utmpx file to `%s'",
115                                     fname);
116                 }
117         }
118         if (what & 1) {
119                 sfname = fname ? fname : _PATH_UTMPX;
120                 if (stat(sfname, &st) == -1) {
121                         warn("Cannot stat `%s'", sfname);
122                         what &= ~1;
123                 } else {
124                         if (timespeccmp(&st.st_mtimespec, &utmpxtime, >))
125                             utmpxtime = st.st_mtimespec;
126                         else
127                             what &= ~1;
128                 }
129         }
130         return what;
131 }
132
133 void
134 endutentries(void)
135 {
136         struct utmpentry *ep;
137
138         timespecclear(&utmpxtime);
139         ep = ehead;
140         while (ep) {
141                 struct utmpentry *sep = ep;
142                 ep = ep->next;
143                 free(sep);
144         }
145         ehead = NULL;
146         numutmp = 0;
147 }
148
149 int
150 getutentries(const char *fname, struct utmpentry **epp)
151 {
152         struct utmpx *utx;
153         struct utmpentry *ep;
154         int what = setup(fname);
155         struct utmpentry **nextp = &ehead;
156         switch (what) {
157         case 0:
158                 /* No updates */
159                 *epp = ehead;
160                 return numutmp;
161         default:
162                 /* Need to re-scan */
163                 ehead = NULL;
164                 numutmp = 0;
165         }
166
167         while ((what & 1) && (utx = getutxent()) != NULL) {
168                 if (fname == NULL && ((1 << utx->ut_type) & etype) == 0) {
169                         continue;
170                 }
171                 if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) {
172                         warn(NULL);
173                         return 0;
174                 }
175                 getentryx(ep, utx);
176                 *nextp = ep;
177                 nextp = &(ep->next);
178         }
179
180         numutmp = 0;
181         if (ehead != NULL) {
182                 struct utmpentry *from = ehead, *save;
183                 
184                 ehead = NULL;
185                 while (from != NULL) {
186                         for (nextp = &ehead;
187                             (*nextp) && strcmp(from->line, (*nextp)->line) > 0;
188                             nextp = &(*nextp)->next)
189                                 continue;
190                         save = from;
191                         from = from->next;
192                         save->next = *nextp;
193                         *nextp = save;
194                         numutmp++;
195                 }
196         }
197         *epp = ehead;
198         return numutmp;
199 }
200
201 static void
202 getentryx(struct utmpentry *e, struct utmpx *up)
203 {
204         COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
205         COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
206         COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
207
208         /*
209          * e has just been calloc'd. We don't need to clear it or
210          * append null-terminators, because its length is strictly
211          * greater than the source string. Use strncpy to _read_
212          * up->ut_* because they may not be terminated. For this
213          * reason we use the size of the _source_ as the length
214          * argument.
215          */
216         snprintf(e->name, sizeof(e->name), "%.*s",
217                  (int)sizeof(up->ut_name), up->ut_name);
218         snprintf(e->line, sizeof(e->line), "%.*s",
219                  (int)sizeof(up->ut_line), up->ut_line);
220         snprintf(e->host, sizeof(e->host), "%.*s",
221                  (int)sizeof(up->ut_host), up->ut_host);
222
223         e->tv = up->ut_tv;
224         e->pid = up->ut_pid;
225         e->term = up->ut_exit.e_termination;
226         e->exit = up->ut_exit.e_exit;
227         e->sess = up->ut_session;
228         e->type = up->ut_type;
229         adjust_size(e);
230 }