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