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