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