pututxline.c revision 202530
1/*- 2 * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/lib/libc/gen/pututxline.c 202530 2010-01-17 21:40:05Z ed $"); 29 30#include "namespace.h" 31#include <sys/endian.h> 32#include <sys/stat.h> 33#include <sys/uio.h> 34#include <fcntl.h> 35#include <stdio.h> 36#include <string.h> 37#include <unistd.h> 38#include <utmpx.h> 39#include "utxdb.h" 40#include "un-namespace.h" 41 42static FILE * 43futx_open(const char *file) 44{ 45 int fd; 46 FILE *fp; 47 struct stat sb; 48 49 fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK, 0644); 50 if (fd < 0) 51 return (NULL); 52 53 /* Safety check: never use broken files. */ 54 if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) { 55 _close(fd); 56 return (NULL); 57 } 58 59 fp = fdopen(fd, "r+"); 60 if (fp == NULL) { 61 _close(fd); 62 return (NULL); 63 } 64 65 return (fp); 66} 67 68static void 69utx_active_add(const struct futx *fu) 70{ 71 FILE *fp; 72 struct futx fe; 73 off_t partial = -1; 74 75 /* 76 * Register user login sessions. Overwrite entries of sessions 77 * that have already been terminated. 78 */ 79 fp = futx_open(_PATH_UTX_ACTIVE); 80 if (fp == NULL) 81 return; 82 while (fread(&fe, sizeof fe, 1, fp) == 1) { 83 switch (fe.fu_type) { 84 case USER_PROCESS: 85 case INIT_PROCESS: 86 case LOGIN_PROCESS: 87 case DEAD_PROCESS: 88 /* Overwrite when ut_id matches. */ 89 if (memcmp(fu->fu_id, fe.fu_id, sizeof fe.fu_id) == 0) { 90 fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); 91 goto exact; 92 } 93 if (fe.fu_type != DEAD_PROCESS) 94 break; 95 /* FALLTHROUGH */ 96 default: 97 /* Allow us to overwrite unused records. */ 98 if (partial == -1) 99 partial = ftello(fp) - (off_t)sizeof fe; 100 break; 101 } 102 } 103 104 /* 105 * No exact match found. Use the partial match. If no partial 106 * match was found, just append a new record. 107 */ 108 if (partial != -1) 109 fseeko(fp, partial, SEEK_SET); 110exact: 111 fwrite(fu, sizeof *fu, 1, fp); 112 fclose(fp); 113} 114 115static int 116utx_active_remove(struct futx *fu) 117{ 118 FILE *fp; 119 struct futx fe; 120 121 /* 122 * Remove user login sessions, having the same ut_id. 123 */ 124 fp = futx_open(_PATH_UTX_ACTIVE); 125 if (fp == NULL) 126 return (0); 127 while (fread(&fe, sizeof fe, 1, fp) == 1) { 128 switch (fe.fu_type) { 129 case USER_PROCESS: 130 case INIT_PROCESS: 131 case LOGIN_PROCESS: 132 if (memcmp(fu->fu_id, fe.fu_id, sizeof fe.fu_id) != 0) 133 continue; 134 135 /* 136 * Prevent login sessions from having a negative 137 * timespan. 138 */ 139 if (be64toh(fu->fu_tv) < be64toh(fe.fu_tv)) 140 fu->fu_tv = fe.fu_tv; 141 142 /* Terminate session. */ 143 fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); 144 fwrite(fu, sizeof *fu, 1, fp); 145 fclose(fp); 146 return (0); 147 } 148 } 149 150 fclose(fp); 151 return (1); 152} 153 154static void 155utx_active_purge(void) 156{ 157 158 truncate(_PATH_UTX_ACTIVE, 0); 159} 160 161static void 162utx_lastlogin_add(const struct futx *fu) 163{ 164 FILE *fp; 165 struct futx fe; 166 167 /* 168 * Write an entry to lastlogin. Overwrite the entry if the 169 * current user already has an entry. If not, append a new 170 * entry. 171 */ 172 fp = futx_open(_PATH_UTX_LASTLOGIN); 173 if (fp == NULL) 174 return; 175 while (fread(&fe, sizeof fe, 1, fp) == 1) { 176 if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0) 177 continue; 178 179 /* Prevent lowering the time value. */ 180 if (be64toh(fu->fu_tv) <= be64toh(fe.fu_tv)) 181 goto done; 182 183 /* Found a previous lastlogin entry for this user. */ 184 fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); 185 break; 186 } 187 fwrite(fu, sizeof *fu, 1, fp); 188done: 189 fclose(fp); 190} 191 192static void 193utx_lastlogin_upgrade(void) 194{ 195 int fd; 196 struct stat sb; 197 198 fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR, 0644); 199 if (fd < 0) 200 return; 201 202 /* 203 * Truncate broken lastlogin files. In the future we should 204 * check for older versions of the file format here and try to 205 * upgrade it. 206 */ 207 if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) 208 ftruncate(fd, 0); 209 _close(fd); 210} 211 212static void 213utx_log_add(const struct futx *fu) 214{ 215 int fd; 216 uint16_t l; 217 struct iovec vec[2]; 218 219 /* 220 * Append an entry to the log file. We only need to append 221 * records to this file, so to conserve space, trim any trailing 222 * zero-bytes. Prepend a length field, indicating the length of 223 * the record, excluding the length field itself. 224 */ 225 for (l = sizeof *fu; l > 0 && ((const char *)fu)[l - 1] == '\0'; l--); 226 vec[0].iov_base = &l; 227 vec[0].iov_len = sizeof l; 228 vec[1].iov_base = __DECONST(void *, fu); 229 vec[1].iov_len = l; 230 l = htobe16(l); 231 232 fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND, 0644); 233 if (fd < 0) 234 return; 235 _writev(fd, vec, 2); 236 _close(fd); 237} 238 239struct utmpx * 240pututxline(const struct utmpx *utmpx) 241{ 242 struct futx fu; 243 244 utx_to_futx(utmpx, &fu); 245 246 switch (fu.fu_type) { 247 case BOOT_TIME: 248 case SHUTDOWN_TIME: 249 utx_active_purge(); 250 utx_lastlogin_upgrade(); 251 break; 252 case OLD_TIME: 253 case NEW_TIME: 254 break; 255 case USER_PROCESS: 256 utx_active_add(&fu); 257 utx_lastlogin_add(&fu); 258 break; 259#if 0 /* XXX: Are these records of any use to us? */ 260 case INIT_PROCESS: 261 case LOGIN_PROCESS: 262 utx_active_add(&fu); 263 break; 264#endif 265 case DEAD_PROCESS: 266 if (utx_active_remove(&fu) != 0) 267 return (NULL); 268 break; 269 default: 270 return (NULL); 271 } 272 273 utx_log_add(&fu); 274 return (futx_to_utx(&fu)); 275} 276