2000-04-18  Ulrich Drepper  <drepper@redhat.com>

	* login/programs/connection.c: Removed.
	* login/programs/database.c Removed.
	* login/programs/error.c: Removed.
	* login/programs/request.c: Removed.
	* login/programs/xtmp.c: Removed.
	* login/programs/xtmp.h: Removed.
	* login/Makefile: Remove rules for utmpd.
	* login/README.utmpd: Removed.
This commit is contained in:
Ulrich Drepper 2000-04-18 18:38:50 +00:00
parent d9317f3d92
commit 74f998221d
8 changed files with 11 additions and 1824 deletions

View File

@ -1,3 +1,14 @@
2000-04-18 Ulrich Drepper <drepper@redhat.com>
* login/programs/connection.c: Removed.
* login/programs/database.c Removed.
* login/programs/error.c: Removed.
* login/programs/request.c: Removed.
* login/programs/xtmp.c: Removed.
* login/programs/xtmp.h: Removed.
* login/Makefile: Remove rules for utmpd.
* login/README.utmpd: Removed.
2000-04-18 Andreas Jaeger <aj@suse.de>
* sysdeps/mips/dl-machine.h (elf_machine_rel): Fix relocation.

View File

@ -1,175 +0,0 @@
With the introduction of version 2 of the GNU C Library the format of
the UTMP and WTMP files changed for some configurations (see Q&A `Why
does getlogin() always return NULL on my Linux box?' of the FAQ).
This version of the GNU C Library contains a solution for the problems
this may cause, by providing an UTMP daemon `utmpd'.
Do I need it?
=============
If your configuration is one of the following:
i[3456]86-*-linux-gnu Linux-2.0 on Intel
m68k-*-linux-gnu Linux-2.0 on Motorola 680x0
you might need it, so please read on. If it is not, please read the
section titled `Programming' at the end of this text.
In principle, you only need the daemon if you want to keep using old
programs linked against the previous version of the Linux C Library
(libc5). In addition you will need the daemon if you are running
Linux on Intel, and you are planning to use iBCS (Intel Binary
Compatibility Standard). If you have no libc5 programs left on your
system and you are not using iBCS, it is probably better not to
install the daemon since it uses (a small amount of) memory and CPU
time. But apart from that it shouldn't hurt to install `utmpd', so
when in doubt install it anyway.
Installation
============
The installation process (`make install') already places the `utmpd'
binary in $(sbindir). The only thing you have to do is modifying your
startup scripts to start the daemon. Unfortunately this is a bit of a
hassle, since the layout of these scripts is not standardized. You
should try to find the command that creates the file `/var/run/utmp'.
This is usually done in a script named `/etc/rc', `/etc/init.d/boot'
(Debian) or `/etc/rc.d/rc.S' (Slackware). You could try:
grep utmp /etc/* /etc/init.d/* /etc/rc.d/*
to find the right script. The creation of `/var/run/utmp' is usually
done with a command like:
: > /var/run/utmp
or
cat /dev/null > /var/run/utmp
Now add a line before this command to create the file `/var/run/utmpx'
e.g.
: > /var/run/utmpx
or
cat /dev/null > /var/run/utmpx
whatever you prefer, and after this command, add a line to start the
daemon
utmpd
The entire fragment could look something like
# Clean up /var/run and create /var/run/utmp so that we can login.
( cd /var/run && find . ! -type d -exec rm -f -- {} \; )
: > /var/run/utmpx
: > /var/run/utmp
utmpd
If the file `/var/log/wtmp' exists on your system, you will probably
want to create the file `/var/log/wtmpx'. Programs linked against the
GNU C Library will now write to `/var/log/wtmpx', while programs
linked against the old library will continue to write to
`/var/log/wtmp'. Of course this means that the information gets
spread over two files. We hope to provide a better solution in the
future.
After a reboot, user accounting should be working again. If not,
please refer to the section titled `Troubleshooting' below before
submitting a bug report.
What is `utmpd' doing?
======================
After installation there will be two files that store the user
accounting information: `/var/run/utmp' and `/var/run/utmpx'. The
file `/var/run/utmp' will be in the old format so libc5 programs will
continue to work (even if they are broken and do not use the library
functions to access the user accounting database). And on Intel, you
can safely link `/var/run/utmp' to `/etc/utmp' for iBCS programs.
Programs linked against the new GNU C Library (glibc2) will contact
the daemon for all user accounting database access. The daemon will
store its information in `/var/run/utmpx' and keeps this file in sync
with `/var/run/utmp'. Entries added to `/var/run/utmpx' will be
converted to the old format and will be added to `/var/run/utmp' and
vice versa. This way both libc5 and glibc2 see the same information
in the same fields of `struct utmp'. Of course libc5 programs see only
part of the information that glibc2 programs see because not all
members of the glibc2 `struct utmp' are present in the libc5 `struct
utmp'. For the same reason libc5 will see a truncated version of
those fields where the length of the glibc2 field is larger than the
corresponding libc5 field (ut_user, ut_line, ut_host).
Troubleshooting
===============
If user accounting is not working on your system, e.g. programs like
`who' or `logname' return rubbish, or you cannot login, make
sure that:
* The file `/var/run/utmpx' exists.
* The file `/var/log/wtmpx' exists.
* No program linked against the GNU C Library (libc6) is accessing
`/var/run/utmp' directly (see the section on `Programming' below).
If that does not solve your problems, please use the `glibcbug' script
to report the problem to <bugs@gnu.org>.
The `utmpd' daemon uses `syslogd' to report problems. It uses the
`daemon' facility and `warning' and `error' levels. Alternatively you
could use the following option to ease debugging:
`--debug'
Use this option if you want the daemon to output its warnings and
error messages to the terminal instead of sending them to the
system logger (`syslogd'). When using this option the daemon does
not auto-background itself.
To use this option you should first kill the daemon that is already
running, and start a fresh one with the desired option:
kill `cat /var/run/utmpd.pid`
utmpd --debug
Please include any warnings or error messages from `utmpd' in your
bug reports.
Programming
===========
In order for the `utmpd' approach to work it is essential that NO
program EVER accesses the UTMP and WTMP files directly. Instead, a
program should use ONLY the available library functions:
* utmpname() Select the database used (UTMP, WTMP, ...).
* setutent() Open the database.
* getutent() Read the next entry from the database.
* getutid() Search for the next entry with a specific ID.
* getutline() Search for the next entry for a specific line.
* pututline() Write an entry to the database.
* endutent() Close the database.
* updwtmp() Add an entry to a database (WTMP, ...).
For details, please refer to `The GNU C Library Reference Manual',
which also contains information about some additional functions
derived from BSD and XPG that may be of interest. The command
info libc "User Accounting Database"
should point you at the right location.
If you encounter a program that reads from or, even worse, writes to
the UTMP and WTMP files directly, please report this as a bug to the
author of that program. Note that the files referred to by the macros
`_PATH_UTMP' and `_PATH_WTMP' might even disappear in the future, so
please do not use these, except in a call to `utmpname()' or
`updwtmp()', not even to check their existence.

View File

@ -1,180 +0,0 @@
/* Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "utmpd-private.h"
/* Prototypes for the local functions. */
static client_connection *alloc_connection (void);
static void free_connection (client_connection *connection);
static int set_nonblock_flag (int desc, int value);
/* The head of the connection list. */
static client_connection *connection_list = NULL;
/* Accept connection on SOCK, with access permissions given by ACCESS.
Returns a pointer to a newly allocated client_connection if
successful, NULL if not. */
client_connection *
accept_connection (int sock, int access)
{
client_connection *connection;
connection = alloc_connection ();
if (connection == NULL)
return NULL;
connection->sock = accept (sock, NULL, NULL);
connection->access = access;
if (connection->sock < 0)
{
free_connection (connection);
return NULL;
}
if (set_nonblock_flag (connection->sock, 1) < 0)
{
close_connection (connection);
return NULL;
}
return connection;
}
/* Close CONNECTION. */
void
close_connection (client_connection *connection)
{
close (connection->sock);
free_connection (connection);
}
/* Return the connection for SOCK. */
client_connection *
find_connection (int sock)
{
client_connection *connection;
for (connection = connection_list; connection;
connection = connection->next)
{
if (connection->sock == sock)
return connection;
}
return NULL;
}
static client_connection *
alloc_connection (void)
{
client_connection *connection;
size_t read_bufsize = 1024;
size_t write_bufsize = 1024;
connection = (client_connection *)malloc (sizeof (client_connection));
if (connection == NULL)
return NULL;
memset (connection, 0, sizeof (client_connection));
/* Allocate read buffer. */
connection->read_base = malloc (read_bufsize);
connection->read_ptr = connection->read_base;
connection->read_end = connection->read_base + read_bufsize;
if (connection->read_base == NULL)
{
free (connection);
return NULL;
}
/* Allocate write buffer. */
connection->write_base = malloc (write_bufsize);
connection->write_ptr = connection->write_base;
connection->write_end = connection->write_base + write_bufsize;
if (connection->write_base == NULL)
{
free (connection->read_base);
free (connection);
return NULL;
}
/* Link connection. */
connection->next = connection_list;
connection_list = connection;
if (connection->next)
connection->next->prev = connection;
return connection;
}
static void
free_connection (client_connection *connection)
{
/* Unlink connection. */
if (connection->next)
connection->next->prev = connection->prev;
if (connection->prev)
connection->prev->next = connection->next;
/* Take care of the head of the list. */
if (connection == connection_list)
connection_list = connection->next;
/* Free buffers. */
if (connection->read_base)
free (connection->read_base);
if (connection->write_base)
free (connection->write_base);
free (connection);
}
/* Set the `O_NONBLOCK' flag of DESC if VALUE is nonzero,
or clear the flag if VALUE is 0.
Return 0 on success, or -1 on error with `errno' set. */
static int
set_nonblock_flag (int desc, int value)
{
int oldflags = fcntl (desc, F_GETFL, 0);
/* If reading the flags failed, return error indication now. */
if (oldflags == -1)
return -1;
/* Set just the flag we want to set. */
if (value != 0)
oldflags |= O_NONBLOCK;
else
oldflags &= ~O_NONBLOCK;
/* Store modified flag word in the descriptor. */
return fcntl (desc, F_SETFL, oldflags);
}

View File

@ -1,543 +0,0 @@
/* Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <utmp.h>
#include <libintl.h>
#include "utmpd-private.h"
#include "xtmp.h"
/* Prototypes for the local functions. */
static int initialize_database (utmp_database *database);
static int store_state_entry (utmp_database *database, int old_position,
const struct utmp *old_entry);
static int store_process_entry (utmp_database *database, int old_position,
const struct utmp *old_entry);
static int replace_entry (utmp_database *database, int old_position,
int new_position, const struct utmp *entry);
static int store_entry (utmp_database *database, int position,
const struct utmp *entry);
static int get_mtime (int filedes, time_t *timer);
/* Open the database specified by FILE and merge it with the contents
of the old format file specified by OLD_FILE. Returns a pointer to
a newly allocated structure describing the database, or NULL on
error. */
utmp_database *
open_database (const char *file, const char *old_file)
{
mode_t mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH;
utmp_database *database;
/* Allocate memory. */
database = (utmp_database *) malloc (sizeof (utmp_database));
if (database == NULL)
{
error (0, 0, _("memory exhausted"));
return NULL;
}
memset (database, 0, sizeof (utmp_database));
/* Open database, create it if it doesn't exist already. */
database->fd = open (file, O_RDWR | O_CREAT, mode);
if (database->fd < 0)
{
error (0, errno, "%s", file);
goto return_error;
}
database->file = strdup (file);
if (database->file == NULL)
{
error (0, 0, _("memory exhausted"));
goto return_error;
}
if (old_file)
{
database->old_fd = open (old_file, O_RDWR|O_CREAT, mode);
if (database->old_fd < 0)
{
error (0, errno, "%s", old_file);
goto return_error;
}
database->old_file = strdup (old_file);
if (database->old_file == NULL)
{
error (0, 0, _("memory exhausted"));
goto return_error;
}
}
/* Initialize database. */
if (initialize_database (database) < 0)
goto return_error;
return database;
return_error:
close_database (database);
return NULL;
}
/* Synchronize DATABASE. */
int
synchronize_database (utmp_database *database)
{
assert (database);
/* Check if there is a file in the old format, that we have to
synchronize with. */
if (database->old_file)
{
time_t curtime;
time_t mtime;
curtime = time (NULL);
if (get_mtime (database->old_fd, &mtime) < 0)
{
error (0, errno, _("%s: cannot get modification time"),
database->old_file);
return -1;
}
if (mtime >= database->mtime)
{
int position = 0;
struct utmp entry;
struct utmp old_entry;
while (1)
{
if (read_old_entry (database, position, &old_entry) < 0)
break;
if (read_entry (database, position, &entry) < 0
|| !compare_entry (&old_entry, &entry))
{
if (write_entry (database, position, &old_entry) < 0)
{
error (0, errno, "%s", database->file);
return -1;
}
}
position++;
}
database->mtime = curtime;
}
}
return 0;
}
/* Close DATABASE. */
void
close_database (utmp_database *database)
{
assert (database);
if (database->fd >= 0)
close (database->fd);
if (database->old_fd >= 0)
close (database->old_fd);
/* Free allocated memory. */
if (database->file)
free (database->file);
if (database->old_file)
free (database->old_file);
free (database);
}
/* Read the entry at POSITION in DATABASE and store the result in
ENTRY. Returns 0 if successful, -1 if not. */
int
read_entry (utmp_database *database, int position, struct utmp *entry)
{
ssize_t nbytes;
off_t offset;
offset = position * sizeof (struct utmp);
if (lseek (database->fd, offset, SEEK_SET) < 0)
return -1;
nbytes = read (database->fd, entry, sizeof (struct utmp));
if (nbytes != sizeof (struct utmp))
return -1;
return 0;
}
/* Write ENTRY at POSITION in DATABASE. Returns 0 if successful, -1
on error. */
int
write_entry (utmp_database *database, int position,
const struct utmp *entry)
{
int result = -1;
struct flock fl;
ssize_t nbytes;
off_t offset;
/* Try to lock the file. */
memset (&fl, 0, sizeof (struct flock));
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fcntl (database->fd, F_SETLKW, &fl);
offset = position * sizeof (struct utmp);
if (lseek (database->fd, offset, SEEK_SET) < 0)
goto fail;
nbytes = write (database->fd, entry, sizeof (struct utmp));
if (nbytes != sizeof (struct utmp))
{
ftruncate (database->fd, offset);
goto fail;
}
result = 0;
fail:
/* And unlock the file. */
fl.l_type = F_UNLCK;
fcntl (database->fd, F_SETLKW, &fl);
return result;
}
/* Append ENTRY to DATABASE. Returns the position of the appended
entry if successful, or -1 on error. */
int
append_entry (utmp_database *database, const struct utmp *entry)
{
int result = -1;
struct flock fl;
ssize_t nbytes;
off_t offset;
/* Try to lock the file. */
memset (&fl, 0, sizeof (struct flock));
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fcntl (database->fd, F_SETLKW, &fl);
offset = lseek (database->fd, 0, SEEK_END);
if (offset % sizeof (struct utmp) != 0)
{
offset -= offset % sizeof (struct utmp);
ftruncate (database->fd, offset);
if (lseek (database->fd, 0, SEEK_END) < 0)
goto fail;
}
nbytes = write (database->fd, entry, sizeof (struct utmp));
if (nbytes != sizeof (struct utmp))
{
ftruncate (database->fd, offset);
goto fail;
}
result = offset / sizeof (struct utmp);
fail:
/* And unlock the file. */
fl.l_type = F_UNLCK;
fcntl (database->fd, F_SETLKW, &fl);
return result;
}
int
read_old_entry (utmp_database *database, int position,
struct utmp *entry)
{
struct xtmp old_entry;
ssize_t nbytes;
off_t offset;
offset = position * sizeof (struct xtmp);
if (lseek (database->old_fd, offset, SEEK_SET) < 0)
return -1;
nbytes = read (database->old_fd, &old_entry, sizeof (struct xtmp));
if (nbytes != sizeof (struct xtmp))
return -1;
xtmp_to_utmp (&old_entry, entry);
return 0;
}
int
write_old_entry (utmp_database *database, int position,
const struct utmp *entry)
{
struct xtmp old_entry;
ssize_t nbytes;
off_t offset;
utmp_to_xtmp (entry, &old_entry);
offset = position * sizeof (struct xtmp);
if (lseek (database->old_fd, offset, SEEK_SET) < 0)
return -1;
nbytes = write (database->old_fd, &old_entry, sizeof (struct xtmp));
if (nbytes != sizeof (struct xtmp))
return -1;
return 0;
}
/* Initialize DATABASE. */
static int
initialize_database (utmp_database *database)
{
struct utmp entry;
int position = 0;
assert (database);
/* Check if there is a file in the old format to read. */
if (database->old_file)
{
while (1)
{
if (read_old_entry (database, position, &entry) < 0)
break;
#if _HAVE_UT_TYPE - 0
/* If the login type is one of RUN_LVL, BOOT_TIME, OLD_TIME or
NEW_TIME, search for an entry of the same type in the
database, and replace it if the entry in the file is newer. */
if (entry.ut_type == RUN_LVL || entry.ut_type == BOOT_TIME
|| entry.ut_type == OLD_TIME || entry.ut_type == NEW_TIME)
{
if (store_state_entry (database, position, &entry) < 0)
{
error (0, errno, "%s", database->file);
return -1;
}
}
else
#endif
{
if (store_process_entry (database, position, &entry) < 0)
{
error (0, errno, "%s", database->file);
return -1;
}
}
/* Update position. */
position++;
}
while (1)
{
if (read_entry (database, position, &entry) < 0)
break;
if (write_old_entry (database, position, &entry) < 0)
{
error (0, errno, "%s", database->file);
return -1;
}
/* Update position. */
position++;
}
}
return synchronize_database (database);
}
#if _HAVE_UT_TYPE - 0
static int
store_state_entry (utmp_database *database, int old_position,
const struct utmp *old_entry)
{
struct utmp new_entry;
int new_position = 0;
int found = 0;
assert (old_entry->ut_type == RUN_LVL
|| old_entry->ut_type == BOOT_TIME
|| old_entry->ut_type == OLD_TIME
|| old_entry->ut_type == NEW_TIME);
while (!found)
{
/* Read the next entry. */
if (read_entry (database, new_position, &new_entry) < 0)
break;
if (old_entry->ut_type == new_entry.ut_type)
{
found = 1;
continue;
}
/* Update position. */
new_position++;
}
if (found)
{
const struct utmp *entry;
if (
#if _HAVE_UT_TV - 0
old_entry->ut_tv.tv_sec > new_entry.ut_tv.tv_sec
#else
old_entry->ut_time > new_entry.ut_time
#endif
)
entry = old_entry;
else
entry = &new_entry;
return replace_entry (database, old_position, new_position, entry);
}
return store_entry (database, old_position, old_entry);
}
#endif
static int
store_process_entry (utmp_database *database, int old_position,
const struct utmp *old_entry)
{
struct utmp new_entry;
int new_position = 0;
int found = 0;
while (!found)
{
/* Read the next entry. */
if (read_entry (database, new_position, &new_entry) < 0)
break;
if (proc_utmp_eq (old_entry, &new_entry))
{
found = 1;
continue;
}
/* Update position. */
new_position++;
}
if (found)
{
const struct utmp *entry;
if (
#if _HAVE_UT_TV - 0
old_entry->ut_tv.tv_sec > new_entry.ut_tv.tv_sec
#else
old_entry->ut_time > new_entry.ut_time
#endif
)
entry = old_entry;
else
entry = &new_entry;
return replace_entry (database, old_position, new_position, entry);
}
return store_entry (database, old_position, old_entry);
}
static int
replace_entry (utmp_database *database, int old_position, int new_position,
const struct utmp *entry)
{
struct utmp tmp;
if (read_entry (database, old_position, &tmp) < 0
|| write_entry (database, old_position, entry) < 0
|| write_entry (database, new_position, &tmp) < 0)
return -1;
return 0;
}
static int
store_entry (utmp_database *database, int position,
const struct utmp *entry)
{
struct utmp tmp;
if (read_entry (database, position, &tmp) < 0)
return write_entry (database, position, entry);
if (write_entry (database, position, entry) < 0
|| append_entry (database, &tmp) < 0)
return -1;
return 0;
}
/* Get modification time of the file with file descriptor FILEDES and
put it in TIMER. Returns 0 if successful, -1 if not. */
static int
get_mtime (int filedes, time_t *timer)
{
struct stat st;
if (fstat (filedes, &st) < 0)
return -1;
*timer = st.st_mtime;
return 0;
}

View File

@ -1,104 +0,0 @@
/* Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "utmpd-private.h"
/* This variable indicates if we have forked. If set, we log messages
via the system logger. Otherwise we simply print the program name
and the message to standard error. */
int forked = 0;
/* Log error message MESSAGE, which is a printf-style format string
with optional args.
If ERRNUM is nonzero, also log its corresponding system error message.
Exit with status STATUS if it is nonzero. */
void
error (int status, int errnum, const char *message, ...)
{
va_list ap;
char *buffer = NULL;
va_start (ap, message);
vasprintf (&buffer, message, ap);
va_end (ap);
if (forked)
{
if (errnum == 0)
syslog (LOG_ERR, "%s", buffer);
else
syslog (LOG_ERR, "%s: %s", buffer, strerror (errnum));
}
else
{
if (errnum == 0)
fprintf (stderr, "%s: %s\n", program_invocation_name, buffer);
else
fprintf (stderr, "%s: %s: %s\n", program_invocation_name, buffer,
strerror (errnum));
}
if (buffer)
free (buffer);
if (status)
exit (status);
}
/* Log warning message MESSAGE, which is a printf-style format string
with optional args.
If ERRNUM is nonzero, also log its corresponding system error message. */
void
warning (int errnum, const char *message, ...)
{
va_list ap;
char *buffer = NULL;
va_start (ap, message);
vasprintf (&buffer, message, ap);
va_end (ap);
if (forked)
{
if (errnum == 0)
syslog (LOG_WARNING, "%s", buffer);
else
syslog (LOG_WARNING, "%s: %s", buffer, strerror (errnum));
}
else
{
if (errnum == 0)
printf ("%s: %s\n", program_invocation_name, buffer);
else
printf ("%s: %s: %s\n", program_invocation_name, buffer,
strerror (errnum));
}
if (buffer)
free (buffer);
}

View File

@ -1,646 +0,0 @@
/* Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <utmp.h>
#include <libintl.h>
#include "utmpd.h"
#include "utmpd-private.h"
/* Prototypes for the local functions. */
static int process_request (client_connection *connection);
static int send_reply (client_connection *connect, const reply_header *reply);
static int do_setutent (client_connection *connection);
static int do_getutent (client_connection *connection);
static int do_endutent (client_connection *connection);
static int do_getutline (client_connection *connection);
static int do_getutid (client_connection *connection);
static int do_pututline (client_connection *connection);
static int do_updwtmp (client_connection *connection);
static int internal_getut_r (client_connection *connection,
const struct utmp *id, struct utmp *buffer);
/* Read data from the client on CONNECTION. */
int
read_data (client_connection *connection)
{
ssize_t nbytes;
assert (connection);
assert ((connection->read_end - connection->read_ptr) > 0);
/* Read data. */
nbytes = read (connection->sock, connection->read_ptr,
connection->read_end - connection->read_ptr);
if (nbytes > 0)
{
size_t total_bytes;
/* Update read pointer. */
connection->read_ptr += nbytes;
/* Check if we have a complete request header. */
total_bytes = connection->read_ptr - connection->read_base;
if (total_bytes >= sizeof (request_header))
{
request_header *header;
/* Check if we have a complete request. */
header = (request_header *)connection->read_base;
if (total_bytes >= header->size)
{
/* Process the request. */
if (process_request (connection) < 0)
return -1;
/* Adjust read pointer, and flush buffer. */
connection->read_ptr -= header->size;
memmove (connection->read_base,
connection->read_base + header->size,
connection->read_ptr - connection->read_base);
}
}
return 0;
}
if (nbytes < 0)
error (0, errno, _("cannot read from client"));
return -1;
}
/* Write data to the client on CONNECTION. */
int
write_data (client_connection *connection)
{
ssize_t nbytes;
assert (connection);
assert ((connection->write_ptr - connection->write_base) > 0);
/* Write data. */
nbytes = write (connection->sock, connection->write_base,
connection->write_ptr - connection->write_base);
if (nbytes > 0)
{
/* Adjust write pointer and flush buffer. */
connection->write_ptr -= nbytes;
memmove (connection->write_base, connection->write_base + nbytes,
connection->write_ptr - connection->write_base);
return 0;
}
if (nbytes < 0)
error (0, errno, _("cannot write to client"));
return -1;
}
/* Process the request received on CONNECTION. Returns 0 if
successful, -1 if not. */
static int
process_request (client_connection *connection)
{
request_header *header;
assert (connection);
assert (connection->read_base);
header = (request_header *)connection->read_base;
if (header->version != UTMPD_VERSION)
{
warning (EINVAL, "invalid protocol version");
return -1;
}
switch (header->type)
{
case UTMPD_REQ_SETUTENT: return do_setutent (connection);
case UTMPD_REQ_GETUTENT: return do_getutent (connection);
case UTMPD_REQ_ENDUTENT: return do_endutent (connection);
case UTMPD_REQ_GETUTLINE: return do_getutline (connection);
case UTMPD_REQ_GETUTID: return do_getutid (connection);
case UTMPD_REQ_PUTUTLINE: return do_pututline (connection);
case UTMPD_REQ_UPDWTMP: return do_updwtmp (connection);
default:
warning (EINVAL, "invalid request type");
return -1;
}
}
/* Send the reply specified by HEADER to the client on CONNECTION.
Returns 0 if successful, -1 if not. */
static int
send_reply (client_connection *connection, const reply_header *reply)
{
/* Check if the reply fits in the buffer. */
if ((size_t) (connection->write_end - connection->write_ptr) < reply->size)
{
error (0, 0, _("buffer overflow"));
return -1;
}
/* Copy reply to buffer, and adjust write pointer. */
memcpy (connection->write_ptr, reply, reply->size);
connection->write_ptr += reply->size;
return 0;
}
static int
do_setutent (client_connection *connection)
{
setutent_request *request;
setutent_reply reply;
/* The request size varies, so don't check it. */
request = (setutent_request *)connection->read_base;
/* Initialize reply. */
reply.header.version = UTMPD_VERSION;
reply.header.size = sizeof (setutent_reply);
reply.header.type = UTMPD_REQ_SETUTENT;
/* Select database. */
if (!strncmp (request->file, _PATH_UTMP,
request->header.size - sizeof (setutent_request)))
connection->database = utmp_db;
else
{
errno = EINVAL;
goto return_error;
}
/* Initialize position pointer. */
connection->position = 0;
#if _HAVE_UT_TYPE - 0
/* Make sure the entry won't match. */
connection->last_entry.ut_type = -1;
#endif
reply.errnum = 0;
reply.result = 0;
return send_reply (connection, &reply.header);
return_error:
reply.errnum = errno;
reply.result = -1;
return send_reply (connection, &reply.header);
}
static int
do_getutent (client_connection *connection)
{
getutent_request *request;
getutent_reply reply;
request = (getutent_request *)connection->read_base;
if (request->header.size != sizeof (getutent_request))
{
warning (EINVAL, "invalid request size");
return -1;
}
/* Initialize reply. */
reply.header.version = UTMPD_VERSION;
reply.header.size = sizeof (getutent_reply);
reply.header.type = UTMPD_REQ_GETUTENT;
if (connection->database == NULL || connection->position == -1)
{
errno = ESRCH;
goto return_error;
}
/* Make sure we're in synch with the ordinary file. */
if (synchronize_database (connection->database) < 0)
{
errno = ESRCH;
goto return_error;
}
/* Read the next entry from the database. */
if (read_entry (connection->database, connection->position,
&connection->last_entry) < 0)
{
connection->position = -1;
errno = ESRCH;
goto return_error;
}
/* Update position pointer. */
connection->position++;
memcpy (&reply.entry, &connection->last_entry, sizeof (struct utmp));
reply.errnum = 0;
reply.result = 0;
return send_reply (connection, (reply_header *)&reply);
return_error:
memset (&reply.entry, 0, sizeof (struct utmp));
reply.errnum = errno;
reply.result = -1;
return send_reply (connection, &reply.header);
}
static int
do_endutent (client_connection *connection)
{
endutent_request *request;
endutent_reply reply;
request = (endutent_request *)connection->read_base;
if (request->header.size != sizeof (endutent_request))
{
warning (EINVAL, "invalid request size");
return -1;
}
/* Deselect database. */
connection->database = NULL;
/* Formulate reply. */
reply.header.version = UTMPD_VERSION;
reply.header.size = sizeof (endutent_reply);
reply.header.type = UTMPD_REQ_ENDUTENT;
reply.errnum = 0;
reply.result = 0;
return send_reply (connection, &reply.header);
}
static int
do_getutline (client_connection *connection)
{
getutline_request *request;
getutline_reply reply;
request = (getutline_request *)connection->read_base;
if (request->header.size != sizeof (getutline_request))
{
warning (EINVAL, "invalid request size");
return -1;
}
/* Initialize reply. */
reply.header.version = UTMPD_VERSION;
reply.header.size = sizeof (getutline_reply);
reply.header.type = UTMPD_REQ_GETUTLINE;
if (connection->database == NULL || connection->position == -1)
{
errno = ESRCH;
goto return_error;
}
/* Make sure we're in synch with the ordinary file. */
if (synchronize_database (connection->database) < 0)
{
errno = ESRCH;
goto return_error;
}
while (1)
{
/* Read the next entry. */
if (read_entry (connection->database, connection->position,
&connection->last_entry) < 0)
{
connection->position = -1;
errno = ESRCH;
goto return_error;
}
connection->position++;
/* Stop if we found a user or login entry. */
if (
#if _HAVE_UT_TYPE - 0
(connection->last_entry.ut_type == USER_PROCESS
|| connection->last_entry.ut_type == LOGIN_PROCESS)
&&
#endif
!strncmp (request->line.ut_line, connection->last_entry.ut_line,
sizeof request->line.ut_line))
break;
}
memcpy (&reply.entry, &connection->last_entry, sizeof (struct utmp));
reply.errnum = 0;
reply.result = 0;
return send_reply (connection, &reply.header);
return_error:
memset (&reply.entry, 0, sizeof (struct utmp));
reply.errnum = errno;
reply.result = -1;
return send_reply (connection, &reply.header);
}
static int
do_getutid (client_connection *connection)
{
getutid_request *request;
getutid_reply reply;
request = (getutid_request *)connection->read_base;
if (request->header.size != sizeof (getutid_request))
{
warning (EINVAL, "invalid request size");
return -1;
}
/* Initialize reply. */
reply.header.version = UTMPD_VERSION;
reply.header.size = sizeof (getutid_reply);
reply.header.type = UTMPD_REQ_GETUTID;
if (connection->database == NULL || connection->position == -1)
{
errno = ESRCH;
goto return_error;
}
/* Make sure we're in synch with the ordinary file. */
if (synchronize_database (connection->database) < 0)
{
errno = ESRCH;
goto return_error;
}
if (internal_getut_r (connection, &request->id,
&connection->last_entry) < 0)
{
errno = ESRCH;
goto return_error;
}
reply.errnum = 0;
reply.result = 0;
memcpy (&reply.entry, &connection->last_entry, sizeof (struct utmp));
return send_reply (connection, &reply.header);
return_error:
memset (&reply.entry, 0, sizeof (struct utmp));
reply.errnum = errno;
reply.result = -1;
return send_reply (connection, &reply.header);
}
static int
do_pututline (client_connection *connection)
{
pututline_request *request;
pututline_reply reply;
struct utmp buffer;
int found;
request = (pututline_request *)connection->read_base;
if (request->header.size != sizeof (pututline_request))
{
warning (EINVAL, "invalid request size");
return -1;
}
/* Initialize reply. */
reply.header.version = UTMPD_VERSION;
reply.header.size = sizeof (pututline_reply);
reply.header.type = UTMPD_REQ_PUTUTLINE;
if (!(connection->access & W_OK))
{
errno = EPERM;
goto return_error;
}
if (connection->database == NULL)
{
errno = ESRCH;
goto return_error;
}
/* Make sure we're in synch with the ordinary file. */
if (synchronize_database (connection->database) < 0)
{
errno = ESRCH;
goto return_error;
}
/* Find the correct place to insert the data. */
if (connection->position > 0
&& (
#if _HAVE_UT_TYPE - 0
(connection->last_entry.ut_type == request->utmp.ut_type
&& (connection->last_entry.ut_type == RUN_LVL
|| connection->last_entry.ut_type == BOOT_TIME
|| connection->last_entry.ut_type == OLD_TIME
|| connection->last_entry.ut_type == NEW_TIME))
||
#endif
proc_utmp_eq (&connection->last_entry, &request->utmp)))
found = 1;
else
found = internal_getut_r (connection, &request->utmp, &buffer);
if (found < 0)
{
/* We append the next entry. */
connection->position =
append_entry (connection->database, &request->utmp);
if (connection->position < 0)
goto return_error;
}
else
{
/* We replace the just read entry. */
connection->position--;
if (write_entry (connection->database, connection->position,
&request->utmp) < 0)
goto return_error;
}
/* Write the entry to the compatibility file. */
write_old_entry (connection->database, connection->position, &request->utmp);
/* Update position pointer. */
connection->position++;
reply.errnum = 0;
reply.result = 0;
return send_reply (connection, &reply.header);
return_error:
reply.errnum = errno;
reply.result = -1;
return send_reply (connection, &reply.header);
}
static int
do_updwtmp (client_connection *connection)
{
updwtmp_request *request;
updwtmp_reply reply;
utmp_database *database;
/* The request size varies, so don't check it. */
request = (updwtmp_request *)connection->read_base;
/* Initialize reply. */
reply.header.version = UTMPD_VERSION;
reply.header.size = sizeof (updwtmp_reply);
reply.header.type = UTMPD_REQ_UPDWTMP;
if (!(connection->access & W_OK))
{
errno = EPERM;
goto return_error;
}
/* Select database. */
if (!strncmp (request->file, _PATH_UTMP,
request->header.size - sizeof (updwtmp_request)))
database = utmp_db;
else
{
errno = EINVAL;
goto return_error;
}
/* Make sure we're in synch with the ordinary file. */
if (synchronize_database (database) < 0)
{
errno = ESRCH;
goto return_error;
}
/* Append the entry. */
if (append_entry (database, &request->utmp) < 0)
goto return_error;
reply.errnum = 0;
reply.result = 0;
return send_reply (connection, &reply.header);
return_error:
reply.errnum = errno;
reply.result = -1;
return send_reply (connection, &reply.header);
}
/* This function is identical to the one in login/utmp_file.c. */
int
proc_utmp_eq (const struct utmp *entry, const struct utmp *match)
{
return
(
#if _HAVE_UT_TYPE - 0
(entry->ut_type == INIT_PROCESS
|| entry->ut_type == LOGIN_PROCESS
|| entry->ut_type == USER_PROCESS
|| entry->ut_type == DEAD_PROCESS)
&&
(match->ut_type == INIT_PROCESS
|| match->ut_type == LOGIN_PROCESS
|| match->ut_type == USER_PROCESS
|| match->ut_type == DEAD_PROCESS)
&&
#endif
#if _HAVE_UT_ID - 0
(entry->ut_id[0] && match->ut_id[0]
? strncmp (entry->ut_id, match->ut_id, sizeof match->ut_id) == 0
: strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0)
#else
strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0
#endif
);
}
/* This function is derived from the one in login/utmp_file.c. */
static int
internal_getut_r (client_connection *connection,
const struct utmp *id, struct utmp *buffer)
{
#if _HAVE_UT_TYPE - 0
if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
|| id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
{
/* Search for next entry with type RUN_LVL, BOOT_TIME,
OLD_TIME, or NEW_TIME. */
while (1)
{
/* Read the next entry. */
if (read_entry (connection->database, connection->position,
buffer) < 0)
{
connection->position = -1;
return -1;
}
connection->position++;
if (id->ut_type == buffer->ut_type)
break;
}
}
else
#endif /* _HAVE_UT_TYPE */
{
/* Search for the next entry with the specified ID and with type
INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS. */
while (1)
{
/* Read the next entry. */
if (read_entry (connection->database, connection->position,
buffer) < 0)
{
connection->position = -1;
return -1;
}
connection->position++;
if (proc_utmp_eq (buffer, id))
break;
}
}
return 0;
}

View File

@ -1,121 +0,0 @@
/* Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <utmp.h>
#include "xtmp.h"
/* Convert the entry XT to the new utmp format and store it in UT.
Fields in UT that were not present in the old utmp format are
initialized to zero. */
void
xtmp_to_utmp (const struct xtmp *xtmp, struct utmp *utmp)
{
memset (utmp, 0, sizeof (struct utmp));
#if _HAVE_XT_TYPE - 0
utmp->ut_type = xtmp->xt_type;
#endif
#if _HAVE_XT_PID - 0
utmp->ut_pid = xtmp->xt_pid;
#endif
strncpy (utmp->ut_line, xtmp->xt_line, XT_LINESIZE);
#if _HAVE_XT_ID - 0
strncpy (utmp->ut_id, xtmp->xt_id, sizeof xtmp->xt_id);
#endif
#if _HAVE_UT_TV - 0
utmp->ut_tv.tv_sec = xtmp->xt_time;
#else
utmp->ut_time = xtmp->xt_time;
#endif
strncpy (utmp->ut_user, xtmp->xt_user, XT_NAMESIZE);
#if _HAVE_XT_HOST - 0
strncpy (utmp->ut_host, xtmp->xt_host, XT_HOSTSIZE);
#endif
utmp->ut_addr = xtmp->xt_addr;
}
/* Convert the entry UTMP to the old utmp format and store it in XTMP. */
void
utmp_to_xtmp (const struct utmp *utmp, struct xtmp *xtmp)
{
memset (xtmp, 0, sizeof (struct xtmp));
#if _HAVE_XT_TYPE - 0
xtmp->xt_type = utmp->ut_type;
#endif
#if _HAVE_XT_PID - 0
xtmp->xt_pid = utmp->ut_pid;
#endif
strncpy (xtmp->xt_line, utmp->ut_line, XT_LINESIZE);
xtmp->xt_line[XT_LINESIZE] = '\0';
#if _HAVE_XT_ID - 0
strncpy (xtmp->xt_id, utmp->ut_id, sizeof xtmp->xt_id);
#endif
#if _HAVE_UT_TV - 0
xtmp->xt_time = utmp->ut_tv.tv_sec;
#else
xtmp->xt_time = utmp->ut_time;
#endif
strncpy (xtmp->xt_user, utmp->ut_user, XT_NAMESIZE);
#if _HAVE_XT_HOST - 0
strncpy (xtmp->xt_host, utmp->ut_host, XT_HOSTSIZE);
xtmp->xt_host[XT_HOSTSIZE] = '\0';
#endif
xtmp->xt_addr = utmp->ut_addr;
}
/* Compare an old style entry XTMP with a new style entry UTMP. The
function returns 1 if the information that is in both old and new
style entries is identical. Otherwise this function returns 0.
The type of the argument `xtmp' is `struct utmp *', not `struct
utmp *'. This is intentional! We convert from and to `struct
xtmp' directly when we read and write an old style entry. But
since XTMP is converted from an old style entry, we compare only
those elements of the structure that are common to both the new and
the old style entry. */
int
compare_entry (const struct utmp *xtmp, const struct utmp *utmp)
{
return
(
#if _HAVE_XT_TYPE - 0
xtmp->ut_type == utmp->ut_type
#endif
#if _HAVE_XT_PID - 0
&& xtmp->ut_pid == utmp->ut_pid
#endif
&& !strncmp (xtmp->ut_line, utmp->ut_line, XT_LINESIZE - 1)
#if _HAVE_XT_ID - 0
&& !strncmp (xtmp->ut_id, utmp->ut_id, sizeof utmp->ut_id)
#endif
#if _HAVE_UT_TV - 0
&& xtmp->ut_tv.tv_sec == utmp->ut_tv.tv_sec
#else
&& xtmp->ut_time == utmp->ut_time
#endif
&& !strncmp (xtmp->ut_user, utmp->ut_user, XT_NAMESIZE)
#if _HAVE_XT_HOST - 0
&& !strncmp (xtmp->ut_host, utmp->ut_host, XT_HOSTSIZE - 1)
#endif
&& xtmp->ut_addr == utmp->ut_addr);
}

View File

@ -1,55 +0,0 @@
/* The `struct xtmp' type, describing the old linux utmp format.
Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef _XTMP_H
#define _XTMP_H 1
#include <sys/time.h>
#include <sys/types.h>
#define XT_LINESIZE 12
#define XT_NAMESIZE 8
#define XT_HOSTSIZE 16
struct xtmp
{
short int xt_type; /* Type of login. */
pid_t xt_pid; /* Pid of login process. */
char xt_line[XT_LINESIZE]; /* NUL-terminated devicename of tty. */
char xt_id[4]; /* Inittab id. */
time_t xt_time; /* Time entry was made. */
char xt_user[XT_NAMESIZE]; /* Username (not NUL terminated). */
char xt_host[XT_HOSTSIZE]; /* Hostname for remote login. */
long xt_addr; /* Internet address of remote host. */
};
#define _HAVE_XT_TYPE 1
#define _HAVE_XT_PID 1
#define _HAVE_XT_ID 1
#define _HAVE_XT_HOST 1
extern void xtmp_to_utmp (const struct xtmp *xtmp, struct utmp *utmp);
extern void utmp_to_xtmp (const struct utmp *utmp, struct xtmp *xtmp);
extern int compare_entry (const struct utmp *xtmp,
const struct utmp *utmp);
#endif /* xtmp.h */