summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/include/utmps/utmps.h35
-rw-r--r--src/include/utmps/utmpx.h75
-rw-r--r--src/include/utmpx.h13
-rw-r--r--src/utmps/deps-exe/utmps-utmpd2
-rw-r--r--src/utmps/deps-exe/utmps-wtmpd2
-rw-r--r--src/utmps/deps-lib/utmps21
-rw-r--r--src/utmps/endutxent.c10
-rw-r--r--src/utmps/getutxent.c12
-rw-r--r--src/utmps/getutxid.c12
-rw-r--r--src/utmps/getutxline.c12
-rw-r--r--src/utmps/logwtmp.c22
-rw-r--r--src/utmps/pututxline.c14
-rw-r--r--src/utmps/setutxent.c11
-rw-r--r--src/utmps/updwtmpx.c10
-rw-r--r--src/utmps/utmps-internal.h16
-rw-r--r--src/utmps/utmps-utmpd.c216
-rw-r--r--src/utmps/utmps-wtmpd.c107
-rw-r--r--src/utmps/utmps_end.c10
-rw-r--r--src/utmps/utmps_getent.c21
-rw-r--r--src/utmps/utmps_getid.c27
-rw-r--r--src/utmps/utmps_getline.c25
-rw-r--r--src/utmps/utmps_here.c8
-rw-r--r--src/utmps/utmps_here_maybe_init.c11
-rw-r--r--src/utmps/utmps_putline.c22
-rw-r--r--src/utmps/utmps_rewind.c19
-rw-r--r--src/utmps/utmps_start.c18
-rw-r--r--src/utmps/utmps_updwtmpx.c28
-rw-r--r--src/utmps/utmps_utmpx_pack.c9
-rw-r--r--src/utmps/utmps_utmpx_unpack.c13
29 files changed, 801 insertions, 0 deletions
diff --git a/src/include/utmps/utmps.h b/src/include/utmps/utmps.h
new file mode 100644
index 0000000..e4760f9
--- /dev/null
+++ b/src/include/utmps/utmps.h
@@ -0,0 +1,35 @@
+/* ISC license. */
+
+#ifndef UTMPS_H
+#define UTMPS_H
+
+#include <skalibs/tai.h>
+#include <utmps/utmpx.h>
+
+typedef struct utmps_s utmps, *utmps_ref ;
+struct utmps_s
+{
+ int fd ;
+} ;
+#define UTMPS_ZERO { .fd = -1 }
+
+extern int utmps_start (utmps *, char const *, tain_t const *, tain_t *) ;
+#define utmps_start_g(a, s, deadline) utmps_start(a, s, (deadline), &STAMP)
+
+extern void utmps_end (utmps *) ;
+
+extern int utmps_rewind (utmps *, tain_t const *, tain_t *) ;
+#define utmps_rewind_g (a, deadline) utmps_rewind(a, (deadline), &STAMP)
+extern int utmps_getent (utmps *, struct utmpx *, tain_t const *, tain_t *) ;
+#define utmps_getent_g (a, b, deadline) utmps_getent(a, b, (deadline), &STAMP)
+extern int utmps_getid (utmps *, unsigned short, char const *, struct utmpx *, tain_t const *, tain_t *) ;
+#define utmps_getid_g(a, type, id, b, deadline) utmps_getid(a, type, id, b, (deadline), &STAMP)
+extern int utmps_getline (utmps *, char const *, struct utmpx *, tain_t const *, tain_t *) ;
+#define utmps_getline_g(a, line, b, deadline) utmps_getline(a, line, b, (deadline), &STAMP)
+extern int utmps_putline (utmps *, struct utmpx const *, tain_t const *, tain_t *) ;
+#define utmps_putline_g(a, entry, deadline) utmps_putline(a, entry, (deadine), &STAMP)
+
+extern int utmps_updwtmpx (char const *, struct utmpx const *, tain_t const *, tain_t *) ;
+#define utmps_updwtmpx_g(file, b, deadline) utmps_updwtmpx(file, b, (deadline), &STAMP)
+
+#endif
diff --git a/src/include/utmps/utmpx.h b/src/include/utmps/utmpx.h
new file mode 100644
index 0000000..4752964
--- /dev/null
+++ b/src/include/utmps/utmpx.h
@@ -0,0 +1,75 @@
+/* ISC license. */
+
+#ifndef UTMPS_UTMPX_H
+#define UTMPS_UTMPX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+#define UTMPS_UT_LINESIZE 32
+#define UTMPS_UT_NAMESIZE 32
+#define UTMPS_UT_HOSTSIZE 256
+#define UTMPS_UT_IDSIZE 4
+
+struct exit_status
+{
+ short e_termination ;
+ short e_exit ;
+} ;
+
+struct utmpx
+{
+ short ut_type ;
+ pid_t ut_pid ;
+ char ut_line[UTMPS_UT_LINESIZE] ;
+ char ut_id[UTMPS_UT_IDSIZE] ;
+ char ut_user[UTMPS_UT_NAMESIZE] ;
+
+ char ut_host[UTMPS_UT_HOSTSIZE] ;
+ struct exit_status ut_exit ;
+ pid_t ut_session ;
+
+ struct timeval ut_tv ;
+
+ uint32_t ut_addr_v6[4] ;
+ char __unused[20] ;
+} ;
+
+#define ut_name ut_user
+
+#define EMPTY 0
+#define BOOT_TIME 2
+#define OLD_TIME 4
+#define NEW_TIME 3
+#define USER_PROCESS 7
+#define INIT_PROCESS 5
+#define LOGIN_PROCESS 6
+#define DEAD_PROCESS 8
+
+#define RUN_LVL 1
+#define ACCOUNTING 9
+
+extern void endutxent (void) ;
+extern void setutxent (void) ;
+extern struct utmpx *getutxent (void) ;
+extern struct utmpx *getutxid (struct utmpx const *) ;
+extern struct utmpx *getutxline (struct utmpx const *) ;
+extern struct utmpx *pututxline (struct utmpx const *) ;
+
+extern void updwtmpx (char const *, struct utmpx const *) ;
+extern void logwtmp (char const *, char const *, char const *) ;
+
+#define UT_LINESIZE UTMPS_UT_LINESIZE
+#define UT_NAMESIZE UTMPS_UT_NAMESIZE
+#define UT_HOSTSIZE UTMPS_UT_HOSTSIZE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/utmpx.h b/src/include/utmpx.h
new file mode 100644
index 0000000..fc61b8b
--- /dev/null
+++ b/src/include/utmpx.h
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+/*
+ This file is part of the utmps package.
+ See https://skarnet.org/software/utmps/
+*/
+
+#ifndef UTMPX_H
+#define UTMPX_H
+
+#include <utmps/utmpx.h>
+
+#endif
diff --git a/src/utmps/deps-exe/utmps-utmpd b/src/utmps/deps-exe/utmps-utmpd
new file mode 100644
index 0000000..60d8fd7
--- /dev/null
+++ b/src/utmps/deps-exe/utmps-utmpd
@@ -0,0 +1,2 @@
+libutmps.a.xyzzy
+-lskarnet
diff --git a/src/utmps/deps-exe/utmps-wtmpd b/src/utmps/deps-exe/utmps-wtmpd
new file mode 100644
index 0000000..60d8fd7
--- /dev/null
+++ b/src/utmps/deps-exe/utmps-wtmpd
@@ -0,0 +1,2 @@
+libutmps.a.xyzzy
+-lskarnet
diff --git a/src/utmps/deps-lib/utmps b/src/utmps/deps-lib/utmps
new file mode 100644
index 0000000..430b7fb
--- /dev/null
+++ b/src/utmps/deps-lib/utmps
@@ -0,0 +1,21 @@
+endutxent.o
+getutxent.o
+getutxid.o
+getutxline.o
+logwtmp.o
+pututxline.o
+setutxent.o
+updwtmpx.o
+utmps_end.o
+utmps_getent.o
+utmps_getid.o
+utmps_getline.o
+utmps_here.o
+utmps_here_maybe_init.o
+utmps_putline.o
+utmps_rewind.o
+utmps_start.o
+utmps_updwtmpx.o
+utmps_utmpx_pack.o
+utmps_utmpx_unpack.o
+-lskarnet
diff --git a/src/utmps/endutxent.c b/src/utmps/endutxent.c
new file mode 100644
index 0000000..bc93778
--- /dev/null
+++ b/src/utmps/endutxent.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+void endutxent (void)
+{
+ utmps_end(&utmps_here) ;
+}
diff --git a/src/utmps/getutxent.c b/src/utmps/getutxent.c
new file mode 100644
index 0000000..97a5917
--- /dev/null
+++ b/src/utmps/getutxent.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+struct utmpx *getutxent (void)
+{
+ utmps_here_maybe_init() ;
+ if (!utmps_getent(&utmps_here, &utmps_utmpx_here, 0, 0)) return 0 ;
+ return &utmps_utmpx_here ;
+}
diff --git a/src/utmps/getutxid.c b/src/utmps/getutxid.c
new file mode 100644
index 0000000..262f35c
--- /dev/null
+++ b/src/utmps/getutxid.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+struct utmpx *getutxid (struct utmpx const *b)
+{
+ utmps_here_maybe_init() ;
+ if (!utmps_getid(&utmps_here, (unsigned short)b->ut_type, b->ut_id, &utmps_utmpx_here, 0, 0)) return 0 ;
+ return &utmps_utmpx_here ;
+}
diff --git a/src/utmps/getutxline.c b/src/utmps/getutxline.c
new file mode 100644
index 0000000..e950816
--- /dev/null
+++ b/src/utmps/getutxline.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+struct utmpx *getutxline (struct utmpx const *b)
+{
+ utmps_here_maybe_init() ;
+ if (!utmps_getline(&utmps_here, b->ut_line, &utmps_utmpx_here, 0, 0)) return 0 ;
+ return &utmps_utmpx_here ;
+}
diff --git a/src/utmps/logwtmp.c b/src/utmps/logwtmp.c
new file mode 100644
index 0000000..e50b68d
--- /dev/null
+++ b/src/utmps/logwtmp.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <string.h>
+#include <skalibs/tai.h>
+#include <utmps/utmpx.h>
+
+void logwtmp (char const *line, char const *name, char const *host)
+{
+ struct utmpx b ;
+ memset(&b, 0, sizeof(struct utmpx)) ;
+ strncpy(b.ut_line, line, UTMPS_UT_LINESIZE - 1) ;
+ strncpy(b.ut_user, name, UTMPS_UT_NAMESIZE - 1) ;
+ strncpy(b.ut_host, host, UTMPS_UT_HOSTSIZE - 1) ;
+ b.ut_pid = getpid() ;
+ {
+ tain_t now ;
+ tain_now(&now) ;
+ timeval_from_tain(&b.ut_tv, &now) ;
+ }
+ updwtmpx("", &b) ;
+}
diff --git a/src/utmps/pututxline.c b/src/utmps/pututxline.c
new file mode 100644
index 0000000..4e149d8
--- /dev/null
+++ b/src/utmps/pututxline.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+struct utmpx *pututxline (struct utmpx const *b)
+{
+ static struct utmpx here ; /* POSIX says we can't use utmps_utmpx_here */
+ utmps_here_maybe_init() ;
+ if (!utmps_putline(&utmps_here, b, 0, 0)) return 0 ;
+ here = *b ;
+ return &here ;
+}
diff --git a/src/utmps/setutxent.c b/src/utmps/setutxent.c
new file mode 100644
index 0000000..b8b8199
--- /dev/null
+++ b/src/utmps/setutxent.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+void setutxent (void)
+{
+ utmps_here_maybe_init() ;
+ utmps_rewind(&utmps_here, 0, 0) ;
+}
diff --git a/src/utmps/updwtmpx.c b/src/utmps/updwtmpx.c
new file mode 100644
index 0000000..0166448
--- /dev/null
+++ b/src/utmps/updwtmpx.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <utmps/config.h>
+#include <utmps/utmps.h>
+
+void updwtmpx (char const *file, struct utmpx const *b)
+{
+ (void)file ;
+ utmps_updwtmpx(UTMPS_WTMPD_PATH, b, 0, 0) ;
+}
diff --git a/src/utmps/utmps-internal.h b/src/utmps/utmps-internal.h
new file mode 100644
index 0000000..476e100
--- /dev/null
+++ b/src/utmps/utmps-internal.h
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#ifndef UTMPS_INTERNAL_H
+#define UTMPS_INTERNAL_H
+
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+
+extern struct utmpx utmps_utmpx_here ;
+extern utmps utmps_here ;
+extern void utmps_here_maybe_init (void) ;
+
+extern void utmps_utmpx_pack (char *, struct utmpx const *) ;
+extern void utmps_utmpx_unpack (char const *, struct utmpx *) ;
+
+#endif
diff --git a/src/utmps/utmps-utmpd.c b/src/utmps/utmps-utmpd.c
new file mode 100644
index 0000000..e460368
--- /dev/null
+++ b/src/utmps/utmps-utmpd.c
@@ -0,0 +1,216 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <skalibs/types.h>
+#include <skalibs/env.h>
+#include <skalibs/error.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-timed.h>
+#include <utmps/utmpx.h>
+#include "utmps-internal.h"
+
+static int fd = -1 ;
+
+static void get0 (char *s, size_t n)
+{
+ tain_t deadline ;
+ tain_ulong(&deadline, 2) ;
+ tain_add_g(&deadline, &deadline) ;
+ if (buffer_timed_get_g(buffer_0small, s, n, &deadline) < n)
+ strerr_diefu1sys(111, "read from stdin") ;
+}
+
+static void flush1 (void)
+{
+ tain_t deadline ;
+ tain_ulong(&deadline, 2) ;
+ tain_add_g(&deadline, &deadline) ;
+ if (!buffer_timed_flush_g(buffer_1small, &deadline))
+ strerr_diefu1sys(111, "write to stdout") ;
+}
+
+static void answer (int e)
+{
+ char c = e ;
+ buffer_putnoflush(buffer_1small, &c, 1) ;
+ flush1() ;
+}
+
+static void maybe_open (void)
+{
+ if (fd < 0)
+ {
+ fd = open("utmp", O_RDWR | O_CREAT) ;
+ if (fd < 0)
+ {
+ int e = errno ;
+ answer(e) ;
+ errno = e ;
+ strerr_diefu1sys(111, "open utmp file") ;
+ }
+ }
+}
+
+static int read_utmp_entry (char *s)
+{
+ ssize_t r ;
+ int e ;
+ if (lock_sh(fd) < 0) { e = errno ; goto err ; }
+ r = read(fd, s, sizeof(struct utmpx)) ;
+ lock_unx(fd) ;
+ if (r < 0) { e = errno ; goto err ; }
+ if (!r) return 0 ;
+ if (r < sizeof(struct utmpx)) { e = EPIPE ; goto err ; }
+ return 1 ;
+ err:
+ unlink("utmp") ;
+ answer(e) ;
+ errno = e ;
+ strerr_diefu1sys(111, "read utmp file") ;
+}
+
+static int idmatch (unsigned short type, char const *id, struct utmpx const *b)
+{
+ if (type == BOOT_TIME || type == OLD_TIME || type == NEW_TIME)
+ {
+ if (type == (unsigned short)b->ut_type) return 1 ;
+ }
+ else if (type == INIT_PROCESS || type == LOGIN_PROCESS || type == USER_PROCESS || type == DEAD_PROCESS)
+ {
+ if ((b->ut_type == INIT_PROCESS || b->ut_type == LOGIN_PROCESS || b->ut_type == USER_PROCESS || b->ut_type == DEAD_PROCESS)
+ && !strncmp(id, b->ut_id, UTMPS_UT_IDSIZE - 1)) return 1 ;
+ }
+ return 0 ;
+}
+
+static void do_getent (void)
+{
+ struct utmpx b ;
+ char buf[1 + sizeof(struct utmpx)] = "" ;
+ maybe_open() ;
+ if (!read_utmp_entry(buf+1)) { answer(ESRCH) ; return ; }
+ utmps_utmpx_unpack(buf+1, &b) ;
+ utmps_utmpx_pack(buf+1, &b) ;
+ buffer_putnoflush(buffer_1small, buf, 1 + sizeof(struct utmpx)) ;
+ flush1() ;
+}
+
+static void do_getid (void)
+{
+ unsigned short type ;
+ char rbuf[USHORT_PACK + UTMPS_UT_IDSIZE] ;
+ char sbuf[1 + sizeof(struct utmpx)] = "" ;
+ get0(rbuf, USHORT_PACK + UTMPS_UT_IDSIZE) ;
+ ushort_unpack_big(rbuf, &type) ;
+ rbuf[USHORT_PACK + UTMPS_UT_IDSIZE - 1] = 0 ;
+ maybe_open() ;
+ for (;;)
+ {
+ struct utmpx b ;
+ if (!read_utmp_entry(sbuf+1)) { answer(ESRCH) ; return ; }
+ utmps_utmpx_unpack(sbuf+1, &b) ;
+ if (idmatch(type, rbuf + USHORT_PACK, &b)) break ;
+ }
+ buffer_putnoflush(buffer_1small, sbuf, 1 + sizeof(struct utmpx)) ;
+ flush1() ;
+}
+
+static void do_getline (void)
+{
+ char rbuf[UTMPS_UT_LINESIZE] ;
+ char sbuf[1 + sizeof(struct utmpx)] = "" ;
+ get0(rbuf, UTMPS_UT_LINESIZE) ;
+ rbuf[UTMPS_UT_LINESIZE - 1] = 0 ;
+ maybe_open() ;
+ for (;;)
+ {
+ struct utmpx b ;
+ if (!read_utmp_entry(sbuf+1)) { answer(ESRCH) ; return ; }
+ utmps_utmpx_unpack(sbuf+1, &b) ;
+ if ((b.ut_type == LOGIN_PROCESS || b.ut_type == USER_PROCESS)
+ && !strncmp(rbuf, b.ut_line, UTMPS_UT_LINESIZE - 1)) break ;
+ }
+ buffer_putnoflush(buffer_1small, sbuf, 1 + sizeof(struct utmpx)) ;
+ flush1() ;
+}
+
+static void do_putline (uid_t uid)
+{
+ struct utmpx u ;
+ char buf[sizeof(struct utmpx)] ;
+ get0(buf, sizeof(struct utmpx)) ;
+ if (uid) { answer(EPERM) ; return ; }
+ utmps_utmpx_unpack(buf, &u) ;
+ maybe_open() ;
+ for (;;)
+ {
+ struct utmpx b ;
+ char tmp[sizeof(struct utmpx)] ;
+ if (!read_utmp_entry(tmp)) goto writeit ;
+ utmps_utmpx_unpack(tmp, &b) ;
+ if (idmatch(u.ut_type, u.ut_id, &b)) break ;
+ }
+ if (lseek(fd, -sizeof(struct utmpx), SEEK_CUR) < 0)
+ {
+ answer(errno) ;
+ return ;
+ }
+ writeit:
+ utmps_utmpx_pack(buf, &u) ;
+ if (lock_ex(fd) < 0) { answer(errno) ; return ; }
+ if (allwrite(fd, buf, sizeof(struct utmpx)) < sizeof(struct utmpx))
+ {
+ int e = errno ;
+ answer(e) ;
+ errno = e ;
+ strerr_diefu1sys(111, "write to utmp") ;
+ }
+ fsync(fd) ;
+ lock_unx(fd) ;
+ answer(0) ;
+}
+
+static void do_rewind (void)
+{
+ maybe_open() ;
+ if (lseek(fd, 0, SEEK_SET) < 0) { answer(errno) ; return ; }
+ answer(0) ;
+}
+
+int main (void)
+{
+ uid_t uid ;
+ char const *x = ucspi_get("REMOTEEUID") ;
+ if (!x) strerr_diefu1x(100, "get $IPCREMOTEEUID from environment") ;
+ if (!uid0_scan(x, &uid)) strerr_dieinvalid(100, "IPCREMOTEEUID") ;
+ if (ndelay_on(0) < 0) strerr_diefu1sys(111, "set stdin non-blocking") ;
+ tain_now_g() ;
+
+ for (;;)
+ {
+ tain_t deadline ;
+ char c ;
+ tain_add_g(&deadline, &tain_infinite_relative) ;
+ if (!buffer_timed_get_g(buffer_0small, &c, 1, &deadline)) break ;
+ switch (c)
+ {
+ case 'e' : do_getent() ; break ;
+ case 'i' : do_getid() ; break ;
+ case 'l' : do_getline() ; break ;
+ case 'E' : do_putline(uid) ; break ;
+ case 'r' : do_rewind() ; break ;
+ default :
+ errno = EPROTO ;
+ strerr_diefu1sys(1, "interpret stdin") ;
+ }
+ }
+ return 0 ;
+}
diff --git a/src/utmps/utmps-wtmpd.c b/src/utmps/utmps-wtmpd.c
new file mode 100644
index 0000000..b8ff5c3
--- /dev/null
+++ b/src/utmps/utmps-wtmpd.c
@@ -0,0 +1,107 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <skalibs/types.h>
+#include <skalibs/error.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/env.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-timed.h>
+#include <utmps/utmpx.h>
+#include "utmps-internal.h"
+
+static void answer (int e)
+{
+ char c = e ;
+ write(1, &c, 1) ;
+}
+
+int main (void)
+{
+ struct utmpx b ;
+ char const *x ;
+ tain_t deadline ;
+ size_t w ;
+ uid_t uid ;
+ int fd ;
+ char buf[sizeof(struct utmpx)] ;
+ PROG = "utmps-wtmpd" ;
+
+ x = ucspi_get("REMOTEEUID") ;
+ if (!x) strerr_diefu1x(100, "get $IPCREMOTEEUID from environment") ;
+ if (!uid0_scan(x, &uid)) strerr_dieinvalid(100, "IPCREMOTEEUID") ;
+ if (ndelay_on(0) < 0) strerr_diefu1sys(111, "set stdin non-blocking") ;
+ tain_now_g() ;
+ tain_ulong(&deadline, 2) ;
+ tain_add_g(&deadline, &deadline) ;
+
+ w = buffer_timed_get_g(buffer_0small, buf, 1, &deadline) ;
+ if (!w) strerr_diefu1sys(111, "read from stdin") ;
+ if (buf[0] != '+') { errno = EPROTO ; strerr_diefu1sys(111, "read command") ; }
+ w = buffer_timed_get_g(buffer_0small, buf, sizeof(struct utmpx), &deadline) ;
+ if (w < sizeof(struct utmpx)) strerr_diefu1sys(111, "read from stdin") ;
+ utmps_utmpx_unpack(buf, &b) ;
+ if (uid)
+ {
+ struct passwd *pw ;
+ errno = 0 ;
+ pw = getpwnam(b.ut_user) ;
+ if (!pw)
+ {
+ if (errno)
+ {
+ answer(errno) ;
+ strerr_diefu1sys(111, "read user database") ;
+ }
+ else
+ {
+ answer(EPERM) ;
+ strerr_diefu2x(1, "verify ut_user identity", ": no such user") ;
+ }
+ }
+ if (pw->pw_uid != uid)
+ {
+ answer(EPERM) ;
+ strerr_diefu2x(1, "verify ut_user identity", ": uid mismatch") ;
+ }
+ }
+
+ fd = open_append("wtmp") ;
+ if (fd < 0)
+ {
+ answer(errno) ;
+ strerr_diefu1sys(111, "open wtmp") ;
+ }
+ if (lock_ex(fd) < 0)
+ {
+ answer(errno) ;
+ strerr_diefu1sys(111, "open wtmp") ;
+ }
+ if (lseek(fd, 0, SEEK_END) < 0)
+ {
+ answer(errno) ;
+ strerr_diefu1sys(111, "lseek on wtmp") ;
+ }
+ w = allwrite(fd, buf + 1, sizeof(struct utmpx)) ;
+ if (w < sizeof(struct utmpx))
+ {
+ int e = errno ;
+ if (w)
+ {
+ struct stat st ;
+ if (!fstat(fd, &st)) ftruncate(fd, st.st_size - w) ;
+ }
+ answer(e) ;
+ strerr_diefu1sys(111, "append to wtmp") ;
+ }
+ fsync(fd) ;
+ answer(0) ;
+ return 0 ;
+}
diff --git a/src/utmps/utmps_end.c b/src/utmps/utmps_end.c
new file mode 100644
index 0000000..141dfb7
--- /dev/null
+++ b/src/utmps/utmps_end.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <utmps/utmps.h>
+
+void utmps_end (utmps *a)
+{
+ fd_close(a->fd) ;
+ a->fd = -1 ;
+}
diff --git a/src/utmps/utmps_getent.c b/src/utmps/utmps_getent.c
new file mode 100644
index 0000000..2b21b04
--- /dev/null
+++ b/src/utmps/utmps_getent.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <skalibs/unix-timed.h>
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+int utmps_getent (utmps *a, struct utmpx *b, tain_t const *deadline, tain_t *stamp)
+{
+ ssize_t r ;
+ char buf[1 + sizeof(struct utmpx)] ;
+ if (!ipc_timed_send(a->fd, "e", 1, deadline, stamp)) return 0 ;
+ r = ipc_timed_recv(a->fd, buf, sizeof(buf), 0, deadline, stamp) ;
+ if (r < 0) return 0 ;
+ if (!r) return (errno = EPIPE, 0) ;
+ if (buf[0]) return (errno = buf[0], 0) ;
+ utmps_utmpx_unpack(buf + 1, b) ;
+ return 1 ;
+}
diff --git a/src/utmps/utmps_getid.c b/src/utmps/utmps_getid.c
new file mode 100644
index 0000000..622fec8
--- /dev/null
+++ b/src/utmps/utmps_getid.c
@@ -0,0 +1,27 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#include <skalibs/types.h>
+#include <skalibs/unix-timed.h>
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+int utmps_getid (utmps *a, unsigned short type, char const *id, struct utmpx *b, tain_t const *deadline, tain_t *stamp)
+{
+ ssize_t r ;
+ char sbuf[1 + USHORT_PACK + UTMPS_UT_IDSIZE] = "i" ;
+ char rbuf[1 + sizeof(struct utmpx)] ;
+ ushort_pack_big(sbuf + 1, type) ;
+ memset(sbuf + 1 + USHORT_PACK, 0, UTMPS_UT_IDSIZE) ;
+ strncpy(sbuf + 1 + USHORT_PACK, id, UTMPS_UT_IDSIZE - 1) ;
+ if (!ipc_timed_send(a->fd, sbuf, sizeof(sbuf), deadline, stamp)) return 0 ;
+ r = ipc_timed_recv(a->fd, rbuf, sizeof(rbuf), 0, deadline, stamp) ;
+ if (r < 0) return 0 ;
+ if (!r) return (errno = EPIPE, 0) ;
+ if (rbuf[0]) return (errno = rbuf[0], 0) ;
+ utmps_utmpx_unpack(rbuf + 1, b) ;
+ return 1 ;
+}
diff --git a/src/utmps/utmps_getline.c b/src/utmps/utmps_getline.c
new file mode 100644
index 0000000..4612082
--- /dev/null
+++ b/src/utmps/utmps_getline.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#include <skalibs/unix-timed.h>
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+int utmps_getline (utmps *a, char const *line, struct utmpx *b, tain_t const *deadline, tain_t *stamp)
+{
+ ssize_t r ;
+ char sbuf[1 + UTMPS_UT_LINESIZE] = "l" ;
+ char rbuf[1 + sizeof(struct utmpx)] ;
+ memset(sbuf + 1, 0, UTMPS_UT_LINESIZE) ;
+ strncpy(sbuf + 1, line, UTMPS_UT_LINESIZE - 1) ;
+ if (!ipc_timed_send(a->fd, sbuf, sizeof(sbuf), deadline, stamp)) return 0 ;
+ r = ipc_timed_recv(a->fd, rbuf, sizeof(rbuf), 0, deadline, stamp) ;
+ if (r < 0) return 0 ;
+ if (!r) return (errno = EPIPE, 0) ;
+ if (rbuf[0]) return (errno = rbuf[0], 0) ;
+ utmps_utmpx_unpack(rbuf + 1, b) ;
+ return 1 ;
+}
diff --git a/src/utmps/utmps_here.c b/src/utmps/utmps_here.c
new file mode 100644
index 0000000..2844c1e
--- /dev/null
+++ b/src/utmps/utmps_here.c
@@ -0,0 +1,8 @@
+/* ISC license. */
+
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+struct utmpx utmps_utmpx_here ;
+utmps utmps_here = UTMPS_ZERO ;
diff --git a/src/utmps/utmps_here_maybe_init.c b/src/utmps/utmps_here_maybe_init.c
new file mode 100644
index 0000000..4f3e207
--- /dev/null
+++ b/src/utmps/utmps_here_maybe_init.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <utmps/config.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+void utmps_here_maybe_init (void)
+{
+ if (utmps_here.fd < 0)
+ utmps_start(&utmps_here, UTMPS_UTMPD_PATH, 0, 0) ;
+}
diff --git a/src/utmps/utmps_putline.c b/src/utmps/utmps_putline.c
new file mode 100644
index 0000000..306fa1b
--- /dev/null
+++ b/src/utmps/utmps_putline.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#include <skalibs/unix-timed.h>
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+int utmps_putline (utmps *a, struct utmpx const *b, tain_t const *deadline, tain_t *stamp)
+{
+ ssize_t r ;
+ char buf[1 + sizeof(struct utmpx)] = "E" ;
+ utmps_utmpx_pack(buf + 1, b) ;
+ if (!ipc_timed_send(a->fd, buf, sizeof(buf), deadline, stamp)) return 0 ;
+ r = ipc_timed_recv(a->fd, buf, 1, 0, deadline, stamp) ;
+ if (r < 0) return 0 ;
+ if (!r) return (errno = EPIPE, 0) ;
+ if (buf[0]) return (errno = buf[0], 0) ;
+ return 1 ;
+}
diff --git a/src/utmps/utmps_rewind.c b/src/utmps/utmps_rewind.c
new file mode 100644
index 0000000..371cb0d
--- /dev/null
+++ b/src/utmps/utmps_rewind.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <skalibs/unix-timed.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+int utmps_rewind (utmps *a, tain_t const *deadline, tain_t *stamp)
+{
+ ssize_t r ;
+ char c ;
+ if (!ipc_timed_send(a->fd, "r", 1, deadline, stamp)) return 0 ;
+ r = ipc_timed_recv(a->fd, &c, 1, 0, deadline, stamp) ;
+ if (r < 0) return 0 ;
+ if (!r) return (errno = EPIPE, 0) ;
+ if (c) return (errno = c, 0) ;
+ return 1 ;
+}
diff --git a/src/utmps/utmps_start.c b/src/utmps/utmps_start.c
new file mode 100644
index 0000000..095fd2c
--- /dev/null
+++ b/src/utmps/utmps_start.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/webipc.h>
+#include <utmps/utmps.h>
+
+int utmps_start (utmps *a, char const *path, tain_t const *deadline, tain_t *stamp)
+{
+ int fd = ipc_stream_nbcoe() ;
+ if (fd < 0) return 0 ;
+ if (!ipc_timed_connect(fd, path, deadline, stamp))
+ {
+ fd_close(fd) ;
+ return 0 ;
+ }
+ a->fd = fd ;
+ return 1 ;
+}
diff --git a/src/utmps/utmps_updwtmpx.c b/src/utmps/utmps_updwtmpx.c
new file mode 100644
index 0000000..55811c4
--- /dev/null
+++ b/src/utmps/utmps_updwtmpx.c
@@ -0,0 +1,28 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <skalibs/unix-timed.h>
+#include <utmps/utmpx.h>
+#include <utmps/utmps.h>
+#include "utmps-internal.h"
+
+int utmps_updwtmpx (char const *path, struct utmpx const *b, tain_t const *deadline, tain_t *stamp)
+{
+ utmps a = UTMPS_ZERO ;
+ ssize_t r ;
+ char buf[1 + sizeof(struct utmpx)] = "+" ;
+ if (!utmps_start(&a, path, deadline, stamp)) return 0 ;
+ utmps_utmpx_pack(buf + 1, b) ;
+ if (!ipc_timed_send(a.fd, buf, 1 + sizeof(struct utmpx), deadline, stamp)) goto err ;
+ r = ipc_timed_recv(a.fd, buf, 1, 0, deadline, stamp) ;
+ if (r < 0) goto err ;
+ if (!r) { errno = EPIPE ; goto err ; }
+ if (buf[0]) { errno = buf[0] ; goto err ; }
+ utmps_end(&a) ;
+ return 1 ;
+
+ err :
+ utmps_end(&a) ;
+ return 0 ;
+}
diff --git a/src/utmps/utmps_utmpx_pack.c b/src/utmps/utmps_utmpx_pack.c
new file mode 100644
index 0000000..c5efecb
--- /dev/null
+++ b/src/utmps/utmps_utmpx_pack.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <string.h>
+#include <utmps/utmpx.h>
+
+void utmps_utmpx_pack (char *s, struct utmpx const *u)
+{
+ memcpy(s, u, sizeof(struct utmpx)) ;
+}
diff --git a/src/utmps/utmps_utmpx_unpack.c b/src/utmps/utmps_utmpx_unpack.c
new file mode 100644
index 0000000..a774356
--- /dev/null
+++ b/src/utmps/utmps_utmpx_unpack.c
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+#include <string.h>
+#include <utmps/utmpx.h>
+
+void utmps_utmpx_unpack (char const *s, struct utmpx *b)
+{
+ memcpy(b, s, sizeof(struct utmpx)) ;
+ b->ut_user[UTMPS_UT_NAMESIZE - 1] = 0 ;
+ b->ut_id[UTMPS_UT_IDSIZE - 1] = 0 ;
+ b->ut_line[UTMPS_UT_LINESIZE - 1] = 0 ;
+ b->ut_host[UTMPS_UT_HOSTSIZE - 1] = 0 ;
+}