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