1202188Sed/*- 2202188Sed * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org> 3202188Sed * All rights reserved. 4202188Sed * 5202188Sed * Redistribution and use in source and binary forms, with or without 6202188Sed * modification, are permitted provided that the following conditions 7202188Sed * are met: 8202188Sed * 1. Redistributions of source code must retain the above copyright 9202188Sed * notice, this list of conditions and the following disclaimer. 10202188Sed * 2. Redistributions in binary form must reproduce the above copyright 11202188Sed * notice, this list of conditions and the following disclaimer in the 12202188Sed * documentation and/or other materials provided with the distribution. 13202188Sed * 14202188Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15202188Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16202188Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17202188Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18202188Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19202188Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20202188Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21202188Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22202188Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23202188Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24202188Sed * SUCH DAMAGE. 25202188Sed */ 26202188Sed 27202188Sed#include <sys/cdefs.h> 28202188Sed__FBSDID("$FreeBSD$"); 29202188Sed 30202188Sed#include "namespace.h" 31202188Sed#include <sys/endian.h> 32202188Sed#include <sys/stat.h> 33202188Sed#include <sys/uio.h> 34215310Sed#include <errno.h> 35202188Sed#include <fcntl.h> 36202188Sed#include <stdio.h> 37202188Sed#include <string.h> 38202188Sed#include <unistd.h> 39202188Sed#include <utmpx.h> 40202188Sed#include "utxdb.h" 41202188Sed#include "un-namespace.h" 42202188Sed 43202188Sedstatic FILE * 44202188Sedfutx_open(const char *file) 45202188Sed{ 46219045Sed FILE *fp; 47218846Sed struct stat sb; 48202188Sed int fd; 49202188Sed 50261813Sjilles fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK|O_CLOEXEC, 0644); 51202188Sed if (fd < 0) 52202188Sed return (NULL); 53202188Sed 54202188Sed /* Safety check: never use broken files. */ 55202188Sed if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) { 56202188Sed _close(fd); 57218846Sed errno = EFTYPE; 58202188Sed return (NULL); 59202188Sed } 60223576Sed 61202188Sed fp = fdopen(fd, "r+"); 62202188Sed if (fp == NULL) { 63202188Sed _close(fd); 64202188Sed return (NULL); 65202188Sed } 66202188Sed return (fp); 67202188Sed} 68202188Sed 69214134Sedstatic int 70202188Sedutx_active_add(const struct futx *fu) 71202188Sed{ 72219045Sed FILE *fp; 73218846Sed struct futx fe; 74219045Sed off_t partial; 75218846Sed int error, ret; 76202188Sed 77219045Sed partial = -1; 78219045Sed ret = 0; 79219045Sed 80202188Sed /* 81202188Sed * Register user login sessions. Overwrite entries of sessions 82202188Sed * that have already been terminated. 83202188Sed */ 84202188Sed fp = futx_open(_PATH_UTX_ACTIVE); 85202188Sed if (fp == NULL) 86218846Sed return (-1); 87218846Sed while (fread(&fe, sizeof(fe), 1, fp) == 1) { 88202188Sed switch (fe.fu_type) { 89202188Sed case USER_PROCESS: 90202188Sed case INIT_PROCESS: 91202188Sed case LOGIN_PROCESS: 92202188Sed case DEAD_PROCESS: 93202188Sed /* Overwrite when ut_id matches. */ 94218846Sed if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) == 95218846Sed 0) { 96218846Sed ret = fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR); 97202188Sed goto exact; 98202188Sed } 99202188Sed if (fe.fu_type != DEAD_PROCESS) 100202188Sed break; 101202188Sed /* FALLTHROUGH */ 102202188Sed default: 103202188Sed /* Allow us to overwrite unused records. */ 104218846Sed if (partial == -1) { 105218846Sed partial = ftello(fp); 106223576Sed /* 107218846Sed * Distinguish errors from valid values so we 108218846Sed * don't overwrite good data by accident. 109218846Sed */ 110218846Sed if (partial != -1) 111218846Sed partial -= (off_t)sizeof(fe); 112218846Sed } 113202188Sed break; 114202188Sed } 115202188Sed } 116218846Sed 117202188Sed /* 118202188Sed * No exact match found. Use the partial match. If no partial 119202188Sed * match was found, just append a new record. 120202188Sed */ 121202188Sed if (partial != -1) 122218846Sed ret = fseeko(fp, partial, SEEK_SET); 123202188Sedexact: 124218846Sed if (ret == -1) 125218846Sed error = errno; 126218846Sed else if (fwrite(fu, sizeof(*fu), 1, fp) < 1) 127218846Sed error = errno; 128218846Sed else 129218846Sed error = 0; 130202188Sed fclose(fp); 131218846Sed errno = error; 132218846Sed return (error == 0 ? 0 : 1); 133202188Sed} 134202188Sed 135202188Sedstatic int 136202188Sedutx_active_remove(struct futx *fu) 137202188Sed{ 138219045Sed FILE *fp; 139218846Sed struct futx fe; 140218846Sed int error, ret; 141202188Sed 142202188Sed /* 143202188Sed * Remove user login sessions, having the same ut_id. 144202188Sed */ 145202188Sed fp = futx_open(_PATH_UTX_ACTIVE); 146202188Sed if (fp == NULL) 147218846Sed return (-1); 148218846Sed error = ESRCH; 149218846Sed ret = -1; 150218846Sed while (fread(&fe, sizeof(fe), 1, fp) == 1 && ret != 0) 151202188Sed switch (fe.fu_type) { 152202188Sed case USER_PROCESS: 153202188Sed case INIT_PROCESS: 154202188Sed case LOGIN_PROCESS: 155218846Sed if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) != 0) 156202188Sed continue; 157202188Sed 158202188Sed /* Terminate session. */ 159218846Sed if (fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR) == -1) 160218846Sed error = errno; 161218846Sed else if (fwrite(fu, sizeof(*fu), 1, fp) < 1) 162218846Sed error = errno; 163218846Sed else 164218846Sed ret = 0; 165218846Sed 166202188Sed } 167202188Sed 168202188Sed fclose(fp); 169218846Sed errno = error; 170218846Sed return (ret); 171202188Sed} 172202188Sed 173202188Sedstatic void 174202188Sedutx_active_purge(void) 175202188Sed{ 176202188Sed 177202188Sed truncate(_PATH_UTX_ACTIVE, 0); 178202188Sed} 179202188Sed 180214134Sedstatic int 181202188Sedutx_lastlogin_add(const struct futx *fu) 182202188Sed{ 183218846Sed struct futx fe; 184202188Sed FILE *fp; 185218846Sed int error, ret; 186202188Sed 187218846Sed ret = 0; 188218846Sed 189202188Sed /* 190202188Sed * Write an entry to lastlogin. Overwrite the entry if the 191202188Sed * current user already has an entry. If not, append a new 192202188Sed * entry. 193202188Sed */ 194202188Sed fp = futx_open(_PATH_UTX_LASTLOGIN); 195202188Sed if (fp == NULL) 196218846Sed return (-1); 197202188Sed while (fread(&fe, sizeof fe, 1, fp) == 1) { 198202188Sed if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0) 199202188Sed continue; 200218846Sed 201202188Sed /* Found a previous lastlogin entry for this user. */ 202218846Sed ret = fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); 203202188Sed break; 204202188Sed } 205218846Sed if (ret == -1) 206218846Sed error = errno; 207218846Sed else if (fwrite(fu, sizeof *fu, 1, fp) < 1) { 208218846Sed error = errno; 209218846Sed ret = -1; 210218846Sed } 211202188Sed fclose(fp); 212218846Sed errno = error; 213218846Sed return (ret); 214202188Sed} 215202188Sed 216202188Sedstatic void 217202188Sedutx_lastlogin_upgrade(void) 218202188Sed{ 219218846Sed struct stat sb; 220202188Sed int fd; 221202188Sed 222261813Sjilles fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR|O_CLOEXEC, 0644); 223202188Sed if (fd < 0) 224202188Sed return; 225202188Sed 226202188Sed /* 227202188Sed * Truncate broken lastlogin files. In the future we should 228202188Sed * check for older versions of the file format here and try to 229202188Sed * upgrade it. 230202188Sed */ 231202188Sed if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) 232202188Sed ftruncate(fd, 0); 233202188Sed _close(fd); 234202188Sed} 235202188Sed 236214134Sedstatic int 237202188Sedutx_log_add(const struct futx *fu) 238202188Sed{ 239218846Sed struct iovec vec[2]; 240218846Sed int error, fd; 241202188Sed uint16_t l; 242202188Sed 243202188Sed /* 244202188Sed * Append an entry to the log file. We only need to append 245202188Sed * records to this file, so to conserve space, trim any trailing 246202188Sed * zero-bytes. Prepend a length field, indicating the length of 247202188Sed * the record, excluding the length field itself. 248202188Sed */ 249218846Sed for (l = sizeof(*fu); l > 0 && ((const char *)fu)[l - 1] == '\0'; l--) ; 250202188Sed vec[0].iov_base = &l; 251218846Sed vec[0].iov_len = sizeof(l); 252202188Sed vec[1].iov_base = __DECONST(void *, fu); 253202188Sed vec[1].iov_len = l; 254202188Sed l = htobe16(l); 255202188Sed 256261813Sjilles fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND|O_CLOEXEC, 0644); 257202188Sed if (fd < 0) 258218846Sed return (-1); 259218846Sed if (_writev(fd, vec, 2) == -1) 260218846Sed error = errno; 261218846Sed else 262218846Sed error = 0; 263202188Sed _close(fd); 264218846Sed errno = error; 265218846Sed return (error == 0 ? 0 : 1); 266202188Sed} 267202188Sed 268202188Sedstruct utmpx * 269202188Sedpututxline(const struct utmpx *utmpx) 270202188Sed{ 271202188Sed struct futx fu; 272219045Sed int bad; 273202188Sed 274219045Sed bad = 0; 275219045Sed 276202188Sed utx_to_futx(utmpx, &fu); 277218846Sed 278202188Sed switch (fu.fu_type) { 279202188Sed case BOOT_TIME: 280202188Sed case SHUTDOWN_TIME: 281202188Sed utx_active_purge(); 282202188Sed utx_lastlogin_upgrade(); 283202188Sed break; 284202188Sed case OLD_TIME: 285202188Sed case NEW_TIME: 286202188Sed break; 287202188Sed case USER_PROCESS: 288214134Sed bad |= utx_active_add(&fu); 289214134Sed bad |= utx_lastlogin_add(&fu); 290202188Sed break; 291202188Sed#if 0 /* XXX: Are these records of any use to us? */ 292202188Sed case INIT_PROCESS: 293202188Sed case LOGIN_PROCESS: 294214134Sed bad |= utx_active_add(&fu); 295202188Sed break; 296202188Sed#endif 297202188Sed case DEAD_PROCESS: 298214134Sed /* 299214134Sed * In case writing a logout entry fails, never attempt 300214134Sed * to write it to utx.log. The logout entry's ut_id 301214134Sed * might be invalid. 302214134Sed */ 303202188Sed if (utx_active_remove(&fu) != 0) 304202188Sed return (NULL); 305202188Sed break; 306202188Sed default: 307218846Sed errno = EINVAL; 308202188Sed return (NULL); 309202188Sed } 310202188Sed 311214134Sed bad |= utx_log_add(&fu); 312214134Sed return (bad ? NULL : futx_to_utx(&fu)); 313202188Sed} 314