1 /* $NetBSD: utmpx.c,v 1.30 2012/06/24 15:26:03 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 * 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.30 2012/06/24 15:26:03 christos 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 <assert.h> 46 #include <db.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <utmp.h> 54 #include <utmpx.h> 55 #include <vis.h> 56 57 static FILE *fp; 58 static int readonly = 0; 59 static int version = 1; 60 static struct utmpx ut; 61 static char utfile[MAXPATHLEN] = _PATH_UTMPX; 62 63 static struct utmpx *utmp_update(const struct utmpx *); 64 65 static const char vers[] = "utmpx-2.00"; 66 67 struct otimeval { 68 long tv_sec; 69 long tv_usec; 70 }; 71 72 static void 73 old2new(struct utmpx *utx) 74 { 75 struct otimeval otv; 76 struct timeval *tv = &utx->ut_tv; 77 (void)memcpy(&otv, tv, sizeof(otv)); 78 tv->tv_sec = otv.tv_sec; 79 tv->tv_usec = (suseconds_t)otv.tv_usec; 80 } 81 82 static void 83 new2old(struct utmpx *utx) 84 { 85 struct timeval tv; 86 struct otimeval *otv = (void *)&utx->ut_tv; 87 (void)memcpy(&tv, otv, sizeof(tv)); 88 otv->tv_sec = (long)tv.tv_sec; 89 otv->tv_usec = (long)tv.tv_usec; 90 } 91 92 void 93 setutxent(void) 94 { 95 96 (void)memset(&ut, 0, sizeof(ut)); 97 if (fp == NULL) 98 return; 99 (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET); 100 } 101 102 103 void 104 endutxent(void) 105 { 106 107 (void)memset(&ut, 0, sizeof(ut)); 108 if (fp != NULL) { 109 (void)fclose(fp); 110 fp = NULL; 111 readonly = 0; 112 } 113 } 114 115 116 struct utmpx * 117 getutxent(void) 118 { 119 120 if (fp == NULL) { 121 struct stat st; 122 123 if ((fp = fopen(utfile, "re+")) == NULL) 124 if ((fp = fopen(utfile, "w+")) == NULL) { 125 if ((fp = fopen(utfile, "r")) == NULL) 126 goto fail; 127 else 128 readonly = 1; 129 } 130 131 132 /* get file size in order to check if new file */ 133 if (fstat(fileno(fp), &st) == -1) 134 goto failclose; 135 136 if (st.st_size == 0) { 137 /* new file, add signature record */ 138 (void)memset(&ut, 0, sizeof(ut)); 139 ut.ut_type = SIGNATURE; 140 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 141 if (fwrite(&ut, sizeof(ut), 1, fp) != 1) 142 goto failclose; 143 } else { 144 /* old file, read signature record */ 145 if (fread(&ut, sizeof(ut), 1, fp) != 1) 146 goto failclose; 147 if (memcmp(ut.ut_user, vers, 5) != 0 || 148 ut.ut_type != SIGNATURE) 149 goto failclose; 150 } 151 version = ut.ut_user[6] - '0'; 152 } 153 154 if (fread(&ut, sizeof(ut), 1, fp) != 1) 155 goto fail; 156 if (version == 1) 157 old2new(&ut); 158 159 return &ut; 160 failclose: 161 (void)fclose(fp); 162 fail: 163 (void)memset(&ut, 0, sizeof(ut)); 164 return NULL; 165 } 166 167 168 struct utmpx * 169 getutxid(const struct utmpx *utx) 170 { 171 172 _DIAGASSERT(utx != NULL); 173 174 if (utx->ut_type == EMPTY) 175 return NULL; 176 177 do { 178 if (ut.ut_type == EMPTY) 179 continue; 180 switch (utx->ut_type) { 181 case EMPTY: 182 return NULL; 183 case RUN_LVL: 184 case BOOT_TIME: 185 case OLD_TIME: 186 case NEW_TIME: 187 if (ut.ut_type == utx->ut_type) 188 return &ut; 189 break; 190 case INIT_PROCESS: 191 case LOGIN_PROCESS: 192 case USER_PROCESS: 193 case DEAD_PROCESS: 194 switch (ut.ut_type) { 195 case INIT_PROCESS: 196 case LOGIN_PROCESS: 197 case USER_PROCESS: 198 case DEAD_PROCESS: 199 if (memcmp(ut.ut_id, utx->ut_id, 200 sizeof(ut.ut_id)) == 0) 201 return &ut; 202 break; 203 default: 204 break; 205 } 206 break; 207 default: 208 return NULL; 209 } 210 } while (getutxent() != NULL); 211 return NULL; 212 } 213 214 215 struct utmpx * 216 getutxline(const struct utmpx *utx) 217 { 218 219 _DIAGASSERT(utx != NULL); 220 221 do { 222 switch (ut.ut_type) { 223 case EMPTY: 224 break; 225 case LOGIN_PROCESS: 226 case USER_PROCESS: 227 if (strncmp(ut.ut_line, utx->ut_line, 228 sizeof(ut.ut_line)) == 0) 229 return &ut; 230 break; 231 default: 232 break; 233 } 234 } while (getutxent() != NULL); 235 return NULL; 236 } 237 238 239 struct utmpx * 240 pututxline(const struct utmpx *utx) 241 { 242 struct utmpx temp, *u = NULL; 243 int gotlock = 0; 244 245 _DIAGASSERT(utx != NULL); 246 247 if (utx == NULL) 248 return NULL; 249 250 if (strcmp(_PATH_UTMPX, utfile) == 0) { 251 if (geteuid() == 0) { 252 if (fp != NULL && readonly) 253 endutxent(); 254 } else { 255 if (fp == NULL || readonly) 256 return utmp_update(utx); 257 } 258 } 259 260 261 (void)memcpy(&temp, utx, sizeof(temp)); 262 263 if (fp == NULL) { 264 (void)getutxent(); 265 if (fp == NULL || readonly) 266 return NULL; 267 } 268 269 if (getutxid(&temp) == NULL) { 270 setutxent(); 271 if (getutxid(&temp) == NULL) { 272 if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1) 273 return NULL; 274 gotlock++; 275 if (fseeko(fp, (off_t)0, SEEK_END) == -1) 276 goto fail; 277 } 278 } 279 280 if (!gotlock) { 281 /* we are not appending */ 282 if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1) 283 return NULL; 284 } 285 286 if (version == 1) 287 new2old(&temp); 288 if (fwrite(&temp, sizeof (temp), 1, fp) != 1) 289 goto fail; 290 291 if (fflush(fp) == -1) 292 goto fail; 293 294 u = memcpy(&ut, &temp, sizeof(ut)); 295 fail: 296 if (gotlock) { 297 if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1) 298 return NULL; 299 } 300 return u; 301 } 302 303 304 static struct utmpx * 305 utmp_update(const struct utmpx *utx) 306 { 307 char buf[sizeof(*utx) * 4 + 1]; 308 pid_t pid; 309 int status; 310 311 _DIAGASSERT(utx != NULL); 312 313 (void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx), 314 VIS_WHITE); 315 switch (pid = fork()) { 316 case 0: 317 (void)execl(_PATH_UTMP_UPDATE, 318 strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf, NULL); 319 _exit(1); 320 /*NOTREACHED*/ 321 case -1: 322 return NULL; 323 default: 324 if (waitpid(pid, &status, 0) == -1) 325 return NULL; 326 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 327 return memcpy(&ut, utx, sizeof(ut)); 328 return NULL; 329 } 330 331 } 332 333 /* 334 * The following are extensions and not part of the X/Open spec. 335 */ 336 int 337 updwtmpx(const char *file, const struct utmpx *utx) 338 { 339 int fd; 340 int saved_errno; 341 342 _DIAGASSERT(file != NULL); 343 _DIAGASSERT(utx != NULL); 344 345 #ifndef __minix 346 fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK); 347 #else 348 fd = open(file, O_WRONLY|O_APPEND); 349 #endif 350 351 if (fd == -1) { 352 #ifndef __minix 353 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1) 354 return -1; 355 #else 356 if ((fd = open(file, O_CREAT|O_WRONLY, 0644)) < 0) 357 return -1; 358 if (flock(fd, LOCK_EX) < 0) 359 return -1; 360 #endif 361 (void)memset(&ut, 0, sizeof(ut)); 362 ut.ut_type = SIGNATURE; 363 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 364 if (write(fd, &ut, sizeof(ut)) == -1) 365 goto failed; 366 } else { 367 #ifdef __minix 368 if (flock(fd, LOCK_SH) < 0 ) 369 return -1; 370 #endif 371 } 372 if (write(fd, utx, sizeof(*utx)) == -1) 373 goto failed; 374 if (close(fd) == -1) 375 return -1; 376 return 0; 377 378 failed: 379 saved_errno = errno; 380 (void) close(fd); 381 errno = saved_errno; 382 return -1; 383 } 384 385 386 int 387 utmpxname(const char *fname) 388 { 389 size_t len; 390 391 _DIAGASSERT(fname != NULL); 392 393 len = strlen(fname); 394 395 if (len >= sizeof(utfile)) 396 return 0; 397 398 /* must end in x! */ 399 if (fname[len - 1] != 'x') 400 return 0; 401 402 (void)strlcpy(utfile, fname, sizeof(utfile)); 403 endutxent(); 404 return 1; 405 } 406 407 408 void 409 getutmp(const struct utmpx *ux, struct utmp *u) 410 { 411 412 _DIAGASSERT(ux != NULL); 413 _DIAGASSERT(u != NULL); 414 415 (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name)); 416 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); 417 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); 418 u->ut_time = ux->ut_tv.tv_sec; 419 } 420 421 void 422 getutmpx(const struct utmp *u, struct utmpx *ux) 423 { 424 425 _DIAGASSERT(ux != NULL); 426 _DIAGASSERT(u != NULL); 427 428 (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name)); 429 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); 430 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); 431 ux->ut_tv.tv_sec = u->ut_time; 432 ux->ut_tv.tv_usec = 0; 433 (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss)); 434 ux->ut_pid = 0; 435 ux->ut_type = USER_PROCESS; 436 ux->ut_session = 0; 437 ux->ut_exit.e_termination = 0; 438 ux->ut_exit.e_exit = 0; 439 } 440 441 struct lastlogx * 442 getlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 443 { 444 DBT key, data; 445 DB *db; 446 447 _DIAGASSERT(fname != NULL); 448 _DIAGASSERT(ll != NULL); 449 450 #ifdef __minix 451 db = dbopen(fname, O_RDONLY, 0, DB_HASH, NULL); 452 #else 453 db = dbopen(fname, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL); 454 #endif 455 456 if (db == NULL) 457 return NULL; 458 #ifdef __minix 459 if (flock(db->fd(db), LOCK_SH) < 0) 460 return NULL; 461 #endif 462 463 key.data = &uid; 464 key.size = sizeof(uid); 465 466 if ((db->get)(db, &key, &data, 0) != 0) 467 goto error; 468 469 if (data.size != sizeof(*ll)) { 470 errno = EFTYPE; 471 goto error; 472 } 473 474 if (ll == NULL) 475 if ((ll = malloc(sizeof(*ll))) == NULL) 476 goto done; 477 478 (void)memcpy(ll, data.data, sizeof(*ll)); 479 goto done; 480 error: 481 ll = NULL; 482 done: 483 (db->close)(db); 484 return ll; 485 } 486 487 int 488 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 489 { 490 DBT key, data; 491 int error = 0; 492 DB *db; 493 494 _DIAGASSERT(fname != NULL); 495 _DIAGASSERT(ll != NULL); 496 497 #ifndef __minix 498 db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0644, DB_HASH, NULL); 499 #else 500 db = dbopen(fname, O_RDWR|O_CREAT, 0644, DB_HASH, NULL); 501 #endif 502 503 if (db == NULL) 504 return -1; 505 506 #ifdef __minix 507 if (flock(db->fd(db), LOCK_EX) < 0) 508 return -1; 509 #endif 510 key.data = &uid; 511 key.size = sizeof(uid); 512 data.data = ll; 513 data.size = sizeof(*ll); 514 if ((db->put)(db, &key, &data, 0) != 0) 515 error = -1; 516 517 (db->close)(db); 518 return error; 519 } 520 521 #if defined(__minix) && defined(__weak_alias) 522 __weak_alias(getlastlogx, __getlastlogx50) 523 __weak_alias(getutmp, __getutmp50) 524 __weak_alias(getutmpx, __getutmpx50) 525 __weak_alias(getutxent, __getutxent50) 526 __weak_alias(getutxid, __getutxid50) 527 __weak_alias(getutxline, __getutxline50) 528 __weak_alias(pututxline, __pututxline50) 529 __weak_alias(updlastlogx, __updlastlogx50) 530 __weak_alias(updwtmpx, __updwtmpx50) 531 #endif 532