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