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