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