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 #include <utmpx.h> 41 42 #include "utmpentry.h" 43 44 /* Operations on timespecs. */ 45 #define timespecclear(tsp) (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L) 46 #define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec) 47 #define timespeccmp(tsp, usp, cmp) \ 48 (((tsp)->tv_sec == (usp)->tv_sec) ? \ 49 ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ 50 ((tsp)->tv_sec cmp (usp)->tv_sec)) 51 #define timespecadd(tsp, usp, vsp) \ 52 do { \ 53 (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ 54 (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ 55 if ((vsp)->tv_nsec >= 1000000000L) { \ 56 (vsp)->tv_sec++; \ 57 (vsp)->tv_nsec -= 1000000000L; \ 58 } \ 59 } while (/* CONSTCOND */ 0) 60 #define timespecsub(tsp, usp, vsp) \ 61 do { \ 62 (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ 63 (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ 64 if ((vsp)->tv_nsec < 0) { \ 65 (vsp)->tv_sec--; \ 66 (vsp)->tv_nsec += 1000000000L; \ 67 } \ 68 } while (/* CONSTCOND */ 0) 69 #define timespec2ns(x) (((uint64_t)(x)->tv_sec) * 1000000000L + (x)->tv_nsec) 70 71 72 #define COMPILE_ASSERT(x) _Static_assert(x, "assertion failed") 73 74 75 static void getentryx(struct utmpentry *, struct utmpx *); 76 static struct timespec utmpxtime = {0, 0}; 77 static int setup(const char *); 78 static void adjust_size(struct utmpentry *e); 79 80 int maxname = 8, maxline = 8, maxhost = 16; 81 int etype = 1 << USER_PROCESS; 82 static int numutmp = 0; 83 static struct utmpentry *ehead; 84 85 static void 86 adjust_size(struct utmpentry *e) 87 { 88 int max; 89 90 if ((max = strlen(e->name)) > maxname) 91 maxname = max; 92 if ((max = strlen(e->line)) > maxline) 93 maxline = max; 94 if ((max = strlen(e->host)) > maxhost) 95 maxhost = max; 96 } 97 98 static int 99 setup(const char *fname) 100 { 101 int what = 3; 102 struct stat st; 103 const char *sfname; 104 105 if (fname == NULL) { 106 setutxent(); 107 } else { 108 size_t len = strlen(fname); 109 if (len == 0) 110 errx(1, "Filename cannot be 0 length."); 111 what = fname[len - 1] == 'x' ? 1 : 2; 112 if (what == 1) { 113 if (utmpxname(fname) == 0) 114 warnx("Cannot set utmpx file to `%s'", 115 fname); 116 } 117 } 118 if (what & 1) { 119 sfname = fname ? fname : _PATH_UTMPX; 120 if (stat(sfname, &st) == -1) { 121 warn("Cannot stat `%s'", sfname); 122 what &= ~1; 123 } else { 124 if (timespeccmp(&st.st_mtimespec, &utmpxtime, >)) 125 utmpxtime = st.st_mtimespec; 126 else 127 what &= ~1; 128 } 129 } 130 return what; 131 } 132 133 void 134 endutentries(void) 135 { 136 struct utmpentry *ep; 137 138 timespecclear(&utmpxtime); 139 ep = ehead; 140 while (ep) { 141 struct utmpentry *sep = ep; 142 ep = ep->next; 143 free(sep); 144 } 145 ehead = NULL; 146 numutmp = 0; 147 } 148 149 int 150 getutentries(const char *fname, struct utmpentry **epp) 151 { 152 struct utmpx *utx; 153 struct utmpentry *ep; 154 int what = setup(fname); 155 struct utmpentry **nextp = &ehead; 156 switch (what) { 157 case 0: 158 /* No updates */ 159 *epp = ehead; 160 return numutmp; 161 default: 162 /* Need to re-scan */ 163 ehead = NULL; 164 numutmp = 0; 165 } 166 167 while ((what & 1) && (utx = getutxent()) != NULL) { 168 if (fname == NULL && ((1 << utx->ut_type) & etype) == 0) { 169 continue; 170 } 171 if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) { 172 warn(NULL); 173 return 0; 174 } 175 getentryx(ep, utx); 176 *nextp = ep; 177 nextp = &(ep->next); 178 } 179 180 numutmp = 0; 181 if (ehead != NULL) { 182 struct utmpentry *from = ehead, *save; 183 184 ehead = NULL; 185 while (from != NULL) { 186 for (nextp = &ehead; 187 (*nextp) && strcmp(from->line, (*nextp)->line) > 0; 188 nextp = &(*nextp)->next) 189 continue; 190 save = from; 191 from = from->next; 192 save->next = *nextp; 193 *nextp = save; 194 numutmp++; 195 } 196 } 197 *epp = ehead; 198 return numutmp; 199 } 200 201 static void 202 getentryx(struct utmpentry *e, struct utmpx *up) 203 { 204 COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); 205 COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); 206 COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); 207 208 /* 209 * e has just been calloc'd. We don't need to clear it or 210 * append null-terminators, because its length is strictly 211 * greater than the source string. Use strncpy to _read_ 212 * up->ut_* because they may not be terminated. For this 213 * reason we use the size of the _source_ as the length 214 * argument. 215 */ 216 snprintf(e->name, sizeof(e->name), "%.*s", 217 (int)sizeof(up->ut_name), up->ut_name); 218 snprintf(e->line, sizeof(e->line), "%.*s", 219 (int)sizeof(up->ut_line), up->ut_line); 220 snprintf(e->host, sizeof(e->host), "%.*s", 221 (int)sizeof(up->ut_host), up->ut_host); 222 223 e->tv = up->ut_tv; 224 e->pid = up->ut_pid; 225 e->term = up->ut_exit.e_termination; 226 e->exit = up->ut_exit.e_exit; 227 e->sess = up->ut_session; 228 e->type = up->ut_type; 229 adjust_size(e); 230 } 231