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