xref: /dragonfly/usr.bin/who/utmpentry.c (revision 7c4f4eee)
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 }
231