1 /* $NetBSD: utmpentry.c,v 1.15 2008/07/13 20:07:48 dholland 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.15 2008/07/13 20:07:48 dholland 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 endutentries(void) 161 { 162 struct utmpentry *ep; 163 164 #ifdef SUPPORT_UTMP 165 timespecclear(&utmptime); 166 #endif 167 #ifdef SUPPORT_UTMPX 168 timespecclear(&utmpxtime); 169 #endif 170 ep = ehead; 171 while (ep) { 172 struct utmpentry *sep = ep; 173 ep = ep->next; 174 free(sep); 175 } 176 ehead = NULL; 177 numutmp = 0; 178 } 179 180 int 181 getutentries(const char *fname, struct utmpentry **epp) 182 { 183 #ifdef SUPPORT_UTMPX 184 struct utmpx *utx; 185 #endif 186 #ifdef SUPPORT_UTMP 187 struct utmp *ut; 188 #endif 189 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) 190 struct utmpentry *ep; 191 int what = setup(fname); 192 struct utmpentry **nextp = &ehead; 193 switch (what) { 194 case 0: 195 /* No updates */ 196 *epp = ehead; 197 return numutmp; 198 default: 199 /* Need to re-scan */ 200 ehead = NULL; 201 numutmp = 0; 202 } 203 #endif 204 205 #ifdef SUPPORT_UTMPX 206 while ((what & 1) && (utx = getutxent()) != NULL) { 207 if (fname == NULL && ((1 << utx->ut_type) & etype) == 0) 208 continue; 209 if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) { 210 warn(NULL); 211 return 0; 212 } 213 getentryx(ep, utx); 214 *nextp = ep; 215 nextp = &(ep->next); 216 } 217 #endif 218 219 #ifdef SUPPORT_UTMP 220 if ((etype & (1 << USER_PROCESS)) != 0) { 221 while ((what & 2) && (ut = getutent()) != NULL) { 222 if (fname == NULL && (*ut->ut_name == '\0' || 223 *ut->ut_line == '\0')) 224 continue; 225 /* Don't process entries that we have utmpx for */ 226 for (ep = ehead; ep != NULL; ep = ep->next) { 227 if (strncmp(ep->line, ut->ut_line, 228 sizeof(ut->ut_line)) == 0) 229 break; 230 } 231 if (ep != NULL) 232 continue; 233 if ((ep = calloc(1, sizeof(*ep))) == NULL) { 234 warn(NULL); 235 return 0; 236 } 237 getentry(ep, ut); 238 *nextp = ep; 239 nextp = &(ep->next); 240 } 241 } 242 #endif 243 numutmp = 0; 244 #if defined(SUPPORT_UTMP) && defined(SUPPORT_UTMPX) 245 if (ehead != NULL) { 246 struct utmpentry *from = ehead, *save; 247 248 ehead = NULL; 249 while (from != NULL) { 250 for (nextp = &ehead; 251 (*nextp) && strcmp(from->line, (*nextp)->line) > 0; 252 nextp = &(*nextp)->next) 253 continue; 254 save = from; 255 from = from->next; 256 save->next = *nextp; 257 *nextp = save; 258 numutmp++; 259 } 260 } 261 *epp = ehead; 262 return numutmp; 263 #else 264 *epp = NULL; 265 return 0; 266 #endif 267 } 268 269 #ifdef SUPPORT_UTMP 270 static void 271 getentry(struct utmpentry *e, struct utmp *up) 272 { 273 COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); 274 COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); 275 COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); 276 277 /* 278 * e has just been calloc'd. We don't need to clear it or 279 * append null-terminators, because its length is strictly 280 * greater than the source string. Use strncpy to _read_ 281 * up->ut_* because they may not be terminated. For this 282 * reason we use the size of the _source_ as the length 283 * argument. 284 */ 285 (void)strncpy(e->name, up->ut_name, sizeof(up->ut_name)); 286 (void)strncpy(e->line, up->ut_line, sizeof(up->ut_line)); 287 (void)strncpy(e->host, up->ut_host, sizeof(up->ut_host)); 288 289 e->tv.tv_sec = up->ut_time; 290 e->tv.tv_usec = 0; 291 e->pid = 0; 292 e->term = 0; 293 e->exit = 0; 294 e->sess = 0; 295 e->type = USER_PROCESS; 296 adjust_size(e); 297 } 298 #endif 299 300 #ifdef SUPPORT_UTMPX 301 static void 302 getentryx(struct utmpentry *e, struct utmpx *up) 303 { 304 COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); 305 COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); 306 COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); 307 308 /* 309 * e has just been calloc'd. We don't need to clear it or 310 * append null-terminators, because its length is strictly 311 * greater than the source string. Use strncpy to _read_ 312 * up->ut_* because they may not be terminated. For this 313 * reason we use the size of the _source_ as the length 314 * argument. 315 */ 316 (void)strncpy(e->name, up->ut_name, sizeof(up->ut_name)); 317 (void)strncpy(e->line, up->ut_line, sizeof(up->ut_line)); 318 (void)strncpy(e->host, up->ut_host, sizeof(up->ut_host)); 319 320 e->tv = up->ut_tv; 321 e->pid = up->ut_pid; 322 e->term = up->ut_exit.e_termination; 323 e->exit = up->ut_exit.e_exit; 324 e->sess = up->ut_session; 325 e->type = up->ut_type; 326 adjust_size(e); 327 } 328 #endif 329