1 /* $NetBSD: utmpx.c,v 1.22 2004/11/11 22:14:20 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 #include <sys/cdefs.h> 39 40 #if defined(LIBC_SCCS) && !defined(lint) 41 __RCSID("$NetBSD: utmpx.c,v 1.22 2004/11/11 22:14:20 christos Exp $"); 42 #endif /* LIBC_SCCS and not lint */ 43 44 #include "namespace.h" 45 #include <sys/types.h> 46 #include <sys/param.h> 47 #include <sys/socket.h> 48 #include <sys/stat.h> 49 #include <sys/time.h> 50 #include <sys/wait.h> 51 52 #include <assert.h> 53 #include <db.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 #include <utmp.h> 61 /* don't define earlier, has side effects in fcntl.h */ 62 #define __LIBC12_SOURCE__ 63 #include <utmpx.h> 64 #include <vis.h> 65 66 __warn_references(getlastlogx, 67 "warning: reference to compatibility getlastlogx(); include <utmpx.h> for correct reference") 68 __warn_references(lastlogxname, 69 "warning: reference to deprecated lastlogxname()") 70 71 static FILE *fp; 72 static int readonly = 0; 73 static struct utmpx ut; 74 static char utfile[MAXPATHLEN] = _PATH_UTMPX; 75 static char llfile[MAXPATHLEN] = _PATH_LASTLOGX; 76 77 static struct utmpx *utmp_update(const struct utmpx *); 78 79 static const char vers[] = "utmpx-1.00"; 80 81 void 82 setutxent() 83 { 84 85 (void)memset(&ut, 0, sizeof(ut)); 86 if (fp == NULL) 87 return; 88 (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET); 89 } 90 91 92 void 93 endutxent() 94 { 95 96 (void)memset(&ut, 0, sizeof(ut)); 97 if (fp != NULL) { 98 (void)fclose(fp); 99 fp = NULL; 100 readonly = 0; 101 } 102 } 103 104 105 struct utmpx * 106 getutxent() 107 { 108 109 if (fp == NULL) { 110 struct stat st; 111 112 if ((fp = fopen(utfile, "r+")) == NULL) 113 if ((fp = fopen(utfile, "w+")) == NULL) { 114 if ((fp = fopen(utfile, "r")) == NULL) 115 goto fail; 116 else 117 readonly = 1; 118 } 119 120 121 /* get file size in order to check if new file */ 122 if (fstat(fileno(fp), &st) == -1) 123 goto failclose; 124 125 if (st.st_size == 0) { 126 /* new file, add signature record */ 127 (void)memset(&ut, 0, sizeof(ut)); 128 ut.ut_type = SIGNATURE; 129 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 130 if (fwrite(&ut, sizeof(ut), 1, fp) != 1) 131 goto failclose; 132 } else { 133 /* old file, read signature record */ 134 if (fread(&ut, sizeof(ut), 1, fp) != 1) 135 goto failclose; 136 if (memcmp(ut.ut_user, vers, sizeof(vers)) != 0 || 137 ut.ut_type != SIGNATURE) 138 goto failclose; 139 } 140 } 141 142 if (fread(&ut, sizeof(ut), 1, fp) != 1) 143 goto fail; 144 145 return &ut; 146 failclose: 147 (void)fclose(fp); 148 fail: 149 (void)memset(&ut, 0, sizeof(ut)); 150 return NULL; 151 } 152 153 154 struct utmpx * 155 getutxid(const struct utmpx *utx) 156 { 157 158 _DIAGASSERT(utx != NULL); 159 160 if (utx->ut_type == EMPTY) 161 return NULL; 162 163 do { 164 if (ut.ut_type == EMPTY) 165 continue; 166 switch (utx->ut_type) { 167 case EMPTY: 168 return NULL; 169 case RUN_LVL: 170 case BOOT_TIME: 171 case OLD_TIME: 172 case NEW_TIME: 173 if (ut.ut_type == utx->ut_type) 174 return &ut; 175 break; 176 case INIT_PROCESS: 177 case LOGIN_PROCESS: 178 case USER_PROCESS: 179 case DEAD_PROCESS: 180 switch (ut.ut_type) { 181 case INIT_PROCESS: 182 case LOGIN_PROCESS: 183 case USER_PROCESS: 184 case DEAD_PROCESS: 185 if (memcmp(ut.ut_id, utx->ut_id, 186 sizeof(ut.ut_id)) == 0) 187 return &ut; 188 break; 189 default: 190 break; 191 } 192 break; 193 default: 194 return NULL; 195 } 196 } while (getutxent() != NULL); 197 return NULL; 198 } 199 200 201 struct utmpx * 202 getutxline(const struct utmpx *utx) 203 { 204 205 _DIAGASSERT(utx != NULL); 206 207 do { 208 switch (ut.ut_type) { 209 case EMPTY: 210 break; 211 case LOGIN_PROCESS: 212 case USER_PROCESS: 213 if (strncmp(ut.ut_line, utx->ut_line, 214 sizeof(ut.ut_line)) == 0) 215 return &ut; 216 break; 217 default: 218 break; 219 } 220 } while (getutxent() != NULL); 221 return NULL; 222 } 223 224 225 struct utmpx * 226 pututxline(const struct utmpx *utx) 227 { 228 struct utmpx temp, *u = NULL; 229 int gotlock = 0; 230 231 _DIAGASSERT(utx != NULL); 232 233 if (utx == NULL) 234 return NULL; 235 236 if (strcmp(_PATH_UTMPX, utfile) == 0) 237 if ((fp != NULL && readonly) || (fp == NULL && geteuid() != 0)) 238 return utmp_update(utx); 239 240 241 (void)memcpy(&temp, utx, sizeof(temp)); 242 243 if (fp == NULL) { 244 (void)getutxent(); 245 if (fp == NULL || readonly) 246 return NULL; 247 } 248 249 if (getutxid(&temp) == NULL) { 250 setutxent(); 251 if (getutxid(&temp) == NULL) { 252 if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1) 253 return NULL; 254 gotlock++; 255 if (fseeko(fp, (off_t)0, SEEK_END) == -1) 256 goto fail; 257 } 258 } 259 260 if (!gotlock) { 261 /* we are not appending */ 262 if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1) 263 return NULL; 264 } 265 266 if (fwrite(&temp, sizeof (temp), 1, fp) != 1) 267 goto fail; 268 269 if (fflush(fp) == -1) 270 goto fail; 271 272 u = memcpy(&ut, &temp, sizeof(ut)); 273 fail: 274 if (gotlock) { 275 if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1) 276 return NULL; 277 } 278 return u; 279 } 280 281 282 static struct utmpx * 283 utmp_update(const struct utmpx *utx) 284 { 285 char buf[sizeof(*utx) * 4 + 1]; 286 pid_t pid; 287 int status; 288 289 _DIAGASSERT(utx != NULL); 290 291 (void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx), 292 VIS_WHITE); 293 switch (pid = fork()) { 294 case 0: 295 (void)execl(_PATH_UTMP_UPDATE, 296 strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf, NULL); 297 exit(1); 298 /*NOTREACHED*/ 299 case -1: 300 return NULL; 301 default: 302 if (waitpid(pid, &status, 0) == -1) 303 return NULL; 304 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 305 return memcpy(&ut, utx, sizeof(ut)); 306 return NULL; 307 } 308 309 } 310 311 /* 312 * The following are extensions and not part of the X/Open spec. 313 */ 314 int 315 updwtmpx(const char *file, const struct utmpx *utx) 316 { 317 int fd; 318 int saved_errno; 319 320 _DIAGASSERT(file != NULL); 321 _DIAGASSERT(utx != NULL); 322 323 fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK); 324 325 if (fd == -1) { 326 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1) 327 return -1; 328 (void)memset(&ut, 0, sizeof(ut)); 329 ut.ut_type = SIGNATURE; 330 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 331 if (write(fd, &ut, sizeof(ut)) == -1) 332 goto failed; 333 } 334 if (write(fd, utx, sizeof(*utx)) == -1) 335 goto failed; 336 if (close(fd) == -1) 337 return -1; 338 return 0; 339 340 failed: 341 saved_errno = errno; 342 (void) close(fd); 343 errno = saved_errno; 344 return -1; 345 } 346 347 348 int 349 utmpxname(const char *fname) 350 { 351 size_t len; 352 353 _DIAGASSERT(fname != NULL); 354 355 len = strlen(fname); 356 357 if (len >= sizeof(utfile)) 358 return 0; 359 360 /* must end in x! */ 361 if (fname[len - 1] != 'x') 362 return 0; 363 364 (void)strlcpy(utfile, fname, sizeof(utfile)); 365 endutxent(); 366 return 1; 367 } 368 369 370 void 371 getutmp(const struct utmpx *ux, struct utmp *u) 372 { 373 374 _DIAGASSERT(ux != NULL); 375 _DIAGASSERT(u != NULL); 376 377 (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name)); 378 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); 379 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); 380 u->ut_time = ux->ut_tv.tv_sec; 381 } 382 383 void 384 getutmpx(const struct utmp *u, struct utmpx *ux) 385 { 386 387 _DIAGASSERT(ux != NULL); 388 _DIAGASSERT(u != NULL); 389 390 (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name)); 391 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); 392 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); 393 ux->ut_tv.tv_sec = u->ut_time; 394 ux->ut_tv.tv_usec = 0; 395 (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss)); 396 ux->ut_pid = 0; 397 ux->ut_type = USER_PROCESS; 398 ux->ut_session = 0; 399 ux->ut_exit.e_termination = 0; 400 ux->ut_exit.e_exit = 0; 401 } 402 403 int 404 lastlogxname(const char *fname) 405 { 406 size_t len; 407 408 _DIAGASSERT(fname != NULL); 409 410 len = strlen(fname); 411 412 if (len >= sizeof(llfile)) 413 return 0; 414 415 /* must end in x! */ 416 if (fname[len - 1] != 'x') 417 return 0; 418 419 (void)strlcpy(llfile, fname, sizeof(llfile)); 420 return 1; 421 } 422 423 struct lastlogx * 424 getlastlogx(uid_t uid, struct lastlogx *ll) 425 { 426 427 return __getlastlogx13(_PATH_LASTLOGX, uid, ll); 428 } 429 430 struct lastlogx * 431 __getlastlogx13(const char *fname, uid_t uid, struct lastlogx *ll) 432 { 433 DBT key, data; 434 DB *db; 435 436 _DIAGASSERT(fname != NULL); 437 _DIAGASSERT(ll != NULL); 438 439 db = dbopen(fname, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL); 440 441 if (db == NULL) 442 return NULL; 443 444 key.data = &uid; 445 key.size = sizeof(uid); 446 447 if ((db->get)(db, &key, &data, 0) != 0) 448 goto error; 449 450 if (data.size != sizeof(*ll)) { 451 errno = EFTYPE; 452 goto error; 453 } 454 455 if (ll == NULL) 456 if ((ll = malloc(sizeof(*ll))) == NULL) 457 goto done; 458 459 (void)memcpy(ll, data.data, sizeof(*ll)); 460 goto done; 461 error: 462 ll = NULL; 463 done: 464 (db->close)(db); 465 return ll; 466 } 467 468 int 469 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 470 { 471 DBT key, data; 472 int error = 0; 473 DB *db; 474 475 _DIAGASSERT(fname != NULL); 476 _DIAGASSERT(ll != NULL); 477 478 db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0644, DB_HASH, NULL); 479 480 if (db == NULL) 481 return -1; 482 483 key.data = &uid; 484 key.size = sizeof(uid); 485 data.data = ll; 486 data.size = sizeof(*ll); 487 if ((db->put)(db, &key, &data, 0) != 0) 488 error = -1; 489 490 (db->close)(db); 491 return error; 492 } 493