1 /* $NetBSD: utmpx.c,v 1.13 2002/09/28 01:43:24 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christos Zoulas. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 #include <sys/cdefs.h> 39 40 #if defined(LIBC_SCCS) && !defined(lint) 41 __RCSID("$NetBSD: utmpx.c,v 1.13 2002/09/28 01:43:24 christos Exp $"); 42 #endif /* LIBC_SCCS and not lint */ 43 44 #include <sys/types.h> 45 #include <sys/param.h> 46 #include <sys/wait.h> 47 #include <sys/socket.h> 48 #include <sys/time.h> 49 #include <sys/stat.h> 50 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <vis.h> 55 #include <utmp.h> 56 #include <utmpx.h> 57 #include <unistd.h> 58 #include <fcntl.h> 59 #include <errno.h> 60 #include <db.h> 61 62 static FILE *fp; 63 static struct utmpx ut; 64 static char utfile[MAXPATHLEN] = _PATH_UTMPX; 65 static char llfile[MAXPATHLEN] = _PATH_LASTLOGX; 66 67 static struct utmpx *utmp_update(const struct utmpx *); 68 69 static const char vers[] = "utmpx-1.00"; 70 71 void 72 setutxent() 73 { 74 75 (void)memset(&ut, 0, sizeof(ut)); 76 if (fp == NULL) 77 return; 78 (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET); 79 } 80 81 82 void 83 endutxent() 84 { 85 86 (void)memset(&ut, 0, sizeof(ut)); 87 if (fp != NULL) { 88 (void)fclose(fp); 89 fp = NULL; 90 } 91 } 92 93 94 struct utmpx * 95 getutxent() 96 { 97 98 if (fp == NULL) { 99 struct stat st; 100 101 if ((fp = fopen(utfile, "r+")) == NULL) 102 if ((fp = fopen(utfile, "w+")) == NULL) 103 if ((fp = fopen(utfile, "r")) == NULL) 104 goto fail; 105 106 /* get file size in order to check if new file */ 107 if (fstat(fileno(fp), &st) == -1) 108 goto failclose; 109 110 if (st.st_size == 0) { 111 /* new file, add signature record */ 112 (void)memset(&ut, 0, sizeof(ut)); 113 ut.ut_type = SIGNATURE; 114 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 115 if (fwrite(&ut, sizeof(ut), 1, fp) != 1) 116 goto failclose; 117 } else { 118 /* old file, read signature record */ 119 if (fread(&ut, sizeof(ut), 1, fp) != 1) 120 goto failclose; 121 if (memcmp(ut.ut_user, vers, sizeof(vers)) != 0 || 122 ut.ut_type != SIGNATURE) 123 goto failclose; 124 } 125 } 126 127 if (fread(&ut, sizeof(ut), 1, fp) != 1) 128 goto fail; 129 130 return &ut; 131 failclose: 132 (void)fclose(fp); 133 fail: 134 (void)memset(&ut, 0, sizeof(ut)); 135 return NULL; 136 } 137 138 139 struct utmpx * 140 getutxid(const struct utmpx *utx) 141 { 142 143 if (utx->ut_type == EMPTY) 144 return NULL; 145 146 do { 147 if (ut.ut_type == EMPTY) 148 continue; 149 switch (utx->ut_type) { 150 case EMPTY: 151 return NULL; 152 case RUN_LVL: 153 case BOOT_TIME: 154 case OLD_TIME: 155 case NEW_TIME: 156 if (ut.ut_type == utx->ut_type) 157 return &ut; 158 break; 159 case INIT_PROCESS: 160 case LOGIN_PROCESS: 161 case USER_PROCESS: 162 case DEAD_PROCESS: 163 switch (ut.ut_type) { 164 case INIT_PROCESS: 165 case LOGIN_PROCESS: 166 case USER_PROCESS: 167 case DEAD_PROCESS: 168 if (memcmp(ut.ut_id, utx->ut_id, 169 sizeof(ut.ut_id)) == 0) 170 return &ut; 171 break; 172 default: 173 break; 174 } 175 break; 176 default: 177 return NULL; 178 } 179 } while (getutxent() != NULL); 180 return NULL; 181 } 182 183 184 struct utmpx * 185 getutxline(const struct utmpx *utx) 186 { 187 188 do { 189 switch (ut.ut_type) { 190 case EMPTY: 191 break; 192 case LOGIN_PROCESS: 193 case USER_PROCESS: 194 if (strncmp(ut.ut_line, utx->ut_line, 195 sizeof(ut.ut_line)) == 0) 196 return &ut; 197 break; 198 default: 199 break; 200 } 201 } while (getutxent() != NULL); 202 return NULL; 203 } 204 205 206 struct utmpx * 207 pututxline(const struct utmpx *utx) 208 { 209 struct utmpx temp, *u = NULL; 210 int gotlock = 0; 211 212 if (strcmp(_PATH_UTMPX, utfile) == 0 && geteuid() != 0) 213 return utmp_update(utx); 214 215 if (utx == NULL) 216 return NULL; 217 218 (void)memcpy(&temp, utx, sizeof(temp)); 219 220 if (fp == NULL) { 221 (void)getutxent(); 222 if (fp == NULL) 223 return NULL; 224 } 225 226 if (getutxid(&temp) == NULL) { 227 setutxent(); 228 if (getutxid(&temp) == NULL) { 229 if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1) 230 return NULL; 231 gotlock++; 232 if (fseeko(fp, (off_t)0, SEEK_END) == -1) 233 goto fail; 234 } 235 } 236 237 if (!gotlock) { 238 /* we are not appending */ 239 if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1) 240 return NULL; 241 } 242 243 if (fwrite(&temp, sizeof (temp), 1, fp) != 1) 244 goto fail; 245 246 if (fflush(fp) == -1) 247 goto fail; 248 249 u = memcpy(&ut, &temp, sizeof(ut)); 250 fail: 251 if (gotlock) { 252 if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1) 253 return NULL; 254 } 255 return u; 256 } 257 258 259 static struct utmpx * 260 utmp_update(const struct utmpx *utx) 261 { 262 char buf[sizeof(*utx) * 4 + 1]; 263 pid_t pid; 264 int status; 265 266 (void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx), 267 VIS_WHITE); 268 switch (pid = fork()) { 269 case 0: 270 (void)execl(_PATH_UTMP_UPDATE, 271 strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf); 272 exit(1); 273 /*NOTREACHED*/ 274 case -1: 275 return NULL; 276 default: 277 if (waitpid(pid, &status, 0) == -1) 278 return NULL; 279 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 280 return memcpy(&ut, utx, sizeof(ut)); 281 return NULL; 282 } 283 284 } 285 286 /* 287 * The following are extensions and not part of the X/Open spec. 288 */ 289 int 290 updwtmpx(const char *file, const struct utmpx *utx) 291 { 292 int fd = open(file, O_WRONLY|O_APPEND|O_EXLOCK); 293 294 if (fd == -1) { 295 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1) 296 return -1; 297 (void)memset(&ut, 0, sizeof(ut)); 298 ut.ut_type = SIGNATURE; 299 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 300 if (write(fd, &ut, sizeof(ut)) == -1) 301 return -1; 302 } 303 if (write(fd, utx, sizeof(*utx)) == -1) 304 return -1; 305 if (close(fd) == -1) 306 return -1; 307 return 0; 308 } 309 310 311 int 312 utmpxname(const char *fname) 313 { 314 size_t len = strlen(fname); 315 316 if (len >= sizeof(utfile)) 317 return 0; 318 319 /* must end in x! */ 320 if (fname[len - 1] != 'x') 321 return 0; 322 323 (void)strcpy(utfile, fname); 324 endutxent(); 325 return 1; 326 } 327 328 329 void 330 getutmp(const struct utmpx *ux, struct utmp *u) 331 { 332 333 (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name)); 334 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); 335 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); 336 u->ut_time = ux->ut_tv.tv_sec; 337 } 338 339 void 340 getutmpx(const struct utmp *u, struct utmpx *ux) 341 { 342 343 (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name)); 344 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); 345 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); 346 ux->ut_tv.tv_sec = u->ut_time; 347 ux->ut_tv.tv_usec = 0; 348 (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss)); 349 ux->ut_pid = 0; 350 ux->ut_type = USER_PROCESS; 351 ux->ut_session = 0; 352 ux->ut_exit.e_termination = 0; 353 ux->ut_exit.e_exit = 0; 354 } 355 356 int 357 lastlogxname(const char *fname) 358 { 359 size_t len = strlen(fname); 360 361 if (len >= sizeof(llfile)) 362 return 0; 363 364 /* must end in x! */ 365 if (fname[len - 1] != 'x') 366 return 0; 367 368 (void)strcpy(llfile, fname); 369 return 1; 370 } 371 372 struct lastlogx * 373 getlastlogx(uid_t uid, struct lastlogx *ll) 374 { 375 DBT key, data; 376 DB *db = dbopen(llfile, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL); 377 378 if (db == NULL) 379 return NULL; 380 381 key.data = &uid; 382 key.size = sizeof(uid); 383 384 if ((db->get)(db, &key, &data, 0) != 0) 385 goto error; 386 387 if (data.size != sizeof(*ll)) { 388 errno = EFTYPE; 389 goto error; 390 } 391 392 if (ll == NULL) 393 if ((ll = malloc(sizeof(*ll))) == NULL) 394 goto done; 395 396 (void)memcpy(ll, data.data, sizeof(*ll)); 397 goto done; 398 error: 399 ll = NULL; 400 done: 401 (db->close)(db); 402 return ll; 403 } 404 405 int 406 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 407 { 408 DBT key, data; 409 int error = 0; 410 DB *db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0, DB_HASH, NULL); 411 412 if (db == NULL) 413 return -1; 414 415 key.data = &uid; 416 key.size = sizeof(uid); 417 data.data = ll; 418 data.size = sizeof(*ll); 419 if ((db->put)(db, &key, &data, 0) != 0) 420 error = -1; 421 422 (db->close)(db); 423 return error; 424 } 425