freebsd-dev/lib/libc/gen/pututxline.c
Pedro F. Giffuni d915a14ef0 libc: further adoption of SPDX licensing ID tags.
Mainly focus on files that use BSD 2-Clause license, however the tool I
was using mis-identified many licenses so this was mostly a manual - error
prone - task.

The Software Package Data Exchange (SPDX) group provides a specification
to make it easier for automated tools to detect and summarize well known
opensource licenses. We are gradually adopting the specification, noting
that the tags are considered only advisory and do not, in any way,
superceed or replace the license texts.
2017-11-25 17:12:48 +00:00

338 lines
7.3 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "namespace.h"
#include <sys/endian.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <utmpx.h>
#include "utxdb.h"
#include "un-namespace.h"
static FILE *
futx_open(const char *file)
{
FILE *fp;
struct stat sb;
int fd;
fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK|O_CLOEXEC, 0644);
if (fd < 0)
return (NULL);
/* Safety check: never use broken files. */
if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) {
_close(fd);
errno = EFTYPE;
return (NULL);
}
fp = fdopen(fd, "r+");
if (fp == NULL) {
_close(fd);
return (NULL);
}
return (fp);
}
static int
utx_active_add(const struct futx *fu)
{
FILE *fp;
struct futx fe;
off_t partial;
int error, ret;
partial = -1;
ret = 0;
/*
* Register user login sessions. Overwrite entries of sessions
* that have already been terminated.
*/
fp = futx_open(_PATH_UTX_ACTIVE);
if (fp == NULL)
return (-1);
while (fread(&fe, sizeof(fe), 1, fp) == 1) {
switch (fe.fu_type) {
case BOOT_TIME:
/* Leave these intact. */
break;
case USER_PROCESS:
case INIT_PROCESS:
case LOGIN_PROCESS:
case DEAD_PROCESS:
/* Overwrite when ut_id matches. */
if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) ==
0) {
ret = fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR);
goto exact;
}
if (fe.fu_type != DEAD_PROCESS)
break;
/* FALLTHROUGH */
default:
/* Allow us to overwrite unused records. */
if (partial == -1) {
partial = ftello(fp);
/*
* Distinguish errors from valid values so we
* don't overwrite good data by accident.
*/
if (partial != -1)
partial -= (off_t)sizeof(fe);
}
break;
}
}
/*
* No exact match found. Use the partial match. If no partial
* match was found, just append a new record.
*/
if (partial != -1)
ret = fseeko(fp, partial, SEEK_SET);
exact:
if (ret == -1)
error = errno;
else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
error = errno;
else
error = 0;
fclose(fp);
if (error != 0)
errno = error;
return (error == 0 ? 0 : 1);
}
static int
utx_active_remove(struct futx *fu)
{
FILE *fp;
struct futx fe;
int error, ret;
/*
* Remove user login sessions, having the same ut_id.
*/
fp = futx_open(_PATH_UTX_ACTIVE);
if (fp == NULL)
return (-1);
error = ESRCH;
ret = -1;
while (fread(&fe, sizeof(fe), 1, fp) == 1 && ret != 0)
switch (fe.fu_type) {
case USER_PROCESS:
case INIT_PROCESS:
case LOGIN_PROCESS:
if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) != 0)
continue;
/* Terminate session. */
if (fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR) == -1)
error = errno;
else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
error = errno;
else
ret = 0;
}
fclose(fp);
if (ret != 0)
errno = error;
return (ret);
}
static void
utx_active_init(const struct futx *fu)
{
int fd;
/* Initialize utx.active with a single BOOT_TIME record. */
fd = _open(_PATH_UTX_ACTIVE, O_CREAT|O_RDWR|O_TRUNC, 0644);
if (fd < 0)
return;
_write(fd, fu, sizeof(*fu));
_close(fd);
}
static void
utx_active_purge(void)
{
truncate(_PATH_UTX_ACTIVE, 0);
}
static int
utx_lastlogin_add(const struct futx *fu)
{
struct futx fe;
FILE *fp;
int error, ret;
ret = 0;
/*
* Write an entry to lastlogin. Overwrite the entry if the
* current user already has an entry. If not, append a new
* entry.
*/
fp = futx_open(_PATH_UTX_LASTLOGIN);
if (fp == NULL)
return (-1);
while (fread(&fe, sizeof fe, 1, fp) == 1) {
if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0)
continue;
/* Found a previous lastlogin entry for this user. */
ret = fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
break;
}
if (ret == -1)
error = errno;
else if (fwrite(fu, sizeof *fu, 1, fp) < 1) {
error = errno;
ret = -1;
}
fclose(fp);
if (ret == -1)
errno = error;
return (ret);
}
static void
utx_lastlogin_upgrade(void)
{
struct stat sb;
int fd;
fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR|O_CLOEXEC, 0644);
if (fd < 0)
return;
/*
* Truncate broken lastlogin files. In the future we should
* check for older versions of the file format here and try to
* upgrade it.
*/
if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0)
ftruncate(fd, 0);
_close(fd);
}
static int
utx_log_add(const struct futx *fu)
{
struct iovec vec[2];
int error, fd;
uint16_t l;
/*
* Append an entry to the log file. We only need to append
* records to this file, so to conserve space, trim any trailing
* zero-bytes. Prepend a length field, indicating the length of
* the record, excluding the length field itself.
*/
for (l = sizeof(*fu); l > 0 && ((const char *)fu)[l - 1] == '\0'; l--) ;
vec[0].iov_base = &l;
vec[0].iov_len = sizeof(l);
vec[1].iov_base = __DECONST(void *, fu);
vec[1].iov_len = l;
l = htobe16(l);
fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND|O_CLOEXEC, 0644);
if (fd < 0)
return (-1);
if (_writev(fd, vec, 2) == -1)
error = errno;
else
error = 0;
_close(fd);
if (error != 0)
errno = error;
return (error == 0 ? 0 : 1);
}
struct utmpx *
pututxline(const struct utmpx *utmpx)
{
struct futx fu;
int bad;
bad = 0;
utx_to_futx(utmpx, &fu);
switch (fu.fu_type) {
case BOOT_TIME:
utx_active_init(&fu);
utx_lastlogin_upgrade();
break;
case SHUTDOWN_TIME:
utx_active_purge();
break;
case OLD_TIME:
case NEW_TIME:
break;
case USER_PROCESS:
bad |= utx_active_add(&fu);
bad |= utx_lastlogin_add(&fu);
break;
#if 0 /* XXX: Are these records of any use to us? */
case INIT_PROCESS:
case LOGIN_PROCESS:
bad |= utx_active_add(&fu);
break;
#endif
case DEAD_PROCESS:
/*
* In case writing a logout entry fails, never attempt
* to write it to utx.log. The logout entry's ut_id
* might be invalid.
*/
if (utx_active_remove(&fu) != 0)
return (NULL);
break;
default:
errno = EINVAL;
return (NULL);
}
bad |= utx_log_add(&fu);
return (bad ? NULL : futx_to_utx(&fu));
}