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