1 /* $NetBSD: utmpx.c,v 1.29 2012/03/13 21:13:37 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.29 2012/03/13 21:13:37 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() 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, "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 fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK); 346 347 if (fd == -1) { 348 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1) 349 return -1; 350 (void)memset(&ut, 0, sizeof(ut)); 351 ut.ut_type = SIGNATURE; 352 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 353 if (write(fd, &ut, sizeof(ut)) == -1) 354 goto failed; 355 } 356 if (write(fd, utx, sizeof(*utx)) == -1) 357 goto failed; 358 if (close(fd) == -1) 359 return -1; 360 return 0; 361 362 failed: 363 saved_errno = errno; 364 (void) close(fd); 365 errno = saved_errno; 366 return -1; 367 } 368 369 370 int 371 utmpxname(const char *fname) 372 { 373 size_t len; 374 375 _DIAGASSERT(fname != NULL); 376 377 len = strlen(fname); 378 379 if (len >= sizeof(utfile)) 380 return 0; 381 382 /* must end in x! */ 383 if (fname[len - 1] != 'x') 384 return 0; 385 386 (void)strlcpy(utfile, fname, sizeof(utfile)); 387 endutxent(); 388 return 1; 389 } 390 391 392 void 393 getutmp(const struct utmpx *ux, struct utmp *u) 394 { 395 396 _DIAGASSERT(ux != NULL); 397 _DIAGASSERT(u != NULL); 398 399 (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name)); 400 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); 401 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); 402 u->ut_time = ux->ut_tv.tv_sec; 403 } 404 405 void 406 getutmpx(const struct utmp *u, struct utmpx *ux) 407 { 408 409 _DIAGASSERT(ux != NULL); 410 _DIAGASSERT(u != NULL); 411 412 (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name)); 413 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); 414 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); 415 ux->ut_tv.tv_sec = u->ut_time; 416 ux->ut_tv.tv_usec = 0; 417 (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss)); 418 ux->ut_pid = 0; 419 ux->ut_type = USER_PROCESS; 420 ux->ut_session = 0; 421 ux->ut_exit.e_termination = 0; 422 ux->ut_exit.e_exit = 0; 423 } 424 425 struct lastlogx * 426 getlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 427 { 428 DBT key, data; 429 DB *db; 430 431 _DIAGASSERT(fname != NULL); 432 _DIAGASSERT(ll != NULL); 433 434 db = dbopen(fname, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL); 435 436 if (db == NULL) 437 return NULL; 438 439 key.data = &uid; 440 key.size = sizeof(uid); 441 442 if ((db->get)(db, &key, &data, 0) != 0) 443 goto error; 444 445 if (data.size != sizeof(*ll)) { 446 errno = EFTYPE; 447 goto error; 448 } 449 450 if (ll == NULL) 451 if ((ll = malloc(sizeof(*ll))) == NULL) 452 goto done; 453 454 (void)memcpy(ll, data.data, sizeof(*ll)); 455 goto done; 456 error: 457 ll = NULL; 458 done: 459 (db->close)(db); 460 return ll; 461 } 462 463 int 464 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 465 { 466 DBT key, data; 467 int error = 0; 468 DB *db; 469 470 _DIAGASSERT(fname != NULL); 471 _DIAGASSERT(ll != NULL); 472 473 db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0644, DB_HASH, NULL); 474 475 if (db == NULL) 476 return -1; 477 478 key.data = &uid; 479 key.size = sizeof(uid); 480 data.data = ll; 481 data.size = sizeof(*ll); 482 if ((db->put)(db, &key, &data, 0) != 0) 483 error = -1; 484 485 (db->close)(db); 486 return error; 487 } 488