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