1 1.22 christos /* $NetBSD: utmpentry.c,v 1.22 2021/02/26 02:45:43 christos Exp $ */ 2 1.1 christos 3 1.1 christos /*- 4 1.1 christos * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * This code is derived from software contributed to The NetBSD Foundation 8 1.1 christos * by Christos Zoulas. 9 1.1 christos * 10 1.1 christos * Redistribution and use in source and binary forms, with or without 11 1.1 christos * modification, are permitted provided that the following conditions 12 1.1 christos * are met: 13 1.1 christos * 1. Redistributions of source code must retain the above copyright 14 1.1 christos * notice, this list of conditions and the following disclaimer. 15 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 christos * notice, this list of conditions and the following disclaimer in the 17 1.1 christos * documentation and/or other materials provided with the distribution. 18 1.1 christos * 19 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 christos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 christos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 christos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 christos * POSSIBILITY OF SUCH DAMAGE. 30 1.1 christos */ 31 1.1 christos 32 1.1 christos #include <sys/cdefs.h> 33 1.1 christos #ifndef lint 34 1.22 christos __RCSID("$NetBSD: utmpentry.c,v 1.22 2021/02/26 02:45:43 christos Exp $"); 35 1.1 christos #endif 36 1.1 christos 37 1.1 christos #include <sys/stat.h> 38 1.1 christos 39 1.1 christos #include <time.h> 40 1.1 christos #include <string.h> 41 1.1 christos #include <err.h> 42 1.1 christos #include <stdlib.h> 43 1.1 christos 44 1.1 christos #ifdef SUPPORT_UTMP 45 1.1 christos #include <utmp.h> 46 1.1 christos #endif 47 1.1 christos #ifdef SUPPORT_UTMPX 48 1.1 christos #include <utmpx.h> 49 1.1 christos #endif 50 1.1 christos 51 1.1 christos #include "utmpentry.h" 52 1.1 christos 53 1.1 christos 54 1.12 dholland /* Fail the compile if x is not true, by constructing an illegal type. */ 55 1.16 christos #define COMPILE_ASSERT(x) /*LINTED null effect */ \ 56 1.16 christos ((void)sizeof(struct { unsigned : ((x) ? 1 : -1); })) 57 1.12 dholland 58 1.12 dholland 59 1.1 christos #ifdef SUPPORT_UTMP 60 1.1 christos static void getentry(struct utmpentry *, struct utmp *); 61 1.11 christos static struct timespec utmptime = {0, 0}; 62 1.1 christos #endif 63 1.1 christos #ifdef SUPPORT_UTMPX 64 1.1 christos static void getentryx(struct utmpentry *, struct utmpx *); 65 1.11 christos static struct timespec utmpxtime = {0, 0}; 66 1.1 christos #endif 67 1.3 christos #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) 68 1.1 christos static int setup(const char *); 69 1.1 christos static void adjust_size(struct utmpentry *e); 70 1.1 christos #endif 71 1.1 christos 72 1.18 christos size_t maxname = 8, maxline = 8, maxhost = 16; 73 1.9 christos int etype = 1 << USER_PROCESS; 74 1.18 christos static size_t numutmp = 0; 75 1.1 christos static struct utmpentry *ehead; 76 1.1 christos 77 1.3 christos #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) 78 1.1 christos static void 79 1.1 christos adjust_size(struct utmpentry *e) 80 1.1 christos { 81 1.18 christos size_t max; 82 1.1 christos 83 1.1 christos if ((max = strlen(e->name)) > maxname) 84 1.1 christos maxname = max; 85 1.1 christos if ((max = strlen(e->line)) > maxline) 86 1.1 christos maxline = max; 87 1.1 christos if ((max = strlen(e->host)) > maxhost) 88 1.1 christos maxhost = max; 89 1.1 christos } 90 1.1 christos 91 1.1 christos static int 92 1.1 christos setup(const char *fname) 93 1.1 christos { 94 1.1 christos int what = 3; 95 1.1 christos struct stat st; 96 1.5 christos const char *sfname; 97 1.1 christos 98 1.22 christos if (fname != NULL) { 99 1.1 christos size_t len = strlen(fname); 100 1.1 christos if (len == 0) 101 1.1 christos errx(1, "Filename cannot be 0 length."); 102 1.1 christos what = fname[len - 1] == 'x' ? 1 : 2; 103 1.1 christos if (what == 1) { 104 1.1 christos #ifdef SUPPORT_UTMPX 105 1.1 christos if (utmpxname(fname) == 0) 106 1.5 christos warnx("Cannot set utmpx file to `%s'", 107 1.5 christos fname); 108 1.1 christos #else 109 1.5 christos warnx("utmpx support not compiled in"); 110 1.1 christos #endif 111 1.1 christos } else { 112 1.3 christos #ifdef SUPPORT_UTMP 113 1.1 christos if (utmpname(fname) == 0) 114 1.5 christos warnx("Cannot set utmp file to `%s'", 115 1.5 christos fname); 116 1.1 christos #else 117 1.5 christos warnx("utmp support not compiled in"); 118 1.1 christos #endif 119 1.1 christos } 120 1.1 christos } 121 1.1 christos #ifdef SUPPORT_UTMPX 122 1.1 christos if (what & 1) { 123 1.5 christos sfname = fname ? fname : _PATH_UTMPX; 124 1.5 christos if (stat(sfname, &st) == -1) { 125 1.5 christos warn("Cannot stat `%s'", sfname); 126 1.1 christos what &= ~1; 127 1.5 christos } else { 128 1.11 christos if (timespeccmp(&st.st_mtimespec, &utmpxtime, >)) 129 1.22 christos utmpxtime = st.st_mtimespec; 130 1.5 christos else 131 1.22 christos what &= ~1; 132 1.5 christos } 133 1.1 christos } 134 1.1 christos #endif 135 1.1 christos #ifdef SUPPORT_UTMP 136 1.1 christos if (what & 2) { 137 1.5 christos sfname = fname ? fname : _PATH_UTMP; 138 1.5 christos if (stat(sfname, &st) == -1) { 139 1.5 christos warn("Cannot stat `%s'", sfname); 140 1.1 christos what &= ~2; 141 1.5 christos } else { 142 1.11 christos if (timespeccmp(&st.st_mtimespec, &utmptime, >)) 143 1.11 christos utmptime = st.st_mtimespec; 144 1.5 christos else 145 1.5 christos what &= ~2; 146 1.5 christos } 147 1.1 christos } 148 1.1 christos #endif 149 1.1 christos return what; 150 1.1 christos } 151 1.1 christos #endif 152 1.1 christos 153 1.1 christos void 154 1.15 dholland endutentries(void) 155 1.1 christos { 156 1.15 dholland struct utmpentry *ep; 157 1.15 dholland 158 1.4 christos #ifdef SUPPORT_UTMP 159 1.11 christos timespecclear(&utmptime); 160 1.4 christos #endif 161 1.4 christos #ifdef SUPPORT_UTMPX 162 1.11 christos timespecclear(&utmpxtime); 163 1.4 christos #endif 164 1.15 dholland ep = ehead; 165 1.1 christos while (ep) { 166 1.2 christos struct utmpentry *sep = ep; 167 1.1 christos ep = ep->next; 168 1.1 christos free(sep); 169 1.1 christos } 170 1.15 dholland ehead = NULL; 171 1.15 dholland numutmp = 0; 172 1.1 christos } 173 1.1 christos 174 1.18 christos size_t 175 1.1 christos getutentries(const char *fname, struct utmpentry **epp) 176 1.1 christos { 177 1.1 christos #ifdef SUPPORT_UTMPX 178 1.1 christos struct utmpx *utx; 179 1.1 christos #endif 180 1.1 christos #ifdef SUPPORT_UTMP 181 1.1 christos struct utmp *ut; 182 1.1 christos #endif 183 1.3 christos #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) 184 1.2 christos struct utmpentry *ep; 185 1.1 christos int what = setup(fname); 186 1.1 christos struct utmpentry **nextp = &ehead; 187 1.5 christos switch (what) { 188 1.5 christos case 0: 189 1.5 christos /* No updates */ 190 1.1 christos *epp = ehead; 191 1.1 christos return numutmp; 192 1.5 christos default: 193 1.5 christos /* Need to re-scan */ 194 1.1 christos ehead = NULL; 195 1.1 christos numutmp = 0; 196 1.1 christos } 197 1.3 christos #endif 198 1.1 christos 199 1.1 christos #ifdef SUPPORT_UTMPX 200 1.22 christos setutxent(); 201 1.1 christos while ((what & 1) && (utx = getutxent()) != NULL) { 202 1.9 christos if (fname == NULL && ((1 << utx->ut_type) & etype) == 0) 203 1.1 christos continue; 204 1.22 christos if ((ep = calloc(1, sizeof(*ep))) == NULL) { 205 1.5 christos warn(NULL); 206 1.5 christos return 0; 207 1.5 christos } 208 1.1 christos getentryx(ep, utx); 209 1.1 christos *nextp = ep; 210 1.1 christos nextp = &(ep->next); 211 1.1 christos } 212 1.1 christos #endif 213 1.1 christos 214 1.1 christos #ifdef SUPPORT_UTMP 215 1.22 christos setutent(); 216 1.9 christos if ((etype & (1 << USER_PROCESS)) != 0) { 217 1.9 christos while ((what & 2) && (ut = getutent()) != NULL) { 218 1.9 christos if (fname == NULL && (*ut->ut_name == '\0' || 219 1.9 christos *ut->ut_line == '\0')) 220 1.9 christos continue; 221 1.9 christos /* Don't process entries that we have utmpx for */ 222 1.9 christos for (ep = ehead; ep != NULL; ep = ep->next) { 223 1.9 christos if (strncmp(ep->line, ut->ut_line, 224 1.9 christos sizeof(ut->ut_line)) == 0) 225 1.9 christos break; 226 1.9 christos } 227 1.9 christos if (ep != NULL) 228 1.9 christos continue; 229 1.9 christos if ((ep = calloc(1, sizeof(*ep))) == NULL) { 230 1.9 christos warn(NULL); 231 1.9 christos return 0; 232 1.9 christos } 233 1.9 christos getentry(ep, ut); 234 1.9 christos *nextp = ep; 235 1.9 christos nextp = &(ep->next); 236 1.5 christos } 237 1.1 christos } 238 1.1 christos #endif 239 1.1 christos numutmp = 0; 240 1.17 christos #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) 241 1.1 christos if (ehead != NULL) { 242 1.1 christos struct utmpentry *from = ehead, *save; 243 1.1 christos 244 1.1 christos ehead = NULL; 245 1.1 christos while (from != NULL) { 246 1.1 christos for (nextp = &ehead; 247 1.1 christos (*nextp) && strcmp(from->line, (*nextp)->line) > 0; 248 1.1 christos nextp = &(*nextp)->next) 249 1.1 christos continue; 250 1.1 christos save = from; 251 1.1 christos from = from->next; 252 1.1 christos save->next = *nextp; 253 1.1 christos *nextp = save; 254 1.1 christos numutmp++; 255 1.1 christos } 256 1.1 christos } 257 1.1 christos *epp = ehead; 258 1.1 christos return numutmp; 259 1.3 christos #else 260 1.3 christos *epp = NULL; 261 1.3 christos return 0; 262 1.3 christos #endif 263 1.1 christos } 264 1.1 christos 265 1.1 christos #ifdef SUPPORT_UTMP 266 1.1 christos static void 267 1.1 christos getentry(struct utmpentry *e, struct utmp *up) 268 1.1 christos { 269 1.12 dholland COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); 270 1.12 dholland COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); 271 1.12 dholland COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); 272 1.12 dholland 273 1.12 dholland /* 274 1.12 dholland * e has just been calloc'd. We don't need to clear it or 275 1.13 dholland * append null-terminators, because its length is strictly 276 1.21 mrg * greater than the source string. Use memcpy to _read_ 277 1.13 dholland * up->ut_* because they may not be terminated. For this 278 1.13 dholland * reason we use the size of the _source_ as the length 279 1.13 dholland * argument. 280 1.12 dholland */ 281 1.21 mrg memcpy(e->name, up->ut_name, sizeof(up->ut_name)); 282 1.21 mrg memcpy(e->line, up->ut_line, sizeof(up->ut_line)); 283 1.21 mrg memcpy(e->host, up->ut_host, sizeof(up->ut_host)); 284 1.12 dholland 285 1.1 christos e->tv.tv_sec = up->ut_time; 286 1.1 christos e->tv.tv_usec = 0; 287 1.8 hubertf e->pid = 0; 288 1.8 hubertf e->term = 0; 289 1.8 hubertf e->exit = 0; 290 1.8 hubertf e->sess = 0; 291 1.10 christos e->type = USER_PROCESS; 292 1.1 christos adjust_size(e); 293 1.1 christos } 294 1.1 christos #endif 295 1.1 christos 296 1.1 christos #ifdef SUPPORT_UTMPX 297 1.1 christos static void 298 1.1 christos getentryx(struct utmpentry *e, struct utmpx *up) 299 1.1 christos { 300 1.12 dholland COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); 301 1.12 dholland COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); 302 1.12 dholland COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); 303 1.12 dholland 304 1.12 dholland /* 305 1.12 dholland * e has just been calloc'd. We don't need to clear it or 306 1.13 dholland * append null-terminators, because its length is strictly 307 1.21 mrg * greater than the source string. Use memcpy to _read_ 308 1.13 dholland * up->ut_* because they may not be terminated. For this 309 1.13 dholland * reason we use the size of the _source_ as the length 310 1.13 dholland * argument. 311 1.12 dholland */ 312 1.21 mrg memcpy(e->name, up->ut_name, sizeof(up->ut_name)); 313 1.21 mrg memcpy(e->line, up->ut_line, sizeof(up->ut_line)); 314 1.21 mrg memcpy(e->host, up->ut_host, sizeof(up->ut_host)); 315 1.12 dholland 316 1.1 christos e->tv = up->ut_tv; 317 1.8 hubertf e->pid = up->ut_pid; 318 1.8 hubertf e->term = up->ut_exit.e_termination; 319 1.8 hubertf e->exit = up->ut_exit.e_exit; 320 1.8 hubertf e->sess = up->ut_session; 321 1.8 hubertf e->type = up->ut_type; 322 1.1 christos adjust_size(e); 323 1.1 christos } 324 1.1 christos #endif 325