1 /* $NetBSD: utmpx.c,v 1.26 2009/01/11 02:46:27 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.26 2009/01/11 02:46:27 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 = 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() 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() 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() 118 { 119 120 if (fp == NULL) { 121 struct stat st; 122 123 if ((fp = fopen(utfile, "r+")) == 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 ((fp != NULL && readonly) || (fp == NULL && geteuid() != 0)) 252 return utmp_update(utx); 253 254 255 (void)memcpy(&temp, utx, sizeof(temp)); 256 257 if (fp == NULL) { 258 (void)getutxent(); 259 if (fp == NULL || readonly) 260 return NULL; 261 } 262 263 if (getutxid(&temp) == NULL) { 264 setutxent(); 265 if (getutxid(&temp) == NULL) { 266 if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1) 267 return NULL; 268 gotlock++; 269 if (fseeko(fp, (off_t)0, SEEK_END) == -1) 270 goto fail; 271 } 272 } 273 274 if (!gotlock) { 275 /* we are not appending */ 276 if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1) 277 return NULL; 278 } 279 280 if (version == 1) 281 new2old(&temp); 282 if (fwrite(&temp, sizeof (temp), 1, fp) != 1) 283 goto fail; 284 285 if (fflush(fp) == -1) 286 goto fail; 287 288 u = memcpy(&ut, &temp, sizeof(ut)); 289 fail: 290 if (gotlock) { 291 if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1) 292 return NULL; 293 } 294 return u; 295 } 296 297 298 static struct utmpx * 299 utmp_update(const struct utmpx *utx) 300 { 301 char buf[sizeof(*utx) * 4 + 1]; 302 pid_t pid; 303 int status; 304 305 _DIAGASSERT(utx != NULL); 306 307 (void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx), 308 VIS_WHITE); 309 switch (pid = fork()) { 310 case 0: 311 (void)execl(_PATH_UTMP_UPDATE, 312 strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf, NULL); 313 _exit(1); 314 /*NOTREACHED*/ 315 case -1: 316 return NULL; 317 default: 318 if (waitpid(pid, &status, 0) == -1) 319 return NULL; 320 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 321 return memcpy(&ut, utx, sizeof(ut)); 322 return NULL; 323 } 324 325 } 326 327 /* 328 * The following are extensions and not part of the X/Open spec. 329 */ 330 int 331 updwtmpx(const char *file, const struct utmpx *utx) 332 { 333 int fd; 334 int saved_errno; 335 336 _DIAGASSERT(file != NULL); 337 _DIAGASSERT(utx != NULL); 338 339 #ifndef __minix 340 fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK); 341 #else 342 fd = open(file, O_WRONLY|O_APPEND); 343 #endif 344 345 if (fd == -1) { 346 #ifndef __minix 347 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1) 348 return -1; 349 #else 350 if ((fd = open(file, O_CREAT|O_WRONLY, 0644)) < 0) 351 return -1; 352 if (flock(fd, LOCK_EX) < 0) 353 return -1; 354 #endif 355 (void)memset(&ut, 0, sizeof(ut)); 356 ut.ut_type = SIGNATURE; 357 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 358 if (write(fd, &ut, sizeof(ut)) == -1) 359 goto failed; 360 } else { 361 #ifdef __minix 362 if (flock(fd, LOCK_SH) < 0 ) 363 return -1; 364 #endif 365 } 366 if (write(fd, utx, sizeof(*utx)) == -1) 367 goto failed; 368 if (close(fd) == -1) 369 return -1; 370 return 0; 371 372 failed: 373 saved_errno = errno; 374 (void) close(fd); 375 errno = saved_errno; 376 return -1; 377 } 378 379 380 int 381 utmpxname(const char *fname) 382 { 383 size_t len; 384 385 _DIAGASSERT(fname != NULL); 386 387 len = strlen(fname); 388 389 if (len >= sizeof(utfile)) 390 return 0; 391 392 /* must end in x! */ 393 if (fname[len - 1] != 'x') 394 return 0; 395 396 (void)strlcpy(utfile, fname, sizeof(utfile)); 397 endutxent(); 398 return 1; 399 } 400 401 402 void 403 getutmp(const struct utmpx *ux, struct utmp *u) 404 { 405 406 _DIAGASSERT(ux != NULL); 407 _DIAGASSERT(u != NULL); 408 409 (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name)); 410 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); 411 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); 412 u->ut_time = ux->ut_tv.tv_sec; 413 } 414 415 void 416 getutmpx(const struct utmp *u, struct utmpx *ux) 417 { 418 419 _DIAGASSERT(ux != NULL); 420 _DIAGASSERT(u != NULL); 421 422 (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name)); 423 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); 424 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); 425 ux->ut_tv.tv_sec = u->ut_time; 426 ux->ut_tv.tv_usec = 0; 427 (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss)); 428 ux->ut_pid = 0; 429 ux->ut_type = USER_PROCESS; 430 ux->ut_session = 0; 431 ux->ut_exit.e_termination = 0; 432 ux->ut_exit.e_exit = 0; 433 } 434 435 struct lastlogx * 436 getlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 437 { 438 DBT key, data; 439 DB *db; 440 441 _DIAGASSERT(fname != NULL); 442 _DIAGASSERT(ll != NULL); 443 444 #ifdef __minix 445 db = dbopen(fname, O_RDONLY, 0, DB_HASH, NULL); 446 #else 447 db = dbopen(fname, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL); 448 #endif 449 450 if (db == NULL) 451 return NULL; 452 #ifdef __minix 453 if (flock(db->fd(db), LOCK_SH) < 0) 454 return NULL; 455 #endif 456 457 key.data = &uid; 458 key.size = sizeof(uid); 459 460 if ((db->get)(db, &key, &data, 0) != 0) 461 goto error; 462 463 if (data.size != sizeof(*ll)) { 464 errno = EFTYPE; 465 goto error; 466 } 467 468 if (ll == NULL) 469 if ((ll = malloc(sizeof(*ll))) == NULL) 470 goto done; 471 472 (void)memcpy(ll, data.data, sizeof(*ll)); 473 goto done; 474 error: 475 ll = NULL; 476 done: 477 (db->close)(db); 478 return ll; 479 } 480 481 int 482 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 483 { 484 DBT key, data; 485 int error = 0; 486 DB *db; 487 488 _DIAGASSERT(fname != NULL); 489 _DIAGASSERT(ll != NULL); 490 491 #ifndef __minix 492 db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0644, DB_HASH, NULL); 493 #else 494 db = dbopen(fname, O_RDWR|O_CREAT, 0644, DB_HASH, NULL); 495 #endif 496 497 if (db == NULL) 498 return -1; 499 500 #ifdef __minix 501 if (flock(db->fd(db), LOCK_EX) < 0) 502 return -1; 503 #endif 504 key.data = &uid; 505 key.size = sizeof(uid); 506 data.data = ll; 507 data.size = sizeof(*ll); 508 if ((db->put)(db, &key, &data, 0) != 0) 509 error = -1; 510 511 (db->close)(db); 512 return error; 513 } 514