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