aboutsummaryrefslogtreecommitdiff
path: root/login
diff options
context:
space:
mode:
Diffstat (limited to 'login')
-rw-r--r--login/Makefile12
-rw-r--r--login/getutent_r.c113
-rw-r--r--login/login.c23
-rw-r--r--login/logwtmp.c47
-rw-r--r--login/pty.h3
-rw-r--r--login/updwtmp.c46
-rw-r--r--login/utmp-private.h17
-rw-r--r--login/utmp.h11
-rw-r--r--login/utmp_daemon.c454
-rw-r--r--login/utmp_db.c104
-rw-r--r--login/utmp_file.c82
-rw-r--r--login/utmpd/connection.c180
-rw-r--r--login/utmpd/database.c516
-rw-r--r--login/utmpd/error.c104
-rw-r--r--login/utmpd/request.c650
-rw-r--r--login/utmpd/utmpd-private.h107
-rw-r--r--login/utmpd/utmpd.c384
-rw-r--r--login/utmpd/utmpd.h141
-rw-r--r--login/utmpd/xtmp.c102
-rw-r--r--login/utmpd/xtmp.h56
-rw-r--r--login/utmpdump.c53
-rw-r--r--login/utmpname.c86
22 files changed, 3015 insertions, 276 deletions
diff --git a/login/Makefile b/login/Makefile
index 6c10a5aff4..832b1caf63 100644
--- a/login/Makefile
+++ b/login/Makefile
@@ -25,9 +25,15 @@ subdir := login
headers := utmp.h utmpbits.h lastlog.h pty.h
routines := getutent getutent_r getutid getutline getutid_r getutline_r \
- utmp_file utmp_db
+ utmp_file utmp_daemon utmpname
-distribute := utmp-private.h
+others = utmpd
+install-sbin = utmpd
+utmpd-routines := connection database error request xtmp
+
+distribute := utmp-private.h utmpd/xtmp.h utmpd/utmpd.h utmpd/utmpd-private.h
+
+vpath %.c utmpd
# Build the -lutil library with these extra functions.
extra-libs := libutil
@@ -37,6 +43,8 @@ libutil-routines:= login login_tty logout logwtmp pty
include ../Rules
+$(objpfx)utmpd: $(utmpd-routines:%=$(objpfx)%.o)
+
# Depend on libc.so so a DT_NEEDED is generated in the shared objects.
# This ensures they will load libc.so for needed symbols if loaded by
# a statically-linked program that hasn't already loaded it.
diff --git a/login/getutent_r.c b/login/getutent_r.c
index 580dcffb21..3cc46da96c 100644
--- a/login/getutent_r.c
+++ b/login/getutent_r.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>
and Paul Janzen <pcj@primenet.com>, 1996.
@@ -19,49 +19,48 @@
Boston, MA 02111-1307, USA. */
#include <assert.h>
-#include <db.h>
-#include <fcntl.h>
+#if _LIBC
#include <libc-lock.h>
-#include <limits.h>
+#else
+#define __libc_lock_lock(lock) ((void) 0)
+#define __libc_lock_unlock(lock) ((void) 0)
+#define __libc_lock_define_initialized(CLASS,NAME)
+#define weak_alias(name, aliasname) \
+ extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));
+#endif
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <utmp.h>
-#include <sys/stat.h>
#include "utmp-private.h"
/* The various backends we have. */
-static int __setutent_unknown (int reset);
-static int __getutent_r_unknown (struct utmp *buffer, struct utmp **result);
-static struct utmp *__pututline_unknown (const struct utmp *data);
-static void __endutent_unknown (void);
+static int setutent_unknown (int reset);
+static int getutent_r_unknown (struct utmp *buffer, struct utmp **result);
+static struct utmp *pututline_unknown (const struct utmp *data);
+static void endutent_unknown (void);
-
-/* We have three jump tables: unknown, db, or file. */
-static struct utfuncs unknown_functions =
+/* Initial Jump table. */
+struct utfuncs __libc_utmp_unknown_functions =
{
- __setutent_unknown,
- __getutent_r_unknown,
+ setutent_unknown,
+ getutent_r_unknown,
NULL,
NULL,
- __pututline_unknown,
- __endutent_unknown,
+ pututline_unknown,
+ endutent_unknown,
NULL
};
/* Currently selected backend. */
-struct utfuncs *__libc_utmp_jump_table = &unknown_functions;
-
-/* The tables from the services. */
-extern struct utfuncs __libc_utmp_db_functions;
-extern struct utfuncs __libc_utmp_file_functions;
-
+struct utfuncs *__libc_utmp_jump_table = &__libc_utmp_unknown_functions;
/* We need to protect the opening of the file. */
__libc_lock_define_initialized (, __libc_utmp_lock)
+
void
__setutent (void)
{
@@ -75,22 +74,31 @@ weak_alias (__setutent, setutent)
static int
-__setutent_unknown (int reset)
+setutent_unknown (int reset)
{
/* We have to test whether it is still not decided which backend to use. */
- assert (__libc_utmp_jump_table == &unknown_functions);
+ assert (__libc_utmp_jump_table == &__libc_utmp_unknown_functions);
- /* See whether utmp db file exists. */
- if ((*__libc_utmp_db_functions.setutent) (reset))
- __libc_utmp_jump_table = &__libc_utmp_db_functions;
+ /* See whether utmpd is running. */
+ if ((*__libc_utmp_daemon_functions.setutent) (reset))
+ __libc_utmp_jump_table = &__libc_utmp_daemon_functions;
else
{
- /* Either the db file does not exist or we have other
- problems. So use the normal file. */
+ /* Use the normal file, but if the current file is _PATH_UTMP or
+ _PATH_WTMP and the corresponding extended file (with an extra
+ 'x' added to the pathname) exists, we use the extended file,
+ because the original file is in a different format. */
+ if (strcmp (__libc_utmp_file_name, _PATH_UTMP) == 0
+ && __access (_PATH_UTMP "x", F_OK) == 0)
+ __utmpname (_PATH_UTMP "x");
+ else if (strcmp (__libc_utmp_file_name, _PATH_WTMP) == 0
+ && __access (_PATH_WTMP "x", F_OK) == 0)
+ __utmpname (_PATH_WTMP "x");
+
(*__libc_utmp_file_functions.setutent) (reset);
__libc_utmp_jump_table = &__libc_utmp_file_functions;
}
-
+
return 0;
}
@@ -108,7 +116,7 @@ weak_alias (__endutent, endutent)
static void
-__endutent_unknown (void)
+endutent_unknown (void)
{
/* Huh, how do we came here? Nothing to do. */
}
@@ -131,10 +139,10 @@ weak_alias (__getutent_r, getutent_r)
static int
-__getutent_r_unknown (struct utmp *buffer, struct utmp **result)
+getutent_r_unknown (struct utmp *buffer, struct utmp **result)
{
/* It is not yet initialized. */
- __setutent_unknown (0);
+ setutent_unknown (0);
return (*__libc_utmp_jump_table->getutent_r) (buffer, result);
}
@@ -157,43 +165,10 @@ weak_alias (__pututline, pututline)
static struct utmp *
-__pututline_unknown (const struct utmp *data)
+pututline_unknown (const struct utmp *data)
{
/* It is not yet initialized. */
- __setutent_unknown (0);
+ setutent_unknown (0);
return (*__libc_utmp_jump_table->pututline) (data);
}
-
-
-int
-__utmpname (const char *file)
-{
- int result = -1;
-
- __libc_lock_lock (__libc_utmp_lock);
-
- /* Close the old file. */
- (*__libc_utmp_jump_table->endutent) ();
-
- /* Store new names. */
- if ((*__libc_utmp_file_functions.utmpname) (file) == 0
- && !(*__libc_utmp_db_functions.utmpname) (file) == 0)
- {
- /* Try to find out whether we are supposed to work with a db
- file or not. Do this by looking for the extension ".db". */
- const char *ext = strrchr (file, '.');
-
- if (ext != NULL && strcmp (ext, ".db") == 0)
- __libc_utmp_jump_table = &__libc_utmp_db_functions;
- else
- __libc_utmp_jump_table = &unknown_functions;
-
- result = 0;
- }
-
- __libc_lock_unlock (__libc_utmp_lock);
-
- return result;
-}
-weak_alias (__utmpname, utmpname)
diff --git a/login/login.c b/login/login.c
index 7cbe8b603e..6bd0e6eaef 100644
--- a/login/login.c
+++ b/login/login.c
@@ -23,7 +23,11 @@
#include <unistd.h>
#include <stdlib.h>
#include <utmp.h>
-
+
+#ifndef _LIBC
+#define __set_errno(val) errno = (val)
+#endif
+
/* Return the result of ttyname in the buffer pointed to by TTY, which should
be of length BUF_LEN. If it is too long to fit in this buffer, a
sufficiently long buffer is allocated using malloc, and returned in TTY.
@@ -135,20 +139,5 @@ login (const struct utmp *ut)
}
/* Update the WTMP file. Here we have to add a new entry. */
- if (utmpname (_PATH_WTMP) != 0)
- {
- struct utmp *up;
-
- /* Open the WTMP file. */
- setutent ();
-
- /* Position at end of file. */
- while (! getutent_r (&utbuf, &up));
-
- /* Write the new entry. */
- pututline (&copy);
-
- /* Close WTMP file. */
- endutent ();
- }
+ updwtmp (_PATH_WTMP, &copy);
}
diff --git a/login/logwtmp.c b/login/logwtmp.c
index b6af813e73..58f07ff1ce 100644
--- a/login/logwtmp.c
+++ b/login/logwtmp.c
@@ -17,53 +17,12 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
-#include <errno.h>
#include <string.h>
+#include <sys/time.h>
+#include <time.h>
#include <unistd.h>
#include <utmp.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-void
-updwtmp (const char *wtmp_file, const struct utmp *ut)
-{
- struct stat st;
- size_t written;
- int fd;
-
- /* Open WTMP file. */
- fd = __open (wtmp_file, O_WRONLY | O_APPEND);
- if (fd < 0)
- return;
-
- /* Try to lock the file. */
- if (__flock (fd, LOCK_EX | LOCK_NB) < 0 && errno != ENOSYS)
- {
- /* Oh, oh. The file is already locked. Wait a bit and try again. */
- sleep (1);
-
- /* This time we ignore the error. */
- __flock (fd, LOCK_EX | LOCK_NB);
- }
-
- /* Remember original size of log file: */
- if (__fstat (fd, &st) < 0)
- goto done;
-
- /* Write the entry. If we can't write all the bytes, reset the file
- size back to the original size. That way, no partial entries
- will remain. */
- written = __write (fd, ut, sizeof (struct utmp));
- if (written > 0 && written != sizeof (struct utmp))
- ftruncate (fd, st.st_size);
-
-done:
- /* And unlock the file. */
- __flock (fd, LOCK_UN);
-
- /* Close WTMP file. */
- __close (fd);
-}
void
logwtmp (const char *line, const char *name, const char *host)
@@ -90,5 +49,5 @@ logwtmp (const char *line, const char *name, const char *host)
time (&ut.ut_time);
#endif
- updwtmp(_PATH_WTMP, &ut);
+ updwtmp (_PATH_WTMP, &ut);
}
diff --git a/login/pty.h b/login/pty.h
index 09024a787a..731ab12d82 100644
--- a/login/pty.h
+++ b/login/pty.h
@@ -1,5 +1,5 @@
/* pty.h - Functions for pseudo TTY handling.
- Copyright (C) 1996 Free Software Foundation, Inc.
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -22,6 +22,7 @@
#define _PTY_H 1
#include <features.h>
+#include <ioctl-types.h>
#include <termios.h>
diff --git a/login/updwtmp.c b/login/updwtmp.c
new file mode 100644
index 0000000000..9965a61fbb
--- /dev/null
+++ b/login/updwtmp.c
@@ -0,0 +1,46 @@
+/* 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 <unistd.h>
+#include <utmp.h>
+
+#include "utmp-private.h"
+
+
+void
+updwtmp (const char *wtmp_file, const struct utmp *utmp)
+{
+ /* See whether utmpd is running. */
+ if ((*__libc_utmp_daemon_functions.updwtmp) (wtmp_file, utmp) < 0)
+ {
+ /* Use the normal file, but if the current file is _PATH_UTMP or
+ _PATH_WTMP and the corresponding extended file (with an extra
+ 'x' added to the pathname) exists, we use the extended file,
+ because the original file is in a different format. */
+ if (strcmp (wtmp_file, _PATH_UTMP) == 0
+ && __access (_PATH_UTMP "x", F_OK) == 0)
+ (*__libc_utmp_file_functions.updwtmp) (_PATH_UTMP "x", utmp);
+ else if (strcmp (wtmp_file, _PATH_WTMP) == 0
+ && __access (_PATH_WTMP "x", F_OK) == 0)
+ (*__libc_utmp_file_functions.updwtmp) (_PATH_WTMP "x", utmp);
+ else
+ (*__libc_utmp_file_functions.updwtmp) (wtmp_file, utmp);
+ }
+}
+
diff --git a/login/utmp-private.h b/login/utmp-private.h
index 4825ae3f6d..c5e7742a4d 100644
--- a/login/utmp-private.h
+++ b/login/utmp-private.h
@@ -34,8 +34,21 @@ struct utfuncs
int (*getutline_r) (const struct utmp *, struct utmp *, struct utmp **);
struct utmp *(*pututline) (const struct utmp *);
void (*endutent) (void);
- int (*utmpname) (const char *);
-
+ int (*updwtmp) (const char *, const struct utmp *);
};
+/* The tables from the services. */
+extern struct utfuncs __libc_utmp_file_functions;
+extern struct utfuncs __libc_utmp_daemon_functions;
+extern struct utfuncs __libc_utmp_unknown_functions;
+
+/* Currently selected backend. */
+extern struct utfuncs *__libc_utmp_jump_table;
+
+/* Current file name. */
+extern const char *__libc_utmp_file_name;
+
#endif /* utmp-private.h */
+
+
+
diff --git a/login/utmp.h b/login/utmp.h
index 9be0ec5b06..799b1a17ac 100644
--- a/login/utmp.h
+++ b/login/utmp.h
@@ -49,21 +49,22 @@ extern void login __P ((__const struct utmp *__entry));
/* Write the utmp entry to say the user on UT_LINE has logged out. */
extern int logout __P ((__const char *__ut_line));
-/* Append the given entry to a wtmp file. */
-extern void updwtmp __P ((__const char *__wtmp_file,
- __const struct utmp *__entry));
-
/* Append to wtmp an entry for the current time and the given info. */
extern void logwtmp __P ((__const char *__ut_line, __const char *__ut_name,
__const char *__ut_host));
+/* Append entry UTMP to the wtmp-like file WTMP_FILE. */
+extern void updwtmp __P ((__const char *__wtmp_file,
+ __const struct utmp *__utmp));
+
/* Change name of the utmp file to be examined. */
+extern int __utmpname __P ((__const char *__file));
extern int utmpname __P ((__const char *__file));
/* Read next entry from a utmp-like file. */
extern struct utmp *getutent __P ((void));
-/* Rest the input stream to the beginning of the file. */
+/* Reset the input stream to the beginning of the file. */
extern void __setutent __P ((void));
extern void setutent __P ((void));
diff --git a/login/utmp_daemon.c b/login/utmp_daemon.c
new file mode 100644
index 0000000000..edaade1eff
--- /dev/null
+++ b/login/utmp_daemon.c
@@ -0,0 +1,454 @@
+/* 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 <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "utmp-private.h"
+#include "utmpd/utmpd.h"
+
+#ifndef _LIBC
+#define __set_errno(val) errno = (val)
+#endif
+
+
+/* Descriptor for the socket. */
+static int daemon_sock = INT_MIN;
+
+
+/* Functions defined here. */
+static int setutent_daemon (int reset);
+static int getutent_r_daemon (struct utmp *buffer, struct utmp **result);
+static int getutid_r_daemon (const struct utmp *line, struct utmp *buffer,
+ struct utmp **result);
+static int getutline_r_daemon (const struct utmp *id, struct utmp *buffer,
+ struct utmp **result);
+static struct utmp *pututline_daemon (const struct utmp *utmp);
+static void endutent_daemon (void);
+static int updwtmp_daemon (const char *file, const struct utmp *utmp);
+
+/* Jump table for daemon functions. */
+struct utfuncs __libc_utmp_daemon_functions =
+{
+ setutent_daemon,
+ getutent_r_daemon,
+ getutid_r_daemon,
+ getutline_r_daemon,
+ pututline_daemon,
+ endutent_daemon,
+ updwtmp_daemon
+};
+
+static int do_setutent (int sock);
+static int do_getutent (int sock, struct utmp *buffer);
+static int do_endutent (int sock);
+static int do_getutline (int sock, const struct utmp *line,
+ struct utmp *buffer);
+static int do_getutid (int sock, const struct utmp *id,
+ struct utmp *buffer);
+static int do_pututline (int sock, const struct utmp *utmp);
+static int do_updwtmp (int sock, const char *file,
+ const struct utmp *utmp);
+
+static int open_socket (const char *name);
+static int send_request (int sock, const request_header *request,
+ reply_header *reply);
+
+
+static int
+setutent_daemon (int reset)
+{
+ if (daemon_sock == INT_MIN)
+ {
+ daemon_sock = open_socket (_PATH_UTMPD_RW);
+ if (daemon_sock < 0)
+ {
+ /* Hhm, read-write access did not work. Try read-only. */
+ daemon_sock = open_socket (_PATH_UTMPD_RO);
+ if (daemon_sock < 0)
+ return 0;
+ }
+
+ /* Send request to the daemon. */
+ if (do_setutent (daemon_sock) < 0)
+ return 0;
+ }
+ else if (reset)
+ {
+ /* Send request to the daemon. */
+ if (do_setutent (daemon_sock) < 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void
+endutent_daemon (void)
+{
+ if (daemon_sock >= 0)
+ {
+ /* Send request to the daemon. */
+ do_endutent (daemon_sock);
+ close (daemon_sock);
+ }
+
+ daemon_sock = INT_MIN;
+}
+
+
+static int
+getutent_r_daemon (struct utmp *buffer, struct utmp **result)
+{
+ /* Open connection if not already done. */
+ if (daemon_sock == INT_MIN)
+ setutent_daemon (1);
+
+ if (daemon_sock < 0)
+ {
+ /* Not available. */
+ *result = NULL;
+ return -1;
+ }
+
+ /* Send request to the daemon. */
+ if (do_getutent (daemon_sock, buffer) < 0)
+ {
+ *result = NULL;
+ return -1;;
+ }
+
+ *result = buffer;
+ return 0;
+}
+
+
+static int
+getutline_r_daemon (const struct utmp *line, struct utmp *buffer,
+ struct utmp **result)
+{
+ if (daemon_sock < 0)
+ {
+ *result = NULL;
+ return -1;
+ }
+
+ /* Send request to the daemon. */
+ if (do_getutline (daemon_sock, line, buffer) < 0)
+ {
+ *result = NULL;
+ return -1;;
+ }
+
+ *result = buffer;
+ return 0;
+}
+
+
+static int
+getutid_r_daemon (const struct utmp *id, struct utmp *buffer,
+ struct utmp **result)
+{
+ if (daemon_sock < 0)
+ {
+ *result = NULL;
+ return -1;
+ }
+
+ /* Send request to the daemon. */
+ if (do_getutid (daemon_sock, id, buffer) < 0)
+ {
+ *result = NULL;
+ return -1;
+ }
+
+ *result = buffer;
+ return 0;
+}
+
+
+static struct utmp *
+pututline_daemon (const struct utmp *utmp)
+{
+ if (daemon_sock == INT_MIN)
+ /* The connection is closed. Open it again. */
+ setutent_daemon (0);
+
+ if (daemon_sock < 0)
+ /* Something went wrong. */
+ return NULL;
+
+ /* Send request to the daemon. */
+ if (do_pututline (daemon_sock, utmp) < 0)
+ return NULL;
+
+ return (struct utmp *)utmp;
+}
+
+
+static int
+updwtmp_daemon (const char *file, const struct utmp *utmp)
+{
+ int sock;
+
+ /* Only try to open for both reading and writing. */
+ sock = open_socket (_PATH_UTMPD_RW);
+ if (sock < 0)
+ return -1;
+
+ /* Send request to the daemon. */
+ if (do_updwtmp (sock, file, utmp) < 0)
+ return -1;
+
+ close (sock);
+ return 0;
+}
+
+
+static int
+do_setutent (int sock)
+{
+ setutent_request request;
+ setutent_reply reply;
+
+ request.header.version = UTMPD_VERSION;
+ request.header.size = sizeof (setutent_request);
+ request.header.type = UTMPD_REQ_SETUTENT;
+ strncpy (request.file, __libc_utmp_file_name, sizeof request.file);
+
+ reply.header.version = UTMPD_VERSION;
+ reply.header.size = sizeof (setutent_reply);
+ reply.header.type = UTMPD_REQ_SETUTENT;
+
+ if (send_request (sock, &request.header, &reply.header) < 0)
+ return -1;
+
+ if (reply.result < 0)
+ __set_errno (reply.errnum);
+
+ return reply.result;
+}
+
+static int
+do_getutent (int sock, struct utmp *buffer)
+{
+ getutent_request request;
+ getutent_reply reply;
+
+ request.header.version = UTMPD_VERSION;
+ request.header.size = sizeof (getutent_request);
+ request.header.type = UTMPD_REQ_GETUTENT;
+
+ reply.header.version = UTMPD_VERSION;
+ reply.header.size = sizeof (getutent_reply);
+ reply.header.type = UTMPD_REQ_GETUTENT;
+
+ if (send_request (sock, &request.header, &reply.header) < 0)
+ return -1;
+
+ if (reply.result < 0)
+ __set_errno (reply.errnum);
+ else
+ memcpy (buffer, &reply.entry, sizeof (struct utmp));
+
+ return reply.result;
+}
+
+static int
+do_endutent (int sock)
+{
+ endutent_request request;
+ endutent_reply reply;
+
+ request.header.version = UTMPD_VERSION;
+ request.header.size = sizeof (endutent_request);
+ request.header.type = UTMPD_REQ_ENDUTENT;
+
+ reply.header.version = UTMPD_VERSION;
+ reply.header.size = sizeof (endutent_reply);
+ reply.header.type = UTMPD_REQ_ENDUTENT;
+
+ if (send_request (sock, &request.header, &reply.header) < 0)
+ return -1;
+
+ if (reply.result < 0)
+ __set_errno (reply.errnum);
+
+ return reply.result;
+}
+
+static int
+do_getutline (int sock, const struct utmp *line, struct utmp *buffer)
+{
+ getutline_request request;
+ getutline_reply reply;
+
+ request.header.version = UTMPD_VERSION;
+ request.header.size = sizeof (getutline_request);
+ request.header.type = UTMPD_REQ_GETUTLINE;
+ memcpy (&request.line, line, sizeof (struct utmp));
+
+ reply.header.version = UTMPD_VERSION;
+ reply.header.size = sizeof (getutline_reply);
+ reply.header.type = UTMPD_REQ_GETUTLINE;
+
+ if (send_request (sock, &request.header, &reply.header) < 0)
+ return -1;
+
+ if (reply.result < 0)
+ __set_errno (reply.errnum);
+ else
+ memcpy (buffer, &reply.entry, sizeof (struct utmp));
+
+ return reply.result;
+}
+
+static int
+do_getutid (int sock, const struct utmp *id, struct utmp *buffer)
+{
+ getutid_request request;
+ getutid_reply reply;
+
+ request.header.version = UTMPD_VERSION;
+ request.header.size = sizeof (getutid_request);
+ request.header.type = UTMPD_REQ_GETUTID;
+ memcpy (&request.id, id, sizeof (struct utmp));
+
+ reply.header.version = UTMPD_VERSION;
+ reply.header.size = sizeof (getutid_reply);
+ reply.header.type = UTMPD_REQ_GETUTID;
+
+ if (send_request (sock, &request.header, &reply.header) < 0)
+ return -1;
+
+ if (reply.result < 0)
+ __set_errno (reply.errnum);
+ else
+ memcpy (buffer, &reply.entry, sizeof (struct utmp));
+
+ return reply.result;
+}
+
+static int
+do_pututline (int sock, const struct utmp *utmp)
+{
+ pututline_request request;
+ pututline_reply reply;
+
+ request.header.version = UTMPD_VERSION;
+ request.header.size = sizeof (pututline_request);
+ request.header.type = UTMPD_REQ_PUTUTLINE;
+ memcpy (&request.utmp, utmp, sizeof (struct utmp));
+
+ reply.header.version = UTMPD_VERSION;
+ reply.header.size = sizeof (pututline_reply);
+ reply.header.type = UTMPD_REQ_PUTUTLINE;
+
+ if (send_request (sock, &request.header, &reply.header) < 0)
+ return -1;
+
+ if (reply.result < 0)
+ __set_errno (reply.errnum);
+
+ return reply.result;
+}
+
+static int
+do_updwtmp (int sock, const char *file, const struct utmp *utmp)
+{
+ updwtmp_request request;
+ updwtmp_reply reply;
+
+ request.header.version = UTMPD_VERSION;
+ request.header.size = sizeof (updwtmp_request);
+ request.header.type = UTMPD_REQ_UPDWTMP;
+ strncpy (request.file, file, sizeof request.file);
+ memcpy (&request.utmp, utmp, sizeof (struct utmp));
+
+ reply.header.version = UTMPD_VERSION;
+ reply.header.size = sizeof (updwtmp_reply);
+ reply.header.type = UTMPD_REQ_UPDWTMP;
+
+ if (send_request (sock, &request.header, &reply.header) < 0)
+ return -1;
+
+ if (reply.result < 0)
+ __set_errno (reply.errnum);
+
+ return reply.result;
+}
+
+
+/* Create a socket connected to NAME. */
+static int
+open_socket (const char *name)
+{
+ struct sockaddr_un addr;
+ int sock;
+
+ sock = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -1;
+
+ addr.sun_family = AF_UNIX;
+ strcpy (addr.sun_path, name);
+ if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
+ {
+ close (sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+/* Send REQUEST to SOCK, and wait for reply. Returns 0 if successful,
+ storing the reply in REPLY, and -1 if not. */
+static int
+send_request (int sock, const request_header *request,
+ reply_header *reply)
+{
+ reply_header header;
+ ssize_t nbytes;
+
+ nbytes = write (sock, request, request->size);
+ if (nbytes != (ssize_t) request->size)
+ return -1;
+
+ nbytes = read (sock, &header, sizeof (reply_header));
+ if (nbytes != sizeof (reply_header))
+ return -1;
+
+ if (reply->version != header.version
+ || reply->size != header.size
+ || reply->type != header.type)
+ return -1;
+
+ nbytes = read (sock, reply + 1, reply->size - sizeof (reply_header));
+ if (nbytes != (ssize_t) (reply->size - sizeof (reply_header)))
+ return -1;
+
+ return 0;
+}
diff --git a/login/utmp_db.c b/login/utmp_db.c
deleted file mode 100644
index fa0e29a52e..0000000000
--- a/login/utmp_db.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* Copyright (C) 1996 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- Contributed by Ulrich Drepper <drepper@cygnus.com>
- and Paul Janzen <pcj@primenet.com>, 1996.
-
- 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 <db.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <utmp.h>
-#include <sys/stat.h>
-
-#include "utmp-private.h"
-
-
-/* This is the default name. */
-static const char default_file_name[] = _PATH_UTMP_DB;
-
-/* Current file name. */
-static const char *file_name = (const char *) default_file_name;
-
-/* Descriptor for database. */
-#if 0
-/* XXX One day this will become meaningful again. */
-static DB *db_fd;
-static char last_date[16];
-#endif
-
-/* Our local functions. */
-static int setutent_db (int reset);
-static void endutent_db (void);
-static int utmpname_db (const char *name);
-
-
-/* The jump table for the local functions. */
-struct utfuncs __libc_utmp_db_functions =
-{
- setutent_db,
- NULL,
- NULL,
- NULL,
- NULL,
- endutent_db,
- utmpname_db
-};
-
-
-static int
-setutent_db (int reset)
-{
- return 0;
-}
-
-
-static void
-endutent_db (void)
-{
-}
-
-
-static int
-utmpname_db (const char *name)
-{
- if (strcmp (name, file_name) != 0)
- {
- if (strcmp (name, default_file_name) == 0)
- {
- if (file_name != default_file_name)
- free ((char *) file_name);
-
- file_name = default_file_name;
- }
- else
- {
- char *new_name = __strdup (name);
- if (new_name == NULL)
- /* Out of memory. */
- return -1;
-
- if (file_name != default_file_name)
- free ((char *) file_name);
-
- file_name = new_name;
- }
- }
- return 0;
-}
diff --git a/login/utmp_file.c b/login/utmp_file.c
index 9a5d687291..1366f38d75 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -31,12 +31,11 @@
#include "utmp-private.h"
+#ifndef _LIBC
+#define _(msg) (msg)
+#define __set_errno(val) errno = (val)
+#endif
-/* This is the default name. */
-static const char default_file_name[] = _PATH_UTMP;
-
-/* Current file name. */
-static const char *file_name = (const char *) default_file_name;
/* Descriptor for the file and position. */
static int file_fd = INT_MIN;
@@ -44,6 +43,7 @@ static off_t file_offset;
static struct utmp last_entry;
+
/* Functions defined here. */
static int setutent_file (int reset);
static int getutent_r_file (struct utmp *buffer, struct utmp **result);
@@ -53,8 +53,7 @@ static int getutline_r_file (const struct utmp *key, struct utmp *buffer,
struct utmp **result);
static struct utmp *pututline_file (const struct utmp *data);
static void endutent_file (void);
-static int utmpname_file (const char *name);
-
+static int updwtmp_file (const char *file, const struct utmp *utmp);
/* Jump table for file functions. */
struct utfuncs __libc_utmp_file_functions =
@@ -65,7 +64,7 @@ struct utfuncs __libc_utmp_file_functions =
getutline_r_file,
pututline_file,
endutent_file,
- utmpname_file
+ updwtmp_file
};
@@ -74,11 +73,11 @@ setutent_file (int reset)
{
if (file_fd == INT_MIN)
{
- file_fd = open (file_name, O_RDWR);
+ file_fd = open (__libc_utmp_file_name, O_RDWR);
if (file_fd == -1)
{
/* Hhm, read-write access did not work. Try read-only. */
- file_fd = open (file_name, O_RDONLY);
+ file_fd = open (__libc_utmp_file_name, O_RDONLY);
if (file_fd == -1)
{
perror (_("while opening UTMP file"));
@@ -231,9 +230,7 @@ proc_utmp_eq (const struct utmp *entry, const struct utmp *match)
&&
#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)
+ strncmp (entry->ut_id, match->ut_id, sizeof match->ut_id) == 0
#else
strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0
#endif
@@ -404,29 +401,50 @@ pututline_file (const struct utmp *data)
static int
-utmpname_file (const char *name)
+updwtmp_file (const char *file, const struct utmp *utmp)
{
- if (strcmp (name, file_name) != 0)
+ int result = -1;
+ struct stat st;
+ ssize_t nbytes;
+ int fd;
+
+ /* Open WTMP file. */
+ fd = __open (file, O_WRONLY | O_APPEND);
+ if (fd < 0)
+ return -1;
+
+ /* Try to lock the file. */
+ if (__flock (fd, LOCK_EX | LOCK_NB) < 0 && errno != ENOSYS)
{
- if (strcmp (name, default_file_name) == 0)
- {
- if (file_name != default_file_name)
- free ((char *) file_name);
+ /* Oh, oh. The file is already locked. Wait a bit and try again. */
+ sleep (1);
- file_name = default_file_name;
- }
- else
- {
- char *new_name = __strdup (name);
- if (new_name == NULL)
- /* Out of memory. */
- return -1;
+ /* This time we ignore the error. */
+ __flock (fd, LOCK_EX | LOCK_NB);
+ }
- if (file_name != default_file_name)
- free ((char *) file_name);
+ /* Remember original size of log file. */
+ if (__fstat (fd, &st) < 0)
+ goto fail;
- file_name = new_name;
- }
+ /* Write the entry. If we can't write all the bytes, reset the file
+ size back to the original size. That way, no partial entries
+ will remain. */
+ nbytes = __write (fd, utmp, sizeof (struct utmp));
+ if (nbytes != sizeof (struct utmp))
+ {
+ ftruncate (fd, st.st_size);
+ goto fail;
}
- return 0;
+
+ result = 0;
+
+fail:
+ /* And unlock the file. */
+ __flock (fd, LOCK_UN);
+
+ /* Close WTMP file. */
+ __close (fd);
+
+ return result;
}
diff --git a/login/utmpd/connection.c b/login/utmpd/connection.c
new file mode 100644
index 0000000000..4e1663189d
--- /dev/null
+++ b/login/utmpd/connection.c
@@ -0,0 +1,180 @@
+/* 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);
+}
diff --git a/login/utmpd/database.c b/login/utmpd/database.c
new file mode 100644
index 0000000000..e31e0d9dae
--- /dev/null
+++ b/login/utmpd/database.c
@@ -0,0 +1,516 @@
+/* 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 "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 proc_utmp_eq (const struct utmp *entry, const struct utmp *match);
+static int get_mtime (const char *file, 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)
+{
+ utmp_database *database;
+
+ /* Allocate memory. */
+ database = (utmp_database *) malloc (sizeof (utmp_database));
+ if (database == NULL)
+ return NULL;
+
+ memset (database, 0, sizeof (utmp_database));
+
+ /* Open database. */
+ database->fd = open (file, O_RDWR);
+ if (database->fd < 0)
+ goto fail;
+
+ database->old_fd = open (old_file, O_RDWR);
+ if (database->old_fd < 0)
+ goto fail;
+
+ if ((file && !(database->file = strdup (file)))
+ || (old_file && !(database->old_file = strdup (old_file))))
+ goto fail;
+
+ if (initialize_database (database) < 0
+ || synchronize_database (database) < 0)
+ goto fail;
+
+ return database;
+
+fail:
+ 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_file, &mtime) < 0)
+ 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)
+ 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)
+ return -1;
+ }
+ else
+#endif
+ {
+ if (store_process_entry (database, position, &entry) < 0)
+ return -1;
+ }
+
+ /* Update position. */
+ position++;
+ }
+
+ while (1)
+ {
+ if (read_entry (database, position, &entry) < 0)
+ break;
+
+ if (write_old_entry (database, position, &entry) < 0)
+ return -1;
+
+ /* Update position. */
+ position++;
+ }
+ }
+
+ return 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 (old_entry->ut_time > new_entry.ut_time)
+ 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
+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 (old_entry->ut_time > new_entry.ut_time)
+ 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;
+}
+
+
+/* This function is identical to the one in login/utmp_file.c. */
+static 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
+ strncmp (entry->ut_id, match->ut_id, sizeof match->ut_id) == 0
+#else
+ strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0
+#endif
+ );
+}
+
+
+/* Get modification time of FILE and put it in TIMER. returns 0 if
+ successful, -1 if not. */
+static int
+get_mtime (const char *file, time_t *timer)
+{
+ struct stat st;
+
+ if (stat (file, &st) < 0)
+ return -1;
+
+ *timer = st.st_mtime;
+
+ return 0;
+}
diff --git a/login/utmpd/error.c b/login/utmpd/error.c
new file mode 100644
index 0000000000..e6511442e3
--- /dev/null
+++ b/login/utmpd/error.c
@@ -0,0 +1,104 @@
+/* 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);
+}
diff --git a/login/utmpd/request.c b/login/utmpd/request.c
new file mode 100644
index 0000000000..0f68b8ae79
--- /dev/null
+++ b/login/utmpd/request.c
@@ -0,0 +1,650 @@
+/* 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 "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 proc_utmp_eq (const struct utmp *entry, const struct utmp *match);
+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;
+
+ request = (setutent_request *)connection->read_base;
+ if (request->header.size != sizeof (setutent_request))
+ {
+ warning (EINVAL, "invalid request size");
+ return -1;
+ }
+
+ /* 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, sizeof request->file))
+ 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 || 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;
+ }
+
+ /* 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;
+
+ request = (updwtmp_request *)connection->read_base;
+ if (request->header.size != sizeof (updwtmp_request))
+ {
+ warning (EINVAL, "invalid request size");
+ return -1;
+ }
+
+ /* 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, sizeof request->file))
+ 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. */
+static 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
+ strncmp (entry->ut_id, match->ut_id, sizeof match->ut_id) == 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;
+}
diff --git a/login/utmpd/utmpd-private.h b/login/utmpd/utmpd-private.h
new file mode 100644
index 0000000000..4a9cdb921e
--- /dev/null
+++ b/login/utmpd/utmpd-private.h
@@ -0,0 +1,107 @@
+/* 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 _UTMPD_PRIVATE_H
+#define _UTMPD_PRIVATE_H 1
+
+#include <time.h>
+#include <utmp.h>
+
+
+/* The number of connections we allow. */
+#ifndef MAX_CONNECTIONS
+#define MAX_CONNECTIONS 16
+#endif
+
+
+typedef struct utmp_database
+{
+ int fd;
+ int old_fd;
+ char *file;
+ char *old_file;
+ time_t mtime;
+} utmp_database;
+
+
+/* The databases we handle. */
+extern utmp_database *utmp_db;
+extern utmp_database *wtmp_db;
+
+
+typedef struct client_connection
+{
+ int sock;
+ /* Access permissions. */
+ int access;
+
+ /* Read pointer. */
+ void *read_base;
+ void *read_ptr;
+ void *read_end;
+
+ /* Write buffer. */
+ void *write_base;
+ void *write_ptr;
+ void *write_end;
+
+ /* Database to use for this connection. */
+ utmp_database *database;
+ /* Position pointer. */
+ int position;
+
+ /* Last read entry. */
+ struct utmp last_entry;
+
+ /* Pointers to the next and previous connections in the list. */
+ struct client_connection *next;
+ struct client_connection *prev;
+} client_connection;
+
+
+/* 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. */
+extern int forked;
+
+
+/* Database functions. */
+utmp_database *open_database (const char *file, const char *old_file);
+int synchronize_database (utmp_database *database);
+void close_database (utmp_database *database);
+int read_entry (utmp_database *database, int position, struct utmp *entry);
+int write_entry (utmp_database *database, int position,
+ const struct utmp *entry);
+int append_entry (utmp_database *database, const struct utmp *entry);
+int read_old_entry (utmp_database *database, int position, struct utmp *entry);
+int write_old_entry (utmp_database *database, int position,
+ const struct utmp *entry);
+
+/* Connection oriented functions. */
+client_connection *accept_connection (int sock, int access);
+client_connection *find_connection (int sock);
+void close_connection (client_connection *connection);
+int read_data (client_connection *connection);
+int write_data (client_connection *connection);
+
+void error (int status, int errnum, const char *message, ...);
+void warning (int errnum, const char *message, ...);
+
+#endif /* utmpd-private.h */
+
diff --git a/login/utmpd/utmpd.c b/login/utmpd/utmpd.c
new file mode 100644
index 0000000000..e11218151c
--- /dev/null
+++ b/login/utmpd/utmpd.c
@@ -0,0 +1,384 @@
+/* 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 <fcntl.h>
+#include <getopt.h>
+#include <libintl.h>
+#include <pwd.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "utmpd.h"
+#include "utmpd-private.h"
+
+/* Get libc version number. */
+#include "../../version.h"
+
+#define PACKAGE "libc"
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+/* The UTMP database. */
+utmp_database *utmp_db;
+
+/* The socket for read only requests. */
+int ro_sock = -1;
+
+/* The socket for read/write requests. */
+int rw_sock = -1;
+
+
+/* Prototypes for the local functions. */
+static void usage (int status) __attribute__ ((noreturn));
+static void drop_priviliges (void);
+static int make_socket (const char *name);
+static void handle_requests (void) __attribute__ ((noreturn));
+static void termination_handler (int signum);
+static int check_pid (const char *file);
+static int write_pid (const char *file);
+
+
+int
+main (int argc, char *argv[])
+{
+ mode_t mask;
+ int debug;
+ int do_help;
+ int do_version;
+ int opt;
+
+ /* Initialize local variables. */
+ debug = 0;
+ do_help = 0;
+ do_version = 0;
+
+ while ((opt = getopt_long (argc, argv, "dhV", long_options, NULL)) != -1)
+ switch (opt)
+ {
+ case '\0': /* Long option. */
+ break;
+ case 'h':
+ do_help = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'V':
+ do_version = 1;
+ break;
+ default:
+ usage (EXIT_FAILURE);
+ }
+
+ /* Version information is reequested. */
+ if (do_version)
+ {
+ printf ("utmpd (GNU %s) %s\n", PACKAGE, VERSION);
+ printf (gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions. There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "1997");
+ printf (gettext ("Written by %s.\n"), "Mark Kettenis");
+
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ signal (SIGINT, termination_handler);
+ signal (SIGTERM, termination_handler);
+
+ /* Check if we are already running. */
+ if (check_pid (_PATH_UTMPDPID))
+ error (EXIT_FAILURE, 0, "already running");
+
+ /* Open UTMP database. */
+ utmp_db = open_database (_PATH_UTMP "x", _PATH_UTMP);
+ if (utmp_db == NULL)
+ error (EXIT_FAILURE, errno, "%s", _PATH_UTMP);
+
+ /* Create sockets, with the right permissions. */
+ mask = umask (S_IXUSR | S_IXGRP | S_IXOTH);
+ ro_sock = make_socket (_PATH_UTMPD_RO);
+ umask (S_IXUSR | S_IRWXG | S_IRWXO);
+ rw_sock = make_socket (_PATH_UTMPD_RW);
+ umask (mask);
+
+ /* Set the sockets up to accept connections. */
+ if (listen (ro_sock, MAX_CONNECTIONS) < 0
+ || listen (rw_sock, MAX_CONNECTIONS) < 0)
+ error (EXIT_FAILURE, errno, "cannot enable socket to accept connections");
+
+ /* Behave like a daemon. */
+ if (!debug)
+ {
+ openlog ("utmpd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
+
+ if (daemon (0, 0) < 0)
+ error (EXIT_FAILURE, errno, "cannot auto-background");
+ forked = 1;
+
+ if (write_pid (_PATH_UTMPDPID) < 0)
+ warning (errno, "%s", _PATH_UTMPDPID);
+ }
+
+ /* Drop priviliges. */
+ drop_priviliges ();
+
+ /* Handle incoming requests. */
+ handle_requests ();
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, gettext ("Try `%s --help' for more information.\n"),
+ program_invocation_name);
+ else
+ {
+ printf (gettext ("\
+Usage: %s [OPTION]...\n\
+ -d, --debug do not fork and display messages on the current tty\n\
+ -h, --help display this help and exit\n\
+ -V, --version output version information and exit\n"),
+ program_invocation_name);
+ fputs (gettext ("\
+Report bugs to <kettenis@phys.uva.nl>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+
+/* Drop priviliges. */
+static void
+drop_priviliges (void)
+{
+ struct passwd *pw;
+
+ pw = getpwnam ("daemon");
+ if (pw)
+ {
+ seteuid (pw->pw_uid);
+ setegid (pw->pw_gid);
+ }
+}
+
+
+/* Make a socket in the file namespace using the filename NAME as the
+ socket's address. */
+static int
+make_socket (const char *name)
+{
+ struct sockaddr_un addr;
+ size_t size;
+ int sock;
+
+ /* Create the socket. */
+ sock = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ error (EXIT_FAILURE, errno, "cannot create socket");
+
+ /* Bind a name to the socket. */
+ addr.sun_family = AF_UNIX;
+ strcpy (addr.sun_path, name);
+
+ /* The size of the address is the offset of the start
+ of the filename, plus its length, plus one for the
+ terminating null byte. */
+ size = (offsetof (struct sockaddr_un, sun_path)
+ + strlen (addr.sun_path));
+
+ if (bind (sock, (struct sockaddr *) &addr, size) < 0)
+ error (EXIT_FAILURE, errno, "%s", name);
+
+ return sock;
+}
+
+
+/* Hanlde incoming requests. */
+static
+void handle_requests (void)
+{
+ client_connection *connection;
+ fd_set active_read_fd_set;
+ fd_set active_write_fd_set;
+ fd_set read_fd_set;
+ fd_set write_fd_set;
+ int fd;
+
+ /* Initialize the set of active sockets. */
+ FD_ZERO (&active_read_fd_set);
+ FD_ZERO (&active_write_fd_set);
+ FD_SET (rw_sock, &active_read_fd_set);
+ FD_SET (ro_sock, &active_read_fd_set);
+
+ while (1)
+ {
+ /* Block until input arrives on one or more active sockets. */
+ read_fd_set = active_read_fd_set;
+ write_fd_set = active_write_fd_set;
+ if (select (FD_SETSIZE, &read_fd_set, &write_fd_set, NULL, NULL) < 0)
+ error (EXIT_FAILURE, errno, "cannot get input on sockets");
+
+ /* Service all the sockets with input pending. */
+ for (fd = 0; fd < FD_SETSIZE; fd++)
+ {
+ if (FD_ISSET (fd, &read_fd_set))
+ {
+ if (fd == ro_sock || fd == rw_sock)
+ {
+ int access = ((fd == rw_sock) ? (R_OK | W_OK) : R_OK);
+
+ connection = accept_connection (fd, access);
+ if (connection == NULL)
+ error (0, errno, "cannot accept connection");
+
+ FD_SET (connection->sock, &active_read_fd_set);
+ }
+ else
+ {
+ connection = find_connection (fd);
+ if (connection == NULL)
+ error (EXIT_FAILURE, 0, "cannot find connection");
+
+ if (read_data (connection) < 0)
+ {
+ close_connection (connection);
+ FD_CLR (fd, &active_read_fd_set);
+ FD_CLR (fd, &active_write_fd_set);
+ }
+
+ if (connection->write_ptr > connection->write_base)
+ FD_SET (fd, &active_write_fd_set);
+ }
+ }
+ if (FD_ISSET (fd, &write_fd_set) &&
+ fd != rw_sock && fd != ro_sock)
+ {
+ connection = find_connection (fd);
+ if (connection == NULL)
+ error (EXIT_FAILURE, 0, "cannot find connection");
+
+ if (write_data (connection) < 0)
+ {
+ close_connection (connection);
+ FD_CLR (fd, &active_read_fd_set);
+ FD_CLR (fd, &active_write_fd_set);
+ }
+
+ if (connection->write_ptr == connection->write_base)
+ FD_CLR (fd, &active_write_fd_set);
+ }
+ }
+ }
+}
+
+
+/* Cleanup. */
+static void
+termination_handler (int signum)
+{
+ /* Close sockets. */
+ close (ro_sock);
+ close (rw_sock);
+
+ /* Restore user id. */
+ seteuid (getuid ());
+
+ /* Clean up the files created by `bind'. */
+ unlink (_PATH_UTMPD_RO);
+ unlink (_PATH_UTMPD_RW);
+
+ if (utmp_db)
+ close_database (utmp_db);
+
+ /* Clean up pid file. */
+ unlink (_PATH_UTMPDPID);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Returns 1 if the process in pid file FILE is running, 0 if not. */
+static int
+check_pid (const char *file)
+{
+ FILE *fp;
+
+ fp = fopen (_PATH_UTMPDPID, "r");
+ if (fp)
+ {
+ pid_t pid;
+
+ fscanf (fp, "%d", &pid);
+ fclose (fp);
+
+ if (kill (pid, 0) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Write the current process id to the file FILE. Returns 0 if
+ successful, -1 if not. */
+static int
+write_pid (const char *file)
+{
+ FILE *fp;
+
+ fp = fopen (_PATH_UTMPDPID, "w");
+ if (fp == NULL)
+ return -1;
+
+ fprintf (fp, "%d\n", getpid ());
+ if (ferror (fp))
+ return -1;
+
+ fclose (fp);
+
+ return 0;
+}
+
diff --git a/login/utmpd/utmpd.h b/login/utmpd/utmpd.h
new file mode 100644
index 0000000000..8fbc33c923
--- /dev/null
+++ b/login/utmpd/utmpd.h
@@ -0,0 +1,141 @@
+/* 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 _UTMPD_H
+#define _UTMPD_H 1
+
+/* This is an *internal* header. */
+
+#include <limits.h>
+#include <stddef.h>
+#include <utmp.h>
+
+
+/* Paths to daemon sockets. */
+#define _PATH_UTMPD_RO "/var/run/utmpd.ro"
+#define _PATH_UTMPD_RW "/var/run/utmpd.rw"
+
+
+/* Path to PID file. */
+#define _PATH_UTMPDPID "/var/run/utmpd.pid"
+
+
+/* Version number of the daemon interface. */
+#define UTMPD_VERSION 1
+
+
+/* Services provided. */
+typedef enum
+{
+ UTMPD_REQ_SETUTENT,
+ UTMPD_REQ_GETUTENT,
+ UTMPD_REQ_ENDUTENT,
+ UTMPD_REQ_GETUTLINE,
+ UTMPD_REQ_GETUTID,
+ UTMPD_REQ_PUTUTLINE,
+ UTMPD_REQ_UPDWTMP
+} request_type;
+
+
+/* Header common to all requests. */
+typedef struct
+{
+ /* Version number of the daemon interface. */
+ int version;
+ /* Number of bytes in this request. */
+ size_t size;
+ /* Service requested. */
+ request_type type;
+} request_header;
+
+typedef struct
+{
+ request_header header;
+ /* File to use. */
+ char file[_POSIX_PATH_MAX + 1];
+} setutent_request;
+
+typedef struct
+{
+ request_header header;
+} getutent_request, endutent_request;
+
+typedef struct
+{
+ request_header header;
+ /* Entry to match. */
+ struct utmp line;
+} getutline_request;
+
+typedef struct
+{
+ request_header header;
+ /* Entry to match. */
+ struct utmp id;
+} getutid_request;
+
+typedef struct
+{
+ request_header header;
+ /* Entry to write. */
+ struct utmp utmp;
+} pututline_request;
+
+typedef struct
+{
+ request_header header;
+ /* File to use. */
+ char file[_POSIX_PATH_MAX + 1];
+ /* Entry to write. */
+ struct utmp utmp;
+} updwtmp_request;
+
+
+/* Header common to all replies. */
+typedef struct
+{
+ /* Version number of the daemon interface. */
+ int version;
+ /* Number of bytes in this reply. */
+ size_t size;
+ /* Answer to the request. */
+ request_type type;
+} reply_header;
+
+typedef struct
+{
+ reply_header header;
+ /* Error code. */
+ int errnum;
+ /* Return value. */
+ int result;
+} setutent_reply, endutent_reply, pututline_reply, updwtmp_reply;
+
+typedef struct
+{
+ reply_header header;
+ /* Found entry. */
+ struct utmp entry;
+ /* Error code. */
+ int errnum;
+ /* Return value. */
+ int result;
+} getutent_reply, getutline_reply, getutid_reply;
+
+#endif /* utmpd.h */
diff --git a/login/utmpd/xtmp.c b/login/utmpd/xtmp.c
new file mode 100644
index 0000000000..d2d5feee3b
--- /dev/null
+++ b/login/utmpd/xtmp.c
@@ -0,0 +1,102 @@
+/* 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
+ utmp->ut_time = xtmp->xt_time;
+ 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
+ xtmp->xt_time = utmp->ut_time;
+ 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. */
+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
+ && xtmp->ut_time == utmp->ut_time
+ && !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);
+}
diff --git a/login/utmpd/xtmp.h b/login/utmpd/xtmp.h
new file mode 100644
index 0000000000..8fa982ee4f
--- /dev/null
+++ b/login/utmpd/xtmp.h
@@ -0,0 +1,56 @@
+/* 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 <features.h>
+
+#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 adress 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 */
diff --git a/login/utmpdump.c b/login/utmpdump.c
new file mode 100644
index 0000000000..e1422b5028
--- /dev/null
+++ b/login/utmpdump.c
@@ -0,0 +1,53 @@
+/* utmpdump - dump utmp-like files.
+ 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 <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+
+void
+print_entry (struct utmp *up)
+{
+ printf ("[%d] [%05d] [%-4.4s] [%-8.8s] [%-12.12s] [%-15.15s] [%ld]\n",
+ up->ut_type, up->ut_pid, up->ut_id, up->ut_user,
+ up->ut_line, 4 + ctime (&up->ut_time), up->ut_tv.tv_usec);
+}
+
+int
+main (int argc, char *argv[])
+{
+ struct utmp *up;
+
+ if (argc > 1)
+ utmpname (argv[1]);
+
+ setutent ();
+
+ while ((up = getutent ()))
+ print_entry (up);
+
+ endutent ();
+
+ return EXIT_SUCCESS;
+}
diff --git a/login/utmpname.c b/login/utmpname.c
new file mode 100644
index 0000000000..81e857a2bc
--- /dev/null
+++ b/login/utmpname.c
@@ -0,0 +1,86 @@
+/* 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. */
+
+#if _LIBC
+#include <libc-lock.h>
+#else
+#define __libc_lock_lock(NAME) ((void) 0)
+#define __libc_lock_unlock(NAME) ((void) 0)
+#define __libc_lock_define(CLASS,NAME)
+#define weak_alias(name, aliasname) \
+ extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <utmp.h>
+
+#include "utmp-private.h"
+
+
+/* This is the default name. */
+static const char default_file_name[] = _PATH_UTMP;
+
+/* Current file name. */
+const char *__libc_utmp_file_name = (const char *) default_file_name;
+
+/* We have to use the lock in getutent_r.c. */
+__libc_lock_define (extern, __libc_utmp_lock)
+
+
+int
+__utmpname (const char *file)
+{
+ int result = -1;
+
+ __libc_lock_lock (__libc_utmp_lock);
+
+ /* Close the old file. */
+ (*__libc_utmp_jump_table->endutent) ();
+
+ if (strcmp (file, __libc_utmp_file_name) != 0)
+ {
+ if (strcmp (file, default_file_name) == 0)
+ {
+ if (__libc_utmp_file_name != default_file_name)
+ free ((char *) __libc_utmp_file_name);
+
+ __libc_utmp_file_name = default_file_name;
+ }
+ else
+ {
+ char *file_name = __strdup (file);
+ if (file_name == NULL)
+ /* Out of memory. */
+ goto done;
+
+ if (__libc_utmp_file_name != default_file_name)
+ free ((char *) __libc_utmp_file_name);
+
+ __libc_utmp_file_name = file_name;
+ }
+ }
+
+ __libc_utmp_jump_table = &__libc_utmp_unknown_functions;
+ result = 0;
+
+done:
+ __libc_lock_unlock (__libc_utmp_lock);
+ return result;
+}
+weak_alias (__utmpname, utmpname)