xref: /dflybsd-src/usr.bin/who/utmpentry.c (revision b12628f04fa083a7d66f4761e91a62cbd96c59db)
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
362