Home | History | Annotate | Line # | Download | only in gen
utmpx.c revision 1.15
      1 /*	$NetBSD: utmpx.c,v 1.15 2002/11/17 20:49:33 itojun 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.15 2002/11/17 20:49:33 itojun 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);
    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