summaryrefslogtreecommitdiff
path: root/src/libunixonacid
diff options
context:
space:
mode:
Diffstat (limited to 'src/libunixonacid')
-rw-r--r--src/libunixonacid/bufalloc_timed_flush.c16
-rw-r--r--src/libunixonacid/buffer_timed_fill.c17
-rw-r--r--src/libunixonacid/buffer_timed_flush.c16
-rw-r--r--src/libunixonacid/buffer_timed_get.c30
-rw-r--r--src/libunixonacid/dd_cancel.c17
-rw-r--r--src/libunixonacid/dd_close.c9
-rw-r--r--src/libunixonacid/dd_commit.c68
-rw-r--r--src/libunixonacid/dd_commit_devino.c16
-rw-r--r--src/libunixonacid/dd_open_read.c14
-rw-r--r--src/libunixonacid/dd_open_write.c34
-rw-r--r--src/libunixonacid/kolbak_call.c13
-rw-r--r--src/libunixonacid/kolbak_enqueue.c15
-rw-r--r--src/libunixonacid/kolbak_queue_init.c14
-rw-r--r--src/libunixonacid/kolbak_unenqueue.c11
-rw-r--r--src/libunixonacid/mkdir_unique.c24
-rw-r--r--src/libunixonacid/netstring_timed_get.c24
-rw-r--r--src/libunixonacid/open2_at.c60
-rw-r--r--src/libunixonacid/open3_at.c60
-rw-r--r--src/libunixonacid/open_appendat.c11
-rw-r--r--src/libunixonacid/open_appendatb.c12
-rw-r--r--src/libunixonacid/open_readat.c10
-rw-r--r--src/libunixonacid/open_readatb.c12
-rw-r--r--src/libunixonacid/open_truncat.c10
-rw-r--r--src/libunixonacid/open_truncatb.c12
-rw-r--r--src/libunixonacid/open_writeat.c10
-rw-r--r--src/libunixonacid/open_writeatb.c12
-rw-r--r--src/libunixonacid/opengetlnclose.c24
-rw-r--r--src/libunixonacid/opengetlnclose_at.c24
-rw-r--r--src/libunixonacid/openreadnclose_at.c19
-rw-r--r--src/libunixonacid/openslurpclose_at.c21
-rw-r--r--src/libunixonacid/openwritenclose.c11
-rw-r--r--src/libunixonacid/openwritenclose_at.c24
-rw-r--r--src/libunixonacid/openwritenclose_devino.c12
-rw-r--r--src/libunixonacid/openwritenclose_devino_tmp.c35
-rw-r--r--src/libunixonacid/openwritenclose_tmp.c11
-rw-r--r--src/libunixonacid/skaclient-internal.h15
-rw-r--r--src/libunixonacid/skaclient_default_cb.c14
-rw-r--r--src/libunixonacid/skaclient_end.c21
-rw-r--r--src/libunixonacid/skaclient_init.c32
-rw-r--r--src/libunixonacid/skaclient_put.c10
-rw-r--r--src/libunixonacid/skaclient_putmsg.c16
-rw-r--r--src/libunixonacid/skaclient_putmsgv.c16
-rw-r--r--src/libunixonacid/skaclient_putv.c11
-rw-r--r--src/libunixonacid/skaclient_send.c11
-rw-r--r--src/libunixonacid/skaclient_sendmsg.c15
-rw-r--r--src/libunixonacid/skaclient_sendmsgv.c15
-rw-r--r--src/libunixonacid/skaclient_sendv.c12
-rw-r--r--src/libunixonacid/skaclient_server_ack.c30
-rw-r--r--src/libunixonacid/skaclient_server_bidi_ack.c12
-rw-r--r--src/libunixonacid/skaclient_server_init.c15
-rw-r--r--src/libunixonacid/skaclient_start.c43
-rw-r--r--src/libunixonacid/skaclient_start_async.c52
-rw-r--r--src/libunixonacid/skaclient_start_cb.c18
-rw-r--r--src/libunixonacid/skaclient_startf.c46
-rw-r--r--src/libunixonacid/skaclient_startf_async.c61
-rw-r--r--src/libunixonacid/skaclient_zero.c5
-rw-r--r--src/libunixonacid/timed_flush.c26
-rw-r--r--src/libunixonacid/timed_get.c22
-rw-r--r--src/libunixonacid/timed_getln.c32
-rw-r--r--src/libunixonacid/timed_getlnmax.c33
-rw-r--r--src/libunixonacid/unixmessage_bits_closeall.c7
-rw-r--r--src/libunixonacid/unixmessage_bits_closenone.c7
-rw-r--r--src/libunixonacid/unixmessage_handle.c19
-rw-r--r--src/libunixonacid/unixmessage_put.c83
-rw-r--r--src/libunixonacid/unixmessage_read.c60
-rw-r--r--src/libunixonacid/unixmessage_receive.c44
-rw-r--r--src/libunixonacid/unixmessage_receiver_free.c11
-rw-r--r--src/libunixonacid/unixmessage_receiver_init.c17
-rw-r--r--src/libunixonacid/unixmessage_sender_flush.c82
-rw-r--r--src/libunixonacid/unixmessage_sender_free.c29
-rw-r--r--src/libunixonacid/unixmessage_sender_getfd.c8
-rw-r--r--src/libunixonacid/unixmessage_sender_init.c14
-rw-r--r--src/libunixonacid/unixmessage_sender_timed_flush.c17
-rw-r--r--src/libunixonacid/unixmessage_sender_zero.c5
-rw-r--r--src/libunixonacid/unixmessage_timed_handle.c30
-rw-r--r--src/libunixonacid/unixmessage_timed_receive.c29
-rw-r--r--src/libunixonacid/unixmessage_v_zero.c5
-rw-r--r--src/libunixonacid/unixmessage_zero.c5
78 files changed, 1783 insertions, 0 deletions
diff --git a/src/libunixonacid/bufalloc_timed_flush.c b/src/libunixonacid/bufalloc_timed_flush.c
new file mode 100644
index 0000000..df0eb79
--- /dev/null
+++ b/src/libunixonacid/bufalloc_timed_flush.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <skalibs/bufalloc.h>
+#include <skalibs/functypes.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+static int bufalloc_isnonempty (bufalloc *ba)
+{
+ return !!bufalloc_len(ba) ;
+}
+
+int bufalloc_timed_flush (bufalloc *ba, tain_t const *deadline, tain_t *stamp)
+{
+ return timed_flush(ba, (initfunc_t_ref)&bufalloc_getfd, (initfunc_t_ref)&bufalloc_isnonempty, (initfunc_t_ref)&bufalloc_flush, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/buffer_timed_fill.c b/src/libunixonacid/buffer_timed_fill.c
new file mode 100644
index 0000000..c6f580f
--- /dev/null
+++ b/src/libunixonacid/buffer_timed_fill.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/functypes.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+static int get (buffer *b)
+{
+ return sanitize_read(buffer_fill(b)) ;
+}
+
+int buffer_timed_fill (buffer *b, tain_t const *deadline, tain_t *stamp)
+{
+ return timed_get(b, (initfunc_t_ref)&buffer_getfd, (initfunc_t_ref)&get, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/buffer_timed_flush.c b/src/libunixonacid/buffer_timed_flush.c
new file mode 100644
index 0000000..63a2587
--- /dev/null
+++ b/src/libunixonacid/buffer_timed_flush.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <skalibs/buffer.h>
+#include <skalibs/functypes.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+static int buffer_isnonempty (buffer *b)
+{
+ return !buffer_isempty(b) ;
+}
+
+int timed_buffer_flush (buffer *b, tain_t const *deadline, tain_t *stamp)
+{
+ return timed_flush(b, (initfunc_t_ref)&buffer_getfd, (initfunc_t_ref)&buffer_isnonempty, (initfunc_t_ref)&buffer_flush, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/buffer_timed_get.c b/src/libunixonacid/buffer_timed_get.c
new file mode 100644
index 0000000..2f3b751
--- /dev/null
+++ b/src/libunixonacid/buffer_timed_get.c
@@ -0,0 +1,30 @@
+/* ISC license. */
+
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+struct blah_s
+{
+ buffer *b ;
+ char *s ;
+ unsigned int len ;
+ unsigned int w ;
+} ;
+
+static int getfd (struct blah_s *blah)
+{
+ return buffer_fd(blah->b) ;
+}
+
+static int get (struct blah_s *blah)
+{
+ return buffer_getall(blah->b, blah->s, blah->len, &blah->w) ;
+}
+
+unsigned int buffer_timed_get (buffer *b, char *s, unsigned int len, tain_t const *deadline, tain_t *stamp)
+{
+ struct blah_s blah = { .b = b, .s = s, .len = len, .w = 0 } ;
+ timed_get(&blah, (initfunc_t_ref)&getfd, (initfunc_t_ref)&get, deadline, stamp) ;
+ return blah.w ;
+}
diff --git a/src/libunixonacid/dd_cancel.c b/src/libunixonacid/dd_cancel.c
new file mode 100644
index 0000000..22adf4e
--- /dev/null
+++ b/src/libunixonacid/dd_cancel.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+void dd_cancel (dirdescriptor_t_ref dd)
+{
+ dirdescriptor_t zero = DIRDESCRIPTOR_ZERO ;
+ register int e = errno ;
+ fd_close(dd->fd) ;
+ rm_rf_in_tmp(&dd->new, 0) ;
+ stralloc_free(&dd->new) ;
+ *dd = zero ;
+ errno = e ;
+}
diff --git a/src/libunixonacid/dd_close.c b/src/libunixonacid/dd_close.c
new file mode 100644
index 0000000..74961a1
--- /dev/null
+++ b/src/libunixonacid/dd_close.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+int dd_close (dirdescriptor_t_ref dd)
+{
+ return (fd_close(dd->fd) >= 0) ;
+}
diff --git a/src/libunixonacid/dd_commit.c b/src/libunixonacid/dd_commit.c
new file mode 100644
index 0000000..b55addb
--- /dev/null
+++ b/src/libunixonacid/dd_commit.c
@@ -0,0 +1,68 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h> /* for rename() */
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/random.h>
+#include <skalibs/unix-transactional.h>
+
+static char const *mybasename (char const *s, unsigned int len)
+{
+ register unsigned int i = len ;
+ while (i--) if (s[i] == '/') return s + i + 1 ;
+ return s ;
+}
+
+int dd_commit (dirdescriptor_t_ref dd)
+{
+ dirdescriptor_t zero = DIRDESCRIPTOR_ZERO ;
+ unsigned int len = str_len(dd->lnkfn) ;
+ unsigned int oldbase = dd->new.len ;
+ unsigned int newlnkbase ;
+ char const *lnkbn = mybasename(dd->lnkfn, len) ;
+ if (!sadirname(&dd->new, dd->lnkfn, len)) return 0 ;
+ if (!stralloc_catb(&dd->new, "/", 1)) goto fail ;
+ if (sareadlink(&dd->new, dd->lnkfn) < 0)
+ {
+ unsigned int lnkbnbase = dd->new.len ;
+ if (errno != EINVAL) goto fail ;
+ if (!stralloc_cats(&dd->new, lnkbn)) goto fail ;
+ if (random_sauniquename(&dd->new, 8) < 0) goto fail ;
+ if (!stralloc_0(&dd->new)) goto fail ;
+ if (rename(dd->lnkfn, dd->new.s + oldbase) < 0) goto fail ;
+ /* /!\ race condition right here: there's no lnkfn in the fs */
+ if (symlink(dd->new.s + lnkbnbase, dd->lnkfn) < 0) /* now that's VERY BAD if it fails */
+ {
+ register int e = errno ;
+ rename(dd->new.s + oldbase, dd->lnkfn) ; /* attempt to revert to initial situation */
+ errno = e ;
+ goto fail ; /* and hope for the best */
+ }
+ }
+ if (!stralloc_0(&dd->new)) goto fail ;
+ newlnkbase = dd->new.len ;
+ if (!stralloc_catb(&dd->new, dd->lnkfn, len)) goto fail ;
+ if (random_sauniquename(&dd->new, 8) < 0) goto fail ;
+ if (!stralloc_0(&dd->new)) goto fail ;
+ if (symlink(dd->new.s, dd->new.s + newlnkbase) < 0) goto fail ;
+ if (rename(dd->new.s + newlnkbase, dd->lnkfn) < 0)
+ {
+ register int e = errno ;
+ unlink(dd->new.s + newlnkbase) ;
+ errno = e ;
+ goto fail ;
+ }
+ fd_close(dd->fd) ;
+ dd->new.len = newlnkbase ;
+ rm_rf_in_tmp(&dd->new, oldbase) ;
+ stralloc_free(&dd->new) ;
+ *dd = zero ;
+ return 1 ;
+
+ fail:
+ dd->new.len = oldbase ;
+ return 0 ;
+}
diff --git a/src/libunixonacid/dd_commit_devino.c b/src/libunixonacid/dd_commit_devino.c
new file mode 100644
index 0000000..481db19
--- /dev/null
+++ b/src/libunixonacid/dd_commit_devino.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <skalibs/uint64.h>
+#include <skalibs/unix-transactional.h>
+
+int dd_commit_devino (dirdescriptor_t_ref dd, uint64 *dev, uint64 *ino)
+{
+ struct stat st ;
+ if (fstat(dd->fd, &st) < 0) return 0 ;
+ if (!dd_commit(dd)) return 0 ;
+ *dev = (uint64)st.st_dev ;
+ *ino = (uint64)st.st_ino ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/dd_open_read.c b/src/libunixonacid/dd_open_read.c
new file mode 100644
index 0000000..1996297
--- /dev/null
+++ b/src/libunixonacid/dd_open_read.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+int dd_open_read (dirdescriptor_t_ref dd, char const *path)
+{
+ dirdescriptor_t d = DIRDESCRIPTOR_ZERO ;
+ d.fd = open_read(path) ;
+ if (d.fd < 0) return 0 ;
+ d.lnkfn = path ;
+ *dd = d ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/dd_open_write.c b/src/libunixonacid/dd_open_write.c
new file mode 100644
index 0000000..efee60f
--- /dev/null
+++ b/src/libunixonacid/dd_open_write.c
@@ -0,0 +1,34 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/random.h>
+#include <skalibs/unix-transactional.h>
+
+int dd_open_write (dirdescriptor_t_ref dd, char const *lnkfn, unsigned int mode)
+{
+ dirdescriptor_t d = DIRDESCRIPTOR_ZERO ;
+ d.lnkfn = lnkfn ;
+ if (!stralloc_cats(&d.new, lnkfn)) return 0 ;
+ if (random_sauniquename(&d.new, 8) < 0) goto fail ;
+ if (!stralloc_0(&d.new)) goto fail ;
+ if (mkdir(d.new.s, mode) < 0) goto fail ;
+ d.fd = open_read(d.new.s) ;
+ if (d.fd < 0)
+ {
+ register int e = errno ;
+ rmdir(d.new.s) ;
+ errno = e ;
+ goto fail ;
+ }
+ *dd = d ;
+ return 1 ;
+
+ fail:
+ stralloc_free(&d.new) ;
+ return 0 ;
+}
diff --git a/src/libunixonacid/kolbak_call.c b/src/libunixonacid/kolbak_call.c
new file mode 100644
index 0000000..b7b97c0
--- /dev/null
+++ b/src/libunixonacid/kolbak_call.c
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/kolbak.h>
+
+int kolbak_call (unixmessage_t const *m, kolbak_queue_t *q)
+{
+ if (q->head == q->tail) return (errno = EILSEQ, 0) ;
+ if (!(*q->x[q->head].f)(m, q->x[q->head].data)) return 0 ;
+ q->head = (q->head + 1) % q->n ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/kolbak_enqueue.c b/src/libunixonacid/kolbak_enqueue.c
new file mode 100644
index 0000000..80fce01
--- /dev/null
+++ b/src/libunixonacid/kolbak_enqueue.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/kolbak.h>
+#include <skalibs/unixmessage.h>
+
+int kolbak_enqueue (kolbak_queue_t *q, unixmessage_handler_func_t *f, void *data)
+{
+ register unsigned int newtail = (q->tail + 1) % q->n ;
+ if (newtail == q->head) return (errno = ENOBUFS, 0) ;
+ q->x[q->tail].f = f ;
+ q->x[q->tail].data = data ;
+ q->tail = newtail ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/kolbak_queue_init.c b/src/libunixonacid/kolbak_queue_init.c
new file mode 100644
index 0000000..6d91232
--- /dev/null
+++ b/src/libunixonacid/kolbak_queue_init.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/kolbak.h>
+
+int kolbak_queue_init (kolbak_queue_t *q, kolbak_closure_t *s, unsigned int len)
+{
+ if (len < 2) return (errno = EINVAL, 0) ;
+ q->x = s ;
+ q->n = len ;
+ q->head = 0 ;
+ q->tail = 0 ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/kolbak_unenqueue.c b/src/libunixonacid/kolbak_unenqueue.c
new file mode 100644
index 0000000..894beea
--- /dev/null
+++ b/src/libunixonacid/kolbak_unenqueue.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/kolbak.h>
+
+int kolbak_unenqueue (kolbak_queue_t *q)
+{
+ if (q->head == q->tail) return (errno = EINVAL, 0) ;
+ q->tail = (q->tail + q->n - 1) % q->n ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/mkdir_unique.c b/src/libunixonacid/mkdir_unique.c
new file mode 100644
index 0000000..523ecef
--- /dev/null
+++ b/src/libunixonacid/mkdir_unique.c
@@ -0,0 +1,24 @@
+/* ISC license. */
+
+#include <sys/stat.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/random.h>
+#include <skalibs/unix-transactional.h>
+
+int mkdir_unique (stralloc *sa, char const *fn, unsigned int mode)
+{
+ unsigned int base = sa->len ;
+ int wasnull = !sa->s ;
+ if (!stralloc_cats(sa, fn)) return 0 ;
+ if (!stralloc_cats(sa, "/mkdir_unique")) goto fail ;
+ if (random_sauniquename(sa, 8) < 0) goto fail ;
+ if (!stralloc_0(sa)) goto fail ;
+ if (mkdir(sa->s + base, mode) < 0) goto fail ;
+ sa->len-- ;
+ return 1 ;
+
+ fail:
+ if (wasnull) stralloc_free(sa) ;
+ else sa->len = base ;
+ return 0 ;
+}
diff --git a/src/libunixonacid/netstring_timed_get.c b/src/libunixonacid/netstring_timed_get.c
new file mode 100644
index 0000000..04ace6c
--- /dev/null
+++ b/src/libunixonacid/netstring_timed_get.c
@@ -0,0 +1,24 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/buffer.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/netstring.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/unix-timed.h>
+
+int netstring_timed_get (buffer *b, stralloc *sa, tain_t const *deadline, tain_t *stamp)
+{
+ iopause_fd x = { .fd = buffer_fd(b), .events = IOPAUSE_READ } ;
+ unsigned int w = 0 ;
+ for (;;)
+ {
+ register int r = netstring_get(b, sa, &w) ;
+ if (r > 0) return r ;
+ if (r < 0) return 0 ;
+ r = iopause_stamp(&x, 1, deadline, stamp) ;
+ if (r < 0) return 0 ;
+ else if (!r) return (errno = ETIMEDOUT, 0) ;
+ }
+}
diff --git a/src/libunixonacid/open2_at.c b/src/libunixonacid/open2_at.c
new file mode 100644
index 0000000..4deb837
--- /dev/null
+++ b/src/libunixonacid/open2_at.c
@@ -0,0 +1,60 @@
+/* ISC license. */
+
+#include <skalibs/sysdeps.h>
+
+#ifdef SKALIBS_HASOPENAT
+
+#ifndef _ATFILE_SOURCE
+#define _ATFILE_SOURCE
+#endif
+
+#include <skalibs/nonposix.h>
+#include <fcntl.h>
+#include <skalibs/unix-transactional.h>
+
+int open2_at (int dirfd, char const *file, int flags)
+{
+ return openat(dirfd, file, flags) ;
+}
+
+#else
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+int open2_at (int dirfd, char const *file, int flags)
+{
+ int fd ;
+ int fdhere = open_read(".") ;
+ if (fdhere < 0) return -1 ;
+ if (fd_chdir(dirfd) < 0)
+ {
+ register int e = errno ;
+ fd_close(fdhere) ;
+ errno = e ;
+ return -1 ;
+ }
+ fd = open2(file, flags) ;
+ if (fd < 0)
+ {
+ register int e = errno ;
+ fd_chdir(fdhere) ;
+ fd_close(fdhere) ;
+ errno = e ;
+ return -1 ;
+ }
+ if (fd_chdir(fdhere) < 0)
+ {
+ register int e = errno ;
+ fd_close(fdhere) ;
+ errno = e ;
+ return -1 ;
+ }
+ return fd ;
+}
+
+#endif
diff --git a/src/libunixonacid/open3_at.c b/src/libunixonacid/open3_at.c
new file mode 100644
index 0000000..d9c7222
--- /dev/null
+++ b/src/libunixonacid/open3_at.c
@@ -0,0 +1,60 @@
+/* ISC license. */
+
+#include <skalibs/sysdeps.h>
+
+#ifdef SKALIBS_HASOPENAT
+
+#ifndef _ATFILE_SOURCE
+#define _ATFILE_SOURCE
+#endif
+
+#include <skalibs/nonposix.h>
+#include <fcntl.h>
+#include <skalibs/unix-transactional.h>
+
+int open3_at (int dirfd, char const *file, int flags, unsigned int mode)
+{
+ return openat(dirfd, file, flags, mode) ;
+}
+
+#else
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+int open3_at (int dirfd, char const *file, int flags, unsigned int mode)
+{
+ int fd ;
+ int fdhere = open_read(".") ;
+ if (fdhere < 0) return -1 ;
+ if (fd_chdir(dirfd) < 0)
+ {
+ register int e = errno ;
+ fd_close(fdhere) ;
+ errno = e ;
+ return -1 ;
+ }
+ fd = open3(file, flags, mode) ;
+ if (fd < 0)
+ {
+ register int e = errno ;
+ fd_chdir(fdhere) ;
+ fd_close(fdhere) ;
+ errno = e ;
+ return -1 ;
+ }
+ if (fd_chdir(fdhere) < 0)
+ {
+ register int e = errno ;
+ fd_close(fdhere) ;
+ errno = e ;
+ return -1 ;
+ }
+ return fd ;
+}
+
+#endif
diff --git a/src/libunixonacid/open_appendat.c b/src/libunixonacid/open_appendat.c
new file mode 100644
index 0000000..debfe8a
--- /dev/null
+++ b/src/libunixonacid/open_appendat.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <skalibs/unix-transactional.h>
+
+int open_appendat (int fd, char const *name)
+{
+ return open3_at(fd, name, O_WRONLY | O_NONBLOCK | O_APPEND | O_CREAT, 0666) ;
+}
diff --git a/src/libunixonacid/open_appendatb.c b/src/libunixonacid/open_appendatb.c
new file mode 100644
index 0000000..5a75120
--- /dev/null
+++ b/src/libunixonacid/open_appendatb.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+int open_appendatb (int dirfd, char const *name)
+{
+ int fd = open_appendat(dirfd, name) ;
+ if (fd < 0) return -1 ;
+ if (ndelay_off(fd) < 0) return -1 ;
+ return fd ;
+}
diff --git a/src/libunixonacid/open_readat.c b/src/libunixonacid/open_readat.c
new file mode 100644
index 0000000..7764ffc
--- /dev/null
+++ b/src/libunixonacid/open_readat.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <skalibs/unix-transactional.h>
+
+int open_readat (int fd, char const *name)
+{
+ return open2_at(fd, name, O_RDONLY | O_NONBLOCK) ;
+}
diff --git a/src/libunixonacid/open_readatb.c b/src/libunixonacid/open_readatb.c
new file mode 100644
index 0000000..6e4cd0f
--- /dev/null
+++ b/src/libunixonacid/open_readatb.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+int open_readatb (int dirfd, char const *name)
+{
+ int fd = open_readat(dirfd, name) ;
+ if (fd < 0) return -1 ;
+ if (ndelay_off(fd) < 0) return -1 ;
+ return fd ;
+}
diff --git a/src/libunixonacid/open_truncat.c b/src/libunixonacid/open_truncat.c
new file mode 100644
index 0000000..2a868d2
--- /dev/null
+++ b/src/libunixonacid/open_truncat.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <skalibs/unix-transactional.h>
+
+int open_truncat (int fd, char const *name)
+{
+ return open3_at(fd, name, O_WRONLY | O_NONBLOCK | O_TRUNC | O_CREAT, 0666) ;
+}
diff --git a/src/libunixonacid/open_truncatb.c b/src/libunixonacid/open_truncatb.c
new file mode 100644
index 0000000..fc4d685
--- /dev/null
+++ b/src/libunixonacid/open_truncatb.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+int open_truncatb (int dirfd, char const *name)
+{
+ int fd = open_truncat(dirfd, name) ;
+ if (fd < 0) return -1 ;
+ if (ndelay_off(fd) < 0) return -1 ;
+ return fd ;
+}
diff --git a/src/libunixonacid/open_writeat.c b/src/libunixonacid/open_writeat.c
new file mode 100644
index 0000000..6b1a173
--- /dev/null
+++ b/src/libunixonacid/open_writeat.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <skalibs/unix-transactional.h>
+
+int open_writeat (int fd, char const *name)
+{
+ return open2_at(fd, name, O_WRONLY | O_NONBLOCK) ;
+}
diff --git a/src/libunixonacid/open_writeatb.c b/src/libunixonacid/open_writeatb.c
new file mode 100644
index 0000000..fd75365
--- /dev/null
+++ b/src/libunixonacid/open_writeatb.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+int open_writeatb (int dirfd, char const *name)
+{
+ int fd = open_writeat(dirfd, name) ;
+ if (fd < 0) return -1 ;
+ if (ndelay_off(fd) < 0) return -1 ;
+ return fd ;
+}
diff --git a/src/libunixonacid/opengetlnclose.c b/src/libunixonacid/opengetlnclose.c
new file mode 100644
index 0000000..ca1959e
--- /dev/null
+++ b/src/libunixonacid/opengetlnclose.c
@@ -0,0 +1,24 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/buffer.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/unix-transactional.h>
+
+int opengetlnclose (char const *fn, stralloc *sa, int sep)
+{
+ char buf[BUFFER_INSIZE] ;
+ buffer b ;
+ register int r ;
+ register int e ;
+ int fd = open_readb(fn) ;
+ if (fd < 0) return -1 ;
+ buffer_init(&b, &buffer_read, fd, buf, BUFFER_INSIZE) ;
+ r = skagetln(&b, sa, sep) ;
+ e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ return r ;
+}
diff --git a/src/libunixonacid/opengetlnclose_at.c b/src/libunixonacid/opengetlnclose_at.c
new file mode 100644
index 0000000..be2ffaf
--- /dev/null
+++ b/src/libunixonacid/opengetlnclose_at.c
@@ -0,0 +1,24 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/buffer.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/unix-transactional.h>
+
+int opengetlnclose_at (int dirfd, char const *fn, stralloc *sa, int sep)
+{
+ char buf[BUFFER_INSIZE] ;
+ buffer b ;
+ register int r ;
+ register int e ;
+ int fd = open_readatb(dirfd, fn) ;
+ if (fd < 0) return -1 ;
+ buffer_init(&b, &buffer_read, fd, buf, BUFFER_INSIZE) ;
+ r = skagetln(&b, sa, sep) ;
+ e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ return r ;
+}
diff --git a/src/libunixonacid/openreadnclose_at.c b/src/libunixonacid/openreadnclose_at.c
new file mode 100644
index 0000000..4e8209d
--- /dev/null
+++ b/src/libunixonacid/openreadnclose_at.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+unsigned int openreadnclose_at (int dirfd, char const *file, char *s, unsigned int n)
+{
+ register unsigned int r ;
+ register int e ;
+ int fd = open_readatb(dirfd, file) ;
+ if (fd < 0) return 0 ;
+ r = allread(fd, s, n) ;
+ e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ return r ;
+}
diff --git a/src/libunixonacid/openslurpclose_at.c b/src/libunixonacid/openslurpclose_at.c
new file mode 100644
index 0000000..cf2f8ee
--- /dev/null
+++ b/src/libunixonacid/openslurpclose_at.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+int openslurpclose_at (int dirfd, char const *fn, stralloc *sa)
+{
+ int fd = open_readatb(dirfd, fn) ;
+ if (fd < 0) return 0 ;
+ if (!slurp(sa, fd))
+ {
+ register int e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ return 0 ;
+ }
+ fd_close(fd) ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/openwritenclose.c b/src/libunixonacid/openwritenclose.c
new file mode 100644
index 0000000..09149e2
--- /dev/null
+++ b/src/libunixonacid/openwritenclose.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+/* MT-unsafe */
+
+#include <skalibs/skamisc.h>
+#include <skalibs/unix-transactional.h>
+
+int openwritenclose (char const *fn, char const *s, unsigned int len)
+{
+ return openwritenclose_tmp(fn, s, len, &satmp) ;
+}
diff --git a/src/libunixonacid/openwritenclose_at.c b/src/libunixonacid/openwritenclose_at.c
new file mode 100644
index 0000000..fe92a5e
--- /dev/null
+++ b/src/libunixonacid/openwritenclose_at.c
@@ -0,0 +1,24 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+unsigned int openwritenclose_at (int dirfd, char const *file, char const *s, unsigned int n)
+{
+ register unsigned int r ;
+ int fd = open_truncatb(dirfd, file) ;
+ if (fd < 0) return 0 ;
+ r = allwrite(fd, s, n) ;
+ if ((r < n) || (fsync(fd) < 0))
+ {
+ register int e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ return r ;
+ }
+ fd_close(fd) ;
+ return r ;
+}
diff --git a/src/libunixonacid/openwritenclose_devino.c b/src/libunixonacid/openwritenclose_devino.c
new file mode 100644
index 0000000..5edc646
--- /dev/null
+++ b/src/libunixonacid/openwritenclose_devino.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+/* MT-unsafe */
+
+#include <skalibs/uint64.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/unix-transactional.h>
+
+int openwritenclose_devino (char const *fn, char const *s, unsigned int len, uint64 *dev, uint64 *ino)
+{
+ return openwritenclose_devino_tmp(fn, s, len, dev, ino, &satmp) ;
+}
diff --git a/src/libunixonacid/openwritenclose_devino_tmp.c b/src/libunixonacid/openwritenclose_devino_tmp.c
new file mode 100644
index 0000000..343da65
--- /dev/null
+++ b/src/libunixonacid/openwritenclose_devino_tmp.c
@@ -0,0 +1,35 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h> /* for rename() */
+#include <skalibs/uint64.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/random.h>
+#include <skalibs/unix-transactional.h>
+
+int openwritenclose_devino_tmp (char const *fn, char const *s, unsigned int len, uint64 *dev, uint64 *ino, stralloc *tmp)
+{
+ uint64 tmpdev, tmpino ;
+ unsigned int base = tmp->len ;
+ if (!stralloc_cats(tmp, fn)) return 0 ;
+ if (random_sauniquename(tmp, 8) < 0) goto fail ;
+ if (!stralloc_0(tmp)) goto fail ;
+ if (!openwritenclose_unsafe_devino_sync(tmp->s + base, s, len, &tmpdev, &tmpino)) goto fail ;
+ if (rename(tmp->s + base, fn) < 0)
+ {
+ register int e = errno ;
+ unlink(tmp->s + base) ;
+ errno = e ;
+ goto fail ;
+ }
+ tmp->len = base ;
+ *dev = tmpdev ;
+ *ino = tmpino ;
+ return 1 ;
+
+ fail:
+ tmp->len = base ;
+ return 0 ;
+}
diff --git a/src/libunixonacid/openwritenclose_tmp.c b/src/libunixonacid/openwritenclose_tmp.c
new file mode 100644
index 0000000..1ada80b
--- /dev/null
+++ b/src/libunixonacid/openwritenclose_tmp.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <skalibs/uint64.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/unix-transactional.h>
+
+int openwritenclose_tmp (char const *fn, char const *s, unsigned int len, stralloc *tmp)
+{
+ uint64 dev, ino ;
+ return openwritenclose_devino_tmp(fn, s, len, &dev, &ino, tmp) ;
+}
diff --git a/src/libunixonacid/skaclient-internal.h b/src/libunixonacid/skaclient-internal.h
new file mode 100644
index 0000000..f0a9bfe
--- /dev/null
+++ b/src/libunixonacid/skaclient-internal.h
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#ifndef SKACLIENT_INTERNAL_H
+#define SKACLIENT_INTERNAL_H
+
+#include <skalibs/kolbak.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+
+extern int skaclient_init (skaclient_t *, int, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, unsigned int) ;
+extern int skaclient_start_async_th (skaclient_t *, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, char const *, unsigned int) ;
+extern int skaclient_startf_async_th (skaclient_t *, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, char const *const *, char const *const *, uint32, char const *, unsigned int) ;
+extern int skaclient_start_cb (unixmessage_t const *, skaclient_cbdata_t *) ;
+
+#endif
diff --git a/src/libunixonacid/skaclient_default_cb.c b/src/libunixonacid/skaclient_default_cb.c
new file mode 100644
index 0000000..10934e5
--- /dev/null
+++ b/src/libunixonacid/skaclient_default_cb.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_default_cb (unixmessage_t const *m, void *p)
+{
+ unsigned char *err = p ;
+ if (m->len != 1 || m->nfds) return (errno = EPROTO, 0) ;
+ *err = m->s[0] ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/skaclient_end.c b/src/libunixonacid/skaclient_end.c
new file mode 100644
index 0000000..23dafab
--- /dev/null
+++ b/src/libunixonacid/skaclient_end.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+
+void skaclient_end (skaclient_t *a)
+{
+ fd_close(a->syncout.fd) ;
+ fd_close(a->asyncout.fd) ;
+ unixmessage_sender_free(&a->syncout) ;
+ unixmessage_sender_free(&a->asyncout) ;
+ unixmessage_receiver_free(&a->syncin) ;
+ unixmessage_receiver_free(&a->asyncin) ;
+ if (a->pid && a->options & SKACLIENT_OPTION_WAITPID)
+ {
+ int wstat ;
+ waitpid_nointr(a->pid, &wstat, 0) ;
+ }
+ *a = skaclient_zero ;
+}
diff --git a/src/libunixonacid/skaclient_init.c b/src/libunixonacid/skaclient_init.c
new file mode 100644
index 0000000..89ed59c
--- /dev/null
+++ b/src/libunixonacid/skaclient_init.c
@@ -0,0 +1,32 @@
+/* ISC license. */
+
+#include <skalibs/kolbak.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+#include "skaclient-internal.h"
+
+int skaclient_init (
+ skaclient_t *a,
+ int fd,
+ char *bufss,
+ unsigned int bufsn,
+ char *auxbufss,
+ unsigned int auxbufsn,
+ char *bufas,
+ unsigned int bufan,
+ char *auxbufas,
+ unsigned int auxbufan,
+ kolbak_closure_t *q,
+ unsigned int qlen,
+ char const *before,
+ unsigned int beforelen)
+{
+ unixmessage_t msg = { .s = (char *)before, .len = beforelen, .fds = 0, .nfds = 0 } ;
+ if (!unixmessage_receiver_init(&a->syncin, fd, bufss, bufsn, auxbufss, auxbufsn)
+ || !unixmessage_receiver_init(&a->asyncin, -1, bufas, bufan, auxbufas, auxbufan)
+ || !kolbak_queue_init(&a->kq, q, qlen)) return 0 ;
+ unixmessage_sender_init(&a->syncout, fd) ;
+ unixmessage_sender_init(&a->asyncout, -1) ;
+ if (!unixmessage_put(&a->syncout, &msg)) return 0 ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/skaclient_put.c b/src/libunixonacid/skaclient_put.c
new file mode 100644
index 0000000..5e3dc92
--- /dev/null
+++ b/src/libunixonacid/skaclient_put.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_put (skaclient_t *a, char const *s, unsigned int len, unixmessage_handler_func_t *cb, void *result)
+{
+ unixmessage_t m = { .s = (char *)s, .len = len, .fds = 0, .nfds = 0 } ;
+ return skaclient_putmsg(a, &m, cb, result) ;
+}
diff --git a/src/libunixonacid/skaclient_putmsg.c b/src/libunixonacid/skaclient_putmsg.c
new file mode 100644
index 0000000..9dcb37d
--- /dev/null
+++ b/src/libunixonacid/skaclient_putmsg.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <skalibs/kolbak.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_putmsg_and_close (skaclient_t *a, unixmessage_t const *m, unsigned char const *bits, unixmessage_handler_func_t *cb, void *result)
+{
+ if (!kolbak_enqueue(&a->kq, cb, result)) return 0 ;
+ if (!unixmessage_put_and_close(&a->syncout, m, bits))
+ {
+ kolbak_unenqueue(&a->kq) ;
+ return 0 ;
+ }
+ return 1 ;
+}
diff --git a/src/libunixonacid/skaclient_putmsgv.c b/src/libunixonacid/skaclient_putmsgv.c
new file mode 100644
index 0000000..e53935a
--- /dev/null
+++ b/src/libunixonacid/skaclient_putmsgv.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <skalibs/kolbak.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_putmsgv_and_close (skaclient_t *a, unixmessage_v_t const *m, unsigned char const *bits, unixmessage_handler_func_t *cb, void *result)
+{
+ if (!kolbak_enqueue(&a->kq, cb, result)) return 0 ;
+ if (!unixmessage_putv_and_close(&a->syncout, m, bits))
+ {
+ kolbak_unenqueue(&a->kq) ;
+ return 0 ;
+ }
+ return 1 ;
+}
diff --git a/src/libunixonacid/skaclient_putv.c b/src/libunixonacid/skaclient_putv.c
new file mode 100644
index 0000000..c41b092
--- /dev/null
+++ b/src/libunixonacid/skaclient_putv.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <skalibs/siovec.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_putv (skaclient_t *a, siovec_t const *v, unsigned int vlen, unixmessage_handler_func_t *cb, void *result)
+{
+ unixmessage_v_t m = { .v = (siovec_t *)v, .vlen = vlen, .fds = 0, .nfds = 0 } ;
+ return skaclient_putmsgv(a, &m, cb, result) ;
+}
diff --git a/src/libunixonacid/skaclient_send.c b/src/libunixonacid/skaclient_send.c
new file mode 100644
index 0000000..8bd31ab
--- /dev/null
+++ b/src/libunixonacid/skaclient_send.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <skalibs/skaclient.h>
+#include <skalibs/tai.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_send (skaclient_t *a, char const *s, unsigned int len, unixmessage_handler_func_t *cb, void *result, tain_t const *deadline, tain_t *stamp)
+{
+ unixmessage_t m = { .s = (char *)s, .len = len, .fds = 0, .nfds = 0 } ;
+ return skaclient_sendmsg(a, &m, cb, result, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/skaclient_sendmsg.c b/src/libunixonacid/skaclient_sendmsg.c
new file mode 100644
index 0000000..3f88ec7
--- /dev/null
+++ b/src/libunixonacid/skaclient_sendmsg.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/tai.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_sendmsg_and_close (skaclient_t *a, unixmessage_t const *m, unsigned char const *bits, unixmessage_handler_func_t *cb, void *result, tain_t const *deadline, tain_t *stamp)
+{
+ register int r ;
+ if (!skaclient_putmsg_and_close(a, m, bits, cb, result)) return 0 ;
+ if (!skaclient_timed_flush(a, deadline, stamp)) return 0 ;
+ r = skaclient_timed_supdate(a, deadline, stamp) ;
+ return r < 0 ? 0 : !r ? (errno = EPIPE, 0) : 1 ;
+}
diff --git a/src/libunixonacid/skaclient_sendmsgv.c b/src/libunixonacid/skaclient_sendmsgv.c
new file mode 100644
index 0000000..ac4bcdb
--- /dev/null
+++ b/src/libunixonacid/skaclient_sendmsgv.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/tai.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_sendmsgv_and_close (skaclient_t *a, unixmessage_v_t const *m, unsigned char const *bits, unixmessage_handler_func_t *cb, void *result, tain_t const *deadline, tain_t *stamp)
+{
+ register int r ;
+ if (!skaclient_putmsgv_and_close(a, m, bits, cb, result)) return 0 ;
+ if (!skaclient_timed_flush(a, deadline, stamp)) return 0 ;
+ r = skaclient_timed_supdate(a, deadline, stamp) ;
+ return r < 0 ? 0 : !r ? (errno = EPIPE, 0) : 1 ;
+}
diff --git a/src/libunixonacid/skaclient_sendv.c b/src/libunixonacid/skaclient_sendv.c
new file mode 100644
index 0000000..a140768
--- /dev/null
+++ b/src/libunixonacid/skaclient_sendv.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/siovec.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/tai.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_sendv (skaclient_t *a, siovec_t const *v, unsigned int vlen, unixmessage_handler_func_t *cb, void *result, tain_t const *deadline, tain_t *stamp)
+{
+ unixmessage_v_t m = { .v = (siovec_t *)v, .vlen = vlen, .fds = 0, .nfds = 0 } ;
+ return skaclient_sendmsgv(a, &m, cb, result, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/skaclient_server_ack.c b/src/libunixonacid/skaclient_server_ack.c
new file mode 100644
index 0000000..31ee9fe
--- /dev/null
+++ b/src/libunixonacid/skaclient_server_ack.c
@@ -0,0 +1,30 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/error.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/webipc.h>
+
+int skaclient_server_ack (unixmessage_t const *clientmsg, unixmessage_sender_t *out, unixmessage_sender_t *asyncout, char const *before, unsigned int beforelen, char const *after, unsigned int afterlen)
+{
+ int fd[2] ;
+ unixmessage_t m = { .s = (char *)after, .len = afterlen, .fds = fd, .nfds = 1 } ;
+ static unsigned char const bits = 0xff ;
+ if (clientmsg->nfds
+ || clientmsg->len != beforelen
+ || byte_diff(clientmsg->s, beforelen, before)) return (errno = EPROTO, 0) ;
+ if (ipc_pair_nbcoe(fd) < 0) return 0 ;
+ unixmessage_sender_init(asyncout, fd[1]) ;
+ if (!unixmessage_put_and_close(out, &m, &bits))
+ {
+ int e = errno ;
+ fd_close(fd[1]) ;
+ fd_close(fd[0]) ;
+ errno = e ;
+ return 0 ;
+ }
+ return 1 ;
+}
diff --git a/src/libunixonacid/skaclient_server_bidi_ack.c b/src/libunixonacid/skaclient_server_bidi_ack.c
new file mode 100644
index 0000000..eede82c
--- /dev/null
+++ b/src/libunixonacid/skaclient_server_bidi_ack.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_server_bidi_ack (unixmessage_t const *clientmsg, unixmessage_sender_t *out, unixmessage_sender_t *asyncout, unixmessage_receiver_t *asyncin, char *mainbuf, unsigned int mainlen, char *auxbuf, unsigned int auxlen, char const *before, unsigned int beforelen, char const *after, unsigned int afterlen)
+{
+ if (!unixmessage_receiver_init(asyncin, -1, mainbuf, mainlen, auxbuf, auxlen)) return 0 ;
+ if (!skaclient_server_ack(clientmsg, out, asyncout, before, beforelen, after, afterlen)) return 0 ;
+ asyncin->mainb.fd = unixmessage_sender_fd(asyncout) ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/skaclient_server_init.c b/src/libunixonacid/skaclient_server_init.c
new file mode 100644
index 0000000..bd7c143
--- /dev/null
+++ b/src/libunixonacid/skaclient_server_init.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <skalibs/skaclient.h>
+#include <skalibs/tai.h>
+#include <skalibs/unixmessage.h>
+
+int skaclient_server_init (unixmessage_receiver_t *in, char *mainbuf, unsigned int mainlen, char *auxbuf, unsigned int auxlen, unixmessage_sender_t *out, unixmessage_sender_t *asyncout, char const *before, unsigned int beforelen, char const *after, unsigned int afterlen, tain_t const *deadline, tain_t *stamp)
+{
+ unixmessage_t m ;
+ if (!unixmessage_receiver_init(in, 0, mainbuf, mainlen, auxbuf, auxlen)) return 0 ;
+ unixmessage_sender_init(out, 1) ;
+ if (unixmessage_timed_receive(in, &m, deadline, stamp) < 0) return 0 ;
+ if (!skaclient_server_ack(&m, out, asyncout, before, beforelen, after, afterlen)) return 0 ;
+ return unixmessage_sender_timed_flush(out, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/skaclient_start.c b/src/libunixonacid/skaclient_start.c
new file mode 100644
index 0000000..b17750b
--- /dev/null
+++ b/src/libunixonacid/skaclient_start.c
@@ -0,0 +1,43 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/kolbak.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/tai.h>
+#include "skaclient-internal.h"
+
+int skaclient_start (
+ skaclient_t *a,
+ char *bufss,
+ unsigned int bufsn,
+ char *auxbufss,
+ unsigned int auxbufsn,
+ char *bufas,
+ unsigned int bufan,
+ char *auxbufas,
+ unsigned int auxbufan,
+ kolbak_closure_t *q,
+ unsigned int qlen,
+ char const *path,
+ char const *before,
+ unsigned int beforelen,
+ char const *after,
+ unsigned int afterlen,
+ tain_t const *deadline,
+ tain_t *stamp)
+{
+ skaclient_cbdata_t blah ;
+ unixmessage_t m ;
+ register int r ;
+ if (!skaclient_start_async(a, bufss, bufsn, auxbufss, auxbufsn, bufas, bufan, auxbufas, auxbufan, q, qlen, path, before, beforelen, after, afterlen, &blah)) return 0 ;
+ r = unixmessage_timed_receive(&a->syncin, &m, deadline, stamp) ;
+ if (r < 1)
+ {
+ int e = errno ;
+ if (!r) e = EPIPE ;
+ skaclient_end(a) ;
+ errno = e ;
+ return 0 ;
+ }
+ return kolbak_call(&m, &a->kq) ;
+}
diff --git a/src/libunixonacid/skaclient_start_async.c b/src/libunixonacid/skaclient_start_async.c
new file mode 100644
index 0000000..4dbbf1c
--- /dev/null
+++ b/src/libunixonacid/skaclient_start_async.c
@@ -0,0 +1,52 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/kolbak.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/webipc.h>
+#include "skaclient-internal.h"
+
+int skaclient_start_async (
+ skaclient_t *a,
+ char *bufss,
+ unsigned int bufsn,
+ char *auxbufss,
+ unsigned int auxbufsn,
+ char *bufas,
+ unsigned int bufan,
+ char *auxbufas,
+ unsigned int auxbufan,
+ kolbak_closure_t *q,
+ unsigned int qlen,
+ char const *path,
+ char const *before,
+ unsigned int beforelen,
+ char const *after,
+ unsigned int afterlen,
+ skaclient_cbdata_t *blah)
+{
+ int fd = ipc_stream_nbcoe() ;
+ if (fd < 0) return 0 ;
+ if ((!ipc_connect(fd, path) && !error_isalready(errno))
+ || !skaclient_init(a, fd, bufss, bufsn, auxbufss, auxbufsn, bufas, bufan, auxbufas, auxbufan, q, qlen, before, beforelen))
+ {
+ register int e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ return 0 ;
+ }
+ a->pid = 0 ;
+ a->options = 0 ;
+ if (!kolbak_enqueue(&a->kq, (unixmessage_handler_func_t_ref)&skaclient_start_cb, blah))
+ {
+ skaclient_end(a) ;
+ return 0 ;
+ }
+ blah->asyncin = &a->asyncin ;
+ blah->asyncout = &a->asyncout ;
+ blah->after = after ;
+ blah->afterlen = afterlen ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/skaclient_start_cb.c b/src/libunixonacid/skaclient_start_cb.c
new file mode 100644
index 0000000..6e641e2
--- /dev/null
+++ b/src/libunixonacid/skaclient_start_cb.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/error.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/unixmessage.h>
+#include "skaclient-internal.h"
+
+int skaclient_start_cb (unixmessage_t const *m, skaclient_cbdata_t *blah)
+{
+ if (m->len != blah->afterlen
+ || byte_diff(m->s, m->len, blah->after)
+ || m->nfds != 1) return (errno = EPROTO, 0) ;
+ blah->asyncin->mainb.fd = m->fds[0] ;
+ blah->asyncout->fd = m->fds[0] ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/skaclient_startf.c b/src/libunixonacid/skaclient_startf.c
new file mode 100644
index 0000000..aa36323
--- /dev/null
+++ b/src/libunixonacid/skaclient_startf.c
@@ -0,0 +1,46 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/kolbak.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/tai.h>
+#include "skaclient-internal.h"
+
+int skaclient_startf (
+ skaclient_t *a,
+ char *bufss,
+ unsigned int bufsn,
+ char *auxbufss,
+ unsigned int auxbufsn,
+ char *bufas,
+ unsigned int bufan,
+ char *auxbufas,
+ unsigned int auxbufan,
+ kolbak_closure_t *q,
+ unsigned int qlen,
+ char const *prog,
+ char const *const *argv,
+ char const *const *envp,
+ uint32 options,
+ char const *before,
+ unsigned int beforelen,
+ char const *after,
+ unsigned int afterlen,
+ tain_t const *deadline,
+ tain_t *stamp)
+{
+ skaclient_cbdata_t blah ;
+ unixmessage_t m ;
+ register int r ;
+ if (!skaclient_startf_async(a, bufss, bufsn, auxbufss, auxbufsn, bufas, bufan, auxbufas, auxbufan, q, qlen, prog, argv, envp, options, before, beforelen, after, afterlen, &blah)) return 0 ;
+ r = unixmessage_timed_receive(&a->syncin, &m, deadline, stamp) ;
+ if (r < 1)
+ {
+ int e = errno ;
+ if (!r) e = EPIPE ;
+ skaclient_end(a) ;
+ errno = e ;
+ return 0 ;
+ }
+ return kolbak_call(&m, &a->kq) ;
+}
diff --git a/src/libunixonacid/skaclient_startf_async.c b/src/libunixonacid/skaclient_startf_async.c
new file mode 100644
index 0000000..4077c1e
--- /dev/null
+++ b/src/libunixonacid/skaclient_startf_async.c
@@ -0,0 +1,61 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <signal.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/kolbak.h>
+#include <skalibs/skaclient.h>
+#include <skalibs/uint32.h>
+#include "skaclient-internal.h"
+
+int skaclient_startf_async (
+ skaclient_t *a,
+ char *bufss,
+ unsigned int bufsn,
+ char *auxbufss,
+ unsigned int auxbufsn,
+ char *bufas,
+ unsigned int bufan,
+ char *auxbufas,
+ unsigned int auxbufan,
+ kolbak_closure_t *q,
+ unsigned int qlen,
+ char const *prog,
+ char const *const *argv,
+ char const *const *envp,
+ uint32 options,
+ char const *before,
+ unsigned int beforelen,
+ char const *after,
+ unsigned int afterlen,
+ skaclient_cbdata_t *blah)
+{
+ int fd ;
+ pid_t pid = child_spawn(prog, argv, envp, &fd, 1) ;
+ if (!pid) return 0 ;
+ if (!skaclient_init(a, fd, bufss, bufsn, auxbufss, auxbufsn, bufas, bufan, auxbufas, auxbufan, q, qlen, before, beforelen))
+ {
+ register int e = errno ;
+ fd_close(fd) ;
+ if (options & SKACLIENT_OPTION_WAITPID)
+ {
+ int wstat ;
+ waitpid_nointr(a->pid, &wstat, 0) ;
+ }
+ errno = e ;
+ return 0 ;
+ }
+ a->pid = pid ;
+ a->options = options ;
+ if (!kolbak_enqueue(&a->kq, (unixmessage_handler_func_t_ref)&skaclient_start_cb, blah))
+ {
+ skaclient_end(a) ;
+ return 0 ;
+ }
+ blah->asyncin = &a->asyncin ;
+ blah->asyncout = &a->asyncout ;
+ blah->after = after ;
+ blah->afterlen = afterlen ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/skaclient_zero.c b/src/libunixonacid/skaclient_zero.c
new file mode 100644
index 0000000..4bb85b5
--- /dev/null
+++ b/src/libunixonacid/skaclient_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <skalibs/skaclient.h>
+
+skaclient_t const skaclient_zero = SKACLIENT_ZERO ;
diff --git a/src/libunixonacid/timed_flush.c b/src/libunixonacid/timed_flush.c
new file mode 100644
index 0000000..2d60256
--- /dev/null
+++ b/src/libunixonacid/timed_flush.c
@@ -0,0 +1,26 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/bufalloc.h>
+#include <skalibs/functypes.h>
+#include <skalibs/iopause.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+int timed_flush (void *b, initfunc_t_ref getfd, initfunc_t_ref isnonempty, initfunc_t_ref flush, tain_t const *deadline, tain_t *stamp)
+{
+ iopause_fd x = { .fd = (*getfd)(b), .events = IOPAUSE_WRITE, .revents = 0 } ;
+ while ((*isnonempty)(b))
+ {
+ register int r = iopause_stamp(&x, 1, deadline, stamp) ;
+ if (r < 0) return 0 ;
+ else if (!r) return (errno = ETIMEDOUT, 0) ;
+ else if (x.revents & IOPAUSE_WRITE)
+ {
+ if (!((*flush)(b)) && !error_isagain(errno)) return 0 ;
+ }
+ else if (x.revents & IOPAUSE_EXCEPT) return (*flush)(b) ;
+ }
+ return 1 ;
+}
diff --git a/src/libunixonacid/timed_get.c b/src/libunixonacid/timed_get.c
new file mode 100644
index 0000000..0a02382
--- /dev/null
+++ b/src/libunixonacid/timed_get.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/functypes.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/unix-timed.h>
+
+int timed_get (void *b, initfunc_t *getfd, initfunc_t *get, tain_t const *deadline, tain_t *stamp)
+{
+ iopause_fd x = { .fd = (*getfd)(b), .events = IOPAUSE_READ, .revents = 0 } ;
+ register int r = (*get)(b) ;
+ while (!r)
+ {
+ r = iopause_stamp(&x, 1, deadline, stamp) ;
+ if (!r) return (errno = ETIMEDOUT, -1) ;
+ else if (r > 0 && x.revents & (IOPAUSE_READ | IOPAUSE_EXCEPT))
+ r = (*get)(b) ;
+ }
+ return unsanitize_read(r) ;
+}
diff --git a/src/libunixonacid/timed_getln.c b/src/libunixonacid/timed_getln.c
new file mode 100644
index 0000000..0c442de
--- /dev/null
+++ b/src/libunixonacid/timed_getln.c
@@ -0,0 +1,32 @@
+/* ISC license. */
+
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/functypes.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+struct blah_s
+{
+ buffer *b ;
+ stralloc *sa ;
+ char sep ;
+} ;
+
+static int getfd (struct blah_s *blah)
+{
+ return buffer_fd(blah->b) ;
+}
+
+static int get (struct blah_s *blah)
+{
+ return sanitize_read(skagetln(blah->b, blah->sa, blah->sep)) ;
+}
+
+int timed_getln (buffer *b, stralloc *sa, char sep, tain_t const *deadline, tain_t *stamp)
+{
+ struct blah_s blah = { .b = b, .sa = sa, .sep = sep } ;
+ return timed_get(&blah, (initfunc_t_ref)&getfd, (initfunc_t_ref)&get, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/timed_getlnmax.c b/src/libunixonacid/timed_getlnmax.c
new file mode 100644
index 0000000..8953606
--- /dev/null
+++ b/src/libunixonacid/timed_getlnmax.c
@@ -0,0 +1,33 @@
+/* ISC license. */
+
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/functypes.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+struct blah_s
+{
+ buffer *b ;
+ char *d ;
+ unsigned int max ;
+ unsigned int w ;
+ char sep ;
+} ;
+
+static int getfd (struct blah_s *blah)
+{
+ return buffer_fd(blah->b) ;
+}
+
+static int get (struct blah_s *blah)
+{
+ return sanitize_read(getlnmax(blah->b, blah->d, blah->max, &blah->w, blah->sep)) ;
+}
+
+int timed_getlnmax (buffer *b, char *d, unsigned int max, unsigned int *w, char sep, tain_t const *deadline, tain_t *stamp)
+{
+ struct blah_s blah = { .b = b, .d = d, .max = max, .w = 0, .sep = sep } ;
+ return timed_get(&blah, (initfunc_t_ref)&getfd, (initfunc_t_ref)&get, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/unixmessage_bits_closeall.c b/src/libunixonacid/unixmessage_bits_closeall.c
new file mode 100644
index 0000000..a963809
--- /dev/null
+++ b/src/libunixonacid/unixmessage_bits_closeall.c
@@ -0,0 +1,7 @@
+/* ISC license. */
+
+#include <skalibs/bitarray.h>
+#include <skalibs/unixmessage.h>
+
+static unsigned char _unixmessage_bits_closeall[bitarray_div8(UNIXMESSAGE_MAXFDS)] = "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" ;
+unsigned char const *const unixmessage_bits_closeall = _unixmessage_bits_closeall ;
diff --git a/src/libunixonacid/unixmessage_bits_closenone.c b/src/libunixonacid/unixmessage_bits_closenone.c
new file mode 100644
index 0000000..79412a5
--- /dev/null
+++ b/src/libunixonacid/unixmessage_bits_closenone.c
@@ -0,0 +1,7 @@
+/* ISC license. */
+
+#include <skalibs/bitarray.h>
+#include <skalibs/unixmessage.h>
+
+static unsigned char _unixmessage_bits_closenone[bitarray_div8(UNIXMESSAGE_MAXFDS)] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" ;
+unsigned char const *const unixmessage_bits_closenone = _unixmessage_bits_closenone ;
diff --git a/src/libunixonacid/unixmessage_handle.c b/src/libunixonacid/unixmessage_handle.c
new file mode 100644
index 0000000..ad5789c
--- /dev/null
+++ b/src/libunixonacid/unixmessage_handle.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <skalibs/unixmessage.h>
+
+int unixmessage_handle (unixmessage_receiver_t *b, unixmessage_handler_func_t *f, void *p)
+{
+ unsigned int n = UNIXMESSAGE_MAXREADS ;
+ int count = 0 ;
+ while (n--)
+ {
+ unixmessage_t m ;
+ register int r = unixmessage_receive(b, &m) ;
+ if (r < 0) return -1 ;
+ if (!r) break ;
+ if (!(*f)(&m, p)) return -2 ;
+ count++ ;
+ }
+ return count ;
+}
diff --git a/src/libunixonacid/unixmessage_put.c b/src/libunixonacid/unixmessage_put.c
new file mode 100644
index 0000000..f6db23b
--- /dev/null
+++ b/src/libunixonacid/unixmessage_put.c
@@ -0,0 +1,83 @@
+/* ISC license. */
+
+#include <skalibs/sysdeps.h>
+#ifdef SKALIBS_HASANCILAUTOCLOSE
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <skalibs/bitarray.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/diuint.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/siovec.h>
+#include <skalibs/unixmessage.h>
+
+static inline int copyfds (char *s, int const *fds, unsigned int n, unsigned char const *bits)
+{
+ register unsigned int i = 0 ;
+ for (; i < n ; i++)
+ {
+ int fd = fds[i] ;
+ if (fd < 0) return (errno = EINVAL, -1) ;
+ if (bitarray_peek(bits, i)) fd = - fd - 1 ;
+#ifdef SKALIBS_HASANCILAUTOCLOSE
+ else
+ {
+ fd = dup(fd) ;
+ if (fd < 0)
+ {
+ int e = errno ;
+ while (i--)
+ {
+ s -= sizeof(int) ;
+ byte_copy((char *)fd, sizeof(int), s) ;
+ if (fd >= 0) fd_close(fd) ;
+ }
+ errno = e ;
+ return 0 ;
+ }
+ }
+#else
+#endif
+ byte_copy(s, sizeof(int), (char const *)&fd) ;
+ s += sizeof(int) ;
+ }
+ return 1 ;
+}
+
+static int reserve_and_copy (unixmessage_sender_t *b, unsigned int len, int const *fds, unsigned int nfds, unsigned char const *bits)
+{
+ diuint cur = { .left = b->data.len, .right = b->fds.len } ;
+ if (!genalloc_readyplus(diuint, &b->offsets, 1)
+ || !genalloc_readyplus(int, &b->fds, nfds)
+ || !stralloc_readyplus(&b->data, len))
+ return 0 ;
+ if (!copyfds(b->fds.s + b->fds.len, fds, nfds, bits)) return 0 ;
+ b->fds.len += nfds * sizeof(int) ;
+ byte_copy(b->offsets.s + b->offsets.len, sizeof(diuint), (char const *)&cur) ;
+ b->offsets.len += sizeof(diuint) ;
+ return 1 ;
+}
+
+int unixmessage_put_and_close (unixmessage_sender_t *b, unixmessage_t const *m, unsigned char const *bits)
+{
+ if (!reserve_and_copy(b, m->len, m->fds, m->nfds, bits)) return 0 ;
+ byte_copy(b->data.s + b->data.len, m->len, m->s) ;
+ b->data.len += m->len ;
+ return 1 ;
+}
+
+int unixmessage_putv_and_close (unixmessage_sender_t *b, unixmessage_v_t const *m, unsigned char const *bits)
+{
+ unsigned int len = 0 ;
+ register unsigned int i = 0 ;
+ for (; i < m->vlen ; i++) len += m->v[i].len ;
+ if (!reserve_and_copy(b, len, m->fds, m->nfds, bits)) return 0 ;
+ for (i = 0 ; i < m->vlen ; i++)
+ {
+ byte_copy(b->data.s + b->data.len, m->v[i].len, m->v[i].s) ;
+ b->data.len += m->v[i].len ;
+ }
+ return 1 ;
+}
diff --git a/src/libunixonacid/unixmessage_read.c b/src/libunixonacid/unixmessage_read.c
new file mode 100644
index 0000000..5a23b84
--- /dev/null
+++ b/src/libunixonacid/unixmessage_read.c
@@ -0,0 +1,60 @@
+/* ISC license. */
+
+#define _XPG4_2
+#include <skalibs/sysdeps.h>
+#include <skalibs/nonposix.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <skalibs/buffer.h>
+#include <skalibs/cbuffer.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/error.h>
+#include <skalibs/siovec.h>
+#include <skalibs/unixmessage.h>
+
+int unixmessage_read (int fd, siovec_t const *v, unsigned int n, void *aux)
+{
+ int r ;
+ char ancilbuf[CMSG_SPACE(UNIXMESSAGE_MAXFDS * sizeof(int))] ;
+ struct iovec iov[n] ;
+ struct msghdr msghdr =
+ {
+ .msg_name = 0,
+ .msg_namelen = 0,
+ .msg_iov = iov,
+ .msg_iovlen = n,
+ .msg_flags = 0,
+ .msg_control = ancilbuf,
+ .msg_controllen = sizeof(ancilbuf)
+ } ;
+ iovec_from_siovec(iov, v, n) ;
+#ifdef SKALIBS_HASCMSGCLOEXEC
+ r = recvmsg(fd, &msghdr, MSG_WAITALL | MSG_CMSG_CLOEXEC) ;
+#else
+ r = recvmsg(fd, &msghdr, MSG_WAITALL) ;
+#endif
+ if (r > 0)
+ {
+ struct cmsghdr *c = CMSG_FIRSTHDR(&msghdr) ;
+ if (c)
+ {
+ cbuffer_t *auxb = aux ;
+ unsigned int len ;
+ if (c->cmsg_level != SOL_SOCKET
+ || c->cmsg_type != SCM_RIGHTS) return (errno = EPROTO, -1-r) ;
+ len = (unsigned int)(c->cmsg_len - (CMSG_DATA(c) - (unsigned char *)c)) ;
+#ifndef SKALIBS_HASCMSGCLOEXEC
+ {
+ register unsigned int i = 0 ;
+ for (; i < len/sizeof(int) ; i++)
+ if (coe(((int *)CMSG_DATA(c))[i]) < 0) return -1-r ;
+ }
+#endif
+ if (msghdr.msg_flags | MSG_CTRUNC) return (errno = EPROTO, -1-r) ;
+ if (cbuffer_put(auxb, (char *)CMSG_DATA(c), len) < len)
+ return (errno = ENOBUFS, -1-r) ;
+ }
+ }
+ return r ;
+}
diff --git a/src/libunixonacid/unixmessage_receive.c b/src/libunixonacid/unixmessage_receive.c
new file mode 100644
index 0000000..dc75263
--- /dev/null
+++ b/src/libunixonacid/unixmessage_receive.c
@@ -0,0 +1,44 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/cbuffer.h>
+#include <skalibs/error.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/uint.h>
+#include <skalibs/unixmessage.h>
+
+int unixmessage_receive (unixmessage_receiver_t *b, unixmessage_t *m)
+{
+ if (b->data.len == b->mainlen)
+ {
+ char pack[sizeof(unsigned int) << 1] ;
+ if (buffer_len(&b->mainb) < sizeof(unsigned int) << 1)
+ {
+ register int r = sanitize_read(buffer_fill(&b->mainb)) ;
+ if (r <= 0) return r ;
+ if (r < sizeof(unsigned int) << 1) return (errno = EWOULDBLOCK, 0) ;
+ }
+ buffer_getnofill(&b->mainb, pack, sizeof(unsigned int) << 1) ;
+ uint_unpack_big(pack, &b->mainlen) ;
+ uint_unpack_big(pack + sizeof(unsigned int), &b->auxlen) ;
+ if (b->auxlen > UNIXMESSAGE_MAXFDS) return (errno = EPROTO, -1) ;
+ b->auxlen *= sizeof(int) ;
+ if (!stralloc_ready(&b->data, b->mainlen)) return -1 ;
+ b->data.len = 0 ;
+ b->auxw = cbuffer_get(&b->auxb, (char *)b->fds, b->auxlen) ;
+ }
+ {
+ register int r = buffer_getall(&b->mainb, b->data.s, b->mainlen, &b->data.len) ;
+ if (r <= 0) return r ;
+ }
+ if (b->auxw < b->auxlen)
+ b->auxw += cbuffer_get(&b->auxb, (char *)b->fds, b->auxlen - b->auxw) ;
+ if (b->auxw < b->auxlen) return (errno = EPROTO, -1) ;
+ m->s = b->data.s ;
+ m->len = b->data.len ;
+ m->fds = b->fds ;
+ m->nfds = b->auxlen / sizeof(int) ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/unixmessage_receiver_free.c b/src/libunixonacid/unixmessage_receiver_free.c
new file mode 100644
index 0000000..c23cc77
--- /dev/null
+++ b/src/libunixonacid/unixmessage_receiver_free.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include <skalibs/unixmessage.h>
+
+void unixmessage_receiver_free (unixmessage_receiver_t *b)
+{
+ stralloc_free(&b->data) ;
+ b->mainb.fd = -1 ;
+ b->mainlen = b->auxlen = b->auxw = 0 ;
+}
diff --git a/src/libunixonacid/unixmessage_receiver_init.c b/src/libunixonacid/unixmessage_receiver_init.c
new file mode 100644
index 0000000..982547c
--- /dev/null
+++ b/src/libunixonacid/unixmessage_receiver_init.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/buffer.h>
+#include <skalibs/cbuffer.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/unixmessage.h>
+
+int unixmessage_receiver_init (unixmessage_receiver_t *b, int fd, char *mainbuf, unsigned int mainlen, char *auxbuf, unsigned int auxlen)
+{
+ if (mainlen < 9 || auxlen < UNIXMESSAGE_MAXFDS * sizeof(int) + 1) return (errno = EINVAL, 0) ;
+ if (!cbuffer_init(&b->auxb, auxbuf, auxlen)) return 0 ;
+ if (!buffer_init_aux(&b->mainb, &unixmessage_read, fd, mainbuf, mainlen, &b->auxb)) return 0 ;
+ b->mainlen = b->auxlen = b->auxw = 0 ;
+ b->data = stralloc_zero ;
+ return 1 ;
+}
diff --git a/src/libunixonacid/unixmessage_sender_flush.c b/src/libunixonacid/unixmessage_sender_flush.c
new file mode 100644
index 0000000..ab8d460
--- /dev/null
+++ b/src/libunixonacid/unixmessage_sender_flush.c
@@ -0,0 +1,82 @@
+/* ISC license. */
+
+#define _XPG4_2
+#include <skalibs/sysdeps.h>
+#include <skalibs/nonposix.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <skalibs/uint.h>
+#include <skalibs/diuint.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unixmessage.h>
+
+ /* MacOS X tries hard to be POSIX-compliant... and fails. */
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+int unixmessage_sender_flush (unixmessage_sender_t *b)
+{
+ diuint last = { .left = b->data.len, .right = genalloc_len(int, &b->fds) } ;
+ diuint *offsets = genalloc_s(diuint, &b->offsets) ;
+ unsigned int n = genalloc_len(diuint, &b->offsets) ;
+ unsigned int oldhead = b->head ;
+ for (; b->head < n ; b->head++)
+ {
+ diuint *next = b->head+1 < n ? offsets + b->head+1 : &last ;
+ unsigned int len = next->left - offsets[b->head].left ;
+ unsigned int nfds = next->right - offsets[b->head].right ;
+ char pack[sizeof(unsigned int) << 1] ;
+ struct iovec v[2] =
+ {
+ { .iov_base = pack, .iov_len = sizeof(unsigned int) << 1 },
+ { .iov_base = b->data.s + offsets[b->head].left, .iov_len = len }
+ } ;
+ char ancilbuf[CMSG_SPACE(nfds * sizeof(int))] ;
+ struct msghdr hdr =
+ {
+ .msg_name = 0,
+ .msg_namelen = 0,
+ .msg_iov = v,
+ .msg_iovlen = 2,
+ .msg_control = nfds ? ancilbuf : 0,
+ .msg_controllen = nfds ? sizeof(ancilbuf) : 0
+ } ;
+ uint_pack_big(pack, len) ;
+ uint_pack_big(pack + sizeof(unsigned int), nfds) ;
+ if (nfds)
+ {
+ struct cmsghdr *cp = CMSG_FIRSTHDR(&hdr) ;
+ register unsigned int i = 0 ;
+ cp->cmsg_level = SOL_SOCKET ;
+ cp->cmsg_type = SCM_RIGHTS ;
+ cp->cmsg_len = CMSG_LEN(nfds * sizeof(int)) ;
+ for (; i < nfds ; i++)
+ {
+ register int fd = genalloc_s(int, &b->fds)[offsets[b->head].right + i] ;
+ ((int *)CMSG_DATA(cp))[i] = fd < 0 ? -(fd+1) : fd ;
+ }
+ }
+ if (sendmsg(b->fd, &hdr, MSG_NOSIGNAL) < len + (sizeof(unsigned int) << 1))
+ return -(int)(b->head-oldhead)-1 ;
+#ifndef SKALIBS_HASANCILAUTOCLOSE
+ if (nfds)
+ {
+ register unsigned int i = 0 ;
+ for (; i < nfds ; i++)
+ {
+ register int fd = genalloc_s(int, &b->fds)[offsets[b->head].right + i] ;
+ if (fd < 0) fd_close(-(fd+1)) ;
+ }
+ }
+#endif
+ }
+ b->data.len = 0 ;
+ genalloc_setlen(int, &b->fds, 0) ;
+ genalloc_setlen(diuint, &b->offsets, 0) ;
+ b->head = 0 ;
+ return (int)(n - oldhead) ;
+}
diff --git a/src/libunixonacid/unixmessage_sender_free.c b/src/libunixonacid/unixmessage_sender_free.c
new file mode 100644
index 0000000..797220c
--- /dev/null
+++ b/src/libunixonacid/unixmessage_sender_free.c
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#include <skalibs/sysdeps.h>
+#include <skalibs/diuint.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/unixmessage.h>
+
+void unixmessage_sender_free (unixmessage_sender_t *b)
+{
+#ifdef SKALIBS_HASANCILAUTOCLOSE
+ {
+ diuint *offsets = genalloc_s(unsigned int, &b->offsets) ;
+ unsigned int n = genalloc_len(unsigned int, &b->offsets) ;
+ int *fds = genalloc_s(int, &b->fds) ;
+ unsigned int nfds = genalloc_len(int, &b->fds) ;
+ for (; b->head < n ; b->head++)
+ {
+ register unsigned int last = b->head+1 < n ? offsets[b->head+1].right : nfds ;
+ register unsigned int i = offsets[b->head].right ;
+ for (; i < last ; i++) if (fds[i] >= 0) fd_close(fds[i]) ;
+ }
+ }
+#endif
+ genalloc_free(diuint, &b->offsets) ;
+ genalloc_free(int, &b->fds) ;
+ stralloc_free(&b->data) ;
+ *b = unixmessage_sender_zero ;
+}
diff --git a/src/libunixonacid/unixmessage_sender_getfd.c b/src/libunixonacid/unixmessage_sender_getfd.c
new file mode 100644
index 0000000..1f130eb
--- /dev/null
+++ b/src/libunixonacid/unixmessage_sender_getfd.c
@@ -0,0 +1,8 @@
+/* ISC license. */
+
+#include <skalibs/unixmessage.h>
+
+int unixmessage_sender_getfd (unixmessage_sender_t const *b)
+{
+ return b->fd ;
+}
diff --git a/src/libunixonacid/unixmessage_sender_init.c b/src/libunixonacid/unixmessage_sender_init.c
new file mode 100644
index 0000000..07c0bc2
--- /dev/null
+++ b/src/libunixonacid/unixmessage_sender_init.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/unixmessage.h>
+
+void unixmessage_sender_init (unixmessage_sender_t *b, int fd)
+{
+ b->fd = fd ;
+ b->data = stralloc_zero ;
+ b->fds = genalloc_zero ;
+ b->offsets = genalloc_zero ;
+ b->head = 0 ;
+}
diff --git a/src/libunixonacid/unixmessage_sender_timed_flush.c b/src/libunixonacid/unixmessage_sender_timed_flush.c
new file mode 100644
index 0000000..74e4937
--- /dev/null
+++ b/src/libunixonacid/unixmessage_sender_timed_flush.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <skalibs/functypes.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+#include <skalibs/unixmessage.h>
+
+static int unixmessage_sender_isnonempty (unixmessage_sender_t *b)
+{
+ return !!genalloc_len(unsigned int, &b->offsets) ;
+}
+
+int unixmessage_sender_timed_flush (unixmessage_sender_t *b, tain_t const *deadline, tain_t *stamp)
+{
+ return timed_flush(b, (initfunc_t_ref)&unixmessage_sender_getfd, (initfunc_t_ref)&unixmessage_sender_isnonempty, (initfunc_t_ref)&unixmessage_sender_flush, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/unixmessage_sender_zero.c b/src/libunixonacid/unixmessage_sender_zero.c
new file mode 100644
index 0000000..bda0a9b
--- /dev/null
+++ b/src/libunixonacid/unixmessage_sender_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <skalibs/unixmessage.h>
+
+unixmessage_sender_t const unixmessage_sender_zero = UNIXMESSAGE_SENDER_ZERO ;
diff --git a/src/libunixonacid/unixmessage_timed_handle.c b/src/libunixonacid/unixmessage_timed_handle.c
new file mode 100644
index 0000000..5ac7ccd
--- /dev/null
+++ b/src/libunixonacid/unixmessage_timed_handle.c
@@ -0,0 +1,30 @@
+/* ISC license. */
+
+#include <skalibs/functypes.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+#include <skalibs/unixmessage.h>
+
+typedef struct unixmessage_handler_blah_s unixmessage_handler_blah_t, *unixmessage_handler_blah_t_ref ;
+struct unixmessage_handler_blah_s
+{
+ unixmessage_receiver_t *b ;
+ unixmessage_handler_func_t *f ;
+ void *p ;
+} ;
+
+static int getfd (unixmessage_handler_blah_t *blah)
+{
+ return unixmessage_receiver_fd(blah->b) ;
+}
+
+static int get (unixmessage_handler_blah_t *blah)
+{
+ return unixmessage_handle(blah->b, blah->f, blah->p) ;
+}
+
+int unixmessage_timed_handle (unixmessage_receiver_t *b, unixmessage_handler_func_t *f, void *p, tain_t const *deadline, tain_t *stamp)
+{
+ unixmessage_handler_blah_t blah = { .b = b, .f = f, .p = p } ;
+ return timed_get(&blah, (initfunc_t_ref)&getfd, (initfunc_t_ref)&get, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/unixmessage_timed_receive.c b/src/libunixonacid/unixmessage_timed_receive.c
new file mode 100644
index 0000000..3761d26
--- /dev/null
+++ b/src/libunixonacid/unixmessage_timed_receive.c
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#include <skalibs/functypes.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+#include <skalibs/unixmessage.h>
+
+typedef struct unixmessage_get_s unixmessage_get_t, *unixmessage_get_t_ref ;
+struct unixmessage_get_s
+{
+ unixmessage_receiver_t *b ;
+ unixmessage_t *m ;
+} ;
+
+static int getfd (unixmessage_get_t *g)
+{
+ return unixmessage_receiver_fd(g->b) ;
+}
+
+static int get (unixmessage_get_t *g)
+{
+ return unixmessage_receive(g->b, g->m) ;
+}
+
+int unixmessage_timed_receive (unixmessage_receiver_t *b, unixmessage_t *m, tain_t const *deadline, tain_t *stamp)
+{
+ unixmessage_get_t g = { .b = b, .m = m } ;
+ return timed_get(&g, (initfunc_t *)&getfd, (initfunc_t *)&get, deadline, stamp) ;
+}
diff --git a/src/libunixonacid/unixmessage_v_zero.c b/src/libunixonacid/unixmessage_v_zero.c
new file mode 100644
index 0000000..b4ab13a
--- /dev/null
+++ b/src/libunixonacid/unixmessage_v_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <skalibs/unixmessage.h>
+
+unixmessage_v_t const unixmessage_v_zero = UNIXMESSAGE_V_ZERO ;
diff --git a/src/libunixonacid/unixmessage_zero.c b/src/libunixonacid/unixmessage_zero.c
new file mode 100644
index 0000000..4cfb242
--- /dev/null
+++ b/src/libunixonacid/unixmessage_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <skalibs/unixmessage.h>
+
+unixmessage_t const unixmessage_zero = UNIXMESSAGE_ZERO ;