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