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