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: releng/10.3/lib/libc/gen/pututxline.c 249593 2013-04-17 21:08:15Z jilles $"); 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 50241046Sjilles 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) { 89226846Sed case BOOT_TIME: 90226846Sed /* Leave these intact. */ 91226846Sed break; 92202188Sed case USER_PROCESS: 93202188Sed case INIT_PROCESS: 94202188Sed case LOGIN_PROCESS: 95202188Sed case DEAD_PROCESS: 96202188Sed /* Overwrite when ut_id matches. */ 97218846Sed if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) == 98218846Sed 0) { 99218846Sed ret = fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR); 100202188Sed goto exact; 101202188Sed } 102202188Sed if (fe.fu_type != DEAD_PROCESS) 103202188Sed break; 104202188Sed /* FALLTHROUGH */ 105202188Sed default: 106202188Sed /* Allow us to overwrite unused records. */ 107218846Sed if (partial == -1) { 108218846Sed partial = ftello(fp); 109223576Sed /* 110218846Sed * Distinguish errors from valid values so we 111218846Sed * don't overwrite good data by accident. 112218846Sed */ 113218846Sed if (partial != -1) 114218846Sed partial -= (off_t)sizeof(fe); 115218846Sed } 116202188Sed break; 117202188Sed } 118202188Sed } 119218846Sed 120202188Sed /* 121202188Sed * No exact match found. Use the partial match. If no partial 122202188Sed * match was found, just append a new record. 123202188Sed */ 124202188Sed if (partial != -1) 125218846Sed ret = fseeko(fp, partial, SEEK_SET); 126202188Sedexact: 127218846Sed if (ret == -1) 128218846Sed error = errno; 129218846Sed else if (fwrite(fu, sizeof(*fu), 1, fp) < 1) 130218846Sed error = errno; 131218846Sed else 132218846Sed error = 0; 133202188Sed fclose(fp); 134249593Sjilles if (error != 0) 135249593Sjilles errno = error; 136218846Sed return (error == 0 ? 0 : 1); 137202188Sed} 138202188Sed 139202188Sedstatic int 140202188Sedutx_active_remove(struct futx *fu) 141202188Sed{ 142219045Sed FILE *fp; 143218846Sed struct futx fe; 144218846Sed int error, ret; 145202188Sed 146202188Sed /* 147202188Sed * Remove user login sessions, having the same ut_id. 148202188Sed */ 149202188Sed fp = futx_open(_PATH_UTX_ACTIVE); 150202188Sed if (fp == NULL) 151218846Sed return (-1); 152218846Sed error = ESRCH; 153218846Sed ret = -1; 154218846Sed while (fread(&fe, sizeof(fe), 1, fp) == 1 && ret != 0) 155202188Sed switch (fe.fu_type) { 156202188Sed case USER_PROCESS: 157202188Sed case INIT_PROCESS: 158202188Sed case LOGIN_PROCESS: 159218846Sed if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) != 0) 160202188Sed continue; 161202188Sed 162202188Sed /* Terminate session. */ 163218846Sed if (fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR) == -1) 164218846Sed error = errno; 165218846Sed else if (fwrite(fu, sizeof(*fu), 1, fp) < 1) 166218846Sed error = errno; 167218846Sed else 168218846Sed ret = 0; 169218846Sed 170202188Sed } 171202188Sed 172202188Sed fclose(fp); 173249593Sjilles if (ret != 0) 174249593Sjilles errno = error; 175218846Sed return (ret); 176202188Sed} 177202188Sed 178202188Sedstatic void 179226846Sedutx_active_init(const struct futx *fu) 180226846Sed{ 181226846Sed int fd; 182226846Sed 183226846Sed /* Initialize utx.active with a single BOOT_TIME record. */ 184226846Sed fd = _open(_PATH_UTX_ACTIVE, O_CREAT|O_RDWR|O_TRUNC, 0644); 185226846Sed if (fd < 0) 186226846Sed return; 187226846Sed _write(fd, fu, sizeof(*fu)); 188226846Sed _close(fd); 189226846Sed} 190226846Sed 191226846Sedstatic void 192202188Sedutx_active_purge(void) 193202188Sed{ 194202188Sed 195202188Sed truncate(_PATH_UTX_ACTIVE, 0); 196202188Sed} 197202188Sed 198214134Sedstatic int 199202188Sedutx_lastlogin_add(const struct futx *fu) 200202188Sed{ 201218846Sed struct futx fe; 202202188Sed FILE *fp; 203218846Sed int error, ret; 204202188Sed 205218846Sed ret = 0; 206218846Sed 207202188Sed /* 208202188Sed * Write an entry to lastlogin. Overwrite the entry if the 209202188Sed * current user already has an entry. If not, append a new 210202188Sed * entry. 211202188Sed */ 212202188Sed fp = futx_open(_PATH_UTX_LASTLOGIN); 213202188Sed if (fp == NULL) 214218846Sed return (-1); 215202188Sed while (fread(&fe, sizeof fe, 1, fp) == 1) { 216202188Sed if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0) 217202188Sed continue; 218218846Sed 219202188Sed /* Found a previous lastlogin entry for this user. */ 220218846Sed ret = fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); 221202188Sed break; 222202188Sed } 223218846Sed if (ret == -1) 224218846Sed error = errno; 225218846Sed else if (fwrite(fu, sizeof *fu, 1, fp) < 1) { 226218846Sed error = errno; 227218846Sed ret = -1; 228218846Sed } 229202188Sed fclose(fp); 230249593Sjilles if (ret == -1) 231249593Sjilles errno = error; 232218846Sed return (ret); 233202188Sed} 234202188Sed 235202188Sedstatic void 236202188Sedutx_lastlogin_upgrade(void) 237202188Sed{ 238218846Sed struct stat sb; 239202188Sed int fd; 240202188Sed 241241046Sjilles fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR|O_CLOEXEC, 0644); 242202188Sed if (fd < 0) 243202188Sed return; 244202188Sed 245202188Sed /* 246202188Sed * Truncate broken lastlogin files. In the future we should 247202188Sed * check for older versions of the file format here and try to 248202188Sed * upgrade it. 249202188Sed */ 250202188Sed if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) 251202188Sed ftruncate(fd, 0); 252202188Sed _close(fd); 253202188Sed} 254202188Sed 255214134Sedstatic int 256202188Sedutx_log_add(const struct futx *fu) 257202188Sed{ 258218846Sed struct iovec vec[2]; 259218846Sed int error, fd; 260202188Sed uint16_t l; 261202188Sed 262202188Sed /* 263202188Sed * Append an entry to the log file. We only need to append 264202188Sed * records to this file, so to conserve space, trim any trailing 265202188Sed * zero-bytes. Prepend a length field, indicating the length of 266202188Sed * the record, excluding the length field itself. 267202188Sed */ 268218846Sed for (l = sizeof(*fu); l > 0 && ((const char *)fu)[l - 1] == '\0'; l--) ; 269202188Sed vec[0].iov_base = &l; 270218846Sed vec[0].iov_len = sizeof(l); 271202188Sed vec[1].iov_base = __DECONST(void *, fu); 272202188Sed vec[1].iov_len = l; 273202188Sed l = htobe16(l); 274202188Sed 275241046Sjilles fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND|O_CLOEXEC, 0644); 276202188Sed if (fd < 0) 277218846Sed return (-1); 278218846Sed if (_writev(fd, vec, 2) == -1) 279218846Sed error = errno; 280218846Sed else 281218846Sed error = 0; 282202188Sed _close(fd); 283249593Sjilles if (error != 0) 284249593Sjilles errno = error; 285218846Sed return (error == 0 ? 0 : 1); 286202188Sed} 287202188Sed 288202188Sedstruct utmpx * 289202188Sedpututxline(const struct utmpx *utmpx) 290202188Sed{ 291202188Sed struct futx fu; 292219045Sed int bad; 293202188Sed 294219045Sed bad = 0; 295219045Sed 296202188Sed utx_to_futx(utmpx, &fu); 297218846Sed 298202188Sed switch (fu.fu_type) { 299202188Sed case BOOT_TIME: 300226846Sed utx_active_init(&fu); 301226846Sed utx_lastlogin_upgrade(); 302226846Sed break; 303202188Sed case SHUTDOWN_TIME: 304202188Sed utx_active_purge(); 305202188Sed break; 306202188Sed case OLD_TIME: 307202188Sed case NEW_TIME: 308202188Sed break; 309202188Sed case USER_PROCESS: 310214134Sed bad |= utx_active_add(&fu); 311214134Sed bad |= utx_lastlogin_add(&fu); 312202188Sed break; 313202188Sed#if 0 /* XXX: Are these records of any use to us? */ 314202188Sed case INIT_PROCESS: 315202188Sed case LOGIN_PROCESS: 316214134Sed bad |= utx_active_add(&fu); 317202188Sed break; 318202188Sed#endif 319202188Sed case DEAD_PROCESS: 320214134Sed /* 321214134Sed * In case writing a logout entry fails, never attempt 322214134Sed * to write it to utx.log. The logout entry's ut_id 323214134Sed * might be invalid. 324214134Sed */ 325202188Sed if (utx_active_remove(&fu) != 0) 326202188Sed return (NULL); 327202188Sed break; 328202188Sed default: 329218846Sed errno = EINVAL; 330202188Sed return (NULL); 331202188Sed } 332202188Sed 333214134Sed bad |= utx_log_add(&fu); 334214134Sed return (bad ? NULL : futx_to_utx(&fu)); 335202188Sed} 336