1 /* $NetBSD: utmpx.c,v 1.25 2008/04/28 20:22:59 martin 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.25 2008/04/28 20:22:59 martin 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 struct utmpx ut; 60 static char utfile[MAXPATHLEN] = _PATH_UTMPX; 61 62 static struct utmpx *utmp_update(const struct utmpx *); 63 64 static const char vers[] = "utmpx-1.00"; 65 66 void 67 setutxent() 68 { 69 70 (void)memset(&ut, 0, sizeof(ut)); 71 if (fp == NULL) 72 return; 73 (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET); 74 } 75 76 77 void 78 endutxent() 79 { 80 81 (void)memset(&ut, 0, sizeof(ut)); 82 if (fp != NULL) { 83 (void)fclose(fp); 84 fp = NULL; 85 readonly = 0; 86 } 87 } 88 89 90 struct utmpx * 91 getutxent() 92 { 93 94 if (fp == NULL) { 95 struct stat st; 96 97 if ((fp = fopen(utfile, "r+")) == NULL) 98 if ((fp = fopen(utfile, "w+")) == NULL) { 99 if ((fp = fopen(utfile, "r")) == NULL) 100 goto fail; 101 else 102 readonly = 1; 103 } 104 105 106 /* get file size in order to check if new file */ 107 if (fstat(fileno(fp), &st) == -1) 108 goto failclose; 109 110 if (st.st_size == 0) { 111 /* new file, add signature record */ 112 (void)memset(&ut, 0, sizeof(ut)); 113 ut.ut_type = SIGNATURE; 114 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 115 if (fwrite(&ut, sizeof(ut), 1, fp) != 1) 116 goto failclose; 117 } else { 118 /* old file, read signature record */ 119 if (fread(&ut, sizeof(ut), 1, fp) != 1) 120 goto failclose; 121 if (memcmp(ut.ut_user, vers, sizeof(vers)) != 0 || 122 ut.ut_type != SIGNATURE) 123 goto failclose; 124 } 125 } 126 127 if (fread(&ut, sizeof(ut), 1, fp) != 1) 128 goto fail; 129 130 return &ut; 131 failclose: 132 (void)fclose(fp); 133 fail: 134 (void)memset(&ut, 0, sizeof(ut)); 135 return NULL; 136 } 137 138 139 struct utmpx * 140 getutxid(const struct utmpx *utx) 141 { 142 143 _DIAGASSERT(utx != NULL); 144 145 if (utx->ut_type == EMPTY) 146 return NULL; 147 148 do { 149 if (ut.ut_type == EMPTY) 150 continue; 151 switch (utx->ut_type) { 152 case EMPTY: 153 return NULL; 154 case RUN_LVL: 155 case BOOT_TIME: 156 case OLD_TIME: 157 case NEW_TIME: 158 if (ut.ut_type == utx->ut_type) 159 return &ut; 160 break; 161 case INIT_PROCESS: 162 case LOGIN_PROCESS: 163 case USER_PROCESS: 164 case DEAD_PROCESS: 165 switch (ut.ut_type) { 166 case INIT_PROCESS: 167 case LOGIN_PROCESS: 168 case USER_PROCESS: 169 case DEAD_PROCESS: 170 if (memcmp(ut.ut_id, utx->ut_id, 171 sizeof(ut.ut_id)) == 0) 172 return &ut; 173 break; 174 default: 175 break; 176 } 177 break; 178 default: 179 return NULL; 180 } 181 } while (getutxent() != NULL); 182 return NULL; 183 } 184 185 186 struct utmpx * 187 getutxline(const struct utmpx *utx) 188 { 189 190 _DIAGASSERT(utx != NULL); 191 192 do { 193 switch (ut.ut_type) { 194 case EMPTY: 195 break; 196 case LOGIN_PROCESS: 197 case USER_PROCESS: 198 if (strncmp(ut.ut_line, utx->ut_line, 199 sizeof(ut.ut_line)) == 0) 200 return &ut; 201 break; 202 default: 203 break; 204 } 205 } while (getutxent() != NULL); 206 return NULL; 207 } 208 209 210 struct utmpx * 211 pututxline(const struct utmpx *utx) 212 { 213 struct utmpx temp, *u = NULL; 214 int gotlock = 0; 215 216 _DIAGASSERT(utx != NULL); 217 218 if (utx == NULL) 219 return NULL; 220 221 if (strcmp(_PATH_UTMPX, utfile) == 0) 222 if ((fp != NULL && readonly) || (fp == NULL && geteuid() != 0)) 223 return utmp_update(utx); 224 225 226 (void)memcpy(&temp, utx, sizeof(temp)); 227 228 if (fp == NULL) { 229 (void)getutxent(); 230 if (fp == NULL || readonly) 231 return NULL; 232 } 233 234 if (getutxid(&temp) == NULL) { 235 setutxent(); 236 if (getutxid(&temp) == NULL) { 237 if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1) 238 return NULL; 239 gotlock++; 240 if (fseeko(fp, (off_t)0, SEEK_END) == -1) 241 goto fail; 242 } 243 } 244 245 if (!gotlock) { 246 /* we are not appending */ 247 if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1) 248 return NULL; 249 } 250 251 if (fwrite(&temp, sizeof (temp), 1, fp) != 1) 252 goto fail; 253 254 if (fflush(fp) == -1) 255 goto fail; 256 257 u = memcpy(&ut, &temp, sizeof(ut)); 258 fail: 259 if (gotlock) { 260 if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1) 261 return NULL; 262 } 263 return u; 264 } 265 266 267 static struct utmpx * 268 utmp_update(const struct utmpx *utx) 269 { 270 char buf[sizeof(*utx) * 4 + 1]; 271 pid_t pid; 272 int status; 273 274 _DIAGASSERT(utx != NULL); 275 276 (void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx), 277 VIS_WHITE); 278 switch (pid = fork()) { 279 case 0: 280 (void)execl(_PATH_UTMP_UPDATE, 281 strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf, NULL); 282 _exit(1); 283 /*NOTREACHED*/ 284 case -1: 285 return NULL; 286 default: 287 if (waitpid(pid, &status, 0) == -1) 288 return NULL; 289 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 290 return memcpy(&ut, utx, sizeof(ut)); 291 return NULL; 292 } 293 294 } 295 296 /* 297 * The following are extensions and not part of the X/Open spec. 298 */ 299 int 300 updwtmpx(const char *file, const struct utmpx *utx) 301 { 302 int fd; 303 int saved_errno; 304 305 _DIAGASSERT(file != NULL); 306 _DIAGASSERT(utx != NULL); 307 308 fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK); 309 310 if (fd == -1) { 311 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1) 312 return -1; 313 (void)memset(&ut, 0, sizeof(ut)); 314 ut.ut_type = SIGNATURE; 315 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 316 if (write(fd, &ut, sizeof(ut)) == -1) 317 goto failed; 318 } 319 if (write(fd, utx, sizeof(*utx)) == -1) 320 goto failed; 321 if (close(fd) == -1) 322 return -1; 323 return 0; 324 325 failed: 326 saved_errno = errno; 327 (void) close(fd); 328 errno = saved_errno; 329 return -1; 330 } 331 332 333 int 334 utmpxname(const char *fname) 335 { 336 size_t len; 337 338 _DIAGASSERT(fname != NULL); 339 340 len = strlen(fname); 341 342 if (len >= sizeof(utfile)) 343 return 0; 344 345 /* must end in x! */ 346 if (fname[len - 1] != 'x') 347 return 0; 348 349 (void)strlcpy(utfile, fname, sizeof(utfile)); 350 endutxent(); 351 return 1; 352 } 353 354 355 void 356 getutmp(const struct utmpx *ux, struct utmp *u) 357 { 358 359 _DIAGASSERT(ux != NULL); 360 _DIAGASSERT(u != NULL); 361 362 (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name)); 363 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); 364 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); 365 u->ut_time = ux->ut_tv.tv_sec; 366 } 367 368 void 369 getutmpx(const struct utmp *u, struct utmpx *ux) 370 { 371 372 _DIAGASSERT(ux != NULL); 373 _DIAGASSERT(u != NULL); 374 375 (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name)); 376 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); 377 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); 378 ux->ut_tv.tv_sec = u->ut_time; 379 ux->ut_tv.tv_usec = 0; 380 (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss)); 381 ux->ut_pid = 0; 382 ux->ut_type = USER_PROCESS; 383 ux->ut_session = 0; 384 ux->ut_exit.e_termination = 0; 385 ux->ut_exit.e_exit = 0; 386 } 387 388 struct lastlogx * 389 getlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 390 { 391 DBT key, data; 392 DB *db; 393 394 _DIAGASSERT(fname != NULL); 395 _DIAGASSERT(ll != NULL); 396 397 db = dbopen(fname, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL); 398 399 if (db == NULL) 400 return NULL; 401 402 key.data = &uid; 403 key.size = sizeof(uid); 404 405 if ((db->get)(db, &key, &data, 0) != 0) 406 goto error; 407 408 if (data.size != sizeof(*ll)) { 409 errno = EFTYPE; 410 goto error; 411 } 412 413 if (ll == NULL) 414 if ((ll = malloc(sizeof(*ll))) == NULL) 415 goto done; 416 417 (void)memcpy(ll, data.data, sizeof(*ll)); 418 goto done; 419 error: 420 ll = NULL; 421 done: 422 (db->close)(db); 423 return ll; 424 } 425 426 int 427 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 428 { 429 DBT key, data; 430 int error = 0; 431 DB *db; 432 433 _DIAGASSERT(fname != NULL); 434 _DIAGASSERT(ll != NULL); 435 436 db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0644, DB_HASH, NULL); 437 438 if (db == NULL) 439 return -1; 440 441 key.data = &uid; 442 key.size = sizeof(uid); 443 data.data = ll; 444 data.size = sizeof(*ll); 445 if ((db->put)(db, &key, &data, 0) != 0) 446 error = -1; 447 448 (db->close)(db); 449 return error; 450 } 451