xref: /netbsd-src/usr.bin/who/utmpentry.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: utmpentry.c,v 1.11 2006/11/27 16:54: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  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __RCSID("$NetBSD: utmpentry.c,v 1.11 2006/11/27 16:54:10 christos Exp $");
42 #endif
43 
44 #include <sys/stat.h>
45 
46 #include <time.h>
47 #include <string.h>
48 #include <err.h>
49 #include <stdlib.h>
50 
51 #ifdef SUPPORT_UTMP
52 #include <utmp.h>
53 #endif
54 #ifdef SUPPORT_UTMPX
55 #include <utmpx.h>
56 #endif
57 
58 #include "utmpentry.h"
59 
60 
61 #ifdef SUPPORT_UTMP
62 static void getentry(struct utmpentry *, struct utmp *);
63 static struct timespec utmptime = {0, 0};
64 #endif
65 #ifdef SUPPORT_UTMPX
66 static void getentryx(struct utmpentry *, struct utmpx *);
67 static struct timespec utmpxtime = {0, 0};
68 #endif
69 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
70 static int setup(const char *);
71 static void adjust_size(struct utmpentry *e);
72 #endif
73 
74 int maxname = 8, maxline = 8, maxhost = 16;
75 int etype = 1 << USER_PROCESS;
76 static int numutmp = 0;
77 static struct utmpentry *ehead;
78 
79 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
80 static void
81 adjust_size(struct utmpentry *e)
82 {
83 	int max;
84 
85 	if ((max = strlen(e->name)) > maxname)
86 		maxname = max;
87 	if ((max = strlen(e->line)) > maxline)
88 		maxline = max;
89 	if ((max = strlen(e->host)) > maxhost)
90 		maxhost = max;
91 }
92 
93 static int
94 setup(const char *fname)
95 {
96 	int what = 3;
97 	struct stat st;
98 	const char *sfname;
99 
100 	if (fname == NULL) {
101 #ifdef SUPPORT_UTMPX
102 		setutxent();
103 #endif
104 #ifdef SUPPORT_UTMP
105 		setutent();
106 #endif
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 #ifdef SUPPORT_UTMPX
114 			if (utmpxname(fname) == 0)
115 				warnx("Cannot set utmpx file to `%s'",
116 				    fname);
117 #else
118 			warnx("utmpx support not compiled in");
119 #endif
120 		} else {
121 #ifdef SUPPORT_UTMP
122 			if (utmpname(fname) == 0)
123 				warnx("Cannot set utmp file to `%s'",
124 				    fname);
125 #else
126 			warnx("utmp support not compiled in");
127 #endif
128 		}
129 	}
130 #ifdef SUPPORT_UTMPX
131 	if (what & 1) {
132 		sfname = fname ? fname : _PATH_UTMPX;
133 		if (stat(sfname, &st) == -1) {
134 			warn("Cannot stat `%s'", sfname);
135 			what &= ~1;
136 		} else {
137 			if (timespeccmp(&st.st_mtimespec, &utmpxtime, >))
138 			    utmpxtime = st.st_mtimespec;
139 			else
140 			    what &= ~1;
141 		}
142 	}
143 #endif
144 #ifdef SUPPORT_UTMP
145 	if (what & 2) {
146 		sfname = fname ? fname : _PATH_UTMP;
147 		if (stat(sfname, &st) == -1) {
148 			warn("Cannot stat `%s'", sfname);
149 			what &= ~2;
150 		} else {
151 			if (timespeccmp(&st.st_mtimespec, &utmptime, >))
152 				utmptime = st.st_mtimespec;
153 			else
154 				what &= ~2;
155 		}
156 	}
157 #endif
158 	return what;
159 }
160 #endif
161 
162 void
163 freeutentries(struct utmpentry *ep)
164 {
165 #ifdef SUPPORT_UTMP
166 	timespecclear(&utmptime);
167 #endif
168 #ifdef SUPPORT_UTMPX
169 	timespecclear(&utmpxtime);
170 #endif
171 	if (ep == ehead) {
172 		ehead = NULL;
173 		numutmp = 0;
174 	}
175 	while (ep) {
176 		struct utmpentry *sep = ep;
177 		ep = ep->next;
178 		free(sep);
179 	}
180 }
181 
182 int
183 getutentries(const char *fname, struct utmpentry **epp)
184 {
185 #ifdef SUPPORT_UTMPX
186 	struct utmpx *utx;
187 #endif
188 #ifdef SUPPORT_UTMP
189 	struct utmp *ut;
190 #endif
191 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
192 	struct utmpentry *ep;
193 	int what = setup(fname);
194 	struct utmpentry **nextp = &ehead;
195 	switch (what) {
196 	case 0:
197 		/* No updates */
198 		*epp = ehead;
199 		return numutmp;
200 	default:
201 		/* Need to re-scan */
202 		ehead = NULL;
203 		numutmp = 0;
204 	}
205 #endif
206 
207 #ifdef SUPPORT_UTMPX
208 	while ((what & 1) && (utx = getutxent()) != NULL) {
209 		if (fname == NULL && ((1 << utx->ut_type) & etype) == 0)
210 			continue;
211 		if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) {
212 			warn(NULL);
213 			return 0;
214 		}
215 		getentryx(ep, utx);
216 		*nextp = ep;
217 		nextp = &(ep->next);
218 	}
219 #endif
220 
221 #ifdef SUPPORT_UTMP
222 	if ((etype & (1 << USER_PROCESS)) != 0) {
223 		while ((what & 2) && (ut = getutent()) != NULL) {
224 			if (fname == NULL && (*ut->ut_name == '\0' ||
225 			    *ut->ut_line == '\0'))
226 				continue;
227 			/* Don't process entries that we have utmpx for */
228 			for (ep = ehead; ep != NULL; ep = ep->next) {
229 				if (strncmp(ep->line, ut->ut_line,
230 				    sizeof(ut->ut_line)) == 0)
231 					break;
232 			}
233 			if (ep != NULL)
234 				continue;
235 			if ((ep = calloc(1, sizeof(*ep))) == NULL) {
236 				warn(NULL);
237 				return 0;
238 			}
239 			getentry(ep, ut);
240 			*nextp = ep;
241 			nextp = &(ep->next);
242 		}
243 	}
244 #endif
245 	numutmp = 0;
246 #if defined(SUPPORT_UTMP) && defined(SUPPORT_UTMPX)
247 	if (ehead != NULL) {
248 		struct utmpentry *from = ehead, *save;
249 
250 		ehead = NULL;
251 		while (from != NULL) {
252 			for (nextp = &ehead;
253 			    (*nextp) && strcmp(from->line, (*nextp)->line) > 0;
254 			    nextp = &(*nextp)->next)
255 				continue;
256 			save = from;
257 			from = from->next;
258 			save->next = *nextp;
259 			*nextp = save;
260 			numutmp++;
261 		}
262 	}
263 	*epp = ehead;
264 	return numutmp;
265 #else
266 	*epp = NULL;
267 	return 0;
268 #endif
269 }
270 
271 #ifdef SUPPORT_UTMP
272 static void
273 getentry(struct utmpentry *e, struct utmp *up)
274 {
275 	(void)strncpy(e->name, up->ut_name, sizeof(up->ut_name));
276 	e->name[sizeof(e->name) - 1] = '\0';
277 	(void)strncpy(e->line, up->ut_line, sizeof(up->ut_line));
278 	e->line[sizeof(e->line) - 1] = '\0';
279 	(void)strncpy(e->host, up->ut_host, sizeof(up->ut_host));
280 	e->name[sizeof(e->name) - 1] = '\0';
281 	e->tv.tv_sec = up->ut_time;
282 	e->tv.tv_usec = 0;
283 	e->pid = 0;
284 	e->term = 0;
285 	e->exit = 0;
286 	e->sess = 0;
287 	e->type = USER_PROCESS;
288 	adjust_size(e);
289 }
290 #endif
291 
292 #ifdef SUPPORT_UTMPX
293 static void
294 getentryx(struct utmpentry *e, struct utmpx *up)
295 {
296 	(void)strncpy(e->name, up->ut_name, sizeof(up->ut_name));
297 	e->name[sizeof(e->name) - 1] = '\0';
298 	(void)strncpy(e->line, up->ut_line, sizeof(up->ut_line));
299 	e->line[sizeof(e->line) - 1] = '\0';
300 	(void)strncpy(e->host, up->ut_host, sizeof(up->ut_host));
301 	e->name[sizeof(e->name) - 1] = '\0';
302 	e->tv = up->ut_tv;
303 	e->pid = up->ut_pid;
304 	e->term = up->ut_exit.e_termination;
305 	e->exit = up->ut_exit.e_exit;
306 	e->sess = up->ut_session;
307 	e->type = up->ut_type;
308 	adjust_size(e);
309 }
310 #endif
311