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$"); 29 30 #include "namespace.h" 31 #include <sys/endian.h> 32 #include <sys/stat.h> 33 #include <sys/uio.h> 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <stdio.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <utmpx.h> 40 #include "utxdb.h" 41 #include "un-namespace.h" 42 43 static FILE * 44 futx_open(const char *file) 45 { 46 FILE *fp; 47 struct stat sb; 48 int fd; 49 50 fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK|O_CLOEXEC, 0644); 51 if (fd < 0) 52 return (NULL); 53 54 /* Safety check: never use broken files. */ 55 if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) { 56 _close(fd); 57 errno = EFTYPE; 58 return (NULL); 59 } 60 61 fp = fdopen(fd, "r+"); 62 if (fp == NULL) { 63 _close(fd); 64 return (NULL); 65 } 66 return (fp); 67 } 68 69 static int 70 utx_active_add(const struct futx *fu) 71 { 72 FILE *fp; 73 struct futx fe; 74 off_t partial; 75 int error, ret; 76 77 partial = -1; 78 ret = 0; 79 80 /* 81 * Register user login sessions. Overwrite entries of sessions 82 * that have already been terminated. 83 */ 84 fp = futx_open(_PATH_UTX_ACTIVE); 85 if (fp == NULL) 86 return (-1); 87 while (fread(&fe, sizeof(fe), 1, fp) == 1) { 88 switch (fe.fu_type) { 89 case BOOT_TIME: 90 /* Leave these intact. */ 91 break; 92 case USER_PROCESS: 93 case INIT_PROCESS: 94 case LOGIN_PROCESS: 95 case DEAD_PROCESS: 96 /* Overwrite when ut_id matches. */ 97 if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) == 98 0) { 99 ret = fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR); 100 goto exact; 101 } 102 if (fe.fu_type != DEAD_PROCESS) 103 break; 104 /* FALLTHROUGH */ 105 default: 106 /* Allow us to overwrite unused records. */ 107 if (partial == -1) { 108 partial = ftello(fp); 109 /* 110 * Distinguish errors from valid values so we 111 * don't overwrite good data by accident. 112 */ 113 if (partial != -1) 114 partial -= (off_t)sizeof(fe); 115 } 116 break; 117 } 118 } 119 120 /* 121 * No exact match found. Use the partial match. If no partial 122 * match was found, just append a new record. 123 */ 124 if (partial != -1) 125 ret = fseeko(fp, partial, SEEK_SET); 126 exact: 127 if (ret == -1) 128 error = errno; 129 else if (fwrite(fu, sizeof(*fu), 1, fp) < 1) 130 error = errno; 131 else 132 error = 0; 133 fclose(fp); 134 if (error != 0) 135 errno = error; 136 return (error == 0 ? 0 : 1); 137 } 138 139 static int 140 utx_active_remove(struct futx *fu) 141 { 142 FILE *fp; 143 struct futx fe; 144 int error, ret; 145 146 /* 147 * Remove user login sessions, having the same ut_id. 148 */ 149 fp = futx_open(_PATH_UTX_ACTIVE); 150 if (fp == NULL) 151 return (-1); 152 error = ESRCH; 153 ret = -1; 154 while (fread(&fe, sizeof(fe), 1, fp) == 1 && ret != 0) 155 switch (fe.fu_type) { 156 case USER_PROCESS: 157 case INIT_PROCESS: 158 case LOGIN_PROCESS: 159 if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) != 0) 160 continue; 161 162 /* Terminate session. */ 163 if (fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR) == -1) 164 error = errno; 165 else if (fwrite(fu, sizeof(*fu), 1, fp) < 1) 166 error = errno; 167 else 168 ret = 0; 169 170 } 171 172 fclose(fp); 173 if (ret != 0) 174 errno = error; 175 return (ret); 176 } 177 178 static void 179 utx_active_init(const struct futx *fu) 180 { 181 int fd; 182 183 /* Initialize utx.active with a single BOOT_TIME record. */ 184 fd = _open(_PATH_UTX_ACTIVE, O_CREAT|O_RDWR|O_TRUNC, 0644); 185 if (fd < 0) 186 return; 187 _write(fd, fu, sizeof(*fu)); 188 _close(fd); 189 } 190 191 static void 192 utx_active_purge(void) 193 { 194 195 truncate(_PATH_UTX_ACTIVE, 0); 196 } 197 198 static int 199 utx_lastlogin_add(const struct futx *fu) 200 { 201 struct futx fe; 202 FILE *fp; 203 int error, ret; 204 205 ret = 0; 206 207 /* 208 * Write an entry to lastlogin. Overwrite the entry if the 209 * current user already has an entry. If not, append a new 210 * entry. 211 */ 212 fp = futx_open(_PATH_UTX_LASTLOGIN); 213 if (fp == NULL) 214 return (-1); 215 while (fread(&fe, sizeof fe, 1, fp) == 1) { 216 if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0) 217 continue; 218 219 /* Found a previous lastlogin entry for this user. */ 220 ret = fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); 221 break; 222 } 223 if (ret == -1) 224 error = errno; 225 else if (fwrite(fu, sizeof *fu, 1, fp) < 1) { 226 error = errno; 227 ret = -1; 228 } 229 fclose(fp); 230 if (ret == -1) 231 errno = error; 232 return (ret); 233 } 234 235 static void 236 utx_lastlogin_upgrade(void) 237 { 238 struct stat sb; 239 int fd; 240 241 fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR|O_CLOEXEC, 0644); 242 if (fd < 0) 243 return; 244 245 /* 246 * Truncate broken lastlogin files. In the future we should 247 * check for older versions of the file format here and try to 248 * upgrade it. 249 */ 250 if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) 251 ftruncate(fd, 0); 252 _close(fd); 253 } 254 255 static int 256 utx_log_add(const struct futx *fu) 257 { 258 struct iovec vec[2]; 259 int error, fd; 260 uint16_t l; 261 262 /* 263 * Append an entry to the log file. We only need to append 264 * records to this file, so to conserve space, trim any trailing 265 * zero-bytes. Prepend a length field, indicating the length of 266 * the record, excluding the length field itself. 267 */ 268 for (l = sizeof(*fu); l > 0 && ((const char *)fu)[l - 1] == '\0'; l--) ; 269 vec[0].iov_base = &l; 270 vec[0].iov_len = sizeof(l); 271 vec[1].iov_base = __DECONST(void *, fu); 272 vec[1].iov_len = l; 273 l = htobe16(l); 274 275 fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND|O_CLOEXEC, 0644); 276 if (fd < 0) 277 return (-1); 278 if (_writev(fd, vec, 2) == -1) 279 error = errno; 280 else 281 error = 0; 282 _close(fd); 283 if (error != 0) 284 errno = error; 285 return (error == 0 ? 0 : 1); 286 } 287 288 struct utmpx * 289 pututxline(const struct utmpx *utmpx) 290 { 291 struct futx fu; 292 int bad; 293 294 bad = 0; 295 296 utx_to_futx(utmpx, &fu); 297 298 switch (fu.fu_type) { 299 case BOOT_TIME: 300 utx_active_init(&fu); 301 utx_lastlogin_upgrade(); 302 break; 303 case SHUTDOWN_TIME: 304 utx_active_purge(); 305 break; 306 case OLD_TIME: 307 case NEW_TIME: 308 break; 309 case USER_PROCESS: 310 bad |= utx_active_add(&fu); 311 bad |= utx_lastlogin_add(&fu); 312 break; 313 #if 0 /* XXX: Are these records of any use to us? */ 314 case INIT_PROCESS: 315 case LOGIN_PROCESS: 316 bad |= utx_active_add(&fu); 317 break; 318 #endif 319 case DEAD_PROCESS: 320 /* 321 * In case writing a logout entry fails, never attempt 322 * to write it to utx.log. The logout entry's ut_id 323 * might be invalid. 324 */ 325 if (utx_active_remove(&fu) != 0) 326 return (NULL); 327 break; 328 default: 329 errno = EINVAL; 330 return (NULL); 331 } 332 333 bad |= utx_log_add(&fu); 334 return (bad ? NULL : futx_to_utx(&fu)); 335 } 336