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 41 #ifdef SUPPORT_UTMP 42 #include <utmp.h> 43 #endif 44 #ifdef SUPPORT_UTMPX 45 #include <utmpx.h> 46 #endif 47 48 #include "utmpentry.h" 49 50 /* Operations on timespecs. */ 51 #define timespecclear(tsp) (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L) 52 #define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec) 53 #define timespeccmp(tsp, usp, cmp) \ 54 (((tsp)->tv_sec == (usp)->tv_sec) ? \ 55 ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ 56 ((tsp)->tv_sec cmp (usp)->tv_sec)) 57 #define timespecadd(tsp, usp, vsp) \ 58 do { \ 59 (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ 60 (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ 61 if ((vsp)->tv_nsec >= 1000000000L) { \ 62 (vsp)->tv_sec++; \ 63 (vsp)->tv_nsec -= 1000000000L; \ 64 } \ 65 } while (/* CONSTCOND */ 0) 66 #define timespecsub(tsp, usp, vsp) \ 67 do { \ 68 (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ 69 (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ 70 if ((vsp)->tv_nsec < 0) { \ 71 (vsp)->tv_sec--; \ 72 (vsp)->tv_nsec += 1000000000L; \ 73 } \ 74 } while (/* CONSTCOND */ 0) 75 #define timespec2ns(x) (((uint64_t)(x)->tv_sec) * 1000000000L + (x)->tv_nsec) 76 77 78 #define COMPILE_ASSERT(x) _Static_assert(x, "assertion failed") 79 80 81 #ifdef SUPPORT_UTMP 82 static void getentry(struct utmpentry *, struct utmp *); 83 static struct timespec utmptime = {0, 0}; 84 #endif 85 #ifdef SUPPORT_UTMPX 86 static void getentryx(struct utmpentry *, struct utmpx *); 87 static struct timespec utmpxtime = {0, 0}; 88 #endif 89 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) 90 static int setup(const char *); 91 static void adjust_size(struct utmpentry *e); 92 #endif 93 94 int maxname = 8, maxline = 8, maxhost = 16; 95 int etype = 1 << USER_PROCESS; 96 static int numutmp = 0; 97 static struct utmpentry *ehead; 98 99 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) 100 static void 101 adjust_size(struct utmpentry *e) 102 { 103 int max; 104 105 if ((max = strlen(e->name)) > maxname) 106 maxname = max; 107 if ((max = strlen(e->line)) > maxline) 108 maxline = max; 109 if ((max = strlen(e->host)) > maxhost) 110 maxhost = max; 111 } 112 113 static int 114 setup(const char *fname) 115 { 116 int what = 3; 117 struct stat st; 118 const char *sfname; 119 120 if (fname == NULL) { 121 #ifdef SUPPORT_UTMPX 122 setutxent(); 123 #endif 124 #ifdef SUPPORT_UTMP 125 setutent(); 126 #endif 127 } else { 128 size_t len = strlen(fname); 129 if (len == 0) 130 errx(1, "Filename cannot be 0 length."); 131 what = fname[len - 1] == 'x' ? 1 : 2; 132 if (what == 1) { 133 #ifdef SUPPORT_UTMPX 134 if (utmpxname(fname) == 0) 135 warnx("Cannot set utmpx file to `%s'", 136 fname); 137 #else 138 warnx("utmpx support not compiled in"); 139 #endif 140 } else { 141 #ifdef SUPPORT_UTMP 142 if (utmpname(fname) == 0) 143 warnx("Cannot set utmp file to `%s'", 144 fname); 145 #else 146 warnx("utmp support not compiled in"); 147 #endif 148 } 149 } 150 #ifdef SUPPORT_UTMPX 151 if (what & 1) { 152 sfname = fname ? fname : _PATH_UTMPX; 153 if (stat(sfname, &st) == -1) { 154 warn("Cannot stat `%s'", sfname); 155 what &= ~1; 156 } else { 157 if (timespeccmp(&st.st_mtimespec, &utmpxtime, >)) 158 utmpxtime = st.st_mtimespec; 159 else 160 what &= ~1; 161 } 162 } 163 #endif 164 #ifdef SUPPORT_UTMP 165 if (what & 2) { 166 sfname = fname ? fname : _PATH_UTMP; 167 if (stat(sfname, &st) == -1) { 168 warn("Cannot stat `%s'", sfname); 169 what &= ~2; 170 } else { 171 if (timespeccmp(&st.st_mtimespec, &utmptime, >)) 172 utmptime = st.st_mtimespec; 173 else 174 what &= ~2; 175 } 176 } 177 #endif 178 return what; 179 } 180 #endif 181 182 void 183 endutentries(void) 184 { 185 struct utmpentry *ep; 186 187 #ifdef SUPPORT_UTMP 188 timespecclear(&utmptime); 189 #endif 190 #ifdef SUPPORT_UTMPX 191 timespecclear(&utmpxtime); 192 #endif 193 ep = ehead; 194 while (ep) { 195 struct utmpentry *sep = ep; 196 ep = ep->next; 197 free(sep); 198 } 199 ehead = NULL; 200 numutmp = 0; 201 } 202 203 int 204 getutentries(const char *fname, struct utmpentry **epp) 205 { 206 #ifdef SUPPORT_UTMPX 207 struct utmpx *utx; 208 #endif 209 #ifdef SUPPORT_UTMP 210 struct utmp *ut; 211 #endif 212 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) 213 struct utmpentry *ep; 214 int what = setup(fname); 215 struct utmpentry **nextp = &ehead; 216 switch (what) { 217 case 0: 218 /* No updates */ 219 *epp = ehead; 220 return numutmp; 221 default: 222 /* Need to re-scan */ 223 ehead = NULL; 224 numutmp = 0; 225 } 226 #endif 227 228 #ifdef SUPPORT_UTMPX 229 while ((what & 1) && (utx = getutxent()) != NULL) { 230 if (fname == NULL && ((1 << utx->ut_type) & etype) == 0) { 231 continue; 232 } 233 if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) { 234 warn(NULL); 235 return 0; 236 } 237 getentryx(ep, utx); 238 *nextp = ep; 239 nextp = &(ep->next); 240 } 241 #endif 242 243 #ifdef SUPPORT_UTMP 244 if ((etype & (1 << USER_PROCESS)) != 0) { 245 while ((what & 2) && (ut = getutent()) != NULL) { 246 if (fname == NULL && (*ut->ut_name == '\0' || 247 *ut->ut_line == '\0')) 248 continue; 249 /* Don't process entries that we have utmpx for */ 250 for (ep = ehead; ep != NULL; ep = ep->next) { 251 if (strncmp(ep->line, ut->ut_line, 252 sizeof(ut->ut_line)) == 0) 253 break; 254 } 255 if (ep != NULL) 256 continue; 257 if ((ep = calloc(1, sizeof(*ep))) == NULL) { 258 warn(NULL); 259 return 0; 260 } 261 getentry(ep, ut); 262 *nextp = ep; 263 nextp = &(ep->next); 264 } 265 } 266 #endif 267 numutmp = 0; 268 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) 269 if (ehead != NULL) { 270 struct utmpentry *from = ehead, *save; 271 272 ehead = NULL; 273 while (from != NULL) { 274 for (nextp = &ehead; 275 (*nextp) && strcmp(from->line, (*nextp)->line) > 0; 276 nextp = &(*nextp)->next) 277 continue; 278 save = from; 279 from = from->next; 280 save->next = *nextp; 281 *nextp = save; 282 numutmp++; 283 } 284 } 285 *epp = ehead; 286 return numutmp; 287 #else 288 *epp = NULL; 289 return 0; 290 #endif 291 } 292 293 #ifdef SUPPORT_UTMP 294 static void 295 getentry(struct utmpentry *e, struct utmp *up) 296 { 297 #if 1 298 COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); 299 COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); 300 COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); 301 #endif 302 303 /* 304 * e has just been calloc'd. We don't need to clear it or 305 * append null-terminators, because its length is strictly 306 * greater than the source string. Use strncpy to _read_ 307 * up->ut_* because they may not be terminated. For this 308 * reason we use the size of the _source_ as the length 309 * argument. 310 */ 311 312 snprintf(e->name, sizeof(e->name), "%.*s", 313 (int)sizeof(up->ut_name), up->ut_name); 314 snprintf(e->line, sizeof(e->line), "%.*s", 315 (int)sizeof(up->ut_line), up->ut_line); 316 snprintf(e->host, sizeof(e->host), "%.*s", 317 (int)sizeof(up->ut_host), up->ut_host); 318 319 e->tv.tv_sec = up->ut_time; 320 e->tv.tv_usec = 0; 321 e->pid = 0; 322 e->term = 0; 323 e->exit = 0; 324 e->sess = 0; 325 e->type = USER_PROCESS; 326 adjust_size(e); 327 } 328 #endif 329 330 #ifdef SUPPORT_UTMPX 331 static void 332 getentryx(struct utmpentry *e, struct utmpx *up) 333 { 334 COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); 335 COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); 336 COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); 337 338 /* 339 * e has just been calloc'd. We don't need to clear it or 340 * append null-terminators, because its length is strictly 341 * greater than the source string. Use strncpy to _read_ 342 * up->ut_* because they may not be terminated. For this 343 * reason we use the size of the _source_ as the length 344 * argument. 345 */ 346 snprintf(e->name, sizeof(e->name), "%.*s", 347 (int)sizeof(up->ut_name), up->ut_name); 348 snprintf(e->line, sizeof(e->line), "%.*s", 349 (int)sizeof(up->ut_line), up->ut_line); 350 snprintf(e->host, sizeof(e->host), "%.*s", 351 (int)sizeof(up->ut_host), up->ut_host); 352 353 e->tv = up->ut_tv; 354 e->pid = up->ut_pid; 355 e->term = up->ut_exit.e_termination; 356 e->exit = up->ut_exit.e_exit; 357 e->sess = up->ut_session; 358 e->type = up->ut_type; 359 adjust_size(e); 360 } 361 #endif 362