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 <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 42 static FILE * 43 futx_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 68 static void 69 utx_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); 110 exact: 111 fwrite(fu, sizeof *fu, 1, fp); 112 fclose(fp); 113 } 114 115 static int 116 utx_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 /* Terminate session. */ 136 fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); 137 fwrite(fu, sizeof *fu, 1, fp); 138 fclose(fp); 139 return (0); 140 } 141 } 142 143 fclose(fp); 144 return (1); 145 } 146 147 static void 148 utx_active_purge(void) 149 { 150 151 truncate(_PATH_UTX_ACTIVE, 0); 152 } 153 154 static void 155 utx_lastlogin_add(const struct futx *fu) 156 { 157 FILE *fp; 158 struct futx fe; 159 160 /* 161 * Write an entry to lastlogin. Overwrite the entry if the 162 * current user already has an entry. If not, append a new 163 * entry. 164 */ 165 fp = futx_open(_PATH_UTX_LASTLOGIN); 166 if (fp == NULL) 167 return; 168 while (fread(&fe, sizeof fe, 1, fp) == 1) { 169 if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0) 170 continue; 171 172 /* Found a previous lastlogin entry for this user. */ 173 fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); 174 break; 175 } 176 fwrite(fu, sizeof *fu, 1, fp); 177 fclose(fp); 178 } 179 180 static void 181 utx_lastlogin_upgrade(void) 182 { 183 int fd; 184 struct stat sb; 185 186 fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR, 0644); 187 if (fd < 0) 188 return; 189 190 /* 191 * Truncate broken lastlogin files. In the future we should 192 * check for older versions of the file format here and try to 193 * upgrade it. 194 */ 195 if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) 196 ftruncate(fd, 0); 197 _close(fd); 198 } 199 200 static void 201 utx_log_add(const struct futx *fu) 202 { 203 int fd; 204 uint16_t l; 205 struct iovec vec[2]; 206 207 /* 208 * Append an entry to the log file. We only need to append 209 * records to this file, so to conserve space, trim any trailing 210 * zero-bytes. Prepend a length field, indicating the length of 211 * the record, excluding the length field itself. 212 */ 213 for (l = sizeof *fu; l > 0 && ((const char *)fu)[l - 1] == '\0'; l--); 214 vec[0].iov_base = &l; 215 vec[0].iov_len = sizeof l; 216 vec[1].iov_base = __DECONST(void *, fu); 217 vec[1].iov_len = l; 218 l = htobe16(l); 219 220 fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND, 0644); 221 if (fd < 0) 222 return; 223 _writev(fd, vec, 2); 224 _close(fd); 225 } 226 227 struct utmpx * 228 pututxline(const struct utmpx *utmpx) 229 { 230 struct futx fu; 231 232 utx_to_futx(utmpx, &fu); 233 234 switch (fu.fu_type) { 235 case BOOT_TIME: 236 case SHUTDOWN_TIME: 237 utx_active_purge(); 238 utx_lastlogin_upgrade(); 239 break; 240 case OLD_TIME: 241 case NEW_TIME: 242 break; 243 case USER_PROCESS: 244 utx_active_add(&fu); 245 utx_lastlogin_add(&fu); 246 break; 247 #if 0 /* XXX: Are these records of any use to us? */ 248 case INIT_PROCESS: 249 case LOGIN_PROCESS: 250 utx_active_add(&fu); 251 break; 252 #endif 253 case DEAD_PROCESS: 254 if (utx_active_remove(&fu) != 0) 255 return (NULL); 256 break; 257 default: 258 return (NULL); 259 } 260 261 utx_log_add(&fu); 262 return (futx_to_utx(&fu)); 263 } 264