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 60static void getentry(struct utmpentry *, struct utmp *); 61static struct timespec utmptime = {0, 0}; 62#endif 63#ifdef SUPPORT_UTMPX 64static void getentryx(struct utmpentry *, struct utmpx *); 65static struct timespec utmpxtime = {0, 0}; 66#endif 67#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) 68static int setup(const char *); 69static void adjust_size(struct utmpentry *e); 70#endif 71 72size_t maxname = 8, maxline = 8, maxhost = 16; 73int etype = 1 << USER_PROCESS; 74static size_t numutmp = 0; 75static struct utmpentry *ehead; 76 77#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) 78static void 79adjust_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 91static int 92setup(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 153void 154endutentries(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 174size_t 175getutentries(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 266static void 267getentry(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 297static void 298getentryx(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