aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.macos (renamed from README.macosx)2
-rw-r--r--doc/skabus-rpc-daemon.html137
-rw-r--r--package/deps.mak57
-rw-r--r--package/modes2
-rw-r--r--package/targets.mak6
-rw-r--r--src/include/skabus/rpc.h229
-rw-r--r--src/include/skabus/skabus.h1
-rw-r--r--src/libskabus/deps-lib/skabus39
-rw-r--r--src/libskabus/skabus-rpc-internal.h20
-rw-r--r--src/libskabus/skabus_rpc_cancel.c15
-rw-r--r--src/libskabus/skabus_rpc_cancel_async.c25
-rw-r--r--src/libskabus/skabus_rpc_end.c19
-rw-r--r--src/libskabus/skabus_rpc_get.c19
-rw-r--r--src/libskabus/skabus_rpc_idstr.c14
-rw-r--r--src/libskabus/skabus_rpc_idstr_async.c31
-rw-r--r--src/libskabus/skabus_rpc_interface_register.c16
-rw-r--r--src/libskabus/skabus_rpc_interface_register_async.c30
-rw-r--r--src/libskabus/skabus_rpc_interface_register_cb.c17
-rw-r--r--src/libskabus/skabus_rpc_interface_unregister.c15
-rw-r--r--src/libskabus/skabus_rpc_interface_unregister_async.c29
-rw-r--r--src/libskabus/skabus_rpc_interface_zero.c5
-rw-r--r--src/libskabus/skabus_rpc_qinfo_zero.c5
-rw-r--r--src/libskabus/skabus_rpc_r_notimpl.c11
-rw-r--r--src/libskabus/skabus_rpc_rcancel_ignore.c11
-rw-r--r--src/libskabus/skabus_rpc_release.c25
-rw-r--r--src/libskabus/skabus_rpc_reply.c17
-rw-r--r--src/libskabus/skabus_rpc_replyv.c19
-rw-r--r--src/libskabus/skabus_rpc_rinfo_pack.c18
-rw-r--r--src/libskabus/skabus_rpc_rinfo_unpack.c18
-rw-r--r--src/libskabus/skabus_rpc_rinfo_zero.c5
-rw-r--r--src/libskabus/skabus_rpc_send.c9
-rw-r--r--src/libskabus/skabus_rpc_send_async.c9
-rw-r--r--src/libskabus/skabus_rpc_send_cb.c53
-rw-r--r--src/libskabus/skabus_rpc_sendpm.c9
-rw-r--r--src/libskabus/skabus_rpc_sendpm_async.c9
-rw-r--r--src/libskabus/skabus_rpc_sendq.c14
-rw-r--r--src/libskabus/skabus_rpc_sendq_async.c34
-rw-r--r--src/libskabus/skabus_rpc_sendv.c9
-rw-r--r--src/libskabus/skabus_rpc_sendv_async.c9
-rw-r--r--src/libskabus/skabus_rpc_sendvpm.c9
-rw-r--r--src/libskabus/skabus_rpc_sendvpm_async.c9
-rw-r--r--src/libskabus/skabus_rpc_sendvq.c13
-rw-r--r--src/libskabus/skabus_rpc_sendvq_async.c38
-rw-r--r--src/libskabus/skabus_rpc_start.c19
-rw-r--r--src/libskabus/skabus_rpc_start_async.c18
-rw-r--r--src/libskabus/skabus_rpc_update.c120
-rw-r--r--src/libskabus/skabus_rpc_zero.c5
-rw-r--r--src/rpc/PROTOCOL82
-rw-r--r--src/rpc/deps-exe/skabus-rpc-daemon1
-rw-r--r--src/rpc/deps-exe/skabus-rpcd8
-rw-r--r--src/rpc/skabus-rpc-daemon.c154
-rw-r--r--src/rpc/skabus-rpcd.c542
-rw-r--r--src/rpc/skabus-rpcd.h146
-rw-r--r--src/rpc/skabus_rpcd_client.c188
-rw-r--r--src/rpc/skabus_rpcd_interface.c113
-rw-r--r--src/rpc/skabus_rpcd_query.c207
56 files changed, 2681 insertions, 3 deletions
diff --git a/README.macosx b/README.macos
index d71a096..4c497de 100644
--- a/README.macosx
+++ b/README.macos
@@ -1,4 +1,4 @@
- This package will compile and run on Darwin (MacOS X), but the building of
+ This package will compile and run on Darwin (MacOS), but the building of
shared libraries is not supported.
Make sure you use the --disable-shared option to configure.
diff --git a/doc/skabus-rpc-daemon.html b/doc/skabus-rpc-daemon.html
new file mode 100644
index 0000000..bd9b575
--- /dev/null
+++ b/doc/skabus-rpc-daemon.html
@@ -0,0 +1,137 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>skabus: the skabus-rpc-daemon program</title>
+ <meta name="Description" content="skabus: the skabus-rpc-daemon program" />
+ <meta name="Keywords" content="skabus skabus-rpc unix linux socket rpc mapper server daemon" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">skabus</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>skabus-rpc-daemon</tt> program </h1>
+
+<p>
+<tt>skabus-rpc-daemon</tt> is a RPC mapper <em>daemon</em>, i.e. a
+long-lived program.
+It listens on a Unix domain socket, then
+accepts client connections. It allows clients to register interfaces
+and methods; it transmits queries from a client Q to the appropriate
+client R that can handle them; it then transmits the answer back to
+client Q.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ skabus-rpc-daemon [ -1 ] [ -v <em>verbosity</em> ] [ -D | -d ] [ -c <em>maxconn</em> ] [ -b <em>backlog</em> ] [ -G <em>gidlist</em> ] [ -g <em>gid</em> ] [ -u <em>uid</em> ] [ -U ] [ -t <em>clienttimeout</em> ] [ -T <em>lameducktimeout</em> ] [ -i <em>rulesdir</em> | -x <em>rulesfile</em> ] [ -S | -s ] [ -J | -j ] <em>path</em>
+</pre>
+
+<ul>
+ <li> skabus-rpc-daemon binds to the Unix domain socket at path. </li>
+ <li> If applicable, it drops root privileges. </li>
+ <li> It listens to its socket and accepts client connections. </li>
+ <li> Clients are handled as described in the
+<a href="skabus-rpcd.html">skabus-rpcd</a> page. </li>
+</ul>
+
+<p>
+ skabus-rpc-daemon is just a wrapper that binds to its socket and drops
+privileges before executing into
+<a href="skabus-rpcd.html">skabus-rpcd</a>. For details of the daemon's
+operation, see the <a href="skabus-rpcd.html">skabus-rpcd</a> documentation.
+</p>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-1</tt>&nbsp;: write a newline to stdout, before
+closing it, right after binding and listening to the Unix socket.
+If stdout is suitably redirected, this can be used by monitoring
+programs to check when the server is ready to accept connections. </li>
+ <li> <tt>-v&nbsp;<em>verbosity</em></tt>&nbsp;: be quiet, normally
+verbose, or more verbose, depending on if <em>verbosity</em> is 0,
+1, or more. The default is 1. </li>
+ <li> <tt>-d</tt>&nbsp;: allow instant rebinding to the same path
+even if it has been used not long ago - this is the SO_REUSEADDR flag to
+<a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html">setsockopt()</a>
+and is generally used with server programs. This is the default. Note that
+<em>path</em> will be deleted if it already exists at program start time. </li>
+ <li> <tt>-D</tt>&nbsp;: disallow instant rebinding to the same path. </li>
+ <li> <tt>-c&nbsp;<em>maxconn</em></tt>&nbsp;: accept at most
+<em>maxconn</em> concurrent client connections. Default is 40. It is
+impossible to set it higher than the value of the SKABUS_RPC_MAX macro,
+which is 1000. Client connections to this server are usually long-lived;
+make sure to correctly tune that number to your needs. </li>
+ <li> <tt>-b&nbsp;<em>backlog</em></tt>&nbsp;: set a maximum of
+<em>backlog</em> backlog connections on the socket. Extra
+connection attempts will rejected by the kernel. </li>
+ <li> <tt>-G&nbsp;<em>gidlist</em></tt>&nbsp;: change skabus-rpc-daemon's
+supplementary group list to <em>gidlist</em> after binding the socket.
+This is only valid when run as root. <em>gidlist</em> must be a
+comma-separated list of numerical group IDs. </li>
+ <li> <tt>-g&nbsp;<em>gid</em></tt>&nbsp;: change skabus-rpc-daemon's groupid
+to <em>gid</em> after binding the socket. This is only valid when run
+as root. </li>
+ <li> <tt>-u&nbsp;<em>uid</em></tt>&nbsp;: change skabus-rpc-daemon's userid
+to <em>uid</em> after binding the socket. This is only valid when run
+as root. </li>
+ <li> <tt>-U</tt>&nbsp;: change skabus-rpc-daemon's user id, group id and
+supplementary group list
+according to the values of the UID, GID and GIDLIST environment variables
+after binding the socket. This is only valid when run as root.
+This can be used with the
+<a href="s6-envuidgid.html">s6-envuidgid</a>
+program to easily script a service that binds to a privileged socket
+then drops its privileges to those of a named non-root account. </li>
+ <li> <tt>-t&nbsp;<em>clienttimeout</em></tt>&nbsp;: disconnect a client
+if it's in the middle of an operation and it has not written or read any
+data in <em>clienttimeout</em> milliseconds. By default, <em>clienttimeout</em>
+is 0, which means infinite. </li>
+ <li> <tt>-T&nbsp;<em>lameducktimeout</em></tt>&nbsp;: give clients
+<em>lameducktimeout</em> milliseconds to finish their current operation
+before exiting after skabus-rpc-daemon has received a SIGTERM. By default,
+<em>lameducktimeout</em> is 0, which means infinite. </li>
+ <li> <tt>-x&nbsp;<em>rulesfile</em></tt>&nbsp;: read access rights
+configuration from CDB file <em>rulesfile</em>. </li>
+ <li> <tt>-i&nbsp;<em>rulesdir</em></tt>&nbsp;: read access rights
+configuration from the filesystem in directory <em>rulesdir</em>. </li>
+ <li> <tt>-S</tt>&nbsp;: paranoid identification mode. Disallows
+unspecified clients from registering
+under any identifier. This is the default. </li>
+ <li> <tt>-s</tt>&nbsp;: free registration. Allows unspecified clients
+to register with any identifier. </li>
+ <li> <tt>-J</tt>&nbsp;: paranoid interface registration. Disallows
+unspecified clients from registering interfaces. This is the default. </li>
+ <li> <tt>-j</tt>&nbsp;: free interface registration. Allows unspecified clients
+to register any interface name. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> skabus-rpc-daemon does not interpret its options itself. It just
+dispatches them to the appropriate program on the command line that
+it builds. </li>
+ <li> From the user's point of view, skabus-rpc-daemon behaves like a
+long-lived process, even if the long-lived process itself is called
+<a href="skabus-rpcd.html">skabus-rpcd</a>. Every operational detail
+of skabus-rpcd applies to skabus-rpc-daemon as well; in particular,
+make sure to properly
+<a href="skabus-rpcd.html#configuration">configure the clients'
+access rights</a>. </li>
+ <li> skabus-rpc-daemon is meant to be used in a s6 run script, as
+a supervised local service. It does not fork itself or write to syslog.
+However, it can be run under any infrastructure, including other
+supervision infrastructures, OpenRC, systemd, or SysV scripts. </li>
+</ul>
+
+</body>
+</html>
diff --git a/package/deps.mak b/package/deps.mak
index 591ded9..fc3ae21 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -2,13 +2,70 @@
# This file has been generated by tools/gen-deps.sh
#
+src/include/skabus/skabus.h: src/include/skabus/rpc.h
+src/libskabus/skabus-rpc-internal.h: src/include/skabus/rpc.h
+src/rpc/skabus-rpcd.h: src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_cancel.o src/libskabus/skabus_rpc_cancel.lo: src/libskabus/skabus_rpc_cancel.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_cancel_async.o src/libskabus/skabus_rpc_cancel_async.lo: src/libskabus/skabus_rpc_cancel_async.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_end.o src/libskabus/skabus_rpc_end.lo: src/libskabus/skabus_rpc_end.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_get.o src/libskabus/skabus_rpc_get.lo: src/libskabus/skabus_rpc_get.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_idstr.o src/libskabus/skabus_rpc_idstr.lo: src/libskabus/skabus_rpc_idstr.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_idstr_async.o src/libskabus/skabus_rpc_idstr_async.lo: src/libskabus/skabus_rpc_idstr_async.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_interface_register.o src/libskabus/skabus_rpc_interface_register.lo: src/libskabus/skabus_rpc_interface_register.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_interface_register_async.o src/libskabus/skabus_rpc_interface_register_async.lo: src/libskabus/skabus_rpc_interface_register_async.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_interface_register_cb.o src/libskabus/skabus_rpc_interface_register_cb.lo: src/libskabus/skabus_rpc_interface_register_cb.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_interface_unregister.o src/libskabus/skabus_rpc_interface_unregister.lo: src/libskabus/skabus_rpc_interface_unregister.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_interface_unregister_async.o src/libskabus/skabus_rpc_interface_unregister_async.lo: src/libskabus/skabus_rpc_interface_unregister_async.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_interface_zero.o src/libskabus/skabus_rpc_interface_zero.lo: src/libskabus/skabus_rpc_interface_zero.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_qinfo_zero.o src/libskabus/skabus_rpc_qinfo_zero.lo: src/libskabus/skabus_rpc_qinfo_zero.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_r_notimpl.o src/libskabus/skabus_rpc_r_notimpl.lo: src/libskabus/skabus_rpc_r_notimpl.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_rcancel_ignore.o src/libskabus/skabus_rpc_rcancel_ignore.lo: src/libskabus/skabus_rpc_rcancel_ignore.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_release.o src/libskabus/skabus_rpc_release.lo: src/libskabus/skabus_rpc_release.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_reply.o src/libskabus/skabus_rpc_reply.lo: src/libskabus/skabus_rpc_reply.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_replyv.o src/libskabus/skabus_rpc_replyv.lo: src/libskabus/skabus_rpc_replyv.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_rinfo_pack.o src/libskabus/skabus_rpc_rinfo_pack.lo: src/libskabus/skabus_rpc_rinfo_pack.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_rinfo_unpack.o src/libskabus/skabus_rpc_rinfo_unpack.lo: src/libskabus/skabus_rpc_rinfo_unpack.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_rinfo_zero.o src/libskabus/skabus_rpc_rinfo_zero.lo: src/libskabus/skabus_rpc_rinfo_zero.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_send.o src/libskabus/skabus_rpc_send.lo: src/libskabus/skabus_rpc_send.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_send_async.o src/libskabus/skabus_rpc_send_async.lo: src/libskabus/skabus_rpc_send_async.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_send_cb.o src/libskabus/skabus_rpc_send_cb.lo: src/libskabus/skabus_rpc_send_cb.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_sendpm.o src/libskabus/skabus_rpc_sendpm.lo: src/libskabus/skabus_rpc_sendpm.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_sendpm_async.o src/libskabus/skabus_rpc_sendpm_async.lo: src/libskabus/skabus_rpc_sendpm_async.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_sendq.o src/libskabus/skabus_rpc_sendq.lo: src/libskabus/skabus_rpc_sendq.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_sendq_async.o src/libskabus/skabus_rpc_sendq_async.lo: src/libskabus/skabus_rpc_sendq_async.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_sendv.o src/libskabus/skabus_rpc_sendv.lo: src/libskabus/skabus_rpc_sendv.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_sendv_async.o src/libskabus/skabus_rpc_sendv_async.lo: src/libskabus/skabus_rpc_sendv_async.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_sendvpm.o src/libskabus/skabus_rpc_sendvpm.lo: src/libskabus/skabus_rpc_sendvpm.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_sendvpm_async.o src/libskabus/skabus_rpc_sendvpm_async.lo: src/libskabus/skabus_rpc_sendvpm_async.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_sendvq.o src/libskabus/skabus_rpc_sendvq.lo: src/libskabus/skabus_rpc_sendvq.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_sendvq_async.o src/libskabus/skabus_rpc_sendvq_async.lo: src/libskabus/skabus_rpc_sendvq_async.c src/libskabus/skabus-rpc-internal.h src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_start.o src/libskabus/skabus_rpc_start.lo: src/libskabus/skabus_rpc_start.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_start_async.o src/libskabus/skabus_rpc_start_async.lo: src/libskabus/skabus_rpc_start_async.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_update.o src/libskabus/skabus_rpc_update.lo: src/libskabus/skabus_rpc_update.c src/include/skabus/rpc.h
+src/libskabus/skabus_rpc_zero.o src/libskabus/skabus_rpc_zero.lo: src/libskabus/skabus_rpc_zero.c src/include/skabus/rpc.h
src/misc/skabus-dyntee-client.o src/misc/skabus-dyntee-client.lo: src/misc/skabus-dyntee-client.c
src/misc/skabus-dyntee.o src/misc/skabus-dyntee.lo: src/misc/skabus-dyntee.c src/include/skabus/config.h
src/misc/skabus-dynteed.o src/misc/skabus-dynteed.lo: src/misc/skabus-dynteed.c
+src/rpc/skabus-rpc-daemon.o src/rpc/skabus-rpc-daemon.lo: src/rpc/skabus-rpc-daemon.c src/include/skabus/config.h
+src/rpc/skabus-rpcd.o src/rpc/skabus-rpcd.lo: src/rpc/skabus-rpcd.c src/rpc/skabus-rpcd.h src/include/skabus/rpc.h
+src/rpc/skabus_rpcd_client.o src/rpc/skabus_rpcd_client.lo: src/rpc/skabus_rpcd_client.c src/rpc/skabus-rpcd.h src/include/skabus/rpc.h
+src/rpc/skabus_rpcd_interface.o src/rpc/skabus_rpcd_interface.lo: src/rpc/skabus_rpcd_interface.c src/rpc/skabus-rpcd.h src/include/skabus/rpc.h
+src/rpc/skabus_rpcd_query.o src/rpc/skabus_rpcd_query.lo: src/rpc/skabus_rpcd_query.c src/rpc/skabus-rpcd.h src/include/skabus/rpc.h
+ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),)
+libskabus.a.xyzzy: src/libskabus/skabus_rpc_cancel.o src/libskabus/skabus_rpc_cancel_async.o src/libskabus/skabus_rpc_end.o src/libskabus/skabus_rpc_get.o src/libskabus/skabus_rpc_idstr.o src/libskabus/skabus_rpc_idstr_async.o src/libskabus/skabus_rpc_interface_register.o src/libskabus/skabus_rpc_interface_register_async.o src/libskabus/skabus_rpc_interface_register_cb.o src/libskabus/skabus_rpc_interface_unregister.o src/libskabus/skabus_rpc_interface_unregister_async.o src/libskabus/skabus_rpc_interface_zero.o src/libskabus/skabus_rpc_qinfo_zero.o src/libskabus/skabus_rpc_r_notimpl.o src/libskabus/skabus_rpc_rcancel_ignore.o src/libskabus/skabus_rpc_release.o src/libskabus/skabus_rpc_reply.o src/libskabus/skabus_rpc_replyv.o src/libskabus/skabus_rpc_rinfo_pack.o src/libskabus/skabus_rpc_rinfo_unpack.o src/libskabus/skabus_rpc_rinfo_zero.o src/libskabus/skabus_rpc_send.o src/libskabus/skabus_rpc_send_async.o src/libskabus/skabus_rpc_send_cb.o src/libskabus/skabus_rpc_sendpm.o src/libskabus/skabus_rpc_sendpm_async.o src/libskabus/skabus_rpc_sendq.o src/libskabus/skabus_rpc_sendq_async.o src/libskabus/skabus_rpc_sendv.o src/libskabus/skabus_rpc_sendv_async.o src/libskabus/skabus_rpc_sendvpm.o src/libskabus/skabus_rpc_sendvpm_async.o src/libskabus/skabus_rpc_sendvq.o src/libskabus/skabus_rpc_sendvq_async.o src/libskabus/skabus_rpc_start.o src/libskabus/skabus_rpc_start_async.o src/libskabus/skabus_rpc_update.o src/libskabus/skabus_rpc_zero.o
+else
+libskabus.a.xyzzy: src/libskabus/skabus_rpc_cancel.lo src/libskabus/skabus_rpc_cancel_async.lo src/libskabus/skabus_rpc_end.lo src/libskabus/skabus_rpc_get.lo src/libskabus/skabus_rpc_idstr.lo src/libskabus/skabus_rpc_idstr_async.lo src/libskabus/skabus_rpc_interface_register.lo src/libskabus/skabus_rpc_interface_register_async.lo src/libskabus/skabus_rpc_interface_register_cb.lo src/libskabus/skabus_rpc_interface_unregister.lo src/libskabus/skabus_rpc_interface_unregister_async.lo src/libskabus/skabus_rpc_interface_zero.lo src/libskabus/skabus_rpc_qinfo_zero.lo src/libskabus/skabus_rpc_r_notimpl.lo src/libskabus/skabus_rpc_rcancel_ignore.lo src/libskabus/skabus_rpc_release.lo src/libskabus/skabus_rpc_reply.lo src/libskabus/skabus_rpc_replyv.lo src/libskabus/skabus_rpc_rinfo_pack.lo src/libskabus/skabus_rpc_rinfo_unpack.lo src/libskabus/skabus_rpc_rinfo_zero.lo src/libskabus/skabus_rpc_send.lo src/libskabus/skabus_rpc_send_async.lo src/libskabus/skabus_rpc_send_cb.lo src/libskabus/skabus_rpc_sendpm.lo src/libskabus/skabus_rpc_sendpm_async.lo src/libskabus/skabus_rpc_sendq.lo src/libskabus/skabus_rpc_sendq_async.lo src/libskabus/skabus_rpc_sendv.lo src/libskabus/skabus_rpc_sendv_async.lo src/libskabus/skabus_rpc_sendvpm.lo src/libskabus/skabus_rpc_sendvpm_async.lo src/libskabus/skabus_rpc_sendvq.lo src/libskabus/skabus_rpc_sendvq_async.lo src/libskabus/skabus_rpc_start.lo src/libskabus/skabus_rpc_start_async.lo src/libskabus/skabus_rpc_update.lo src/libskabus/skabus_rpc_zero.lo
+endif
+libskabus.so.xyzzy: EXTRA_LIBS := -lskarnet
+libskabus.so.xyzzy: src/libskabus/skabus_rpc_cancel.lo src/libskabus/skabus_rpc_cancel_async.lo src/libskabus/skabus_rpc_end.lo src/libskabus/skabus_rpc_get.lo src/libskabus/skabus_rpc_idstr.lo src/libskabus/skabus_rpc_idstr_async.lo src/libskabus/skabus_rpc_interface_register.lo src/libskabus/skabus_rpc_interface_register_async.lo src/libskabus/skabus_rpc_interface_register_cb.lo src/libskabus/skabus_rpc_interface_unregister.lo src/libskabus/skabus_rpc_interface_unregister_async.lo src/libskabus/skabus_rpc_interface_zero.lo src/libskabus/skabus_rpc_qinfo_zero.lo src/libskabus/skabus_rpc_r_notimpl.lo src/libskabus/skabus_rpc_rcancel_ignore.lo src/libskabus/skabus_rpc_release.lo src/libskabus/skabus_rpc_reply.lo src/libskabus/skabus_rpc_replyv.lo src/libskabus/skabus_rpc_rinfo_pack.lo src/libskabus/skabus_rpc_rinfo_unpack.lo src/libskabus/skabus_rpc_rinfo_zero.lo src/libskabus/skabus_rpc_send.lo src/libskabus/skabus_rpc_send_async.lo src/libskabus/skabus_rpc_send_cb.lo src/libskabus/skabus_rpc_sendpm.lo src/libskabus/skabus_rpc_sendpm_async.lo src/libskabus/skabus_rpc_sendq.lo src/libskabus/skabus_rpc_sendq_async.lo src/libskabus/skabus_rpc_sendv.lo src/libskabus/skabus_rpc_sendv_async.lo src/libskabus/skabus_rpc_sendvpm.lo src/libskabus/skabus_rpc_sendvpm_async.lo src/libskabus/skabus_rpc_sendvq.lo src/libskabus/skabus_rpc_sendvq_async.lo src/libskabus/skabus_rpc_start.lo src/libskabus/skabus_rpc_start_async.lo src/libskabus/skabus_rpc_update.lo src/libskabus/skabus_rpc_zero.lo
skabus-dyntee: EXTRA_LIBS := ${SOCKET_LIB}
skabus-dyntee: src/misc/skabus-dyntee.o -lskarnet
skabus-dyntee-client: EXTRA_LIBS := ${SOCKET_LIB}
skabus-dyntee-client: src/misc/skabus-dyntee-client.o -lskarnet
skabus-dynteed: EXTRA_LIBS :=
skabus-dynteed: src/misc/skabus-dynteed.o -ls6 -lskarnet
+skabus-rpc-daemon: EXTRA_LIBS :=
+skabus-rpc-daemon: src/rpc/skabus-rpc-daemon.o -lskarnet
+skabus-rpcd: EXTRA_LIBS := ${TAINNOW_LIB} ${SOCKET_LIB}
+skabus-rpcd: src/rpc/skabus-rpcd.o src/rpc/skabus_rpcd_client.o src/rpc/skabus_rpcd_interface.o src/rpc/skabus_rpcd_query.o libskabus.a.xyzzy -ls6 -lskarnet
diff --git a/package/modes b/package/modes
index 5002c47..0d08c10 100644
--- a/package/modes
+++ b/package/modes
@@ -1,3 +1,5 @@
skabus-dyntee 0755
skabus-dynteed 0755
skabus-dyntee-client 0755
+skabus-rpc-daemon 0755
+skabus-rpcd 0755
diff --git a/package/targets.mak b/package/targets.mak
index e0b6e6b..235c646 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -1,8 +1,10 @@
BIN_TARGETS := \
skabus-dyntee \
skabus-dynteed \
-skabus-dyntee-client
+skabus-dyntee-client \
+skabus-rpc-daemon \
+skabus-rpcd
LIBEXEC_TARGETS :=
-LIB_DEFS :=
+LIB_DEFS := SKABUS=skabus
diff --git a/src/include/skabus/rpc.h b/src/include/skabus/rpc.h
new file mode 100644
index 0000000..733a1bf
--- /dev/null
+++ b/src/include/skabus/rpc.h
@@ -0,0 +1,229 @@
+/* ISC license. */
+
+#ifndef SKABUS_RPC_H
+#define SKABUS_RPC_H
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <stdint.h>
+#include <errno.h>
+#include <skalibs/config.h>
+#include <skalibs/uint64.h>
+#include <skalibs/types.h>
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+#include <skalibs/skaclient.h>
+
+
+/* Misc constants */
+
+#define SKABUS_RPC_MAX 1000
+#define SKABUS_RPC_BANNER1 "skabus-rpc v1.0 (b)\n"
+#define SKABUS_RPC_BANNER1_LEN (sizeof SKABUS_RPC_BANNER1 - 1)
+#define SKABUS_RPC_BANNER2 "skabus-rpc v1.0 (a)\n"
+#define SKABUS_RPC_BANNER2_LEN (sizeof SKABUS_RPC_BANNER2 - 1)
+#define SKABUS_RPC_IDSTR_SIZE 254
+#define SKABUS_RPC_INTERFACE_MAXLEN 255
+
+typedef struct skabus_rpc_s skabus_rpc_t, *skabus_rpc_t_ref ;
+
+ /* Additional data transmitted to methods */
+
+typedef struct skabus_rpc_rinfo_s skabus_rpc_rinfo_t, *skabus_rpc_rinfo_t_ref ;
+struct skabus_rpc_rinfo_s
+{
+ uint64_t serial ;
+ tain_t limit ;
+ tain_t timestamp ;
+ uid_t uid ;
+ gid_t gid ;
+ char idstr[SKABUS_RPC_IDSTR_SIZE + 1] ;
+} ;
+#define SKABUS_RPC_RINFO_ZERO { .serial = 0, .limit = TAIN_ZERO, .timestamp = TAIN_ZERO, .uid = -1, .gid = -1, .idstr = "" }
+extern skabus_rpc_rinfo_t const skabus_rpc_rinfo_zero ;
+
+#define SKABUS_RPC_RINFO_PACK (9 + (TAIN_PACK << 1) + UID_PACK + GID_PACK + SKABUS_RPC_IDSTR_SIZE)
+extern void skabus_rpc_rinfo_pack (char *, skabus_rpc_rinfo_t const *) ;
+extern void skabus_rpc_rinfo_unpack (char const *, skabus_rpc_rinfo_t *) ;
+
+
+ /* Serving queries */
+
+typedef int skabus_rpc_r_func_t (skabus_rpc_t *, skabus_rpc_rinfo_t const *, unixmessage_t const *, void *) ;
+typedef skabus_rpc_r_func_t *skabus_rpc_r_func_t_ref ;
+typedef int skabus_rpc_rcancel_func_t (uint64_t, char, void *) ;
+typedef skabus_rpc_rcancel_func_t *skabus_rpc_rcancel_func_t_ref ;
+
+typedef struct skabus_rpc_interface_s skabus_rpc_interface_t, *skabus_rpc_interface_t_ref ;
+struct skabus_rpc_interface_s
+{
+ skabus_rpc_r_func_t_ref f ;
+ skabus_rpc_rcancel_func_t_ref cancelf ;
+ void *data ;
+} ;
+#define SKABUS_RPC_INTERFACE_ZERO { .f = &skabus_rpc_r_notimpl, .cancelf = &skabus_rpc_rcancel_ignore, .data = 0 }
+extern skabus_rpc_interface_t const skabus_rpc_interface_zero ;
+
+extern skabus_rpc_r_func_t skabus_rpc_r_notimpl ;
+extern skabus_rpc_rcancel_func_t skabus_rpc_rcancel_ignore ;
+
+extern int skabus_rpc_reply_withfds (skabus_rpc_t *, uint64_t, char, char const *, size_t, int const *, unsigned int, unsigned char const *) ;
+#define skabus_rpc_reply(a, serial, result, s, len) skabus_rpc_reply_withfds(a, serial, result, s, len, 0, 0, unixmessage_bits_closenone)
+extern int skabus_rpc_replyv_withfds (skabus_rpc_t *, uint64_t, char, struct iovec const *, unsigned int, int const *, unsigned int, unsigned char const *) ;
+#define skabus_rpc_replyv(a, serial, result, v, vlen) skabus_rpc_replyv_withfds(a, serial, result, v, vlen, 0, 0, unixmessage_bits_closenone)
+
+#define skabus_rpc_rfd(a) skaclient_afd(&(a)->connection)
+#define skabus_rpc_riswritable skaclient_aiswritable(&(a)->connection)
+#define skabus_rpc_rflush(a) skaclient_aflush(&(a)->connection)
+
+
+ /* Internal client interface storage */
+
+typedef struct skabus_rpc_ifnode_s skabus_rpc_ifnode_t, *skabus_rpc_ifnode_t_ref ;
+struct skabus_rpc_ifnode_s
+{
+ char name[SKABUS_RPC_INTERFACE_MAXLEN + 1] ;
+ skabus_rpc_interface_t body ;
+} ;
+#define SKABUS_RPC_IFNODE_ZERO { .name = "", .body = SKABUS_RPC_INTERFACE_ZERO }
+
+
+ /* Internal client query storage */
+
+typedef struct skabus_rpc_qinfo_s skabus_rpc_qinfo_t, *skabus_rpc_qinfo_t_ref ;
+struct skabus_rpc_qinfo_s
+{
+ uint64_t serial ;
+ char status ;
+ char result ;
+ unixmessage_t message ;
+} ;
+#define SKABUS_RPC_QINFO_ZERO { .serial = 0, .status = EINVAL, .result = ECONNABORTED, .message = UNIXMESSAGE_ZERO }
+extern skabus_rpc_qinfo_t const skabus_rpc_qinfo_zero ;
+
+
+ /* Client handle */
+
+struct skabus_rpc_s
+{
+ skaclient_t connection ;
+ uint32_t pmid ; /* index of the object interface */
+ gensetdyn r ; /* set of skabus_rpc_ifnode_t */
+ genalloc qlist ; /* array of uint64_t */
+ gensetdyn q ; /* set of skabus_rpc_qinfo_t */
+ avltree qmap ; /* serial -> index in q */
+ skaclient_buffer_t buffers ;
+} ;
+#define SKABUS_RPC_ZERO \
+{ \
+ .connection = SKACLIENT_ZERO, \
+ .pmid = (uint32_t)-1, \
+ .r = GENSETDYN_ZERO, \
+ .qlist = GENALLOC_ZERO, \
+ .q = GENSETDYN_ZERO, \
+ .qmap = AVLTREE_ZERO \
+}
+
+
+/* Starting and ending a session */
+
+typedef struct skabus_rpc_interface_result_s skabus_rpc_interface_result_t, *skabus_rpc_interface_result_t_ref ;
+
+typedef struct skabus_rpc_start_result_s skabus_rpc_start_result_t, *skabus_rpc_start_result_t_ref ;
+struct skabus_rpc_start_result_s
+{
+ skaclient_cbdata_t skaclient_cbdata ;
+} ;
+
+#define skabus_rpc_init(a, path, id, ifbody, re, deadline, stamp) (skabus_rpc_start(a, path, deadline, stamp) && skabus_rpc_idstr(a, id, ifbody, re, deadline, stamp))
+#define skabus_rpc_init_g(a, path, id, ifbody, re, deadline) skabus_rpc_init(a, path, id, ifbody, re, (deadline), &STAMP)
+
+extern int skabus_rpc_start_async (skabus_rpc_t *, char const *, skabus_rpc_start_result_t *) ;
+extern int skabus_rpc_start (skabus_rpc_t *, char const *, tain_t const *, tain_t *) ;
+#define skabus_rpc_start_g(a, path, deadline) skabus_rpc_start(a, path, (deadline), &STAMP)
+
+extern int skabus_rpc_idstr_async (skabus_rpc_t *, char const *, skabus_rpc_interface_t const *, char const *, skabus_rpc_interface_result_t *) ;
+extern int skabus_rpc_idstr (skabus_rpc_t *, char const *, skabus_rpc_interface_t const *, char const *, tain_t const *, tain_t *) ;
+#define skabus_rpc_idstr_g(a, idstr, ifbody, re, deadline) skabus_rpc_idstr(a, idstr, ifbody, re, (deadline), &STAMP)
+
+extern void skabus_rpc_end (skabus_rpc_t *) ;
+
+
+ /* Getting results */
+
+extern int skabus_rpc_update (skabus_rpc_t *) ;
+extern int skabus_rpc_get (skabus_rpc_t *, uint64_t, int *, unixmessage_t *) ;
+extern int skabus_rpc_release (skabus_rpc_t *, uint64_t) ;
+
+
+ /* Registering an interface */
+
+struct skabus_rpc_interface_result_s
+{
+ gensetdyn *r ;
+ uint32_t ifid ;
+ unsigned char err ;
+} ;
+
+extern int skabus_rpc_interface_register_async (skabus_rpc_t *, char const *, skabus_rpc_interface_t const *, char const *, skabus_rpc_interface_result_t *) ;
+extern int skabus_rpc_interface_register (skabus_rpc_t *, uint32_t *, char const *, skabus_rpc_interface_t const *, char const *, tain_t const *, tain_t *) ;
+#define skabus_rpc_interface_register_g(a, ifid, ifname, ifbody, re, deadline) skabus_rpc_interface_register(a, ifid, ifname, ifbody, re, (deadline), &STAMP)
+
+extern int skabus_rpc_interface_unregister_async (skabus_rpc_t *, uint32_t, skabus_rpc_interface_result_t *) ;
+extern int skabus_rpc_interface_unregister (skabus_rpc_t *, uint32_t, tain_t const *, tain_t *) ;
+#define skabus_rpc_interface_unregister_g(a, ifid, deadline) skabus_rpc_interface_unregister(a, ifid, (deadline), &STAMP)
+
+
+ /* Sending a query */
+
+typedef struct skabus_rpc_send_result_s skabus_rpc_send_result_t, *skabus_rpc_send_result_t_ref ;
+struct skabus_rpc_send_result_s
+{
+ skabus_rpc_t *a ;
+ uint64_t u ;
+ uint32_t i ;
+ unsigned char err ;
+} ;
+
+extern int skabus_rpc_send_withfds_async (skabus_rpc_t *, char const *, char const *, size_t, int const *, unsigned int, unsigned char const *, tain_t const *, skabus_rpc_send_result_t *) ;
+#define skabus_rpc_send_async(a, ifname, s, len, limit, r) skabus_rpc_send_withfds_async(a, ifname, s, len, 0, 0, unixmessage_bits_closenone, limit, r)
+
+extern uint64_t skabus_rpc_send_withfds (skabus_rpc_t *, char const *, char const *, size_t, int const *, unsigned int, unsigned char const *, tain_t const *, tain_t const *, tain_t *) ;
+#define skabus_rpc_send_withfds_g(a, ifname, s, len, fds, nfds, bits, limit, deadline) skabus_rpc_send_withfds(a, ifname, s, len, fds, nfds, limit, (deadline), &STAMP)
+#define skabus_rpc_send(a, ifname, s, len, limit, deadline, stamp) skabus_rpc_send_withfds(a, ifname, s, len, 0, 0, unixmessage_bits_closenone, limit, deadline, stamp)
+#define skabus_rpc_send_g(a, ifname, s, len, limit, deadline) skabus_rpc_send(a, ifname, s, len, limit, (deadline), &STAMP)
+
+extern int skabus_rpc_sendv_withfds_async (skabus_rpc_t *, char const *, struct iovec const *, unsigned int, int const *, unsigned int, unsigned char const *, tain_t const *, skabus_rpc_send_result_t *) ;
+#define skabus_rpc_sendv_async(a, ifname, v, vlen, limit, r) skabus_rpc_sendv_withfds_async(a, ifname, v, vlen, 0, 0, unixmessage_bits_closenone, limit, r)
+
+extern uint64_t skabus_rpc_sendv_withfds (skabus_rpc_t *, char const *, struct iovec const *, unsigned int, int const *, unsigned int, unsigned char const *, tain_t const *, tain_t const *, tain_t *) ;
+#define skabus_rpc_sendv_withfds_g(a, ifname, v, vlen, fds, nfds, bits, limit, deadline) skabus_rpc_sendv_withfds(a, ifname, v, vlen, fds, nfds, limit, (deadline), &STAMP)
+#define skabus_rpc_sendv(a, ifname, v, vlen, limit, deadline, stamp) skabus_rpc_sendv_withfds(a, ifname, v, vlen, 0, 0, unixmessage_bits_closenone, limit, deadline, stamp)
+#define skabus_rpc_sendv_g(a, ifname, v, vlen, limit, deadline) skabus_rpc_sendv(a, ifname, v, vlen, limit, (deadline), &STAMP)
+
+extern int skabus_rpc_sendpm_withfds_async (skabus_rpc_t *, char const *, char const *, size_t, int const *, unsigned int, unsigned char const *, tain_t const *, skabus_rpc_send_result_t *) ;
+#define skabus_rpc_sendpm_async(a, cname, s, len, limit, r) skabus_rpc_sendpm_withfds_async(a, cname, s, len, 0, 0, unixmessage_bits_closenone, limit, r)
+
+extern uint64_t skabus_rpc_sendpm_withfds (skabus_rpc_t *, char const *, char const *, size_t, int const *, unsigned int, unsigned char const *, tain_t const *, tain_t const *, tain_t *) ;
+#define skabus_rpc_sendpm_withfds_g(a, cname, s, len, fds, nfds, bits, limit, deadline) skabus_rpc_sendpm_withfds(a, cname, s, len, fds, nfds, limit, (deadline), &STAMP)
+#define skabus_rpc_sendpm(a, cname, s, len, limit, deadline, stamp) skabus_rpc_sendpm_withfds(a, cname, s, len, 0, 0, unixmessage_bits_closenone, limit, deadline, stamp)
+#define skabus_rpc_sendpm_g(a, cname, s, len, limit, deadline) skabus_rpc_sendpm(a, cname, s, len, limit, (deadline), &STAMP)
+
+extern int skabus_rpc_sendvpm_withfds_async (skabus_rpc_t *, char const *, struct iovec const *, unsigned int, int const *, unsigned int, unsigned char const *, tain_t const *, skabus_rpc_send_result_t *) ;
+#define skabus_rpc_sendvpm_async(a, cname, v, vlen, limit, r) skabus_rpc_sendvpm_withfds_async(a, cname, v, vlen, 0, 0, unixmessage_bits_closenone, limit, r)
+
+extern uint64_t skabus_rpc_sendvpm_withfds (skabus_rpc_t *, char const *, struct iovec const *, unsigned int, int const *, unsigned int, unsigned char const *, tain_t const *, tain_t const *, tain_t *) ;
+#define skabus_rpc_sendvpm_withfds_g(a, cname, v, vlen, fds, nfds, bits, limit, deadline) skabus_rpc_sendvpm_withfds(a, cname, v, vlen, fds, nfds, limit, (deadline), &STAMP)
+#define skabus_rpc_sendvpm(a, cname, v, vlen, limit, deadline, stamp) skabus_rpc_sendvpm_withfds(a, cname, v, vlen, 0, 0, unixmessage_bits_closenone, limit, deadline, stamp)
+#define skabus_rpc_sendvpm_g(a, cname, v, vlen, limit, deadline) skabus_rpc_sendvpm(a, cname, v, vlen, limit, (deadline), &STAMP)
+
+
+ /* Cancelling an in-flight query */
+
+extern int skabus_rpc_cancel_async (skabus_rpc_t *, uint64_t, unsigned char *) ;
+extern int skabus_rpc_cancel (skabus_rpc_t *, uint64_t, tain_t const *, tain_t *) ;
+#define skabus_rpc_cancel_g(a, qid, deadline) skabus_rpc_cancel(a, qid, (deadine), &STAMP)
+
+#endif
diff --git a/src/include/skabus/skabus.h b/src/include/skabus/skabus.h
index 51b161f..1c07537 100644
--- a/src/include/skabus/skabus.h
+++ b/src/include/skabus/skabus.h
@@ -7,6 +7,7 @@
extern "C" {
#endif
+#include <skabus/rpc.h>
#ifdef __cplusplus
}
diff --git a/src/libskabus/deps-lib/skabus b/src/libskabus/deps-lib/skabus
new file mode 100644
index 0000000..423b672
--- /dev/null
+++ b/src/libskabus/deps-lib/skabus
@@ -0,0 +1,39 @@
+skabus_rpc_cancel.o
+skabus_rpc_cancel_async.o
+skabus_rpc_end.o
+skabus_rpc_get.o
+skabus_rpc_idstr.o
+skabus_rpc_idstr_async.o
+skabus_rpc_interface_register.o
+skabus_rpc_interface_register_async.o
+skabus_rpc_interface_register_cb.o
+skabus_rpc_interface_unregister.o
+skabus_rpc_interface_unregister_async.o
+skabus_rpc_interface_zero.o
+skabus_rpc_qinfo_zero.o
+skabus_rpc_r_notimpl.o
+skabus_rpc_rcancel_ignore.o
+skabus_rpc_release.o
+skabus_rpc_reply.o
+skabus_rpc_replyv.o
+skabus_rpc_rinfo_pack.o
+skabus_rpc_rinfo_unpack.o
+skabus_rpc_rinfo_zero.o
+skabus_rpc_send.o
+skabus_rpc_send_async.o
+skabus_rpc_send_cb.o
+skabus_rpc_sendpm.o
+skabus_rpc_sendpm_async.o
+skabus_rpc_sendq.o
+skabus_rpc_sendq_async.o
+skabus_rpc_sendv.o
+skabus_rpc_sendv_async.o
+skabus_rpc_sendvpm.o
+skabus_rpc_sendvpm_async.o
+skabus_rpc_sendvq.o
+skabus_rpc_sendvq_async.o
+skabus_rpc_start.o
+skabus_rpc_start_async.o
+skabus_rpc_update.o
+skabus_rpc_zero.o
+-lskarnet
diff --git a/src/libskabus/skabus-rpc-internal.h b/src/libskabus/skabus-rpc-internal.h
new file mode 100644
index 0000000..04f29e4
--- /dev/null
+++ b/src/libskabus/skabus-rpc-internal.h
@@ -0,0 +1,20 @@
+/* ISC license. */
+
+#ifndef SKABUS_RPC_INTERNAL_H
+#define SKABUS_RPC_INTERNAL_H
+
+#include <sys/uio.h>
+#include <skalibs/uint64.h>
+#include <skalibs/tai.h>
+#include <skalibs/unixmessage.h>
+#include <skabus/rpc.h>
+
+extern int skabus_rpc_sendq_withfds_async (skabus_rpc_t *, char const *, size_t, char const *, char const *, size_t, int const *, unsigned int, unsigned char const *, tain_t const *, skabus_rpc_send_result_t *) ;
+extern uint64_t skabus_rpc_sendq_withfds (skabus_rpc_t *, char const *, size_t, char const *, char const *, size_t, int const *, unsigned int, unsigned char const *, tain_t const *, tain_t const *, tain_t *) ;
+extern int skabus_rpc_sendvq_withfds_async (skabus_rpc_t *, char const *, size_t, char const *, struct iovec const *, unsigned int, int const *, unsigned int, unsigned char const *, tain_t const *, skabus_rpc_send_result_t *) ;
+extern uint64_t skabus_rpc_sendvq_withfds (skabus_rpc_t *, char const *, size_t, char const *, struct iovec const *, unsigned int, int const *, unsigned int, unsigned char const *, tain_t const *, tain_t const *, tain_t *) ;
+
+extern unixmessage_handler_func_t skabus_rpc_send_cb ;
+extern unixmessage_handler_func_t skabus_rpc_interface_register_cb ;
+
+#endif
diff --git a/src/libskabus/skabus_rpc_cancel.c b/src/libskabus/skabus_rpc_cancel.c
new file mode 100644
index 0000000..d731c6e
--- /dev/null
+++ b/src/libskabus/skabus_rpc_cancel.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/tai.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_cancel (skabus_rpc_t *a, uint64_t serial, tain_t const *deadline, tain_t *stamp)
+{
+ unsigned char r ;
+ if (!skabus_rpc_cancel_async(a, serial, &r)) return 0 ;
+ if (!skaclient_syncify(&a->connection, deadline, stamp)) return 0 ;
+ if (r) return (errno = r, 0) ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_cancel_async.c b/src/libskabus/skabus_rpc_cancel_async.c
new file mode 100644
index 0000000..f836ed1
--- /dev/null
+++ b/src/libskabus/skabus_rpc_cancel_async.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <errno.h>
+#include <skalibs/uint64.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_cancel_async (skabus_rpc_t *a, uint64_t serial, unsigned char *err)
+{
+ uint32_t id ;
+ skabus_rpc_qinfo_t *p ;
+ char pack[9] = "C" ;
+ if (!avltree_search(&a->qmap, &serial, &id)) return 0 ;
+ p = GENSETDYN_P(skabus_rpc_qinfo_t, &a->q, id) ;
+ if (p->status != EAGAIN) return (errno = EINVAL, 0) ;
+ uint64_pack_big(pack+1, serial) ;
+ if (!skaclient_put(&a->connection, pack, 9, &skaclient_default_cb, err)) return 0 ;
+ p->status = EINVAL ;
+ avltree_delete(&a->qmap, &serial) ;
+ gensetdyn_delete(&a->q, id) ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_end.c b/src/libskabus/skabus_rpc_end.c
new file mode 100644
index 0000000..34c95fb
--- /dev/null
+++ b/src/libskabus/skabus_rpc_end.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <skalibs/uint64.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+void skabus_rpc_end (skabus_rpc_t *a)
+{
+ skaclient_end(&a->connection) ;
+ genalloc_free(uint64_t, &a->qlist) ;
+ avltree_free(&a->qmap) ;
+ gensetdyn_free(&a->q) ;
+ gensetdyn_free(&a->r) ;
+ a->pmid = (uint32_t)-1 ;
+}
diff --git a/src/libskabus/skabus_rpc_get.c b/src/libskabus/skabus_rpc_get.c
new file mode 100644
index 0000000..d780211
--- /dev/null
+++ b/src/libskabus/skabus_rpc_get.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <errno.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_get (skabus_rpc_t *a, uint64_t serial, int *result, unixmessage_t *m)
+{
+ uint32_t id ;
+ skabus_rpc_qinfo_t *p ;
+ if (!avltree_search(&a->qmap, &serial, &id)) return 0 ;
+ p = GENSETDYN_P(skabus_rpc_qinfo_t, &a->q, id) ;
+ if (p->status) return (errno = p->status, 0) ;
+ *result = (int)(unsigned char)p->result ;
+ *m = p->message ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_idstr.c b/src/libskabus/skabus_rpc_idstr.c
new file mode 100644
index 0000000..887e100
--- /dev/null
+++ b/src/libskabus/skabus_rpc_idstr.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/tai.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_idstr (skabus_rpc_t *a, char const *idstr, skabus_rpc_interface_t const *ifbody, char const *re, tain_t const *deadline, tain_t *stamp)
+{
+ skabus_rpc_interface_result_t r ;
+ if (!skabus_rpc_idstr_async(a, idstr, ifbody, re, &r)) return 0 ;
+ if (!skaclient_syncify(&a->connection, deadline, stamp)) return 0 ;
+ return r.err ? (errno = r.err, 0) : 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_idstr_async.c b/src/libskabus/skabus_rpc_idstr_async.c
new file mode 100644
index 0000000..9737086
--- /dev/null
+++ b/src/libskabus/skabus_rpc_idstr_async.c
@@ -0,0 +1,31 @@
+/* ISC license. */
+
+#include <string.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <skalibs/uint32.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+int skabus_rpc_idstr_async (skabus_rpc_t *a, char const *idstr, skabus_rpc_interface_t const *ifbody, char const *re, skabus_rpc_interface_result_t *result)
+{
+ size_t idlen = strlen(idstr) ;
+ size_t relen = strlen(re) ;
+ skabus_rpc_ifnode_t *ifnode ;
+ char pack[10] = "S" ;
+ struct iovec v[3] = { { .iov_base = pack, .iov_len = 10 }, { .iov_base = (char *)idstr, .iov_len = idlen+1 }, { .iov_base = (char *)re, .iov_len = relen+1 } } ;
+ if (idlen > SKABUS_RPC_IDSTR_SIZE) return (errno = ENAMETOOLONG, 0) ;
+ if (relen > 0x6ffffffe) return (errno = ENAMETOOLONG, 0) ;
+ if (!gensetdyn_new(&a->r, &a->pmid)) return 0 ;
+ result->ifid = a->pmid ;
+ result->r = &a->r ;
+ ifnode = GENSETDYN_P(skabus_rpc_ifnode_t, &a->r, a->pmid) ;
+ ifnode->name[0] = 0 ;
+ ifnode->body = *ifbody ;
+ uint32_pack_big(pack+1, a->pmid) ;
+ pack[5] = (unsigned char)idlen ;
+ uint32_pack_big(pack+6, relen) ;
+ return skaclient_putv(&a->connection, v, 3, &skabus_rpc_interface_register_cb, result) ;
+}
diff --git a/src/libskabus/skabus_rpc_interface_register.c b/src/libskabus/skabus_rpc_interface_register.c
new file mode 100644
index 0000000..5978a7b
--- /dev/null
+++ b/src/libskabus/skabus_rpc_interface_register.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/tai.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_interface_register (skabus_rpc_t *a, uint32_t *ifid, char const *ifname, skabus_rpc_interface_t const *ifbody, char const *re, tain_t const *deadline, tain_t *stamp)
+{
+ skabus_rpc_interface_result_t r ;
+ if (!skabus_rpc_interface_register_async(a, ifname, ifbody, re, &r)) return 0 ;
+ if (!skaclient_syncify(&a->connection, deadline, stamp)) return 0 ;
+ if (r.err) return (errno = r.err, 0) ;
+ *ifid = r.ifid ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_interface_register_async.c b/src/libskabus/skabus_rpc_interface_register_async.c
new file mode 100644
index 0000000..986ae5f
--- /dev/null
+++ b/src/libskabus/skabus_rpc_interface_register_async.c
@@ -0,0 +1,30 @@
+/* ISC license. */
+
+#include <string.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <skalibs/uint32.h>
+#include <skalibs/error.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+int skabus_rpc_interface_register_async (skabus_rpc_t *a, char const *ifname, skabus_rpc_interface_t const *ifbody, char const *re, skabus_rpc_interface_result_t *result)
+{
+ size_t ifnamelen = strlen(ifname) ;
+ size_t relen = strlen(re) ;
+ skabus_rpc_ifnode_t *ifnode ;
+ char pack[10] = "I" ;
+ struct iovec v[3] = { { .iov_base = pack, .iov_len = 10 }, { .iov_base = (char *)ifname, .iov_len = ifnamelen + 1 }, { .iov_base = (char *)re, .iov_len = relen + 1 } } ;
+ if (ifnamelen > SKABUS_RPC_INTERFACE_MAXLEN) return (errno = ENAMETOOLONG, 0) ;
+ if (!gensetdyn_new(&a->r, &result->ifid)) return 0 ;
+ result->r = &a->r ;
+ ifnode = GENSETDYN_P(skabus_rpc_ifnode_t, &a->r, result->ifid) ;
+ memcpy(ifnode->name, ifname, ifnamelen + 1) ;
+ ifnode->body = *ifbody ;
+ uint32_pack_big(pack+1, result->ifid) ;
+ pack[5] = (unsigned char)ifnamelen ;
+ uint32_pack_big(pack+6, relen) ;
+ return skaclient_putv(&a->connection, v, 3, &skabus_rpc_interface_register_cb, result) ;
+}
diff --git a/src/libskabus/skabus_rpc_interface_register_cb.c b/src/libskabus/skabus_rpc_interface_register_cb.c
new file mode 100644
index 0000000..88d27e7
--- /dev/null
+++ b/src/libskabus/skabus_rpc_interface_register_cb.c
@@ -0,0 +1,17 @@
+ /* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/unixmessage.h>
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+int skabus_rpc_interface_register_cb (unixmessage_t const *m, void *p)
+{
+ skabus_rpc_interface_result_t *r = p ;
+ if (m->len != 1 || m->nfds) return (errno = EPROTO, 0) ;
+ r->err = m->s[0] ;
+ if (r->err) gensetdyn_delete(r->r, r->ifid) ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_interface_unregister.c b/src/libskabus/skabus_rpc_interface_unregister.c
new file mode 100644
index 0000000..9263c0c
--- /dev/null
+++ b/src/libskabus/skabus_rpc_interface_unregister.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/tai.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_interface_unregister (skabus_rpc_t *a, uint32_t ifid, tain_t const *deadline, tain_t *stamp)
+{
+ skabus_rpc_interface_result_t r ;
+ if (!skabus_rpc_interface_unregister_async(a, ifid, &r)) return 0 ;
+ if (!skaclient_syncify(&a->connection, deadline, stamp)) return 0 ;
+ if (r.err) return (errno = r.err, 0) ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_interface_unregister_async.c b/src/libskabus/skabus_rpc_interface_unregister_async.c
new file mode 100644
index 0000000..54a05ba
--- /dev/null
+++ b/src/libskabus/skabus_rpc_interface_unregister_async.c
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#include <string.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+static int skabus_rpc_interface_unregister_cb (unixmessage_t const *m, void *p)
+{
+ skabus_rpc_interface_result_t *r = p ;
+ if (m->len != 1 || m->nfds) return (errno = EPROTO, 0) ;
+ r->err = m->s[0] ;
+ return r->err ? 1 : gensetdyn_delete(r->r, r->ifid) ;
+}
+
+int skabus_rpc_interface_unregister_async (skabus_rpc_t *a, uint32_t ifid, skabus_rpc_interface_result_t *result)
+{
+ char *name = GENSETDYN_P(skabus_rpc_ifnode_t, &a->r, ifid)->name ;
+ size_t n = strlen(name) ;
+ char pack[2] = { 'i', (unsigned char)n } ;
+ struct iovec v[2] = { { .iov_base = pack, .iov_len = 2 }, { .iov_base = name, .iov_len = n+1 } } ;
+ result->r = &a->r ;
+ result->ifid = ifid ;
+ return skaclient_putv(&a->connection, v, 2, &skabus_rpc_interface_unregister_cb, result) ;
+}
diff --git a/src/libskabus/skabus_rpc_interface_zero.c b/src/libskabus/skabus_rpc_interface_zero.c
new file mode 100644
index 0000000..df94b97
--- /dev/null
+++ b/src/libskabus/skabus_rpc_interface_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+
+skabus_rpc_interface_t const skabus_rpc_interface_zero = SKABUS_RPC_INTERFACE_ZERO ;
diff --git a/src/libskabus/skabus_rpc_qinfo_zero.c b/src/libskabus/skabus_rpc_qinfo_zero.c
new file mode 100644
index 0000000..257f34b
--- /dev/null
+++ b/src/libskabus/skabus_rpc_qinfo_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+
+skabus_rpc_qinfo_t const skabus_rpc_qinfo_zero = SKABUS_RPC_QINFO_ZERO ;
diff --git a/src/libskabus/skabus_rpc_r_notimpl.c b/src/libskabus/skabus_rpc_r_notimpl.c
new file mode 100644
index 0000000..8ec3922
--- /dev/null
+++ b/src/libskabus/skabus_rpc_r_notimpl.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_r_notimpl (skabus_rpc_t *a, skabus_rpc_rinfo_t const *info, unixmessage_t const *m, void *data)
+{
+ (void)m ;
+ (void)data ;
+ return skabus_rpc_reply(a, info->serial, ENOSYS, "", 0) ;
+}
diff --git a/src/libskabus/skabus_rpc_rcancel_ignore.c b/src/libskabus/skabus_rpc_rcancel_ignore.c
new file mode 100644
index 0000000..9697201
--- /dev/null
+++ b/src/libskabus/skabus_rpc_rcancel_ignore.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+
+int skabus_rpc_rcancel_ignore (uint64 serial, char reason, void *data)
+{
+ (void)serial ;
+ (void)reason ;
+ (void)data ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_release.c b/src/libskabus/skabus_rpc_release.c
new file mode 100644
index 0000000..d0a6875
--- /dev/null
+++ b/src/libskabus/skabus_rpc_release.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <errno.h>
+#include <skalibs/uint64.h>
+#include <skalibs/alloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_release (skabus_rpc_t *a, uint64_t serial)
+{
+ uint32_t id ;
+ skabus_rpc_qinfo_t *p ;
+ if (!avltree_search(&a->qmap, &serial, &id)) return 0 ;
+ p = GENSETDYN_P(skabus_rpc_qinfo_t, &a->q, id) ;
+ if (p->status) return (errno = p->status, 0) ;
+ alloc_free(p->message.s) ;
+ alloc_free(p->message.fds) ;
+ /* fds are purposefully left open for the client. */
+ p->status = EINVAL ;
+ avltree_delete(&a->qmap, &serial) ;
+ gensetdyn_delete(&a->q, id) ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_reply.c b/src/libskabus/skabus_rpc_reply.c
new file mode 100644
index 0000000..491772b
--- /dev/null
+++ b/src/libskabus/skabus_rpc_reply.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <sys/uio.h>
+#include <skalibs/uint64.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_reply_withfds (skabus_rpc_t *a, uint64_t serial, char result, char const *s, size_t len, int const *fds, unsigned int nfds, unsigned char const *bits)
+{
+ char pack[10] = "R" ;
+ struct iovec v[2] = { { .iov_base = pack, .iov_len = 10 }, { .iov_base = (char *)s, .iov_len = len } } ;
+ unixmessage_v_t m = { .v = v, .vlen = 2, .fds = (int *)fds, .nfds = nfds } ;
+ uint64_pack_big(pack+1, serial) ;
+ pack[9] = result ;
+ return skaclient_aputv_and_close(&a->connection, &m, bits) ;
+}
diff --git a/src/libskabus/skabus_rpc_replyv.c b/src/libskabus/skabus_rpc_replyv.c
new file mode 100644
index 0000000..ef727e9
--- /dev/null
+++ b/src/libskabus/skabus_rpc_replyv.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <sys/uio.h>
+#include <skalibs/uint64.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_replyv_withfds (skabus_rpc_t *a, uint64_t serial, char result, struct iovec const *v, unsigned int vlen, int const *fds, unsigned int nfds, unsigned char const *bits)
+{
+ char pack[10] = "R" ;
+ struct iovec vv[vlen + 1] ;
+ unixmessage_v_t m = { .v = vv, .vlen = vlen+1, .fds = (int *)fds, .nfds = nfds } ;
+ vv[0].iov_base = pack ; vv[0].iov_len = 10 ;
+ for (unsigned int i = 0 ; i < vlen ; i++) vv[1+i] = v[i] ;
+ uint64_pack_big(pack+1, serial) ;
+ pack[9] = result ;
+ return skaclient_aputv_and_close(&a->connection, &m, bits) ;
+}
diff --git a/src/libskabus/skabus_rpc_rinfo_pack.c b/src/libskabus/skabus_rpc_rinfo_pack.c
new file mode 100644
index 0000000..4e0d34c
--- /dev/null
+++ b/src/libskabus/skabus_rpc_rinfo_pack.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <string.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/types.h>
+#include <skalibs/tai.h>
+#include <skabus/rpc.h>
+
+void skabus_rpc_rinfo_pack (char *s, skabus_rpc_rinfo_t const *ri)
+{
+ uint64_pack_big(s, ri->serial) ; s += 8 ;
+ tain_pack(s, &ri->limit) ; s += TAIN_PACK ;
+ tain_pack(s, &ri->timestamp) ; s += TAIN_PACK ;
+ uid_pack_big(s, ri->uid) ; s += UID_PACK ;
+ gid_pack_big(s, ri->gid) ; s += GID_PACK ;
+ memcpy(s, ri->idstr, SKABUS_RPC_IDSTR_SIZE + 1) ;
+}
diff --git a/src/libskabus/skabus_rpc_rinfo_unpack.c b/src/libskabus/skabus_rpc_rinfo_unpack.c
new file mode 100644
index 0000000..ed04219
--- /dev/null
+++ b/src/libskabus/skabus_rpc_rinfo_unpack.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <string.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/types.h>
+#include <skalibs/tai.h>
+#include <skabus/rpc.h>
+
+void skabus_rpc_rinfo_unpack (char const *s, skabus_rpc_rinfo_t *ri)
+{
+ uint64_unpack_big(s, &ri->serial) ; s += 8 ;
+ tain_unpack(s, &ri->limit) ; s += TAIN_PACK ;
+ tain_unpack(s, &ri->timestamp) ; s += TAIN_PACK ;
+ uid_unpack_big(s, &ri->uid) ; s += UID_PACK ;
+ gid_unpack_big(s, &ri->gid) ; s += GID_PACK ;
+ memcpy(ri->idstr, s, SKABUS_RPC_IDSTR_SIZE + 1) ;
+}
diff --git a/src/libskabus/skabus_rpc_rinfo_zero.c b/src/libskabus/skabus_rpc_rinfo_zero.c
new file mode 100644
index 0000000..57be272
--- /dev/null
+++ b/src/libskabus/skabus_rpc_rinfo_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+
+skabus_rpc_rinfo_t const skabus_rpc_rinfo_zero = SKABUS_RPC_RINFO_ZERO ;
diff --git a/src/libskabus/skabus_rpc_send.c b/src/libskabus/skabus_rpc_send.c
new file mode 100644
index 0000000..aa0131c
--- /dev/null
+++ b/src/libskabus/skabus_rpc_send.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+uint64_t skabus_rpc_send_withfds (skabus_rpc_t *a, char const *ifname, char const *s, size_t len, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, tain_t const *deadline, tain_t *stamp)
+{
+ return skabus_rpc_sendq_withfds(a, 0, 0, ifname, s, len, fds, nfds, bits, limit, deadline, stamp) ;
+}
diff --git a/src/libskabus/skabus_rpc_send_async.c b/src/libskabus/skabus_rpc_send_async.c
new file mode 100644
index 0000000..7fc29d2
--- /dev/null
+++ b/src/libskabus/skabus_rpc_send_async.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+int skabus_rpc_send_withfds_async (skabus_rpc_t *a, char const *ifname, char const *s, size_t len, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, skabus_rpc_send_result_t *r)
+{
+ return skabus_rpc_sendq_withfds_async(a, 0, 0, ifname, s, len, fds, nfds, bits, limit, r) ;
+}
diff --git a/src/libskabus/skabus_rpc_send_cb.c b/src/libskabus/skabus_rpc_send_cb.c
new file mode 100644
index 0000000..7aa7fbc
--- /dev/null
+++ b/src/libskabus/skabus_rpc_send_cb.c
@@ -0,0 +1,53 @@
+/* ISC license. */
+
+#include <sys/uio.h>
+#include <errno.h>
+#include <skalibs/uint64.h>
+#include <skalibs/error.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+static int autocancel_cb (unixmessage_t const *m, void *p)
+{
+ (void)m ;
+ (void)p ;
+ return 1 ;
+}
+
+int skabus_rpc_send_cb (unixmessage_t const *m, void *p)
+{
+ skabus_rpc_send_result_t *r = p ;
+ skabus_rpc_qinfo_t *info = GENSETDYN_P(skabus_rpc_qinfo_t, &r->a->q, r->i) ;
+ if (!m->len) return (errno = EPROTO, 0) ;
+ if (m->s[0])
+ {
+ r->err = m->s[0] ;
+ r->u = 0 ;
+ info->status = EINVAL ;
+ gensetdyn_delete(&r->a->q, r->i) ;
+ return 1 ;
+ }
+ if (m->len != 9) return (errno = EPROTO, 0) ;
+ uint64_unpack_big(m->s+1, &info->serial) ;
+ if (!avltree_insert(&r->a->qmap, r->i))
+ {
+ /* the client can't store the info but the server is performing the query,
+ so we try to send a stealthy cancel */
+ char what = 'C' ;
+ struct iovec v[2] = { { .iov_base = &what, .iov_len = 1 }, { .iov_base = m->s + 1, .iov_len = 8 } } ;
+ r->err = errno ;
+ r->u = 0 ;
+ info->status = EINVAL ;
+ gensetdyn_delete(&r->a->q, r->i) ;
+ if (skaclient_putv(&r->a->connection, v, 2, &autocancel_cb, 0))
+ skaclient_flush(&r->a->connection) ;
+ return 1 ;
+ }
+ r->u = info->serial ;
+ info->status = EBUSY ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_sendpm.c b/src/libskabus/skabus_rpc_sendpm.c
new file mode 100644
index 0000000..5f175e4
--- /dev/null
+++ b/src/libskabus/skabus_rpc_sendpm.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+uint64_t skabus_rpc_sendpm_withfds (skabus_rpc_t *a, char const *cname, char const *s, size_t len, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, tain_t const *deadline, tain_t *stamp)
+{
+ return skabus_rpc_sendq_withfds(a, "\xff", 1, cname, s, len, fds, nfds, bits, limit, deadline, stamp) ;
+}
diff --git a/src/libskabus/skabus_rpc_sendpm_async.c b/src/libskabus/skabus_rpc_sendpm_async.c
new file mode 100644
index 0000000..b1b09d4
--- /dev/null
+++ b/src/libskabus/skabus_rpc_sendpm_async.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+int skabus_rpc_sendpm_withfds_async (skabus_rpc_t *a, char const *cname, char const *s, size_t len, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, skabus_rpc_send_result_t *r)
+{
+ return skabus_rpc_sendq_withfds_async(a, "\xff", 1, cname, s, len, fds, nfds, bits, limit, r) ;
+}
diff --git a/src/libskabus/skabus_rpc_sendq.c b/src/libskabus/skabus_rpc_sendq.c
new file mode 100644
index 0000000..06944fa
--- /dev/null
+++ b/src/libskabus/skabus_rpc_sendq.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+uint64_t skabus_rpc_sendq_withfds (skabus_rpc_t *a, char const *prefix, size_t plen, char const *ifname, char const *s, size_t len, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, tain_t const *deadline, tain_t *stamp)
+{
+ skabus_rpc_send_result_t r ;
+ if (!skabus_rpc_sendq_withfds_async(a, prefix, plen, ifname, s, len, fds, nfds, bits, limit, &r)) return 0 ;
+ if (!skaclient_syncify(&a->connection, deadline, stamp)) return 0 ;
+ return r.err ? (errno = r.err, 0) : r.u ;
+}
diff --git a/src/libskabus/skabus_rpc_sendq_async.c b/src/libskabus/skabus_rpc_sendq_async.c
new file mode 100644
index 0000000..b579291
--- /dev/null
+++ b/src/libskabus/skabus_rpc_sendq_async.c
@@ -0,0 +1,34 @@
+/* ISC license. */
+
+#include <string.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <skalibs/tai.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+int skabus_rpc_sendq_withfds_async (skabus_rpc_t *a, char const *prefix, size_t plen, char const *ifname, char const *s, size_t len, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, skabus_rpc_send_result_t *r)
+{
+ size_t iflen = strlen(ifname) ;
+ char pack[2 + TAIN_PACK] = "Q" ;
+ struct iovec v[4] = { { .iov_base = pack, .iov_len = 2 + TAIN_PACK }, { .iov_base = (char *)prefix, .iov_len = plen }, { .iov_base = (char *)ifname, .iov_len = iflen + 1 }, { .iov_base = (char *)s, .iov_len = len } } ;
+ unixmessage_v_t m = { .v = v, .vlen = 4, .fds = (int *)fds, .nfds = nfds } ;
+ iflen += plen ;
+ if (iflen > SKABUS_RPC_INTERFACE_MAXLEN) return (errno = ENAMETOOLONG, 0) ;
+ if (!gensetdyn_new(&a->q, &r->i)) return 0 ;
+ r->a = a ;
+ if (limit) tain_pack(pack + 1, limit) ; else memset(pack + 1, 0, TAIN_PACK) ;
+ pack[1 + TAIN_PACK] = (unsigned char)iflen ;
+ if (!skaclient_putmsgv_and_close(&a->connection, &m, bits, &skabus_rpc_send_cb, r))
+ {
+ int e = errno ;
+ gensetdyn_delete(&a->q, r->i) ;
+ errno = e ;
+ return 0 ;
+ }
+ GENSETDYN_P(skabus_rpc_qinfo_t, &a->q, r->i)->status = EINPROGRESS ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_sendv.c b/src/libskabus/skabus_rpc_sendv.c
new file mode 100644
index 0000000..24d598d
--- /dev/null
+++ b/src/libskabus/skabus_rpc_sendv.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+uint64_t skabus_rpc_sendv_withfds (skabus_rpc_t *a, char const *ifname, struct iovec const *v, unsigned int vlen, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, tain_t const *deadline, tain_t *stamp)
+{
+ return skabus_rpc_sendvq_withfds(a, 0, 0, ifname, v, vlen, fds, nfds, bits, limit, deadline, stamp) ;
+}
diff --git a/src/libskabus/skabus_rpc_sendv_async.c b/src/libskabus/skabus_rpc_sendv_async.c
new file mode 100644
index 0000000..c9b7910
--- /dev/null
+++ b/src/libskabus/skabus_rpc_sendv_async.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+int skabus_rpc_sendv_withfds_async (skabus_rpc_t *a, char const *ifname, struct iovec const *v, unsigned int vlen, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, skabus_rpc_send_result_t *r)
+{
+ return skabus_rpc_sendvq_withfds_async(a, 0, 0, ifname, v, vlen, fds, nfds, bits, limit, r) ;
+}
diff --git a/src/libskabus/skabus_rpc_sendvpm.c b/src/libskabus/skabus_rpc_sendvpm.c
new file mode 100644
index 0000000..8ca74e6
--- /dev/null
+++ b/src/libskabus/skabus_rpc_sendvpm.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+uint64_t skabus_rpc_sendvpm_withfds (skabus_rpc_t *a, char const *cname, struct iovec const *v, unsigned int vlen, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, tain_t const *deadline, tain_t *stamp)
+{
+ return skabus_rpc_sendvq_withfds(a, "\xff", 1, cname, v, vlen, fds, nfds, bits, limit, deadline, stamp) ;
+}
diff --git a/src/libskabus/skabus_rpc_sendvpm_async.c b/src/libskabus/skabus_rpc_sendvpm_async.c
new file mode 100644
index 0000000..090423c
--- /dev/null
+++ b/src/libskabus/skabus_rpc_sendvpm_async.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+int skabus_rpc_sendvpm_withfds_async (skabus_rpc_t *a, char const *cname, struct iovec const *v, unsigned int vlen, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, skabus_rpc_send_result_t *r)
+{
+ return skabus_rpc_sendvq_withfds_async(a, "\xff", 1, cname, v, vlen, fds, nfds, bits, limit, r) ;
+}
diff --git a/src/libskabus/skabus_rpc_sendvq.c b/src/libskabus/skabus_rpc_sendvq.c
new file mode 100644
index 0000000..3cd0e3a
--- /dev/null
+++ b/src/libskabus/skabus_rpc_sendvq.c
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+uint64_t skabus_rpc_sendvq_withfds (skabus_rpc_t *a, char const *prefix, size_t plen, char const *ifname, struct iovec const *v, unsigned int vlen, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, tain_t const *deadline, tain_t *stamp)
+{
+ skabus_rpc_send_result_t r ;
+ if (!skabus_rpc_sendvq_withfds_async(a, prefix, plen, ifname, v, vlen, fds, nfds, bits, limit, &r)) return 0 ;
+ if (!skaclient_syncify(&a->connection, deadline, stamp)) return 0 ;
+ return r.err ? (errno = r.err, 0) : r.u ;
+}
diff --git a/src/libskabus/skabus_rpc_sendvq_async.c b/src/libskabus/skabus_rpc_sendvq_async.c
new file mode 100644
index 0000000..97117ea
--- /dev/null
+++ b/src/libskabus/skabus_rpc_sendvq_async.c
@@ -0,0 +1,38 @@
+/* ISC license. */
+
+#include <string.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <skalibs/tai.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+#include "skabus-rpc-internal.h"
+
+int skabus_rpc_sendvq_withfds_async (skabus_rpc_t *a, char const *prefix, size_t plen, char const *ifname, struct iovec const *v, unsigned int vlen, int const *fds, unsigned int nfds, unsigned char const *bits, tain_t const *limit, skabus_rpc_send_result_t *r)
+{
+ size_t iflen = strlen(ifname) ;
+ char pack[2 + TAIN_PACK] = "Q" ;
+ struct iovec vv[vlen + 3] ;
+ unixmessage_v_t m = { .v = vv, .vlen = vlen + 3, .fds = (int *)fds, .nfds = nfds } ;
+ iflen += plen ;
+ if (iflen > SKABUS_RPC_INTERFACE_MAXLEN) return (errno = ENAMETOOLONG, 0) ;
+ if (!gensetdyn_new(&a->q, &r->i)) return 0 ;
+ vv[0].iov_base = pack ; vv[0].iov_len = 2 + TAIN_PACK ;
+ vv[1].iov_base = (char *)prefix ; vv[1].iov_len = plen ;
+ vv[2].iov_base = (char *)ifname ; vv[2].iov_len = iflen + 1 ;
+ for (unsigned int i = 0 ; i < vlen ; i++) vv[3 + i] = v[i] ;
+ r->a = a ;
+ if (limit) tain_pack(pack + 1, limit) ; else memset(pack + 1, 0, TAIN_PACK) ;
+ pack[1 + TAIN_PACK] = (unsigned char)iflen ;
+ if (!skaclient_putmsgv_and_close(&a->connection, &m, bits, &skabus_rpc_send_cb, r))
+ {
+ int e = errno ;
+ gensetdyn_delete(&a->q, r->i) ;
+ errno = e ;
+ return 0 ;
+ }
+ GENSETDYN_P(skabus_rpc_qinfo_t, &a->q, r->i)->status = EINPROGRESS ;
+ return 1 ;
+}
diff --git a/src/libskabus/skabus_rpc_start.c b/src/libskabus/skabus_rpc_start.c
new file mode 100644
index 0000000..8e9c99b
--- /dev/null
+++ b/src/libskabus/skabus_rpc_start.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_start (skabus_rpc_t *a, char const *path, tain_t const *deadline, tain_t *stamp)
+{
+ return skaclient_start_b(
+ &a->connection,
+ &a->buffers,
+ path,
+ SKACLIENT_OPTION_ASYNC_ACCEPT_FDS,
+ SKABUS_RPC_BANNER1,
+ SKABUS_RPC_BANNER1_LEN,
+ SKABUS_RPC_BANNER2,
+ SKABUS_RPC_BANNER2_LEN,
+ deadline,
+ stamp) ;
+}
diff --git a/src/libskabus/skabus_rpc_start_async.c b/src/libskabus/skabus_rpc_start_async.c
new file mode 100644
index 0000000..d5b3e8c
--- /dev/null
+++ b/src/libskabus/skabus_rpc_start_async.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+int skabus_rpc_start_async (skabus_rpc_t *a, char const *path, skabus_rpc_start_result_t *data)
+{
+ return skaclient_start_async_b(
+ &a->connection,
+ &a->buffers,
+ path,
+ SKACLIENT_OPTION_ASYNC_ACCEPT_FDS,
+ SKABUS_RPC_BANNER1,
+ SKABUS_RPC_BANNER1_LEN,
+ SKABUS_RPC_BANNER2,
+ SKABUS_RPC_BANNER2_LEN,
+ &data->skaclient_cbdata) ;
+}
diff --git a/src/libskabus/skabus_rpc_update.c b/src/libskabus/skabus_rpc_update.c
new file mode 100644
index 0000000..d727de1
--- /dev/null
+++ b/src/libskabus/skabus_rpc_update.c
@@ -0,0 +1,120 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/alloc.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+
+typedef int localhandler_func_t (skabus_rpc_t *, unixmessage_t *) ;
+typedef localhandler_func_t *localhandler_func_t_ref ;
+
+static int skabus_rpc_serve (skabus_rpc_t *a, unixmessage_t *m)
+{
+ skabus_rpc_rinfo_t rinfo ;
+ uint32_t ifid ;
+ skabus_rpc_interface_t *p ;
+ if (m->len < 4 + SKABUS_RPC_RINFO_PACK) return (errno = EPROTO, 0) ;
+ uint32_unpack_big(m->s, &ifid) ; m->s += 4 ; m->len -= 4 ;
+ skabus_rpc_rinfo_unpack(m->s, &rinfo) ;
+ m->s += SKABUS_RPC_RINFO_PACK ; m->len -= SKABUS_RPC_RINFO_PACK ;
+ p = GENSETDYN_P(skabus_rpc_interface_t, &a->r, ifid) ;
+ if (!p) return (errno = ESRCH, 0) ;
+ return (*p->f)(a, &rinfo, m, p->data) ;
+}
+
+static int skabus_rpc_cancelr (skabus_rpc_t *a, unixmessage_t *m)
+{
+ uint64_t serial ;
+ uint32_t ifid ;
+ skabus_rpc_interface_t *p ;
+ if (m->len != 13 || m->nfds) return (errno = EPROTO, 0) ;
+ uint32_unpack_big(m->s, &ifid) ;
+ uint64_unpack_big(m->s+4, &serial) ;
+ p = GENSETDYN_P(skabus_rpc_interface_t, &a->r, ifid) ;
+ if (!p) return (errno = ESRCH, 0) ;
+ return (*p->cancelf)(serial, m->s[12], p->data) ;
+}
+
+static int skabus_rpc_handle_reply (skabus_rpc_t *a, unixmessage_t *m)
+{
+ skabus_rpc_qinfo_t *p ;
+ uint64_t serial ;
+ uint32_t id ;
+ if (m->len < 9) return (errno = EPROTO, 0) ;
+ uint64_unpack_big(m->s, &serial) ;
+ if (!avltree_search(&a->qmap, &serial, &id))
+ {
+ unixmessage_drop(m) ;
+ return 1 ;
+ }
+ p = GENSETDYN_P(skabus_rpc_qinfo_t, &a->q, id) ;
+ if (!genalloc_readyplus(uint64_t, &a->qlist, 1)) return 0 ;
+ if (!m->s[8])
+ {
+ if (m->len < 10) return (errno = EPROTO, 0) ;
+ p->message.s = alloc(m->len - 10) ;
+ if (!p->message.s) return 0 ;
+ p->message.fds = (int *)alloc(m->nfds * sizeof(int)) ;
+ if (!p->message.fds)
+ {
+ alloc_free(p->message.s) ;
+ p->message.s = 0 ;
+ return 0 ;
+ }
+ p->result = m->s[9] ;
+ p->message.len = m->len - 10 ;
+ p->message.nfds = m->nfds ;
+ memcpy(p->message.s, m->s + 10, p->message.len) ;
+ memcpy(p->message.fds, m->fds, p->message.nfds * sizeof(int)) ;
+ }
+ p->status = m->s[8] ;
+ genalloc_append(uint64_t, &a->qlist, &serial) ;
+ return 1 ;
+}
+
+static int skabus_rpc_error (skabus_rpc_t *a, unixmessage_t *m)
+{
+ (void)a ;
+ (void)m ;
+ return (errno = EPROTO, 0) ;
+}
+
+static int handler (unixmessage_t const *m, void *x)
+{
+ skabus_rpc_t *a = x ;
+ unixmessage_t mm = { .s = m->s + 1, .len = m->len - 1, .fds = m->fds, .nfds = m->nfds } ;
+ static localhandler_func_t_ref const f[4] =
+ {
+ &skabus_rpc_serve,
+ &skabus_rpc_handle_reply,
+ &skabus_rpc_cancelr,
+ &skabus_rpc_error
+ } ;
+ if (!m->len)
+ {
+ unixmessage_drop(m) ;
+ return (errno = EPROTO, 0) ;
+ }
+ if (!(*f[byte_chr("QRC", 3, m->s[0])])(a, &mm))
+ {
+ unixmessage_drop(m) ;
+ return 0 ;
+ }
+ return 1 ;
+}
+
+int skabus_rpc_update (skabus_rpc_t *a)
+{
+ genalloc_setlen(uint64_t, &a->qlist, 0) ;
+ return skaclient_update(&a->connection, &handler, a) ;
+}
diff --git a/src/libskabus/skabus_rpc_zero.c b/src/libskabus/skabus_rpc_zero.c
new file mode 100644
index 0000000..b9db269
--- /dev/null
+++ b/src/libskabus/skabus_rpc_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <skabus/rpc.h>
+
+skabus_rpc_t const skabus_rpc_zero = SKABUS_RPC_ZERO ;
diff --git a/src/rpc/PROTOCOL b/src/rpc/PROTOCOL
new file mode 100644
index 0000000..4022556
--- /dev/null
+++ b/src/rpc/PROTOCOL
@@ -0,0 +1,82 @@
+
+ Registering (1st connection) (client -> server)
+
+ 'S' : 1
+ pmid : 4
+ idlen : 1
+ relen : 4
+ idstr : idlen
+ '\0' : 1
+ re : relen
+ '\0' : 1
+
+ Sending a query (qclient -> server)
+
+ 'Q' : 1
+ deadline : TAIN_PACK (12)
+ contains sec(8), nano(4)
+ iflen : 1
+ ifname : iflen
+ '\0' : 1
+ msg : msglen
+
+
+ Sending a query (server -> rclient)
+
+ 'Q' : 1
+ ifid : 4
+ rinfo: SKABUS_RPC_RINFO_PACK
+ contains serial(8), deadline(12), timestamp(12), uid(4), gid(4), idstr(SKABUS_RPC_IDSTR_SIZE), '\0'(1)
+ msg : msglen
+
+
+ Sending a reply (rclient -> server)
+
+ 'R' : 1
+ serial : 8
+ result : 1
+ msg : msglen
+
+
+ Sending a reply (server -> qclient)
+
+ 'R' : 1
+ serial : 8
+ status : 1. If not '\0', the rest is ignored.
+ result : 1
+ msg : msglen
+
+
+ Cancelling a query (qclient -> server)
+
+ 'C' : 1
+ serial : 8
+
+
+ Cancelling a query (server -> rclient)
+
+ 'C' : 1
+ ifid : 4
+ serial : 8
+ reason : 1
+
+
+ Registering an interface (client -> server)
+
+ 'I' : 1
+ ifid : 4
+ iflen : 1
+ relen : 4
+ ifname : iflen
+ '\0' : 1
+ re : relen
+ '\0' : 1
+
+
+ Unregistering an interface (client -> server)
+
+ 'i' : 1
+ iflen : 1
+ ifname : iflen
+ '\0' : 1
+
diff --git a/src/rpc/deps-exe/skabus-rpc-daemon b/src/rpc/deps-exe/skabus-rpc-daemon
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/rpc/deps-exe/skabus-rpc-daemon
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/rpc/deps-exe/skabus-rpcd b/src/rpc/deps-exe/skabus-rpcd
new file mode 100644
index 0000000..a4a573f
--- /dev/null
+++ b/src/rpc/deps-exe/skabus-rpcd
@@ -0,0 +1,8 @@
+skabus_rpcd_client.o
+skabus_rpcd_interface.o
+skabus_rpcd_query.o
+libskabus.a.xyzzy
+-ls6
+-lskarnet
+${TAINNOW_LIB}
+${SOCKET_LIB}
diff --git a/src/rpc/skabus-rpc-daemon.c b/src/rpc/skabus-rpc-daemon.c
new file mode 100644
index 0000000..20c5eea
--- /dev/null
+++ b/src/rpc/skabus-rpc-daemon.c
@@ -0,0 +1,154 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <skalibs/types.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <s6/config.h>
+#include <skabus/config.h>
+
+#define USAGE "skabus-rpc-daemon [ -v verbosity ] [ -d | -D ] [ -1 ] [ -c maxconn ] [ -b backlog ] [ -G gid,gid,... ] [ -g gid ] [ -u uid ] [ -U ] [ -t timeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ] [ -S | -s ] [ -J | -j ] path"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int verbosity = 1 ;
+ int flag1 = 0 ;
+ int flagU = 0 ;
+ int flagreuse = 1 ;
+ uid_t uid = 0 ;
+ gid_t gid = 0 ;
+ gid_t gids[NGROUPS_MAX] ;
+ size_t gidn = (unsigned int)-1 ;
+ unsigned int maxconn = 0 ;
+ unsigned int backlog = (unsigned int)-1 ;
+ int flagpublic = 0 ;
+ int flagifpublic = 0 ;
+ unsigned int timeout = 0 ;
+ unsigned int ltimeout = 0 ;
+ char const *rulesdir = 0 ;
+ char const *rulesfile = 0 ;
+ PROG = "skabus-rpc-daemon" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "Dd1USsJjv:c:b:u:g:G:t:T:i:x:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'D' : flagreuse = 0 ; break ;
+ case 'd' : flagreuse = 1 ; break ;
+ case '1' : flag1 = 1 ; break ;
+ case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ;
+ case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; if (!maxconn) maxconn = 1 ; break ;
+ case 'b' : if (!uint0_scan(l.arg, &backlog)) dieusage() ; break ;
+ case 'u' : if (!uid0_scan(l.arg, &uid)) dieusage() ; break ;
+ case 'g' : if (!gid0_scan(l.arg, &gid)) dieusage() ; break ;
+ case 'G' : if (!gid_scanlist(gids, NGROUPS_MAX, l.arg, &gidn) && *l.arg) dieusage() ; break ;
+ case 'U' : flagU = 1 ; uid = 0 ; gid = 0 ; gidn = (size_t)-1 ; break ;
+ case 'S' : flagpublic = 0 ; break ;
+ case 's' : flagpublic = 1 ; break ;
+ case 'J' : flagifpublic = 0 ; break ;
+ case 'j' : flagifpublic = 1 ; break ;
+ case 't' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ;
+ case 'T' : if (!uint0_scan(l.arg, &ltimeout)) dieusage() ; break ;
+ case 'i' : rulesdir = l.arg ; rulesfile = 0 ; break ;
+ case 'x' : rulesfile = l.arg ; rulesdir = 0 ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (!argc) dieusage() ;
+ }
+
+ {
+ unsigned int m = 0, pos = 0 ;
+ char const *newargv[30] ;
+ char fmt[UINT_FMT * 6 + UID_FMT + GID_FMT * (1 + NGROUPS_MAX)] ;
+ newargv[m++] = S6_EXTBINPREFIX "s6-ipcserver-socketbinder" ;
+ if (!flagreuse) newargv[m++] = "-D" ;
+ if (backlog != (unsigned int)-1)
+ {
+ newargv[m++] = "-b" ;
+ newargv[m++] = fmt + pos ;
+ pos += uint_fmt(fmt + pos, backlog) ;
+ fmt[pos++] = 0 ;
+ }
+ newargv[m++] = "--" ;
+ newargv[m++] = *argv++ ;
+ if (flagU || uid || gid || gidn != (size_t)-1)
+ {
+ newargv[m++] = S6_EXTBINPREFIX "s6-applyuidgid" ;
+ if (flagU) newargv[m++] = "-Uz" ;
+ if (uid)
+ {
+ newargv[m++] = "-u" ;
+ newargv[m++] = fmt + pos ;
+ pos += uint_fmt(fmt + pos, uid) ;
+ fmt[pos++] = 0 ;
+ }
+ if (gid)
+ {
+ newargv[m++] = "-g" ;
+ newargv[m++] = fmt + pos ;
+ pos += uint_fmt(fmt + pos, gid) ;
+ fmt[pos++] = 0 ;
+ }
+ if (gidn != (size_t)-1)
+ {
+ newargv[m++] = "-G" ;
+ newargv[m++] = fmt + pos ;
+ pos += gid_fmtlist(fmt + pos, gids, gidn) ;
+ fmt[pos++] = 0 ;
+ }
+ newargv[m++] = "--" ;
+ }
+ newargv[m++] = SKABUS_BINPREFIX "skabus-rpcd" ;
+ if (verbosity != 1)
+ {
+ newargv[m++] = "-v" ;
+ newargv[m++] = fmt + pos ;
+ pos += uint_fmt(fmt + pos, verbosity) ;
+ fmt[pos++] = 0 ;
+ }
+ if (flag1) newargv[m++] = "-1" ;
+ if (flagpublic) newargv[m++] = "-s" ;
+ if (flagifpublic) newargv[m++] = "-j" ;
+ if (maxconn)
+ {
+ newargv[m++] = "-c" ;
+ newargv[m++] = fmt + pos ;
+ pos += uint_fmt(fmt + pos, maxconn) ;
+ fmt[pos++] = 0 ;
+ }
+ if (timeout)
+ {
+ newargv[m++] = "-t" ;
+ newargv[m++] = fmt + pos ;
+ pos += uint_fmt(fmt + pos, timeout) ;
+ fmt[pos++] = 0 ;
+ }
+ if (ltimeout)
+ {
+ newargv[m++] = "-T" ;
+ newargv[m++] = fmt + pos ;
+ pos += uint_fmt(fmt + pos, timeout) ;
+ fmt[pos++] = 0 ;
+ }
+ if (rulesdir)
+ {
+ newargv[m++] = "-i" ;
+ newargv[m++] = rulesdir ;
+ }
+ else if (rulesfile)
+ {
+ newargv[m++] = "-x" ;
+ newargv[m++] = rulesfile ;
+ }
+ newargv[m++] = 0 ;
+ xpathexec_run(newargv[0], newargv, envp) ;
+ }
+}
diff --git a/src/rpc/skabus-rpcd.c b/src/rpc/skabus-rpcd.c
new file mode 100644
index 0000000..36c6a78
--- /dev/null
+++ b/src/rpc/skabus-rpcd.c
@@ -0,0 +1,542 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <regex.h>
+#include <errno.h>
+#include <signal.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/types.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/error.h>
+#include <skalibs/cdb.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/selfpipe.h>
+#include <skalibs/sig.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/env.h>
+#include <skalibs/getpeereid.h>
+#include <skalibs/webipc.h>
+#include <skalibs/genset.h>
+#include <skalibs/unixmessage.h>
+#include <s6/accessrules.h>
+#include <skabus/rpc.h>
+#include "skabus-rpcd.h"
+
+#define USAGE "skabus-rpcd [ -v verbosity ] [ -1 ] [ -d | -D ] [ -c maxconn ] [ -t timeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ] [ -S | -s ] [ -J | -j ]"
+#define dieusage() strerr_dieusage(100, USAGE) ;
+
+tain_t answertto = TAIN_INFINITE_RELATIVE ;
+
+static unsigned int verbosity = 1 ;
+static int cont = 1 ;
+static tain_t lameduckdeadline = TAIN_INFINITE_RELATIVE ;
+
+static unsigned int rulestype = 0 ;
+static char const *rules = 0 ;
+static int cdbfd = -1 ;
+static struct cdb cdbmap = CDB_ZERO ;
+static int flagidstrpub = 0, flaginterfacespub = 0 ;
+
+static inline void handle_signals (void)
+{
+ for (;;) switch (selfpipe_read())
+ {
+ case -1 : strerr_diefu1sys(111, "selfpipe_read()") ;
+ case 0 : return ;
+ case SIGTERM :
+ {
+ if (cont)
+ {
+ cont = 0 ;
+ tain_add_g(&lameduckdeadline, &lameduckdeadline) ;
+ }
+ break ;
+ }
+ case SIGHUP :
+ {
+ int fd ;
+ struct cdb c = CDB_ZERO ;
+ if (rulestype != 2) break ;
+ fd = open_readb(rules) ;
+ if (fd < 0) break ;
+ if (cdb_init(&c, fd) < 0)
+ {
+ fd_close(fd) ;
+ break ;
+ }
+ cdb_free(&cdbmap) ;
+ fd_close(cdbfd) ;
+ cdbfd = fd ;
+ cdbmap = c ;
+ }
+ break ;
+ default : break ;
+ }
+}
+
+int parse_protocol_async (unixmessage_t const *m, void *p)
+{
+ uint64_t serial ;
+ uint32_t qq ;
+ unixmessage_t mtosend = { .s = m->s + 10, .len = m->len - 10, .fds = m->fds, .nfds = m->nfds } ;
+ if (m->len < 10 || m->s[0] != 'R')
+ {
+ unixmessage_drop(m) ;
+ return (errno = EPROTO, 0) ;
+ }
+ uint64_unpack_big(m->s + 1, &serial) ;
+ if (!query_lookup_by_serial(serial, &qq))
+ {
+ unixmessage_drop(m) ;
+ return 1 ;
+ }
+ query_reply(qq, m->s[9], &mtosend) ;
+ return 1 ;
+}
+
+typedef int hlparsefunc_t (uint32_t, unixmessage_t *) ;
+typedef hlparsefunc_t *hlparsefunc_t_ref ;
+
+static int answer (uint32_t cc, char e)
+{
+ unixmessage_t m = { .s = &e, .len = 1, .fds = 0, .nfds = 0 } ;
+ client_t *c = CLIENT(cc) ;
+ if (!unixmessage_put(&c->sync.out, &m)) return 0 ;
+ if (client_isregistered(cc)) client_setdeadline(c) ;
+ return 1 ;
+}
+
+static int do_idstr (uint32_t cc, unixmessage_t *m)
+{
+ uint32_t relen, pmid, yy ;
+ unsigned char idlen ;
+ if (m->len < 11 || m->nfds) return (errno = EPROTO, 0) ;
+ uint32_unpack_big(m->s, &pmid) ;
+ idlen = m->s[4] ;
+ uint32_unpack_big(m->s + 5, &relen) ;
+ if (m->len != 11 + idlen + relen || m->s[9 + idlen] || m->s[10 + idlen + relen]) return (errno = EPROTO, 0) ;
+ if (client_isregistered(cc)) return answer(cc, EISCONN) ;
+ if (regexec(&CLIENT(cc)->idstr_re, m->s + 9, 0, 0, 0)) return answer(cc, EPERM) ;
+ m->s[8] = 0xff ;
+ if (interface_lookup_by_name(m->s + 8, &yy)) return answer(cc, EADDRINUSE) ;
+ if (!interface_add(&yy, m->s + 8, idlen + 1, cc, m->s + 10 + idlen, pmid))
+ return answer(cc, errno) ;
+ regfree(&CLIENT(cc)->idstr_re) ;
+ return answer(cc, 0) ;
+}
+
+static int do_interface_register (uint32_t cc, unixmessage_t *m)
+{
+ uint32_t id, relen, yy ;
+ unsigned char iflen ;
+ if (m->len < 11 || m->nfds) return (errno = EPROTO, 0) ;
+ uint32_unpack_big(m->s, &id) ;
+ iflen = m->s[4] ;
+ uint32_unpack_big(m->s + 5, &relen) ;
+ if (m->len != 11 + iflen + relen) return (errno = EPROTO, 0) ;
+ if (m->s[9 + iflen] || m->s[10 + iflen + relen]) return (errno = EPROTO, 0) ;
+ if (!client_isregistered(cc)) return answer(cc, ENOTCONN) ;
+ if (regexec(&CLIENT(cc)->interfaces_re, m->s + 9, 0, 0, 0)) return answer(cc, EPERM) ;
+ if (interface_lookup_by_name(m->s + 9, &yy)) return answer(cc, EADDRINUSE) ;
+ if (!interface_add(&yy, m->s + 9, iflen, cc, m->s + 10 + iflen, id)) return answer(cc, errno) ;
+ return answer(cc, 0) ;
+}
+
+static int do_interface_unregister (uint32_t cc, unixmessage_t *m)
+{
+ uint32_t yy ;
+ unsigned char iflen ;
+ if (!m->len || m->nfds) return (errno = EPROTO, 0) ;
+ iflen = m->s[0] ;
+ if ((m->len != iflen + 2) || m->s[iflen+1]) return (errno = EPROTO, 0) ;
+ if (!client_isregistered(cc)) return answer(cc, ENOTCONN) ;
+ if (!iflen || ((unsigned char const *)m->s)[1] == 0xff) return answer(cc, EINVAL) ;
+ if (!interface_lookup_by_name(m->s + 1, &yy)) return answer(cc, errno) ;
+ if (INTERFACE(yy)->client != cc) return answer(cc, EPERM) ;
+ interface_remove(yy) ;
+ return answer(cc, 0) ;
+}
+
+static int do_query_send (uint32_t cc, unixmessage_t *m)
+{
+ tain_t limit ;
+ uint32_t yy, qq ;
+ unsigned char iflen ;
+ int e ;
+ if (m->len < 2 + TAIN_PACK) return (errno = EPROTO, 0) ;
+ tain_unpack(m->s, &limit) ;
+ iflen = m->s[TAIN_PACK] ;
+ if (m->len < 2 + TAIN_PACK + iflen || m->s[TAIN_PACK + 1 + iflen]) return (errno = EPROTO, 0) ;
+ if (!client_isregistered(cc)) { e = ENOTCONN ; goto nope ; }
+ if (!interface_lookup_by_name(m->s + TAIN_PACK + 1, &yy)) { e = ESRCH ; goto nope ; }
+ if (regexec(&INTERFACE(yy)->re, client_idstr(CLIENT(cc)), 0, 0, 0)) { e = EPERM ; goto nope ; }
+ m->s += 2 + TAIN_PACK + iflen ;
+ m->len -= 2 + TAIN_PACK + iflen ;
+ if (!query_add(&qq, &limit, cc, yy) || !query_send(qq, m)) { e = errno ; goto nope ; }
+ return answer(cc, 0) ;
+
+ nope:
+ if (!answer(cc, e)) return 0 ;
+ unixmessage_drop(m) ;
+ return 1 ;
+}
+
+static int do_query_cancel (uint32_t cc, unixmessage_t *m)
+{
+ uint64_t serial ;
+ uint32_t qq ;
+ if (m->len != 8 || m->nfds) return (errno = EPROTO, 0) ;
+ if (!client_isregistered(cc)) return answer(cc, ENOTCONN) ;
+ uint64_unpack_big(m->s, &serial) ;
+ if (!query_lookup_by_serial(serial, &qq)) return answer(cc, errno) ;
+ if (cc != QUERY(qq)->client) return answer(cc, EPERM) ;
+ if (!query_cancelremove(qq, 0)) return answer(cc, errno) ;
+ return answer(cc, 0) ;
+}
+
+static int do_error (uint32_t cc, unixmessage_t *m)
+{
+ (void)cc ;
+ (void)m ;
+ return (errno = EPROTO, 0) ;
+}
+
+int parse_protocol_sync (unixmessage_t const *m, void *p)
+{
+ static hlparsefunc_t_ref const f[6] =
+ {
+ &do_idstr,
+ &do_interface_register,
+ &do_interface_unregister,
+ &do_query_send,
+ &do_query_cancel,
+ &do_error
+ } ;
+ unixmessage_t mcopy = { .s = m->s + 1, .len = m->len -1, .fds = m->fds, .nfds = m->nfds } ;
+ if (!m->len)
+ {
+ unixmessage_drop(m) ;
+ return (errno = EPROTO, 0) ;
+ }
+ if (!(*f[byte_chr("SIiQC", 5, m->s[0])])(*(uint32_t *)p, &mcopy))
+ {
+ unixmessage_drop(m) ;
+ return 0 ;
+ }
+ return 1 ;
+}
+
+static void removeclient (uint32_t *i, uint32_t j)
+{
+ if (verbosity >= 2)
+ {
+ char fmt[UINT32_FMT] ;
+ fmt[uint32_fmt(fmt, *i)] = 0 ;
+ strerr_warni2sys("removing client ", fmt) ;
+ }
+ client_remove(*i, j) ;
+ *i = j ;
+}
+
+static int makere (regex_t *re, char const *s, char const *var)
+{
+ size_t varlen = strlen(var) ;
+ if (str_start(s, var) && (s[varlen] == '='))
+ {
+ int r = regcomp(re, s + varlen + 1, REG_EXTENDED | REG_NOSUB) ;
+ if (r)
+ {
+ if (verbosity)
+ {
+ char buf[256] ;
+ regerror(r, re, buf, 256) ;
+ strerr_warnw6x("invalid ", var, " value: ", s + varlen + 1, ": ", buf) ;
+ }
+ return -1 ;
+ }
+ else return 1 ;
+ }
+ return 0 ;
+}
+
+static void defaultre (regex_t *re, int flag)
+{
+ char const *s = flag ? ".*" : ".^" ;
+ int r = regcomp(re, s, REG_EXTENDED | REG_NOSUB) ;
+ if (r)
+ {
+ char buf[256] ;
+ regerror(r, re, buf, 256) ;
+ strerr_diefu4x(100, "compile ", s, " into a regular expression: ", buf) ;
+ }
+}
+
+static inline int parse_env (char const *const *envp, regex_t *idstr_re, regex_t *interfaces_re, uint32_t *flags)
+{
+ uint32_t fl = 0 ;
+ int idstr_done = 0, interfaces_done = 0 ;
+ for (; *envp ; envp++)
+ {
+ if (str_start(*envp, "SKABUS_RPC_QSENDFDS=")) fl |= 1 ;
+ if (str_start(*envp, "SKABUS_RPC_RSENDFDS=")) fl |= 2 ;
+ if (!idstr_done)
+ {
+ idstr_done = makere(idstr_re, *envp, "SKABUS_RPC_ID_REGEX") ;
+ if (idstr_done < 0)
+ {
+ if (interfaces_done) regfree(interfaces_re) ;
+ return 0 ;
+ }
+ }
+ if (!interfaces_done)
+ {
+ interfaces_done = makere(interfaces_re, *envp, "SKABUS_RPC_INTERFACES_REGEX") ;
+ if (interfaces_done < 0)
+ {
+ if (idstr_done) regfree(idstr_re) ;
+ return 0 ;
+ }
+ }
+ if (idstr_done && interfaces_done) return 1 ;
+ }
+ if (!idstr_done) defaultre(idstr_re, flagidstrpub) ;
+ if (!interfaces_done) defaultre(interfaces_re, flaginterfacespub) ;
+ *flags = fl ;
+ return 1 ;
+}
+
+static inline int new_connection (int fd, uid_t *uid, gid_t *gid, regex_t *idstr_re, regex_t *interfaces_re, uint32_t *flags)
+{
+ s6_accessrules_params_t params = S6_ACCESSRULES_PARAMS_ZERO ;
+ s6_accessrules_result_t result = S6_ACCESSRULES_ERROR ;
+
+ if (getpeereid(fd, uid, gid) < 0)
+ {
+ if (verbosity) strerr_warnwu1sys("getpeereid") ;
+ return 0 ;
+ }
+
+ switch (rulestype)
+ {
+ case 0 :
+ result = S6_ACCESSRULES_ALLOW ; break ;
+ case 1 :
+ result = s6_accessrules_uidgid_fs(*uid, *gid, rules, &params) ; break ;
+ case 2 :
+ result = s6_accessrules_uidgid_cdb(*uid, *gid, &cdbmap, &params) ; break ;
+ default : break ;
+ }
+ if (result != S6_ACCESSRULES_ALLOW)
+ {
+ if (verbosity && (result == S6_ACCESSRULES_ERROR))
+ strerr_warnw1sys("error while checking rules") ;
+ return 0 ;
+ }
+ if (params.exec.s)
+ {
+ stralloc_free(&params.exec) ;
+ if (verbosity)
+ {
+ char fmtuid[UID_FMT] ;
+ char fmtgid[GID_FMT] ;
+ fmtuid[uid_fmt(fmtuid, *uid)] = 0 ;
+ fmtgid[gid_fmt(fmtgid, *gid)] = 0 ;
+ strerr_warnw4x("unused exec string in rules for uid ", fmtuid, " gid ", fmtgid) ;
+ }
+ }
+ if (params.env.s)
+ {
+ size_t n = byte_count(params.env.s, params.env.len, '\0') ;
+ char const *envp[n+1] ;
+ if (!env_make(envp, n, params.env.s, params.env.len))
+ {
+ if (verbosity) strerr_warnwu1sys("env_make") ;
+ stralloc_free(&params.env) ;
+ return 0 ;
+ }
+ envp[n] = 0 ;
+ if (!parse_env(envp, idstr_re, interfaces_re, flags))
+ {
+ if (verbosity) strerr_warnwu1sys("parse_env") ;
+ s6_accessrules_params_free(&params) ;
+ return 0 ;
+ }
+ s6_accessrules_params_free(&params) ;
+ }
+ return 1 ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ int spfd ;
+ int flag1 = 0 ;
+ uint32_t maxconn = 64 ;
+ PROG = "skabus-rpcd" ;
+
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ unsigned int t = 0, T = 0 ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "v:SsJj1i:x:t:T:c:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ;
+ case 'S' : flagidstrpub = 0 ; break ;
+ case 's' : flagidstrpub = 1 ; break ;
+ case 'J' : flaginterfacespub = 0 ; break ;
+ case 'j' : flaginterfacespub = 1 ; break ;
+ case '1' : flag1 = 1 ; break ;
+ case 'i' : rules = l.arg ; rulestype = 1 ; break ;
+ case 'x' : rules = l.arg ; rulestype = 2 ; break ;
+ case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
+ case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ;
+ case 'c' : if (!uint320_scan(l.arg, &maxconn)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (t) tain_from_millisecs(&answertto, t) ;
+ if (T) tain_from_millisecs(&lameduckdeadline, T) ;
+ }
+ if (maxconn > SKABUS_RPC_MAX) maxconn = SKABUS_RPC_MAX ;
+ if (!maxconn) maxconn = 1 ;
+ {
+ struct stat st ;
+ if (fstat(0, &st) < 0) strerr_diefu1sys(111, "fstat stdin") ;
+ if (!S_ISSOCK(st.st_mode)) strerr_dief1x(100, "stdin is not a socket") ;
+ }
+ if (flag1)
+ {
+ if (fcntl(1, F_GETFD) < 0)
+ strerr_dief1sys(100, "called with option -1 but stdout said") ;
+ }
+ else close(1) ;
+ spfd = selfpipe_init() ;
+ if (spfd < 0) strerr_diefu1sys(111, "selfpipe_init") ;
+ if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ;
+ {
+ sigset_t set ;
+ sigemptyset(&set) ;
+ sigaddset(&set, SIGTERM) ;
+ sigaddset(&set, SIGHUP) ;
+ if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ;
+ }
+
+ if (rulestype == 2)
+ {
+ cdbfd = open_readb(rules) ;
+ if (cdbfd < 0) strerr_diefu3sys(111, "open ", rules, " for reading") ;
+ if (cdb_init(&cdbmap, cdbfd) < 0)
+ strerr_diefu2sys(111, "cdb_init ", rules) ;
+ }
+
+ {
+ GENSETB_TYPE(client_t, 1+maxconn) blob ;
+ iopause_fd x[2 + (maxconn << 1)] ;
+ GENSETB_init(client_t, &blob, 1+maxconn) ;
+ sentinel = gensetb_new(&blob) ;
+ blob.storage[sentinel].next = sentinel ;
+ clients = &blob.info ;
+ x[0].fd = spfd ; x[0].events = IOPAUSE_READ ;
+ x[1].fd = 0 ;
+
+ if (flag1)
+ {
+ fd_write(1, "\n", 1) ;
+ fd_close(1) ;
+ }
+ tain_now_g() ;
+
+ for (;;)
+ {
+ tain_t deadline ;
+ int r = 1 ;
+ uint32_t i = blob.storage[sentinel].next, j = 2 ;
+ query_get_mindeadline(&deadline) ;
+ if (!cont && tain_less(&lameduckdeadline, &deadline)) deadline = lameduckdeadline ;
+ if (queries_pending()) r = 0 ;
+
+ x[1].events = (cont && (numconn < maxconn)) ? IOPAUSE_READ : 0 ;
+ for (; i != sentinel ; i = blob.storage[i].next)
+ if (client_prepare_iopause(i, &deadline, x, &j, cont)) r = 0 ;
+ if (!cont && r) break ;
+
+ r = iopause_g(x, j, &deadline) ;
+ if (r < 0) strerr_diefu1sys(111, "iopause") ;
+
+
+ /* Timeout */
+
+ if (!r)
+ {
+ if (!cont && !tain_future(&lameduckdeadline)) return 1 ;
+ for (;;)
+ {
+ if (!query_lookup_by_mindeadline(&i)) break ;
+ if (tain_future(&QUERY(i)->deadline)) break ;
+ query_fail(i, ETIMEDOUT) ;
+ }
+ errno = ETIMEDOUT ;
+ for (i = blob.storage[sentinel].next, j = sentinel ; i != sentinel ; j = i, i = blob.storage[i].next)
+ if (!tain_future(&blob.storage[i].deadline)) removeclient(&i, j) ;
+ continue ;
+ }
+
+
+ /* Signal */
+
+ if (x[0].revents & IOPAUSE_READ) handle_signals() ;
+
+
+ /* Event */
+
+ for (j = sentinel, i = blob.storage[sentinel].next ; i != sentinel ; j = i, i = blob.storage[i].next)
+ if (!client_flush(i, x)) removeclient(&i, j) ;
+
+ for (j = sentinel, i = blob.storage[sentinel].next ; i != sentinel ; j = i, i = blob.storage[i].next)
+ switch(client_read(i, x))
+ {
+ case 0 : errno = 0 ;
+ case -1 :
+ case -2 :
+ {
+ removeclient(&i, j) ;
+ break ;
+ }
+ case 1 : break ;
+ default : X() ;
+ }
+
+
+ /* New connection */
+
+ if (x[1].revents & IOPAUSE_READ)
+ {
+ uint32_t flags = 0 ;
+ uid_t uid ;
+ gid_t gid ;
+ regex_t idstr_re, interfaces_re ;
+ int fd = ipc_accept_nb(x[1].fd, 0, 0, 0) ;
+ if (fd < 0)
+ if (!error_isagain(errno)) strerr_diefu1sys(111, "accept") ;
+ else continue ;
+ else if (!new_connection(fd, &uid, &gid, &idstr_re, &interfaces_re, &flags))
+ fd_close(fd) ;
+ else client_add(&i, &idstr_re, &interfaces_re, uid, gid, fd, flags) ;
+ }
+ }
+ }
+ return 0 ;
+}
diff --git a/src/rpc/skabus-rpcd.h b/src/rpc/skabus-rpcd.h
new file mode 100644
index 0000000..fec72f1
--- /dev/null
+++ b/src/rpc/skabus-rpcd.h
@@ -0,0 +1,146 @@
+/* ISC license. */
+
+#ifndef SKABUS_RPCD_H
+#define SKABUS_RPCD_H
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <regex.h>
+#include <skalibs/uint64.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/genset.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/unixconnection.h>
+#include <skabus/rpc.h>
+
+#define X() strerr_dief1x(101, "unexpected error - please submit a bug-report.") ;
+
+
+ /*
+ query: queries accepted from client, sent to interface
+ The list is stored in a gensetdyn.
+ Looked up by serial for answers or cancels.
+ */
+
+typedef struct query_s query_t, *query_t_ref ;
+struct query_s
+{
+ uint64_t serial ;
+ tain_t deadline ;
+ uint32_t client ;
+ uint32_t clientindex ;
+ uint32_t interface ;
+ uint32_t interfaceindex ;
+} ;
+
+#define QUERY_ZERO \
+{ \
+ .serial = 0, \
+ .deadline = TAIN_ZERO, \
+ .client = 0, \
+ .clientindex = 0, \
+ .interface = 0, \
+ .interfaceindex = 0 \
+}
+
+
+ /*
+ interfaces: registered R interfaces.
+ The list is stored in a gensetdyn.
+ Looked up by name.
+ */
+
+typedef struct interface_s interface_t, *interface_t_ref ;
+struct interface_s
+{
+ char name[SKABUS_RPC_INTERFACE_MAXLEN+1] ;
+ regex_t re ; /* clients who can access that interface */
+ uint32_t id ;
+ uint32_t client ;
+ uint32_t index ; /* in the owner's interfaces list */
+ gensetdyn queries ; /* uint32_t */
+} ;
+#define INTERFACE_ZERO { .name = "", .id = 0, .client = 0, .index = 0, .queries = GENSETDYN_ZERO }
+
+
+ /*
+ client: client connections.
+ The list is stored in a genset.
+ List browsed at every iopause iteration, so needs a next field.
+ */
+
+typedef struct client_s client_t, *client_t_ref ;
+struct client_s
+{
+ uint32_t next ;
+ uid_t uid ;
+ gid_t gid ;
+ tain_t deadline ;
+ genalloc interfaces ; /* uint32_t */
+ gensetdyn queries ; /* uint32_t */
+ unixconnection_t sync ;
+ unixconnection_t async ;
+ uint32_t xindex[2] ;
+ regex_t idstr_re ;
+ regex_t interfaces_re ;
+} ;
+#define CLIENT_ZERO \
+{ \
+ .next = 0, \
+ .uid = (uid_t)-1, \
+ .gid = (gid_t)-1, \
+ .deadline = TAIN_ZERO, \
+ .interfaces = GENALLOC_ZERO, \
+ .queries = GENALLOC_ZERO, \
+ .sync = UNIXCONNECTION_ZERO, \
+ .async = UNIXCONNECTION_ZERO, \
+ .xindex = { 0, 0 }, \
+}
+
+extern gensetdyn queries ;
+#define QUERY(i) GENSETDYN_P(query_t, &queries, (i))
+#define queries_pending() gensetdyn_n(&queries)
+
+extern gensetdyn interfaces ;
+#define INTERFACE(i) GENSETDYN_P(interface_t, &interfaces, (i))
+
+extern genset *clients ;
+extern unsigned int sentinel ;
+#define CLIENT(i) genset_p(client_t, clients, (i))
+#define numconn (genset_n(clients) - 1)
+
+extern void query_remove (uint32_t) ;
+extern void query_fail (uint32_t, char) ;
+extern int query_cancel (uint32_t, char) ;
+extern int query_cancelremove (uint32_t, char) ;
+extern int query_lookup_by_serial (uint64_t, uint32_t *) ;
+extern int query_lookup_by_mindeadline (uint32_t *) ;
+extern void query_get_mindeadline (tain_t *) ;
+extern int query_add (uint32_t *, tain_t const *, uint32_t, uint32_t) ;
+extern int query_send (uint32_t, unixmessage_t const *) ;
+extern int query_sendpm (uint32_t, unixmessage_t const *) ;
+extern void query_reply (uint32_t, char, unixmessage_t const *) ;
+
+extern void interface_remove (uint32_t) ;
+extern int interface_lookup_by_name (char const *, uint32_t *) ;
+extern int interface_add (uint32_t *, char const *, size_t, uint32_t, char const *, uint32_t) ;
+
+#define client_isregistered(cc) genalloc_len(uint32_t, &CLIENT(cc)->interfaces)
+#define client_idstr(c) (INTERFACE(genalloc_s(uint32_t, &(c)->interfaces)[0])->name + 1)
+extern void client_remove (uint32_t, uint32_t) ;
+extern void client_add (uint32_t *, regex_t const *, regex_t const *, uid_t, gid_t, int, uint32_t) ;
+extern void client_nextdeadline (uint32_t, tain_t *) ;
+extern void client_setdeadline (client_t *) ;
+extern int client_prepare_iopause (uint32_t, tain_t *, iopause_fd *, uint32_t *, int) ;
+extern int client_flush (uint32_t, iopause_fd const *) ;
+extern int client_read (uint32_t, iopause_fd const *) ;
+
+extern int parse_protocol_sync (unixmessage_t const *, void *) ;
+extern int parse_protocol_async (unixmessage_t const *, void *) ;
+
+extern tain_t answertto ;
+
+#endif
diff --git a/src/rpc/skabus_rpcd_client.c b/src/rpc/skabus_rpcd_client.c
new file mode 100644
index 0000000..deac459
--- /dev/null
+++ b/src/rpc/skabus_rpcd_client.c
@@ -0,0 +1,188 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <errno.h>
+#include <regex.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/error.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/genset.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/unixconnection.h>
+#include <skalibs/skaclient.h>
+#include <skabus/rpc.h>
+#include "skabus-rpcd.h"
+
+static inline void client_free (client_t *c)
+{
+ if (!genalloc_len(uint32_t, &c->interfaces)) regfree(&c->idstr_re) ;
+ regfree(&c->interfaces_re) ;
+ genalloc_free(unsigned int, &c->interfaces) ;
+ gensetdyn_free(&c->queries) ;
+ fd_close(unixmessage_sender_fd(&c->sync.out)) ;
+ unixconnection_free(&c->sync) ;
+ if (unixmessage_sender_fd(&c->async.out) >= 0)
+ {
+ fd_close(unixmessage_sender_fd(&c->async.out)) ;
+ unixconnection_free(&c->async) ;
+ }
+}
+
+genset *clients ;
+unsigned int sentinel ;
+
+static inline void client_delete (uint32_t i, uint32_t prev)
+{
+ CLIENT(prev)->next = CLIENT(i)->next ;
+ client_free(CLIENT(i)) ;
+ genset_delete(clients, i) ;
+}
+
+static int query_cancelremove_iter (char *s, void *reason)
+{
+ uint32_t i = *(uint32_t *)s ;
+ return query_cancelremove(i, *(char *)reason) ;
+}
+
+void client_remove (uint32_t i, uint32_t prev)
+{
+ client_t *c = CLIENT(i) ;
+ char reason = ECONNABORTED ;
+ if (gensetdyn_iter(&c->queries, &query_cancelremove_iter, &reason) < gensetdyn_n(&c->queries))
+ strerr_diefu1sys(111, "query_cancelremove_iter in client_remove") ;
+ while (genalloc_len(uint32_t, &c->interfaces))
+ interface_remove(genalloc_s(uint32_t, &c->interfaces)[genalloc_len(uint32_t, &c->interfaces) - 1]) ;
+ client_delete(i, prev) ;
+}
+
+void client_setdeadline (client_t *c)
+{
+ tain_t blah ;
+ tain_half(&blah, &tain_infinite_relative) ;
+ tain_add_g(&blah, &blah) ;
+ if (tain_less(&blah, &c->deadline)) tain_add_g(&c->deadline, &answertto) ;
+}
+
+void client_add (uint32_t *d, regex_t const *idstr_re, regex_t const *interfaces_re, uid_t uid, gid_t gid, int fdsock, uint32_t flags)
+{
+ uint32_t cc = genset_new(clients) ;
+ client_t *c = CLIENT(cc) ;
+ c->next = CLIENT(sentinel)->next ;
+ c->uid = uid ;
+ c->gid = gid ;
+ tain_add_g(&c->deadline, &answertto) ;
+ c->interfaces = genalloc_zero ;
+ c->queries = gensetdyn_zero ;
+ c->idstr_re = *idstr_re ;
+ c->interfaces_re = *interfaces_re ;
+ unixconnection_init(&c->sync, fdsock, fdsock) ;
+ unixconnection_init(&c->async, -1, -1) ;
+ c->async.out.fd = -(int)flags-1 ;
+ CLIENT(sentinel)->next = cc ;
+ *d = cc ;
+}
+
+void client_nextdeadline (uint32_t i, tain_t *deadline)
+{
+ client_t *c = CLIENT(i) ;
+ if (tain_less(&c->deadline, deadline)) *deadline = c->deadline ;
+}
+
+int client_prepare_iopause (uint32_t cc, tain_t *deadline, iopause_fd *x, uint32_t *j, int notlameduck)
+{
+ client_t *c = CLIENT(cc) ;
+ int inflight = 0 ;
+ uint32_t i = genalloc_len(uint32_t, &c->interfaces) ;
+ if (tain_less(&c->deadline, deadline)) *deadline = c->deadline ;
+ if (!unixmessage_sender_isempty(&c->sync.out) | !unixmessage_receiver_isempty(&c->sync.in) || (notlameduck && !unixmessage_receiver_isfull(&c->sync.in)))
+ {
+ x[*j].fd = unixmessage_sender_fd(&c->sync.out) ;
+ x[*j].events = ((!unixmessage_receiver_isempty(&c->sync.in) || (notlameduck && !unixmessage_receiver_isfull(&c->sync.in))) ? IOPAUSE_READ : 0)
+ | (!unixmessage_sender_isempty(&c->sync.out) ? IOPAUSE_WRITE : 0) ;
+ c->xindex[0] = (*j)++ ;
+ }
+ else c->xindex[0] = 0 ;
+ while (i--)
+ {
+ interface_t *y = INTERFACE(genalloc_s(uint32_t, &c->interfaces)[i]) ;
+ if (gensetdyn_n(&y->queries))
+ {
+ inflight = 1 ;
+ break ;
+ }
+ }
+ if (!unixmessage_sender_isempty(&c->async.out) || !unixmessage_receiver_isempty(&c->async.in) || inflight)
+ {
+ x[*j].fd = unixmessage_sender_fd(&c->async.out) ;
+ x[*j].events = (unixmessage_sender_isempty(&c->async.out) ? IOPAUSE_WRITE : 0)
+ | (!unixmessage_receiver_isempty(&c->async.in) || inflight ? IOPAUSE_READ : 0) ;
+ c->xindex[1] = (*j)++ ;
+ }
+ else c->xindex[1] = 0 ;
+ return c->xindex[0] || c->xindex[1] ;
+}
+
+int client_flush (uint32_t i, iopause_fd const *x)
+{
+ client_t *c = CLIENT(i) ;
+ int isflushed = 2 ;
+ if (c->xindex[0] && (x[c->xindex[0]].revents & IOPAUSE_WRITE))
+ {
+ if (!unixmessage_sender_flush(&c->sync.out))
+ if (!error_isagain(errno)) return 0 ;
+ else isflushed = 0 ;
+ else isflushed = 1 ;
+ }
+
+ if (c->xindex[1] && (x[c->xindex[1]].revents & IOPAUSE_WRITE))
+ {
+ if (!unixmessage_sender_flush(&c->async.out))
+ if (!error_isagain(errno)) return 0 ;
+ else isflushed = 0 ;
+ else isflushed = !!isflushed ;
+ }
+
+ if (isflushed == 1) tain_add_g(&c->deadline, &tain_infinite_relative) ;
+ return 1 ;
+}
+
+int client_read (uint32_t cc, iopause_fd const *x)
+{
+ client_t *c = CLIENT(cc) ;
+ if (!unixmessage_receiver_isempty(&c->sync.in) || (c->xindex[0] && x[c->xindex[0]].revents & IOPAUSE_READ))
+ {
+ if (unixmessage_sender_fd(&c->async.out) < 0)
+ {
+ unixmessage_t m ;
+ int r = unixmessage_receive(&c->sync.in, &m) ;
+ if (r < 0) return -1 ;
+ if (r)
+ {
+ uint32_t flags = -(unixmessage_sender_fd(&c->async.out) + 1) ;
+ if (!skaclient_server_bidi_ack(&m, &c->sync.out, &c->async.out, &c->async.in, c->async.mainbuf, UNIXMESSAGE_BUFSIZE, c->async.auxbuf, UNIXMESSAGE_AUXBUFSIZE, SKABUS_RPC_BANNER1, SKABUS_RPC_BANNER1_LEN, SKABUS_RPC_BANNER2, SKABUS_RPC_BANNER2_LEN))
+ {
+ unixmessage_drop(&m) ;
+ return -1 ;
+ }
+ if (!(flags & 1)) unixmessage_receiver_refuse_fds(&c->sync.in) ;
+ if (!(flags & 2)) unixmessage_receiver_refuse_fds(&c->async.in) ;
+ }
+ }
+ else
+ {
+ int r = unixmessage_handle(&c->sync.in, &parse_protocol_sync, &cc) ;
+ if (r <= 0) return r ;
+ }
+ }
+ if (!unixmessage_receiver_isempty(&c->async.in) || (c->xindex[1] && x[c->xindex[1]].revents & IOPAUSE_READ))
+ {
+ int r = unixmessage_handle(&c->async.in, &parse_protocol_async, &cc) ;
+ if (r <= 0) return r ;
+ }
+ return 1 ;
+}
diff --git a/src/rpc/skabus_rpcd_interface.c b/src/rpc/skabus_rpcd_interface.c
new file mode 100644
index 0000000..458b2d8
--- /dev/null
+++ b/src/rpc/skabus_rpcd_interface.c
@@ -0,0 +1,113 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <regex.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+#include <skabus/rpc.h>
+#include "skabus-rpcd.h"
+
+static inline void interface_free (interface_t *p)
+{
+ p->name[0] = p->name[1] = 0 ;
+ gensetdyn_free(&p->queries) ;
+}
+
+static void *if_dtok (uint32_t d, void *x)
+{
+ (void)x ;
+ return INTERFACE(d)->name ;
+}
+
+static int if_cmp (void const *a, void const *b, void *x)
+{
+ (void)x ;
+ return strncmp((char const *)a, (char const *)b, SKABUS_RPC_INTERFACE_MAXLEN) ;
+}
+
+gensetdyn interfaces = GENSETDYN_ZERO ;
+static avltree ifdict = AVLTREE_INIT(2, 3, 8, &if_dtok, &if_cmp, 0) ;
+
+static inline void interface_delete (uint32_t i)
+{
+ interface_t *y = INTERFACE(i) ;
+ if (!avltree_delete(&ifdict, y->name))
+ strerr_diefu1sys(111, "avltree_delete in interface_delete") ;
+ interface_free(y) ;
+ if (!gensetdyn_delete(&interfaces, i))
+ strerr_diefu1sys(111, "gensetdyn_delete in interface_delete") ;
+}
+
+static int query_fail_iter (char *s, void *reason)
+{
+ uint32_t i = *(uint32_t *)s ;
+ query_fail(i, *(char *)reason) ;
+ return 1 ;
+}
+
+static inline void client_interfacemove (client_t *c, uint32_t from, uint32_t to)
+{
+ uint32_t *ifaces = genalloc_s(uint32_t, &c->interfaces) ;
+ INTERFACE(ifaces[from])->index = to ;
+ ifaces[to] = ifaces[from] ;
+}
+
+void interface_remove (uint32_t i)
+ {
+ interface_t *y = INTERFACE(i) ;
+ client_t *c = CLIENT(y->client) ;
+ uint32_t n = gensetdyn_n(&y->queries) ;
+ char reason = ECONNRESET ;
+ gensetdyn_iter(&y->queries, &query_fail_iter, &reason) ;
+ n = genalloc_len(uint32_t, &c->interfaces) ;
+ client_interfacemove(c, n-1, y->index) ;
+ genalloc_setlen(uint32_t, &c->interfaces, n-1) ;
+ interface_delete(i) ;
+}
+
+int interface_lookup_by_name (char const *s, uint32_t *d)
+{
+ return avltree_search(&ifdict, s, d) ;
+}
+
+int interface_add (uint32_t *d, char const *name, size_t namelen, uint32_t client, char const *re, uint32_t id)
+{
+ uint32_t yy ;
+ int e ;
+ genalloc *g = &CLIENT(client)->interfaces ;
+ if (!genalloc_readyplus(uint32_t, g, 1)) return 0 ;
+ if (!gensetdyn_new(&interfaces, &yy)) return 0 ;
+ {
+ interface_t *y = INTERFACE(yy) ;
+ int r = regcomp(&y->re, re, REG_EXTENDED | REG_NOSUB) ;
+ if (r)
+ {
+ e = r == REG_ESPACE ? ENOMEM : EINVAL ;
+ goto err ;
+ }
+ memcpy(y->name, name, namelen) ; y->name[namelen] = 0 ;
+ y->id = id ;
+ y->client = client ;
+ y->index = genalloc_len(uint32_t, g) - 1 ;
+ y->queries = gensetdyn_zero ;
+ if (!avltree_insert(&ifdict, yy))
+ {
+ e = errno ;
+ regfree(&y->re) ;
+ goto err ;
+ }
+ }
+ genalloc_append(uint32_t, g, &yy) ;
+ *d = yy ;
+ return 1 ;
+
+ err:
+ if (!gensetdyn_delete(&interfaces, yy))
+ strerr_diefu1sys(111, "gensetdyn_delete in interface_add") ;
+ errno = e ;
+ return 0 ;
+}
diff --git a/src/rpc/skabus_rpcd_query.c b/src/rpc/skabus_rpcd_query.c
new file mode 100644
index 0000000..baa6df5
--- /dev/null
+++ b/src/rpc/skabus_rpcd_query.c
@@ -0,0 +1,207 @@
+ /* ISC license. */
+
+#include <sys/uio.h>
+#include <string.h>
+#include <stdint.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/tai.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+#include <skalibs/unixmessage.h>
+#include <skabus/rpc.h>
+#include "skabus-rpcd.h"
+
+static void *query_serial_dtok (uint32_t d, void *x)
+{
+ (void)x ;
+ return &QUERY(d)->serial ;
+}
+
+static void *query_deadline_dtok (uint32_t d, void *x)
+{
+ (void)x ;
+ return &QUERY(d)->deadline ;
+}
+
+static int query_serial_cmp (void const *a, void const *b, void *x)
+{
+ uint64_t aa = *(uint64_t *)a ;
+ uint64_t bb = *(uint64_t *)b ;
+ (void)x ;
+ return aa < bb ? -1 : aa > bb ;
+}
+
+static int query_deadline_cmp (void const *a, void const *b, void *x)
+{
+ tain_t const *aa = (tain_t const *)a ;
+ tain_t const *bb = (tain_t const *)b ;
+ (void)x ;
+ return tain_less(aa, bb) ? -1 : tain_less(bb, aa) ;
+}
+
+gensetdyn queries = GENSETDYN_ZERO ;
+static avltree qserialdict = AVLTREE_INIT(10, 1, 2, &query_serial_dtok, &query_serial_cmp, 0) ;
+static avltree qdeadlinedict = AVLTREE_INIT(10, 1, 2, &query_deadline_dtok, &query_deadline_cmp, 0) ;
+
+static inline void query_delete (uint32_t i)
+{
+ if (!avltree_delete(&qdeadlinedict, &QUERY(i)->deadline))
+ strerr_diefu1sys(111, "avltree_delete qdeadlinedict in query_delete") ;
+ if (!avltree_delete(&qserialdict, &QUERY(i)->serial))
+ strerr_diefu1sys(111, "avltree_delete qserialdict in query_delete") ;
+ if (!gensetdyn_delete(&queries, i))
+ strerr_diefu1sys(111, "gensetdyn_delete in query_delete") ;
+}
+
+void query_remove (uint32_t i)
+{
+ query_t *q = QUERY(i) ;
+ client_t *c = CLIENT(q->client) ;
+ interface_t *y = INTERFACE(q->interface) ;
+ if (!gensetdyn_delete(&c->queries, q->clientindex))
+ strerr_diefu1sys(111, "gensetdyn_delete c->queries in query_remove") ;
+ if (!gensetdyn_delete(&y->queries, q->interfaceindex))
+ strerr_diefu1sys(111, "gensetdyn_delete y->queries in query_remove") ;
+ query_delete(i) ;
+}
+
+void query_fail (uint32_t i, char status)
+{
+ query_t *q = QUERY(i) ;
+ client_t *c = CLIENT(q->client) ;
+ char pack[10] = "Rssssssssr" ;
+ unixmessage_t m = { .s = pack, .len = 10, .fds = 0, .nfds = 0 } ;
+ uint64_pack_big(pack+1, q->serial) ;
+ pack[9] = status ;
+ if (!unixmessage_put(&c->async.out, &m))
+ strerr_diefu1sys(111, "unixmessage_put in query_fail") ;
+ query_remove(i) ;
+ client_setdeadline(c) ;
+}
+
+int query_cancel (uint32_t i, char reason)
+{
+ query_t *q = QUERY(i) ;
+ interface_t *y = INTERFACE(q->interface) ;
+ client_t *c = CLIENT(y->client) ;
+ char pack[14] = "Ciiiissssssssr" ;
+ unixmessage_t m = { .s = pack, .len = 14, .fds = 0, .nfds = 0 } ;
+ uint32_pack_big(pack+1, y->id) ;
+ uint64_pack_big(pack+5, q->serial) ;
+ pack[13] = reason ;
+ if (!unixmessage_put(&c->async.out, &m)) return 0 ;
+ client_setdeadline(c) ;
+ return 1 ;
+}
+
+int query_cancelremove (uint32_t i, char reason)
+{
+ if (!query_cancel(i, reason)) return 0 ;
+ query_remove(i) ;
+ return 1 ;
+}
+
+int query_lookup_by_serial (uint64_t serial, uint32_t *d)
+{
+ return avltree_search(&qserialdict, &serial, d) ;
+}
+
+int query_lookup_by_mindeadline (uint32_t *d)
+{
+ return avltree_min(&qdeadlinedict, d) ;
+}
+
+void query_get_mindeadline (tain_t *deadline)
+{
+ uint32_t d ;
+ if (query_lookup_by_mindeadline(&d)) *deadline = QUERY(d)->deadline ;
+ else tain_add_g(deadline, &tain_infinite_relative) ;
+}
+
+int query_add (uint32_t *d, tain_t const *deadline, uint32_t client, uint32_t interface)
+{
+ static uint64_t serial = 1 ;
+ uint32_t qq, cc, yy ;
+ query_t *q ;
+ if (!gensetdyn_new(&queries, &qq)) return 0 ;
+ if (!gensetdyn_new(&CLIENT(client)->queries, &cc)) goto end0 ;
+ if (!gensetdyn_new(&INTERFACE(interface)->queries, &yy)) goto end1 ;
+ q = QUERY(qq) ;
+ q->serial = serial ;
+ q->deadline = *deadline ;
+ q->client = client ;
+ q->clientindex = cc ;
+ q->interface = interface ;
+ q->interfaceindex = yy ;
+ if (!avltree_insert(&qserialdict, qq)) goto end2 ;
+ for (;;)
+ {
+ static tain_t const nano1 = { .sec = TAI_ZERO, .nano = 1 } ;
+ uint32_t d ;
+ if (!avltree_search(&qdeadlinedict, &q->deadline, &d)) break ;
+ tain_add(&q->deadline, &q->deadline, &nano1) ;
+ }
+ if (!avltree_insert(&qdeadlinedict, qq)) goto end3 ;
+ serial++ ;
+ *d = qq ;
+ return 1 ;
+
+ end3:
+ if (!avltree_delete(&qserialdict, &serial))
+ strerr_diefu1sys(111, "avltree_delete in query_add") ;
+ end2:
+ if (!gensetdyn_delete(&INTERFACE(interface)->queries, yy))
+ strerr_diefu1sys(111, "gensetdyn_delete INTERFACE(interface)->queries in query_add") ;
+ end1:
+ if (!gensetdyn_delete(&CLIENT(client)->queries, cc))
+ strerr_diefu1sys(111, "gensetdyn_delete CLIENT(client)->queries in query_add") ;
+ end0:
+ if (!gensetdyn_delete(&queries, qq))
+ strerr_diefu1sys(111, "gensetdyn_delete queries in query_add") ;
+ return 0 ;
+}
+
+int query_send (uint32_t qq, unixmessage_t const *m)
+{
+ skabus_rpc_rinfo_t rinfo ;
+ char pack[4 + SKABUS_RPC_RINFO_PACK] = "Q" ;
+ struct iovec v[2] = { { .iov_base = pack, .iov_len = 4 + SKABUS_RPC_RINFO_PACK }, { .iov_base = m->s, .iov_len = m->len } } ;
+ unixmessage_v_t mtosend = { .v = v, .vlen = 2, .fds = m->fds, .nfds = m->nfds } ;
+ query_t *q = QUERY(qq) ;
+ interface_t *y = INTERFACE(q->interface) ;
+ client_t *c = CLIENT(q->client) ;
+ char const *idstr = client_idstr(c) ;
+ size_t idstrlen = strlen(idstr) ;
+ rinfo.serial = q->serial ;
+ rinfo.limit = q->deadline ;
+ tain_copynow(&rinfo.timestamp) ;
+ rinfo.uid = c->uid ;
+ rinfo.gid = c->gid ;
+ memcpy(rinfo.idstr, idstr, idstrlen) ;
+ memset(rinfo.idstr + idstrlen, 0, SKABUS_RPC_IDSTR_SIZE + 1 - idstrlen) ;
+ uint32_pack_big(pack+1, y->id) ;
+ skabus_rpc_rinfo_pack(pack + 5, &rinfo) ;
+ c = CLIENT(y->client) ;
+ if (!unixmessage_putv_and_close(&c->async.out, &mtosend, unixmessage_bits_closeall)) return 0 ;
+ client_setdeadline(c) ;
+ return 1 ;
+}
+
+void query_reply (uint32_t qq, char result, unixmessage_t const *m)
+{
+ char pack[11] = "R" ;
+ struct iovec v[2] = { { .iov_base = pack, .iov_len = 11 }, { .iov_base = m->s, .iov_len = m->len } } ;
+ unixmessage_v_t mtosend = { .v = v, .vlen = 2, .fds = m->fds, .nfds = m->nfds } ;
+ query_t *q = QUERY(qq) ;
+ client_t *c = CLIENT(q->client) ;
+ uint64_pack_big(pack+1, q->serial) ;
+ pack[9] = 0 ;
+ pack[10] = result ;
+ if (!unixmessage_putv_and_close(&c->async.out, &mtosend, unixmessage_bits_closeall))
+ strerr_diefu1sys(111, "unixmessage_put in query_reply") ;
+ client_setdeadline(c) ;
+ query_remove(qq) ;
+}