1/* $NetBSD: utmpx.c,v 1.25 2008/04/28 20:22:59 martin 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31#include <sys/cdefs.h> 32 33#if defined(LIBC_SCCS) && !defined(lint) 34__RCSID("$NetBSD: utmpx.c,v 1.25 2008/04/28 20:22:59 martin Exp $"); 35#endif /* LIBC_SCCS and not lint */ 36 37#include "namespace.h" 38#include <sys/types.h> 39#include <sys/param.h> 40#include <sys/socket.h> 41#include <sys/stat.h> 42#include <sys/time.h> 43#include <sys/wait.h> 44 45#include <fcntl.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49#include <unistd.h> 50#ifdef UNIFDEF_LEGACY_UTMP_APIS 51#include <utmp.h> 52#endif /* UNIFDEF_LEGACY_UTMP_APIS */ 53#include <utmpx.h> 54#include <utmpx-darwin.h> 55#include <errno.h> 56#include <vis.h> 57#include <notify.h> 58 59static struct _utmpx *__utx__ = NULL; 60 61static void 62__default_utx_init(void) 63{ 64 __utx__ = calloc(1, sizeof(struct _utmpx)); 65 const char magic[] = __UTX_MAGIC__; 66 memcpy(&__utx__->magic, magic, UTMPX_MAGIC); 67 pthread_mutex_init(&__utx__->utmpx_mutex, NULL); 68 __utx__->utfile = _PATH_UTMPX; 69 __utx__->utfile_system = 1; 70} 71 72struct _utmpx * 73__default_utx(void) 74{ 75 static pthread_once_t once = PTHREAD_ONCE_INIT; 76 pthread_once(&once, &__default_utx_init); 77 return __utx__; 78} 79 80static struct utmpx *__getutxid(struct _utmpx *, const struct utmpx *); 81 82__private_extern__ const char _utmpx_vers[] = "utmpx-1.00"; 83 84__private_extern__ void 85__setutxent(struct _utmpx *U) 86{ 87 88 (void)memset(&U->ut, 0, sizeof(U->ut)); 89 if (U->fp == NULL) 90 return; 91#ifdef __LP64__ 92 (void)fseeko(U->fp, (off_t)sizeof(struct utmpx32), SEEK_SET); 93#else /* __LP64__ */ 94 (void)fseeko(U->fp, (off_t)sizeof(U->ut), SEEK_SET); 95#endif /* __LP64__ */ 96} 97 98void 99_setutxent(struct _utmpx *U) 100{ 101 102 TEST_UTMPX_T("_setutxent", U); 103 UTMPX_LOCK(U); 104 __setutxent(U); 105 UTMPX_UNLOCK(U); 106} 107 108 109void 110setutxent(void) 111{ 112 _setutxent(__default_utx()); 113} 114 115 116__private_extern__ void 117__endutxent(struct _utmpx *U) 118{ 119 (void)memset(&U->ut, 0, sizeof(U->ut)); 120 if (U->fp != NULL) { 121 int saveerrno = errno; 122 (void)fclose(U->fp); 123 errno = saveerrno; 124 U->fp = NULL; 125 U->readonly = 0; 126 } 127} 128 129 130void 131_endutxent(struct _utmpx *U) 132{ 133 TEST_UTMPX_T("_endutxent", U); 134 UTMPX_LOCK(U); 135 __endutxent(U); 136 UTMPX_UNLOCK(U); 137} 138 139 140void 141endutxent(void) 142{ 143 _endutxent(__default_utx()); 144} 145 146 147__private_extern__ struct utmpx * 148__getutxent(struct _utmpx *U) 149{ 150 int saveerrno; 151#ifdef __LP64__ 152 struct utmpx32 ut32; 153#endif /* __LP64__ */ 154 155 if (U->fp == NULL) { 156 struct stat st; 157 158 if ((U->fp = fopen(U->utfile, "r+")) == NULL) 159 if ((U->fp = fopen(U->utfile, "w+")) == NULL) { 160 if ((U->fp = fopen(U->utfile, "r")) == NULL) 161 goto fail; 162 else 163 U->readonly = 1; 164 } 165 166 fcntl(fileno(U->fp), F_SETFD, 1); /* set close-on-exec flag */ 167 168 /* get file size in order to check if new file */ 169 if (fstat(fileno(U->fp), &st) == -1) 170 goto failclose; 171 172 if (st.st_size == 0) { 173 /* new file, add signature record */ 174#ifdef __LP64__ 175 (void)memset(&ut32, 0, sizeof(ut32)); 176 ut32.ut_type = SIGNATURE; 177 (void)memcpy(ut32.ut_user, _utmpx_vers, sizeof(_utmpx_vers)); 178 if (fwrite(&ut32, sizeof(ut32), 1, U->fp) != 1) 179#else /* __LP64__ */ 180 (void)memset(&U->ut, 0, sizeof(U->ut)); 181 U->ut.ut_type = SIGNATURE; 182 (void)memcpy(U->ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers)); 183 if (fwrite(&U->ut, sizeof(U->ut), 1, U->fp) != 1) 184#endif /* __LP64__ */ 185 goto failclose; 186 } else { 187 /* old file, read signature record */ 188#ifdef __LP64__ 189 if (fread(&ut32, sizeof(ut32), 1, U->fp) != 1) 190#else /* __LP64__ */ 191 if (fread(&U->ut, sizeof(U->ut), 1, U->fp) != 1) 192#endif /* __LP64__ */ 193 goto failclose; 194#ifdef __LP64__ 195 if (memcmp(ut32.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 || 196 ut32.ut_type != SIGNATURE) 197#else /* __LP64__ */ 198 if (memcmp(U->ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 || 199 U->ut.ut_type != SIGNATURE) 200#endif /* __LP64__ */ 201 { 202 errno = EINVAL; 203 goto failclose; 204 } 205 } 206 } 207 208#ifdef __LP64__ 209 if (fread(&ut32, sizeof(ut32), 1, U->fp) != 1) 210#else /* __LP64__ */ 211 if (fread(&U->ut, sizeof(U->ut), 1, U->fp) != 1) 212#endif /* __LP64__ */ 213 goto fail; 214 215#ifdef __LP64__ 216 _utmpx32_64(&ut32, &U->ut); 217#endif /* __LP64__ */ 218 return &U->ut; 219failclose: 220 saveerrno = errno; 221 (void)fclose(U->fp); 222 errno = saveerrno; 223 U->fp = NULL; 224fail: 225 (void)memset(&U->ut, 0, sizeof(U->ut)); 226 return NULL; 227} 228 229 230struct utmpx * 231_getutxent(struct _utmpx *U) 232{ 233 struct utmpx *ret; 234 235 TEST_UTMPX_T("_getutxent", U); 236 UTMPX_LOCK(U); 237 ret = __getutxent(U); 238 UTMPX_UNLOCK(U); 239 return ret; 240} 241 242 243struct utmpx * 244getutxent(void) 245{ 246 return _getutxent(__default_utx()); 247} 248 249 250struct utmpx * 251_getutxid(struct _utmpx *U, const struct utmpx *utx) 252{ 253 struct utmpx temp; 254 const struct utmpx *ux; 255 struct utmpx *ret; 256 257 if (utx->ut_type == EMPTY) 258 return NULL; 259 260 TEST_UTMPX_T("_getutxid", U); 261 UTMPX_LOCK(U); 262 /* make a copy as needed, and auto-fill if requested */ 263 ux = _utmpx_working_copy(utx, &temp, 1); 264 if (!ux) { 265 UTMPX_UNLOCK(U); 266 return NULL; 267 } 268 269 ret = __getutxid(U, ux); 270 UTMPX_UNLOCK(U); 271 return ret; 272} 273 274 275struct utmpx * 276getutxid(const struct utmpx *utx) 277{ 278 return _getutxid(__default_utx(), utx); 279} 280 281 282static struct utmpx * 283__getutxid(struct _utmpx *U, const struct utmpx *utx) 284{ 285 286 do { 287 if (U->ut.ut_type == EMPTY) 288 continue; 289 switch (utx->ut_type) { 290 case EMPTY: 291 return NULL; 292 case RUN_LVL: 293 case BOOT_TIME: 294 case OLD_TIME: 295 case NEW_TIME: 296 if (U->ut.ut_type == utx->ut_type) 297 return &U->ut; 298 break; 299 case INIT_PROCESS: 300 case LOGIN_PROCESS: 301 case USER_PROCESS: 302 case DEAD_PROCESS: 303 switch (U->ut.ut_type) { 304 case INIT_PROCESS: 305 case LOGIN_PROCESS: 306 case USER_PROCESS: 307 case DEAD_PROCESS: 308 if (memcmp(U->ut.ut_id, utx->ut_id, 309 sizeof(U->ut.ut_id)) == 0) 310 return &U->ut; 311 break; 312 default: 313 break; 314 } 315 break; 316 default: 317 return NULL; 318 } 319 } while (__getutxent(U) != NULL); 320 return NULL; 321} 322 323 324static struct utmpx * 325__getutxline(struct _utmpx *U, const struct utmpx *utx) 326{ 327 do { 328 switch (U->ut.ut_type) { 329 case EMPTY: 330 break; 331 case LOGIN_PROCESS: 332 case USER_PROCESS: 333 if (strncmp(U->ut.ut_line, utx->ut_line, 334 sizeof(U->ut.ut_line)) == 0) 335 return &U->ut; 336 break; 337 default: 338 break; 339 } 340 } while (__getutxent(U) != NULL); 341 return NULL; 342} 343 344 345struct utmpx * 346_getutxline(struct _utmpx *U, const struct utmpx *utx) 347{ 348 struct utmpx *ret; 349 350 TEST_UTMPX_T("_getutxline", U); 351 UTMPX_LOCK(U); 352 ret = __getutxline(U, utx); 353 UTMPX_UNLOCK(U); 354 return ret; 355} 356 357 358struct utmpx * 359getutxline(const struct utmpx *utx) 360{ 361 return _getutxline(__default_utx(), utx); 362} 363 364 365struct utmpx * 366_pututxline(struct _utmpx *U, const struct utmpx *utx) 367{ 368 struct utmpx *ux; 369 370 if (utx == NULL) { 371 errno = EINVAL; 372 return NULL; 373 } 374 375 TEST_UTMPX_T("_pututxline", U); 376 UTMPX_LOCK(U); 377 if ((ux = __pututxline(__default_utx(), utx)) != NULL && __default_utx()->utfile_system) { 378 _utmpx_asl(ux); /* the equivalent of wtmpx and lastlogx */ 379#ifdef UTMP_COMPAT 380 _write_utmp_compat(ux); 381#endif /* UTMP_COMPAT */ 382 } 383 UTMPX_UNLOCK(U); 384 return ux; 385} 386 387 388struct utmpx * 389pututxline(const struct utmpx *utx) 390{ 391 return _pututxline(__default_utx(), utx); 392} 393 394__private_extern__ struct utmpx * 395__pututxline(struct _utmpx *U, const struct utmpx *utx) 396{ 397 struct utmpx temp, *u = NULL, *x; 398 const struct utmpx *ux; 399#ifdef __LP64__ 400 struct utmpx32 ut32; 401#endif /* __LP64__ */ 402 struct flock fl; 403#define gotlock (fl.l_start >= 0) 404 405 fl.l_start = -1; /* also means we haven't locked */ 406 if (U->utfile_system) 407 if ((U->fp != NULL && U->readonly) || (U->fp == NULL && geteuid() != 0)) { 408 errno = EPERM; 409 return NULL; 410 } 411 412 if (U->fp == NULL) { 413 (void)__getutxent(U); 414 if (U->fp == NULL || U->readonly) { 415 errno = EPERM; 416 return NULL; 417 } 418 } 419 420 /* make a copy as needed, and auto-fill if requested */ 421 ux = _utmpx_working_copy(utx, &temp, 0); 422 if (!ux) 423 return NULL; 424 425 if ((x = __getutxid(U, ux)) == NULL) { 426 __setutxent(U); 427 if ((x = __getutxid(U, ux)) == NULL) { 428 /* 429 * utx->ut_type has any original mask bits, while 430 * ux->ut_type has those mask bits removed. If we 431 * are trying to record a dead process, and 432 * UTMPX_DEAD_IF_CORRESPONDING_MASK is set, then since 433 * there is no matching entry, we return NULL. 434 */ 435 if (ux->ut_type == DEAD_PROCESS && 436 (utx->ut_type & UTMPX_DEAD_IF_CORRESPONDING_MASK)) { 437 errno = EINVAL; 438 return NULL; 439 } 440 /* 441 * Replace lockf() with fcntl() and a fixed start 442 * value. We should already be at EOF. 443 */ 444 if ((fl.l_start = lseek(fileno(U->fp), 0, SEEK_CUR)) < 0) 445 return NULL; 446 fl.l_len = 0; 447 fl.l_whence = SEEK_SET; 448 fl.l_type = F_WRLCK; 449 if (fcntl(fileno(U->fp), F_SETLKW, &fl) == -1) 450 return NULL; 451 if (fseeko(U->fp, (off_t)0, SEEK_END) == -1) 452 goto fail; 453 } 454 } 455 456 if (!gotlock) { 457 /* 458 * utx->ut_type has any original mask bits, while 459 * ux->ut_type has those mask bits removed. If we 460 * are trying to record a dead process, if 461 * UTMPX_DEAD_IF_CORRESPONDING_MASK is set, but the found 462 * entry is not a (matching) USER_PROCESS, then return NULL. 463 */ 464 if (ux->ut_type == DEAD_PROCESS && 465 (utx->ut_type & UTMPX_DEAD_IF_CORRESPONDING_MASK) && 466 x->ut_type != USER_PROCESS) { 467 errno = EINVAL; 468 return NULL; 469 } 470 /* we are not appending */ 471#ifdef __LP64__ 472 if (fseeko(U->fp, -(off_t)sizeof(ut32), SEEK_CUR) == -1) 473#else /* __LP64__ */ 474 if (fseeko(U->fp, -(off_t)sizeof(U->ut), SEEK_CUR) == -1) 475#endif /* __LP64__ */ 476 return NULL; 477 } 478 479#ifdef __LP64__ 480 _utmpx64_32(ux, &ut32); 481 if (fwrite(&ut32, sizeof (ut32), 1, U->fp) != 1) 482#else /* __LP64__ */ 483 if (fwrite(ux, sizeof (*ux), 1, U->fp) != 1) 484#endif /* __LP64__ */ 485 goto fail; 486 487 if (fflush(U->fp) == -1) 488 goto fail; 489 490 u = memcpy(&U->ut, ux, sizeof(U->ut)); 491 notify_post(UTMPX_CHANGE_NOTIFICATION); 492fail: 493 if (gotlock) { 494 int save = errno; 495 fl.l_type = F_UNLCK; 496 if (fcntl(fileno(U->fp), F_SETLK, &fl) == -1) 497 return NULL; 498 errno = save; 499 } 500 return u; 501} 502 503 504/* 505 * The following are extensions and not part of the X/Open spec. 506 */ 507__private_extern__ int 508__utmpxname(struct _utmpx *U, const char *fname) 509{ 510 size_t len; 511 512 if (fname == NULL) { 513 if(!U->utfile_system) 514 free(U->utfile); 515 U->utfile = _PATH_UTMPX; 516 U->utfile_system = 1; 517 __endutxent(U); 518 return 1; 519 } 520 521 len = strlen(fname); 522 523 if (len >= MAXPATHLEN) 524 return 0; 525 526 /* must end in x! */ 527 if (fname[len - 1] != 'x') 528 return 0; 529 530 if (U->utfile_system) 531 U->utfile = NULL; 532 U->utfile_system = 0; 533 if ((U->utfile = reallocf(U->utfile, len + 1)) == NULL) 534 return 0; 535 536 (void)strcpy(U->utfile, fname); 537 __endutxent(U); 538 return 1; 539} 540 541int 542_utmpxname(struct _utmpx *U, const char *fname) 543{ 544 int ret; 545 546 TEST_UTMPX_T("_utmpxname", U); 547 UTMPX_LOCK(U); 548 ret = __utmpxname(U, fname); 549 UTMPX_UNLOCK(U); 550 return ret; 551} 552 553int 554utmpxname(const char *fname) 555{ 556 return _utmpxname(__default_utx(), fname); 557} 558 559#ifdef UNIFDEF_LEGACY_UTMP_APIS 560void 561getutmp(const struct utmpx *ux, struct utmp *u) 562{ 563 564 bzero(u, sizeof(*u)); 565 (void)memcpy(u->ut_name, ux->ut_user, sizeof(u->ut_name)); 566 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); 567 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); 568 u->ut_time = ux->ut_tv.tv_sec; 569} 570 571void 572getutmpx(const struct utmp *u, struct utmpx *ux) 573{ 574 575 bzero(ux, sizeof(*ux)); 576 (void)memcpy(ux->ut_user, u->ut_name, sizeof(u->ut_name)); 577 ux->ut_user[sizeof(u->ut_name)] = 0; 578 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); 579 ux->ut_line[sizeof(u->ut_line)] = 0; 580 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); 581 ux->ut_host[sizeof(u->ut_host)] = 0; 582 ux->ut_tv.tv_sec = u->ut_time; 583 ux->ut_tv.tv_usec = 0; 584 ux->ut_pid = getpid(); 585 ux->ut_type = USER_PROCESS; 586} 587#endif /* UNIFDEF_LEGACY_UTMP_APIS */ 588