xref: /minix3/usr.bin/who/utmpentry.c (revision b6d4a4c155b8d689603434c3da8b55571af40e3b)
1 /*	$NetBSD: utmpentry.c,v 1.17 2009/05/01 14:26:10 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 #ifndef lint
34 __RCSID("$NetBSD: utmpentry.c,v 1.17 2009/05/01 14:26:10 christos Exp $");
35 #endif
36 
37 #include <sys/stat.h>
38 
39 #include <time.h>
40 #include <string.h>
41 #include <err.h>
42 #include <stdlib.h>
43 
44 #ifdef SUPPORT_UTMP
45 #include <utmp.h>
46 #endif
47 #ifdef SUPPORT_UTMPX
48 #include <utmpx.h>
49 #endif
50 
51 #include "utmpentry.h"
52 
53 
54 /* Fail the compile if x is not true, by constructing an illegal type. */
55 #define COMPILE_ASSERT(x) /*LINTED null effect */ \
56 	((void)sizeof(struct { unsigned : ((x) ? 1 : -1); }))
57 
58 
59 #ifdef SUPPORT_UTMP
60 static void getentry(struct utmpentry *, struct utmp *);
61 static struct timespec utmptime = {0, 0};
62 #endif
63 #ifdef SUPPORT_UTMPX
64 static void getentryx(struct utmpentry *, struct utmpx *);
65 static struct timespec utmpxtime = {0, 0};
66 #endif
67 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
68 static int setup(const char *);
69 static void adjust_size(struct utmpentry *e);
70 #endif
71 
72 int maxname = 8, maxline = 8, maxhost = 16;
73 int etype = 1 << USER_PROCESS;
74 static int numutmp = 0;
75 static struct utmpentry *ehead;
76 
77 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
78 static void
adjust_size(struct utmpentry * e)79 adjust_size(struct utmpentry *e)
80 {
81 	int max;
82 
83 	if ((max = strlen(e->name)) > maxname)
84 		maxname = max;
85 	if ((max = strlen(e->line)) > maxline)
86 		maxline = max;
87 	if ((max = strlen(e->host)) > maxhost)
88 		maxhost = max;
89 }
90 
91 static int
setup(const char * fname)92 setup(const char *fname)
93 {
94 	int what = 3;
95 	struct stat st;
96 	const char *sfname;
97 
98 	if (fname == NULL) {
99 #ifdef SUPPORT_UTMPX
100 		setutxent();
101 #endif
102 #ifdef SUPPORT_UTMP
103 		setutent();
104 #endif
105 	} else {
106 		size_t len = strlen(fname);
107 		if (len == 0)
108 			errx(1, "Filename cannot be 0 length.");
109 		what = fname[len - 1] == 'x' ? 1 : 2;
110 		if (what == 1) {
111 #ifdef SUPPORT_UTMPX
112 			if (utmpxname(fname) == 0)
113 				warnx("Cannot set utmpx file to `%s'",
114 				    fname);
115 #else
116 			warnx("utmpx support not compiled in");
117 #endif
118 		} else {
119 #ifdef SUPPORT_UTMP
120 			if (utmpname(fname) == 0)
121 				warnx("Cannot set utmp file to `%s'",
122 				    fname);
123 #else
124 			warnx("utmp support not compiled in");
125 #endif
126 		}
127 	}
128 #ifdef SUPPORT_UTMPX
129 	if (what & 1) {
130 		sfname = fname ? fname : _PATH_UTMPX;
131 		if (stat(sfname, &st) == -1) {
132 			warn("Cannot stat `%s'", sfname);
133 			what &= ~1;
134 		} else {
135 			if (timespeccmp(&st.st_mtimespec, &utmpxtime, >))
136 			    utmpxtime = st.st_mtimespec;
137 			else
138 			    what &= ~1;
139 		}
140 	}
141 #endif
142 #ifdef SUPPORT_UTMP
143 	if (what & 2) {
144 		sfname = fname ? fname : _PATH_UTMP;
145 		if (stat(sfname, &st) == -1) {
146 			warn("Cannot stat `%s'", sfname);
147 			what &= ~2;
148 		} else {
149 			if (timespeccmp(&st.st_mtimespec, &utmptime, >))
150 				utmptime = st.st_mtimespec;
151 			else
152 				what &= ~2;
153 		}
154 	}
155 #endif
156 	return what;
157 }
158 #endif
159 
160 void
endutentries(void)161 endutentries(void)
162 {
163 	struct utmpentry *ep;
164 
165 #ifdef SUPPORT_UTMP
166 	timespecclear(&utmptime);
167 #endif
168 #ifdef SUPPORT_UTMPX
169 	timespecclear(&utmpxtime);
170 #endif
171 	ep = ehead;
172 	while (ep) {
173 		struct utmpentry *sep = ep;
174 		ep = ep->next;
175 		free(sep);
176 	}
177 	ehead = NULL;
178 	numutmp = 0;
179 }
180 
181 int
getutentries(const char * fname,struct utmpentry ** epp)182 getutentries(const char *fname, struct utmpentry **epp)
183 {
184 #ifdef SUPPORT_UTMPX
185 	struct utmpx *utx;
186 #endif
187 #ifdef SUPPORT_UTMP
188 	struct utmp *ut;
189 #endif
190 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
191 	struct utmpentry *ep;
192 	int what = setup(fname);
193 	struct utmpentry **nextp = &ehead;
194 	switch (what) {
195 	case 0:
196 		/* No updates */
197 		*epp = ehead;
198 		return numutmp;
199 	default:
200 		/* Need to re-scan */
201 		ehead = NULL;
202 		numutmp = 0;
203 	}
204 #endif
205 
206 #ifdef SUPPORT_UTMPX
207 	while ((what & 1) && (utx = getutxent()) != NULL) {
208 		if (fname == NULL && ((1 << utx->ut_type) & etype) == 0)
209 			continue;
210 		if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) {
211 			warn(NULL);
212 			return 0;
213 		}
214 		getentryx(ep, utx);
215 		*nextp = ep;
216 		nextp = &(ep->next);
217 	}
218 #endif
219 
220 #ifdef SUPPORT_UTMP
221 	if ((etype & (1 << USER_PROCESS)) != 0) {
222 		while ((what & 2) && (ut = getutent()) != NULL) {
223 			if (fname == NULL && (*ut->ut_name == '\0' ||
224 			    *ut->ut_line == '\0'))
225 				continue;
226 			/* Don't process entries that we have utmpx for */
227 			for (ep = ehead; ep != NULL; ep = ep->next) {
228 				if (strncmp(ep->line, ut->ut_line,
229 				    sizeof(ut->ut_line)) == 0)
230 					break;
231 			}
232 			if (ep != NULL)
233 				continue;
234 			if ((ep = calloc(1, sizeof(*ep))) == NULL) {
235 				warn(NULL);
236 				return 0;
237 			}
238 			getentry(ep, ut);
239 			*nextp = ep;
240 			nextp = &(ep->next);
241 		}
242 	}
243 #endif
244 	numutmp = 0;
245 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
246 	if (ehead != NULL) {
247 		struct utmpentry *from = ehead, *save;
248 
249 		ehead = NULL;
250 		while (from != NULL) {
251 			for (nextp = &ehead;
252 			    (*nextp) && strcmp(from->line, (*nextp)->line) > 0;
253 			    nextp = &(*nextp)->next)
254 				continue;
255 			save = from;
256 			from = from->next;
257 			save->next = *nextp;
258 			*nextp = save;
259 			numutmp++;
260 		}
261 	}
262 	*epp = ehead;
263 	return numutmp;
264 #else
265 	*epp = NULL;
266 	return 0;
267 #endif
268 }
269 
270 #ifdef SUPPORT_UTMP
271 static void
getentry(struct utmpentry * e,struct utmp * up)272 getentry(struct utmpentry *e, struct utmp *up)
273 {
274 	COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
275 	COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
276 	COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
277 
278 	/*
279 	 * e has just been calloc'd. We don't need to clear it or
280 	 * append null-terminators, because its length is strictly
281 	 * greater than the source string. Use strncpy to _read_
282 	 * up->ut_* because they may not be terminated. For this
283 	 * reason we use the size of the _source_ as the length
284 	 * argument.
285 	 */
286 	(void)strncpy(e->name, up->ut_name, sizeof(up->ut_name));
287 	(void)strncpy(e->line, up->ut_line, sizeof(up->ut_line));
288 	(void)strncpy(e->host, up->ut_host, sizeof(up->ut_host));
289 
290 	e->tv.tv_sec = up->ut_time;
291 	e->tv.tv_usec = 0;
292 	e->pid = 0;
293 	e->term = 0;
294 	e->exit = 0;
295 	e->sess = 0;
296 	e->type = USER_PROCESS;
297 	adjust_size(e);
298 }
299 #endif
300 
301 #ifdef SUPPORT_UTMPX
302 static void
getentryx(struct utmpentry * e,struct utmpx * up)303 getentryx(struct utmpentry *e, struct utmpx *up)
304 {
305 	COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
306 	COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
307 	COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
308 
309 	/*
310 	 * e has just been calloc'd. We don't need to clear it or
311 	 * append null-terminators, because its length is strictly
312 	 * greater than the source string. Use strncpy to _read_
313 	 * up->ut_* because they may not be terminated. For this
314 	 * reason we use the size of the _source_ as the length
315 	 * argument.
316 	 */
317 	(void)strncpy(e->name, up->ut_name, sizeof(up->ut_name));
318 	(void)strncpy(e->line, up->ut_line, sizeof(up->ut_line));
319 	(void)strncpy(e->host, up->ut_host, sizeof(up->ut_host));
320 
321 	e->tv = up->ut_tv;
322 	e->pid = up->ut_pid;
323 	e->term = up->ut_exit.e_termination;
324 	e->exit = up->ut_exit.e_exit;
325 	e->sess = up->ut_session;
326 	e->type = up->ut_type;
327 	adjust_size(e);
328 }
329 #endif
330