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