s6

Mirror/fork of https://skarnet.org/software/s6/
git clone https://ccx.te2000.cz/git/s6
Log | Files | Refs | README | LICENSE

commit 87c5b2118efcee65eeda3f743d081ea9c2b866d9
parent cd2500fcc704287c4994a3253b593593c867913e
Author: Laurent Bercot <ska-skaware@skarnet.org>
Date:   Thu, 15 Jan 2015 20:14:44 +0000

 Move Unix domain utilities and access control utilites,
as well as the accessrules library, from s6-networking to here

Diffstat:
Mdoc/fifodir.html | 8++++----
Adoc/ftrig.html | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdoc/index.html | 50+++++++++++++++++++++++++++++++++++++++++---------
Ddoc/libftrig.html | 184-------------------------------------------------------------------------------
Ddoc/libftrigr.html | 283-------------------------------------------------------------------------------
Ddoc/libftrigw.html | 115-------------------------------------------------------------------------------
Adoc/libs6/accessrules.html | 331+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/libs6/ftrigr.html | 268+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/libs6/ftrigw.html | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/libs6/index.html | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/libs6/s6-ftrigrd.html | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/libs6/s6lock.html | 238+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/libs6/s6lockd-helper.html | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/libs6/s6lockd.html | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ddoc/libs6lock/index.html | 256-------------------------------------------------------------------------------
Ddoc/libs6lock/s6lockd-helper.html | 52----------------------------------------------------
Ddoc/libs6lock/s6lockd.html | 73-------------------------------------------------------------------------
Adoc/localservice.html | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/s6-accessrules-cdb-from-fs.html | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/s6-accessrules-fs-from-cdb.html | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdoc/s6-cleanfifodir.html | 2+-
Adoc/s6-connlimit.html | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdoc/s6-ftrig-notify.html | 2+-
Ddoc/s6-ftrigrd.html | 79-------------------------------------------------------------------------------
Adoc/s6-ioconnect.html | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/s6-ipcclient.html | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/s6-ipcserver-access.html | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/s6-ipcserver-socketbinder.html | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/s6-ipcserver.html | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/s6-ipcserverd.html | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdoc/s6-notifywhenup.html | 8++++----
Mdoc/s6-setlock.html | 2+-
Adoc/s6-sudo.html | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/s6-sudoc.html | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/s6-sudod.html | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdoc/s6-supervise.html | 2+-
Mdoc/s6-svc.html | 2+-
Mdoc/s6-svwait.html | 4++--
Mdoc/upgrade.html | 8++++++++
Mdoc/why.html | 4++--
Mpackage/deps.mak | 50+++++++++++++++++++++++++++++++++++++++++++++++---
Mpackage/info | 2+-
Mpackage/modes | 70+++++++++++++++++++++++++++++++++++++++++-----------------------------
Mpackage/targets.mak | 12++++++++++++
Asrc/conn-tools/deps-exe/s6-accessrules-cdb-from-fs | 3+++
Asrc/conn-tools/deps-exe/s6-accessrules-fs-from-cdb | 1+
Asrc/conn-tools/deps-exe/s6-connlimit | 1+
Asrc/conn-tools/deps-exe/s6-ioconnect | 3+++
Asrc/conn-tools/deps-exe/s6-ipcclient | 2++
Asrc/conn-tools/deps-exe/s6-ipcserver | 1+
Asrc/conn-tools/deps-exe/s6-ipcserver-access | 3+++
Asrc/conn-tools/deps-exe/s6-ipcserver-socketbinder | 2++
Asrc/conn-tools/deps-exe/s6-ipcserverd | 2++
Asrc/conn-tools/deps-exe/s6-sudo | 1+
Asrc/conn-tools/deps-exe/s6-sudoc | 3+++
Asrc/conn-tools/deps-exe/s6-sudod | 3+++
Asrc/conn-tools/s6-accessrules-cdb-from-fs.c | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-accessrules-fs-from-cdb.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-connlimit.c | 39+++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-ioconnect.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-ipcclient.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-ipcserver-access.c | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-ipcserver-socketbinder.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-ipcserver.c | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-ipcserverd.c | 399+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-sudo.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-sudo.h | 11+++++++++++
Asrc/conn-tools/s6-sudoc.c | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/conn-tools/s6-sudod.c | 233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/s6/accessrules.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/s6/s6.h | 1+
Msrc/libs6/deps-lib/s6 | 8++++++++
Asrc/libs6/s6_accessrules_backend_cdb.c | 38++++++++++++++++++++++++++++++++++++++
Asrc/libs6/s6_accessrules_backend_fs.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libs6/s6_accessrules_keycheck_ip4.c | 24++++++++++++++++++++++++
Asrc/libs6/s6_accessrules_keycheck_ip6.c | 27+++++++++++++++++++++++++++
Asrc/libs6/s6_accessrules_keycheck_reversedns.c | 27+++++++++++++++++++++++++++
Asrc/libs6/s6_accessrules_keycheck_uidgid.c | 16++++++++++++++++
Asrc/libs6/s6_accessrules_uidgid_cdb.c | 11+++++++++++
Asrc/libs6/s6_accessrules_uidgid_fs.c | 10++++++++++
80 files changed, 5221 insertions(+), 1101 deletions(-)

diff --git a/doc/fifodir.html b/doc/fifodir.html @@ -31,13 +31,13 @@ create fifodirs in a RAM filesystem. <ul> <li> You can create fifodirs via the <tt>ftrigw_fifodir_create()</tt> function in -<a href="libftrigw.html">libftrig</a>. </li> +<a href="libs6/ftrigw.html">libftrig</a>. </li> <li> You can send an event to a fifodir via the <tt>ftrigw_notify()</tt> function in -<a href="libftrigw.html">libftrig</a>. </li> +<a href="libs6/ftrigw.html">libftrig</a>. </li> <li> You can clean up a fifodir via the <tt>ftrigw_clean()</tt> function in -<a href="libftrigw.html">libftrig</a>. </li> +<a href="libs6/ftrigw.html">libftrig</a>. </li> <li> You can destroy fifodirs via the <tt>rm_rf()</tt> function in <a href="http://skarnet.org/software/skalibs/doc/libstddjb/djbunix.html">libstddjb</a>. </li> @@ -115,7 +115,7 @@ can write to the fifo </li> </ul> <p> - The <a href="libftrig.html">libftrig<a/> interface takes care of all + The <a href="ftrig.html">libftrig<a/> interface takes care of all the subtleties. </p> diff --git a/doc/ftrig.html b/doc/ftrig.html @@ -0,0 +1,184 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: libftrig</title> + <meta name="Description" content="s6 libftrig" /> + <meta name="Keywords" content="s6 libftrig" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> libftrig </h1> + +<p> +<tt>libftrig</tt> is a portable Unix C programming interface allowing a +process (the <em>subscriber</em> or <em>listener</em>) to be instantly +notified when another process (the <em>notifier</em> or <em>writer</em>) +signals an event. +</p> + +<a name="notification"> +<h2> What is notification&nbsp;? </h2> +</a> + +<h3> Notification vs. polling </h3> + +<p> + Process A is <em>notified</em> of an event E when it gets a instant +notice that event E has happened; the notice may disrupt A's execution +flow. Notification is the opposite of <em>polling</em>, where A has to +periodically (every T milliseconds) check whether E happened and has no +other way to be informed of it. +</p> + +<p> + Polling is generally considered bad practice - and is inferior to +notification in practically every case - for three reasons: +</p> + +<ul> + <li> Reaction time. When event E happens, process A does not know it +instantly. It will only learn of E, and be able to react to it, when +it explicitly checks for E; and if E happened right after A performed +the check, this can take as long as T milliseconds (the <em>polling +period</em>). Polling processes have reaction delays due to the polling +periods. </li> + <li> Resource consumption. Even if <em>no</em> event ever happens, process A +will still wake up needlessly every T milliseconds. This might not seem like +a problem, but it is a serious one in energy-critical environments. Polling +processes use more CPU time than is necessary and are not energy-friendly. </li> + <li> Conflict between the two above reasons. The longer the polling period, +the more energy-friendly the process, but the longer the reaction time. The +shorter the polling period, the shorter the reaction time, but the more +resource-consuming the process. A delicate balance has to be found, and +acceptable behaviour is different in every case, so there's no general rule +of optimization. </li> +</ul> + +<p> + Notification, on the other hand, is generally optimal: reaction time is +zero, and resource consumption is minimal - a process can sleep as soon as +it's not handling an event, and only wake up when needed. +</p> + +<p> + Of course, the problem of notification is that it's often more difficult +to implement. Notification frameworks are generally more complex, involving +lots of asynchronism; polling is widely used +<a href="http://lib.store.yahoo.net/lib/demotivators/mediocritydemotivationalposter.jpg">because +it's easy.</a> +</p> + +<h3> Notifications and Unix </h3> + +<p> + Unix provides several frameworks so that a process B (or the kernel) can +notify process A. +</p> + +<ul> + <li> Signals. The simplest Unix notification mechanism. Sending events amounts +to a <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html">kill()</a> +call, and receiving events amounts to installing a signal handler (preferrably +using a <a href="http://skarnet.org/software/skalibs/libstddjb/selfpipe.html">self-pipe</a> +if mixing signals with an event loop). Unfortunately, Unix signals, even the more +recent and powerful real-time Posix signals, have important limitations when it's +about generic notification: + <ul> + <li> non-root processes can only send signals to a very restricted and +implementation-dependent set of processes (roughly, processes with the same UID). This is a problem when +designing secure programs that make use of the Unix privilege separation. </li> + <li> you need to know the PID of a process to send it signals. This is generally +impractical; process management systems that do not use supervisor processes have +to do exactly that, and they resort to unreliable, ugly hacks (.pid files) to track +down process PIDs. </li> + </ul> </li> + <li> BSD-style IPCs, i.e. file descriptors to perform select()/poll() system +calls on, in an <em>asynchronous event loop</em>. This mechanism is very widely used, +and rightly so, because it's extremely generic and works in every ordinary situation; +you have to be doing <a href="http://www.kegel.com/c10k.html">very specific stuff</a> +to reach its limits. If process A is reading on +fd <em>f</em>, it is notified everytime another process makes <em>f</em> readable - +for instance by writing a byte to the other end if <em>f</em> is the reading end +of a pipe. And indeed, this is how libftrig works internally; but libftrig is needed +because direct use of BSD-style IPCs also has limitations. + <ul> + <li> Anonymous pipes are the simplest and most common BSD-style IPC. If there is a +pipe from process B to process A, then B can notify A by writing to the pipe. The +limitation is that A and B must have a common ancestor that created the pipe; two +unrelated processes cannot communicate this way. </li> + <li> Sockets are a good many-to-one notification system: once a server is up, it +can be notified by any client, and notify all its clients. The limitation of sockets +is that the server must be up before the client, which prevents us from using them +in a general notification scheme. </li> + </ul> </li> + <li> System V IPCs, i.e. message queues and semaphores. The interfaces to those IPCs +are quite specific and can't mix with select/poll loops, that's why nobody in their +right mind uses them. </li> +</ul> + +<h3> What we want </h3> + +<p> + We need a general framework to: +</p> + +<ul> + <li> Allow an event-generating process to broadcast notifications to every process +that asked for one, without having to know their PIDs </li> + <li> Allow a process to subscribe to a "notification channel" and be instantly, +asynchronously notified when an event occurs on this channel. </li> +</ul> + +<p> + This requires a many-to-many approach that Unix does not provide natively, and +that is what libftrig does. +</p> + +<a name="bus"> +<h2> That's what a bus is for. D-Bus already does all this. </h2> +</a> + +<p> + Yes, a bus is a good many-to-many notification mechanism indeed. However, +a Unix bus can only be implemented via a daemon - you need a long-running +process, i.e. a <em>service</em>, to implement a bus. And s6 is a +<em>supervision suite</em>, i.e. a set of programs designed to manage +services; we would like to be able to use notifications in the supervision +suite, to be able to wait for a service to be up or down... <em>without</em> +relying on a particular service to be up. libftrig provides a notification +mechanism that <em>does not need</em> a bus service to be up, that's its +main advantage over a bus. +</p> + +<p> + If you are not concerned with supervision and can depend on a bus service, +though, then yes, by all means, use a bus for your notification needs. +There is a <a href="http://skarnet.org/software/skabus/">skabus</a> +project in the making, which aims to be simpler, smaller and more +maintainable than D-Bus. +</p> + +<h2> How to use libftrig </h2> + +<p> + <tt>libftrig</tt> is really a part of <a href="libs6/">libs6</a>: all the functions +are implemented in the <tt>libs6.a</tt> archive, or the <tt>libs6.so</tt> +dynamic shared object. However, the interfaces are different for notifiers +and listeners: +</p> + +<ul> +<li> Notifiers use the <a href="libs6/ftrigw.html">ftrigw</a> interface. </li> +<li> Listeners use the <a href="libs6/ftrigr.html">ftrigr</a> interface. </li> +</ul> + +</body> +</html> diff --git a/doc/index.html b/doc/index.html @@ -37,7 +37,8 @@ supervision that might help you understand the basics. <ul> <li> <a href="why.html">Why another supervision suite?</a> Isn't <a href="http://smarden.org/runit/">runit</a> good enough?</li> -<li> What is instant notification? What does the <a href="libftrig.html">libftrig</a> do exactly?</li> +<li> What is <a href="ftrig.html">instant notification</a>? What does the +<a href="libs6/ftrigr.html">ftrigr library</a> do exactly?</li> <li> How to run a s6-svscan-based supervision tree <a href="s6-svscan-not-1.html">without replacing init</a> </li> <li> How to <a href="s6-svscan-1.html">replace init</a> </li> </ul> @@ -67,7 +68,7 @@ supervision that might help you understand the basics. <h3> Download </h3> <ul> - <li> The current released version of s6 is <a href="s6-2.0.1.0.tar.gz">2.0.1.0</a>. </li> + <li> The current released version of s6 is <a href="s6-2.0.2.0.tar.gz">2.0.2.0</a>. </li> <li> Alternatively, you can checkout a copy of the s6 git repository: <pre> git clone git://git.skarnet.org/s6 </pre> </li> </ul> @@ -151,7 +152,7 @@ counterpart. These programs are a clean rewrite of the obsolete "pipe-tools" package; they are now based on a properly designed notification library. They provide a command-line interface to -<a href="libftrig.html#notification">inter-process notification and +<a href="ftrig.html#notification">inter-process notification and synchronization</a>. </p> @@ -172,21 +173,51 @@ synchronization</a>. <li><a href="s6-ftrig-listen.html">The <tt>s6-ftrig-listen</tt> program</a></li> </ul> +<h4> Local service management and access control </h4> + +<ul> +<li><a href="s6-ipcclient.html">The <tt>s6-ipcclient</tt> program</a></li> +<li><a href="s6-ipcserver.html">The <tt>s6-ipcserver</tt> program</a></li> +<li><a href="s6-ipcserver-socketbinder.html">The <tt>s6-ipcserver-socketbinder</tt> program</a></li> +<li><a href="s6-ipcserverd.html">The <tt>s6-ipcserverd</tt> program</a></li> +<li><a href="s6-ioconnect.html">The <tt>s6-ioconnect</tt> program</a></li> +</ul> +<p></p> +<ul> +<li><a href="s6-ipcserver-access.html">The <tt>s6-ipcserver-access</tt> program</a></li> +<li><a href="s6-connlimit.html">The <tt>s6-connlimit</tt> program</a></li> +</ul> +<p></p> +<ul> +<li><a href="s6-accessrules-cdb-from-fs.html">The <tt>s6-accessrules-cdb-from-fs</tt> program</a></li> +<li><a href="s6-accessrules-fs-from-cdb.html">The <tt>s6-accessrules-fs-from-cdb</tt> program</a></li> +</ul> + +<h4> suidless privilege gain </h4> + +<ul> +<li><a href="s6-sudo.html">The <tt>s6-sudo</tt> program</a></li> +<li><a href="s6-sudoc.html">The <tt>s6-sudoc</tt> program</a></li> +<li><a href="s6-sudod.html">The <tt>s6-sudod</tt> program</a></li> +</ul> + <h4> Internal commands </h4> <ul> <li><a href="s6-ftrigrd.html">The <tt>s6-ftrigrd</tt> internal program</a></li> -<li><a href="libs6lock/s6lockd.html">The <tt>s6lockd</tt> internal program</a></li> -<li><a href="libs6lock/s6lockd-helper.html">The <tt>s6lockd-helper</tt> internal program</a></li> +<li><a href="libs6/s6lockd.html">The <tt>s6lockd</tt> internal program</a></li> +<li><a href="libs6/s6lockd-helper.html">The <tt>s6lockd-helper</tt> internal program</a></li> </ul> <h3> Libraries </h3> <ul> -<li><a href="libftrigw.html">The <tt>ftrigw</tt> library interface</a></li> -<li><a href="libftrigr.html">The <tt>ftrigr</tt> library interface</a></li> -<li><a href="libs6lock/">The <tt>s6lock</tt> library interface</a></li> +<li><a href="libs6/"><tt>s6/s6.h</tt>, the main entry point</a></li> +<li><a href="libs6/ftrigw.html">The <tt>ftrigw</tt> library interface</a></li> +<li><a href="libs6/ftrigr.html">The <tt>ftrigr</tt> library interface</a></li> +<li><a href="libs6/s6lock.html">The <tt>s6lock</tt> library interface</a></li> +<li><a href="libs6/accessrules.html">The <tt>accessrules</tt> library interface</a></li> </ul> <h3> Definitions </h3> @@ -195,7 +226,8 @@ synchronization</a>. <li> What is a <a href="fifodir.html">fifodir</a></li> <li> What is a <a href="servicedir.html">service directory</a></li> <li> What is a <a href="scandir.html">scan directory</a></li> -<li> Why are the <a href="libftrig.html">libftrigw and libftrigr</a> needed </li> +<li> What is a <a href="localservice.html">local service</a></li> +<li> Why are the <a href="ftrig.html">libftrigw and libftrigr</a> needed </li> </ul> <hr /> diff --git a/doc/libftrig.html b/doc/libftrig.html @@ -1,184 +0,0 @@ -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <meta http-equiv="Content-Language" content="en" /> - <title>s6: libftrig</title> - <meta name="Description" content="s6 libftrig" /> - <meta name="Keywords" content="s6 libftrig" /> - <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> - </head> -<body> - -<p> -<a href="index.html">s6</a><br /> -<a href="http://skarnet.org/software/">Software</a><br /> -<a href="http://skarnet.org/">skarnet.org</a> -</p> - -<h1> libftrig </h1> - -<p> -<t>libftrig</t> is a portable Unix C programming interface allowing a -process (the <em>subscriber</em> or <em>listener</em>) to be instantly -notified when another process (the <em>notifier</em> or <em>writer</em>) -signals an event. -</p> - -<a name="notification"> -<h2> What is notification&nbsp;? </h2> -</a> - -<h3> Notification vs. polling </h3> - -<p> - Process A is <em>notified</em> of an event E when it gets a instant -notice that event E has happened; the notice may disrupt A's execution -flow. Notification is the opposite of <em>polling</em>, where A has to -periodically (every T milliseconds) check whether E happened and has no -other way to be informed of it. -</p> - -<p> - Polling is generally considered bad practice - and is inferior to -notification in practically every case - for three reasons: -</p> - -<ul> - <li> Reaction time. When event E happens, process A does not know it -instantly. It will only learn of E, and be able to react to it, when -it explicitly checks for E; and if E happened right after A performed -the check, this can take as long as T milliseconds (the <em>polling -period</em>). Polling processes have reaction delays due to the polling -periods. </li> - <li> Resource consumption. Even if <em>no</em> event ever happens, process A -will still wake up needlessly every T milliseconds. This might not seem like -a problem, but it is a serious one in energy-critical environments. Polling -processes use more CPU time than is necessary and are not energy-friendly. </li> - <li> Conflict between the two above reasons. The longer the polling period, -the more energy-friendly the process, but the longer the reaction time. The -shorter the polling period, the shorter the reaction time, but the more -resource-consuming the process. A delicate balance has to be found, and -acceptable behaviour is different in every case, so there's no general rule -of optimization. </li> -</ul> - -<p> - Notification, on the other hand, is generally optimal: reaction time is -zero, and resource consumption is minimal - a process can sleep as soon as -it's not handling an event, and only wake up when needed. -</p> - -<p> - Of course, the problem of notification is that it's often more difficult -to implement. Notification frameworks are generally more complex, involving -lots of asynchronism; polling is widely used -<a href="http://lib.store.yahoo.net/lib/demotivators/mediocritydemotivationalposter.jpg">because -it's easy.</a> -</p> - -<h3> Notifications and Unix </h3> - -<p> - Unix provides several frameworks so that a process B (or the kernel) can -notify process A. -</p> - -<ul> - <li> Signals. The simplest Unix notification mechanism. Sending events amounts -to a <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html">kill()</a> -call, and receiving events amounts to installing a signal handler (preferrably -using a <a href="http://skarnet.org/software/skalibs/libstddjb/selfpipe.html">self-pipe</a> -if mixing signals with an event loop). Unfortunately, Unix signals, even the more -recent and powerful real-time Posix signals, have important limitations when it's -about generic notification: - <ul> - <li> non-root processes can only send signals to a very restricted and -implementation-dependent set of processes (roughly, processes with the same UID). This is a problem when -designing secure programs that make use of the Unix privilege separation. </li> - <li> you need to know the PID of a process to send it signals. This is generally -impractical; process management systems that do not use supervisor processes have -to do exactly that, and they resort to unreliable, ugly hacks (.pid files) to track -down process PIDs. </li> - </ul> </li> - <li> BSD-style IPCs, i.e. file descriptors to perform select()/poll() system -calls on, in an <em>asynchronous event loop</em>. This mechanism is very widely used, -and rightly so, because it's extremely generic and works in every ordinary situation; -you have to be doing <a href="http://www.kegel.com/c10k.html">very specific stuff</a> -to reach its limits. If process A is reading on -fd <em>f</em>, it is notified everytime another process makes <em>f</em> readable - -for instance by writing a byte to the other end if <em>f</em> is the reading end -of a pipe. And indeed, this is how libftrig works internally; but libftrig is needed -because direct use of BSD-style IPCs also has limitations. - <ul> - <li> Anonymous pipes are the simplest and most common BSD-style IPC. If there is a -pipe from process B to process A, then B can notify A by writing to the pipe. The -limitation is that A and B must have a common ancestor that created the pipe; two -unrelated processes cannot communicate this way. </li> - <li> Sockets are a good many-to-one notification system: once a server is up, it -can be notified by any client, and notify all its clients. The limitation of sockets -is that the server must be up before the client, which prevents us from using them -in a general notification scheme. </li> - </ul> </li> - <li> System V IPCs, i.e. message queues and semaphores. The interfaces to those IPCs -are quite specific and can't mix with select/poll loops, that's why nobody in their -right mind uses them. </li> -</ul> - -<h3> What we want </h3> - -<p> - We need a general framework to: -</p> - -<ul> - <li> Allow an event-generating process to broadcast notifications to every process -that asked for one, without having to know their PIDs </li> - <li> Allow a process to subscribe to a "notification channel" and be instantly, -asynchronously notified when an event occurs on this channel. </li> -</ul> - -<p> - This requires a many-to-many approach that Unix does not provide natively, and -that is what libftrig does. -</p> - -<a name="bus"> -<h2> That's what a bus is for. D-Bus already does all this. </h2> -</a> - -<p> - Yes, a bus is a good many-to-many notification mechanism indeed. However, -a Unix bus can only be implemented via a daemon - you need a long-running -process, i.e. a <em>service</em>, to implement a bus. And s6 is a -<em>supervision suite</em>, i.e. a set of programs designed to manage -services; we would like to be able to use notifications in the supervision -suite, to be able to wait for a service to be up or down... <em>without</em> -relying on a particular service to be up. libftrig provides a notification -mechanism that <em>does not need</em> a bus service to be up, that's its -main advantage over a bus. -</p> - -<p> - If you are not concerned with supervision and can depend on a bus service, -though, then yes, by all means, use a bus for your notification needs. -There is a <a href="http://skarnet.org/software/skabus/">skabus</a> -project in the making, which aims to be simpler, smaller and more -maintainable than D-Bus. -</p> - -<h2> How to use libftrig </h2> - -<p> - <tt>libftrig</tt> is really a part of <tt>libs6</tt>: all the functions -are implemented in the <tt>libs6.a</tt> archive, or the <tt>libs6.so</tt> -dynamic shared object. However, the interfaces are different for notifiers -and listeners: -</p> - -<ul> -<li> Notifiers use the <a href="libftrigw.html">libftrigw</a> interface. </li> -<li> Listeners use the <a href="libftrigr.html">libftrigr</a> interface. </li> -</ul> - -</body> -</html> diff --git a/doc/libftrigr.html b/doc/libftrigr.html @@ -1,283 +0,0 @@ -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <meta http-equiv="Content-Language" content="en" /> - <title>s6: the ftrigr library interface</title> - <meta name="Description" content="s6: the ftrigr library interface" /> - <meta name="Keywords" content="s6 ftrig notification subscriber listener libftrigr ftrigr library interface" /> - <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> - </head> -<body> - -<p> -<a href="index.html">s6</a><br /> -<a href="http://skarnet.org/software/">Software</a><br /> -<a href="http://skarnet.org/">skarnet.org</a> -</p> - -<h1> The <tt>ftrigr</tt> library interface </h1> - -<p> - The <tt>ftrigr</tt> library provides an API for listeners, i.e. -programs that want to subscribe to fifodirs and be instantly -notified when the proper sequence of events happens. -</p> - -<h2> Compiling </h2> - -<ul> - <li> Make sure the s6 headers, as well as the skalibs headers, -are visible in your header search path. </li> - <li> Use <tt>#include &lt;s6/ftrigr.h&gt;</tt> </li> -</ul> - -<h2> Linking </h2> - -<ul> - <li> Make sure the s6 libraries, as well as the skalibs libraries, -are visible in your library search path. </li> - <li> Link against <tt>-ls6</tt> and <tt>-lskarnet</tt>. </li> -</ul> - -<h2> Programming </h2> - -<p> - Check the <tt>s6/ftrigr.h</tt> header for the -exact function prototypes. -</p> - -<p> - Make sure your application is not disturbed by children it doesn't -know it has. This means paying some attention to the SIGCHLD handler, -if any, and to the way you perform <tt>waitpid()</tt>s. The best -practice is to use a -<a href="http://www.skarnet.org/software/skalibs/libstddjb/selfpipe.html">self-pipe</a> -to handle SIGCHLD (as well as other signals the application needs to trap), -and to <em>always</em> use <tt>wait_nohang()</tt> to reap children, -simply ignoring pids you don't know. -</p> - -<p> - If your (badly programmed) application has trouble handling unknown -children, consider using a ftrigrd service. -</p> - -<h3> A programming example </h3> - -<p> - The <tt>src/pipe-tools/s6-ftrig-listen1.c</tt> and -<tt>src/supervision/s6-svwait.c</tt> files in the s6 package, -for instance, illustrate how to use the ftrigr library. -</p> - - -<h3> Synchronous functions with a specified maximum execution time </h3> - -<ul> - <li> Synchronous functions take a <tt>tain_t const *</tt> -(<em>deadline</em>) parameter and a <tt>tain_t *</tt> (<em>stamp</em>) -parameter. Those are pointers to tain_t structures containing absolute times; -the former represents a deadline (in most cases, this time will be in the -future) and the latter must be an accurate enough timestamp. These -structures can be filled using the <tt>tain_</tt> primitives declared in -<a href="http://skarnet.org/software/skalibs/libstddjb/tai.html">skalibs/tai.h</a>. </li> - <li> ("Accurate enough" means that <strong>no blocking system call must have -been made</strong> since the last time <em>stamp</em> was updated (by -<tt>tain_now(&amp;stamp)</tt>). It's a good policy to always update -<em>stamp</em> right after a (potentially) blocking system call like -<tt>select()</tt> returns. And unless the application is extremely CPU-intensive -(think calculus for physicists or astronomers) updating <em>stamp</em> more -frequently is unnecessary.) </li> - <li> If such a synchronous function still hasn't returned when the deadline -occurs, then it will immediately return a failure code and set errno to ETIMEDOUT. -It is possible to pass null pointers to the function instead of pointers to -tain_t structures, in which case the function will never timeout. </li> - <li> If a timeout occurs, the library does not guarantee proper interprocess -communication later on; the application should either die, or at least close -the communication channel and open a new one. </li> - <li> If any waiting occurred, the <em>stamp</em> structure is automatically -updated by the called function, so it always represents an accurate enough estimation -of the current time. This allows the programmer to call several such functions -in a sequence without modifying the <em>deadline</em> and <em>stamp</em> -parameters: then the whole sequence is bound in execution time. </li> - <li> This is a general safety mechanism implemented in -<a href="http://skarnet.org/software/skalibs/libunixonacid/">libunixonacid</a>: -in interprocess communication, purely synchronous primitives are dangerous -because they make the calling process rely on proper behaviour of the called -process. Giving synchronous primitives the ability to timeout allows developers -to write reliable programs even when interacting with software they have no -control on. </li> -</ul> - - -<h3> Starting and ending a session </h3> - -<pre> -ftrigr_t a = FTRIGR_ZERO ; -tain_t deadline, stamp ; - -tain_now(&amp;stamp) ; -tain_addsec(&amp;deadline, &amp;stamp, 2) - -// char const *path = FTRIGR_IPCPATH ; -// ftrigr_start(&amp;a, path, &amp;deadline, &amp;stamp) ; -ftrigr_startf(&amp;a, &amp;deadline, &amp;stamp) ; -</pre> - -<p> -<tt>ftrigr_start</tt> starts a session with a ftrigrd service listening on -<em>path</em>. <br /> -<tt>ftrigr_startf</tt> starts a session with a ftrigrd process as a child -(which is the simplest usage). <br /> -<tt>a</tt> is a ftrigr_t structure that must be declared in the stack and -initialized to FTRIGR_ZERO. -<tt>stamp</tt> must be an accurate enough timestamp. <br /> -If the session initialization fails, the function returns 0 and errno is set; -else the function returns 1. -</p> -<p> -If the absolute time <tt>deadline</tt> is reached and the function -has not returned yet, it immediately returns 0 with errno set to ETIMEDOUT. - -Only local interprocess communications are involved; unless your system is -heavily overloaded, the function should return near-instantly. One or two -seconds of delay between <tt>stamp</tt> and <tt>deadline</tt> should be -enough: if the function takes more than that to return, then there is a -problem with the underlying processes. -</p> - -<p> - You can have more than one session open in parallel, by declaring -several distinct <tt>ftrigr_t</tt> structures and calling -<tt>ftrigr_startf</tt> (or <tt>ftrigr_start</tt>) more than once. -However, this is useless, since one single session can handle -virtually as many concurrent fifodirs as your application needs. -</p> - -<pre> -ftrigr_end(&amp;a) ; -</pre> - -<p> -<tt>ftrigr_end</tt> frees all the resources used by the session. The -<tt>a</tt> structure is then reusable for another session. -</p> - -<h3> Subscribing to a fifodir </h3> - -<pre> -char const *path = "/var/lib/myservice/fifodir" ; -char const *re = "a.*b|c*d" ; -uint32 options = 0 ; - -uint16 id = ftrigr_subscribe (&amp;a, path, re, options, &amp;deadline, &amp;stamp) ; -</pre> - -<p> -<tt>ftrigr_subscribe</tt> instructs the -<a href="s6-ftrigrd.html">s6-ftrigrd daemon</a>, related to the open -session represented by the <tt>a</tt> structure, to subscribe to the -<tt>path</tt> fifodir, and to notify the application when it receives -a series of events that matches the <tt>re</tt> regexp. -<tt>options</tt> can be 0 or FTRIGR_REPEAT. If it is 0, the daemon will -automatically unsubscribe from <tt>path</tt> once <tt>re</tt> has been -matched by a series of events. If it is FTRIGR_REPEAT, it will remain -subscribed until told otherwise. -</p> - -<p> - <tt>ftrigr_subscribe()</tt> returns 0 and sets errno in case of failure, or -a nonzero 16-bit number identifying the subscription in case of success. -</p> - -<p> -<tt>ftrigr_subscribe</tt> should return near-instantly, but if -<em>deadline</em> is reached, it will return 0 ETIMEDOUT. If -<tt>ftrigr_subscribe</tt> returns successfully, then the -s6-ftrigrd daemon is guaranteed to be listening on <tt>path</tt>, -and events can be sent without the risk of a race condition. -</p> - -<h3> Synchronously waiting for events </h3> - -<pre> -uint16 list[1] ; -unsigned int n = 1 ; -char trigger ; -list[0] = id ; - -// r = ftrigr_wait_and(&amp;a, list, n, &amp;deadline) ; -r = ftrigr_wait_or(&amp;a, list, n, &amp;deadline, &amp;trigger) ; -</pre> - -<p> - <tt>ftrigr_wait_and()</tt> waits for <em>all</em> the <tt>n</tt> fifodirs -whose ids are listed in <tt>list</tt> to receive an event. It returns -1 -in case of error or timeout, or a non-negative integer in case of success. <br /> - <tt>ftrigr_wait_or()</tt> waits for <em>one</em> of the <tt>n</tt> fifodirs -whose ids are listed in <tt>list</tt> to receive an event. It returns -1 -in case of error or timeout; if it succeeds, the return value is the -position in <tt>list</tt>, starting at 0, of the identifier that received -an event; and <tt>trigger</tt> is set to the character that triggered that -event, i.e. the last character of a sequence that matched the regular -expression <tt>re</tt> used in the subscription. -</p> - -<h3> Asynchronously waiting for events </h3> - -<p> -<em> (from now on, the functions are listed with their prototypes instead -of usage examples.) </em> -</p> - -<pre> -int ftrigr_fd (ftrigr_t const *a) -</pre> - -<p> - Returns a file descriptor to select on for reading. Do not -<tt>read()</tt> it though. -</p> - -<pre> -int ftrigr_update (ftrigr_t *a) -</pre> - -<p> - Call this function whenever the fd checks readability: it will -update <em>a</em>'s internal structures with information from the -<a href="s6-ftrigrd.html">s6-ftrigrd</a> daemon. It returns -1 if an error -occurs; in case of success, it returns the number of identifiers for -which something happened. -</p> - -<p> - When <tt>ftrigr_update</tt> returns, -<tt>genalloc_s(uint16, &amp;a-&gt;list)</tt> points to an array of -<tt>genalloc_len(uint16, &amp;a-&gt;list)</tt> 16-bit unsigned -integers. Those integers are ids waiting to be passed to -<tt>ftrigr_check</tt>. -</p> - -<pre> -int ftrigr_check (ftrigr_t *a, uint16 id, char *what) -</pre> - -<p> - Checks whether an event happened to <em>id</em>. Use after a -call to <tt>ftrigr_update()</tt>. -</p> - -<ul> - <li> If an error occurred, returns -1 and sets errno. The error -number may have been transmitted from -<a href="s6-ftrigrd.html">s6-ftrigrd</a>. </li> - <li> If no notification happened yet, returns 0. </li> - <li> If something happened, writes the character that triggered the -latest notification into <em>what</em> and returns the number of -times that an event happened to this identifier since the last -call to <tt>ftrigr_check()</tt>. </li> -</ul> - -</body> -</html> diff --git a/doc/libftrigw.html b/doc/libftrigw.html @@ -1,115 +0,0 @@ -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <meta http-equiv="Content-Language" content="en" /> - <title>s6: the ftrigw library interface</title> - <meta name="Description" content="s6: the ftrigw library interface" /> - <meta name="Keywords" content="s6 ftrig notification notifier writer libftrigw ftrigw library interface" /> - <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> - </head> -<body> - -<p> -<a href="index.html">s6</a><br /> -<a href="http://skarnet.org/software/">Software</a><br /> -<a href="http://skarnet.org/">skarnet.org</a> -</p> - -<h1> The <tt>ftrigw</tt> library interface </h1> - -<p> - The <tt>ftrigw</tt> library provides an API for notifiers, i.e. -programs that want to regularly announce what they're doing. -</p> - -<p> - Notifiers should create a fifodir in a hardcoded place in the -filesystem, and document its location. Listeners will then be -able to subscribe to that fifodir, and receive the events. -</p> - -<h2> Compiling </h2> - -<ul> - <li> Make sure the s6 headers, as well as the skalibs headers, -are visible in your header search path. </li> - <li> Use <tt>#include &lt;s6/ftrigw.h&gt;</tt> </li> -</ul> - -<h2> Linking </h2> - -<ul> - <li> Make sure the s6 libraries, as well as the skalibs libraries, -are visible in your library search path. </li> - <li> Link against <tt>-ls6</tt> and <tt>-lskarnet</tt>. </li> -</ul> - -<h2> Programming </h2> - -<p> - Check the <tt>s6/ftrigw.h</tt> header for the -exact function prototypes. -</p> - -<h3> Creating a fifodir </h3> - -<pre> -char const *path = "/var/lib/myservice/fifodir" ; -int gid = -1 ; -int forceperms = 0 ; -int r = ftrigw_fifodir_make(path, gid, forceperms) ; -</pre> - -<p> -<tt>ftrigw_fifodir_make</tt> creates a fifodir at the <tt>path</tt> location. -It returns 0, and sets errno, if an error occurs. -It returns 1 if it succeeds. <br /> -If a fifodir, owned by the user, already exists at <tt>path</tt>, and -<tt>forceperms</tt> is zero, then <tt>ftrigw_fifodir_make</tt> immediately -returns a success. If <tt>forceperms</tt> is nonzero, then -it tries to adjust <tt>path</tt>'s permissions before returning. -</p> - -<p> -If <tt>gid</tt> is negative, then <tt>path</tt> is created "public". -Any listener will be able to subscribe to <tt>path</tt>. -If <tt>gid</tt> is nonnegative, then <tt>path</tt> is created "private". -Only processes belonging to group <tt>gid</tt> will be able to -subscribe to <tt>path</tt>. -</p> - -<h3> Sending an event </h3> - -<pre> -char event = 'a' ; -r = ftrigw_notify(path, event) ; -</pre> - -<p> -<tt>ftrigw_notify</tt> sends <tt>event</tt> to all the processes that are -currently subscribed to <tt>path</tt>. -It returns -1 if an error occurs, or the number of successfully notified -processes. -</p> - -<h3> Cleaning up </h3> - -<p> -When stray KILL signals hit <a href="s6-ftrigrd.html">s6-ftrigrd</a> processes, -1. it's a sign of incorrect system administration, 2. they can leave -unused named pipes in the fifodir. It's the fifodir's owner's job, i.e. -the notifier's job, to periodically do some housecleaning and remove -those unused pipes. -</p> - -<pre> -r = ftrigw_clean(path) ; -</pre> - -<p> -<tt>ftrigw_clean</tt> cleans <tt>path</tt>. It returns 0, and sets errno, -if it encounters an error. It returns 1 if it succeeds. -</p> - -</body> -</html> diff --git a/doc/libs6/accessrules.html b/doc/libs6/accessrules.html @@ -0,0 +1,331 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the accessrules library interface</title> + <meta name="Description" content="s6: the accessrules library interface" /> + <meta name="Keywords" content="s6 net accessrules library libs6net unix tcp access control dns ipv4 ipv6" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">libs6</a><br /> +<a href="../">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>accessrules</tt> library interface </h1> + +<p> + The following functions and structures are declared in the <tt>s6/accessrules.h</tt> header, +and implemented in the <tt>libs6.a</tt> or <tt>libs6.so</tt> library. +</p> + +<h2> General information </h2> + +<p> + <tt>accessrules</tt> is an access control library. It looks up +a key in a user-specified database, then returns a code depending on +whether the database allows access (in which case additional information +can also be returned), denies access, or does not contain the key. +</p> + +<p> + <tt>accessrules</tt> has been designed to be easily extensible to any +database format and any key format. +</p> + +<p> + Check the <tt>s6/accessrules.h</tt> header for the exact definitions. +</p> + +<h2> Data structures </h2> + +<ul> + <li> A <tt>s6_accessrules_result_t</tt> is a scalar that +can have the following values: S6_ACCESSRULES_ERROR, +S6_ACCESSRULES_DENY, S6_ACCESSRULES_ALLOW or S6_ACCESSRULES_NOTFOUND. </li> + <li> A <tt>s6_accessrules_params_t</tt> is a structure containing two +<a href="http://skarnet.org/software/skalibs/libstddjb/stralloc.html">strallocs</a>, +<em>.env</em> and <em>.exec</em>, used to return data contained in the +database when a key has been allowed. The interpretation of this data is +application-defined. </li> +</ul> + +<h2> Function types </h2> + +<h3> Backend lookups </h3> + +<p> + A <tt>s6_accessrules_backend_func_t</tt> is the type of a function +that takes a single key, looks it up in a database, and returns the result. +Namely: +</p> + +<p> +<code>s6_accessrules_result_t f (char const *key, unsigned int keylen, void *handle, s6_accessrules_params_t *params) </code> +</p> + +<p> + <em>f</em> looks up key <em>key</em> of length <em>keylen</em> in the database +represented by <em>handle</em> in an implementation-defined way. It returns a +number that says the key has been allowed, denied or not found, or an error +occurred. If the key has been allowed, <em>f</em> stores additional information +from the database into *<em>params</em>. +</p> + +<p> + Two s6_accessrules_backend_func_t functions are natively implemented: +</p> + +<ul> + <li> <tt>s6_accessrules_backend_fs</tt> takes a <tt>char const *</tt> +<em>handle</em> and interprets it as a base directory to look up <em>key</em> +under, in the format understood by +<a href="../s6-accessrules-cdb-from-fs.html">s6-accessrules-cdb-from-fs</a>. </li> + <li> <tt>s6_accessrules_backend_cdb</tt> takes a <tt>struct cdb *</tt> +<em>handle</em> and looks up <em>key</em> in the +<a href="http://cr.yp.to/cdb.html">CDB</a> it points to. <em>handle</em> must +already be mapped to a CDB file. Such a file can be built with the +<a href="../s6-accessrules-cdb-from-fs.html">s6-accessrules-cdb-from-fs</a> +utility. </li> +</ul> + +<h3> Frontend key checking </h3> + +<p> + A <tt>s6_accessrules_keycheck_func_t</tt> is the type of a function that +takes a user-level key, makes a list of corresponding backend-level keys and +calls a <tt>s6_accessrules_backend_func_t</tt> function until it finds +a match. Namely: +</p> + +<p> +<code>s6_accessrules_result_t f (void const *key, void *handle, s6_accessrules_params_t *params, s6_accessrules_backend_func_t *backend) </code> +</p> + +<p> + <em>f</em> derives a list of low-level keys to check from <em>key</em>. +Then, for each key <em>k</em> of length <em>klen</em> in this list, it calls +<tt>(*backend)(k, klen, handle, params)</tt>, returning *<em>backend</em>'s result if it +is not S6_ACCESSRULES_NOTFOUND. If no match can be found in the whole list, +<em>f</em> finally returns S6_ACCESSRULES_NOTFOUND. +</p> + +<p> + Five s6_accessrules_keycheck_func_t functions are natively implemented: +</p> + +<ul> + <li> +<a name="uidgid" /> + <tt>s6_accessrules_keycheck_uidgid</tt> interprets <em>key</em> as a +<a href="http://skarnet.org/software/skalibs/libstddjb/">diuint</a>, i.e. a +structure containing two unsigned ints. The first one is interpreted as an +uid <em>u</em>, the second one as a gid <em>g</em>. The function first looks +for a <tt>uid/<em>u</em></tt> match; if it cannot find one, it looks for a +<tt>gid/<em>g</em></tt> match. If it cannot find one either, it checks +<tt>uid/default</tt> and returns the result. </li> + <li> +<a name="reversedns" /> + <tt>s6_accessrules_keycheck_reversedns</tt> interprets <em>key</em> +as a string containing a FQDN. Then for each suffix <em>k</em> of <em>key</em>, +starting with <em>key</em> itself and ending with <em>key</em>'s TLD, +it looks up <tt>reversedns/<em>k</em></tt>. The final dot is excluded from +<em>k</em>. If no match can be found, the function checks <tt>reversedns/@</tt> +and returns the result. For instance, if <em>key</em> is "foo.bar.com", +the following strings are looked up, in that order: + <ul> + <li> reversedns/foo.bar.com </li> + <li> reversedns/bar.com </li> + <li> reversedns/com </li> + <li> reversedns/@ </li> + </ul> </li> + <li> +<a name="ip4" /> + <tt>s6_accessrules_keycheck_ip4</tt> interprets <em>key</em> as +4 network-byte-order characters containing an IPv4 address. Then for each +netmask <em>mask</em> from 32 to 0, it constructs the IPv4 network +prefix <em>addr</em> corresponding to that address, and looks up +<tt>ip4/<em>addr</em>_<em>mask</em></tt>. For instance, if <em>key</em> +is "\300\250\001\007", representing the 192.168.1.7 address, the following +strings are looked up, in that order: + <ul> + <li> ip4/192.168.1.7_32 </li> + <li> ip4/192.168.1.6_31 </li> + <li> ip4/192.168.1.4_30 </li> + <li> ip4/192.168.1.0_29 </li> + <li> ip4/192.168.0.0_28 </li> + <li> ip4/192.168.0.0_27 </li> + </ul> + and so on, down to: + <ul> + <li> ip4/192.0.0.0_3 </li> + <li> ip4/192.0.0.0_2 </li> + <li> ip4/128.0.0.0_1 </li> + <li> ip4/0.0.0.0_0 </li> + </ul> + Note that the <tt>ip4/0.0.0.0_0</tt> string is a catch-all key that +matches everything. </li> + <li> +<a name="ip6" /> + <tt>s6_accessrules_keycheck_ip6</tt> interprets <em>key</em> as +16 network-byte-order characters containing an IPv6 address. Then for each +netmask <em>mask</em> from 128 to 0, it constructs the IPv6 network +prefix <em>addr</em> corresponding to that address, +<strong>in canonical form</strong>, +and looks up +<tt>ip6/<em>addr</em>_<em>mask</em></tt>. For instance, if <em>key</em> +is "*\0\024P@\002\b\003\0\0\0\0\0\0\020\006", representing the +2a00:1450:4002:803::1006 address, the following +strings are looked up, in that order: + <ul> + <li> ip6/2a00:1450:4002:803::1006_128 </li> + <li> ip6/2a00:1450:4002:803::1006_127 </li> + <li> ip6/2a00:1450:4002:803::1004_126 </li> + <li> ip6/2a00:1450:4002:803::1000_125 </li> + <li> ip6/2a00:1450:4002:803::1000_124 </li> + <li> ip6/2a00:1450:4002:803::1000_123 </li> + <li> ip6/2a00:1450:4002:803::1000_122 </li> + <li> ip6/2a00:1450:4002:803::1000_121 </li> + <li> ip6/2a00:1450:4002:803::1000_120 </li> + <li> ip6/2a00:1450:4002:803::1000_119 </li> + <li> ip6/2a00:1450:4002:803::1000_118 </li> + <li> ip6/2a00:1450:4002:803::1000_117 </li> + <li> ip6/2a00:1450:4002:803::1000_116 </li> + <li> ip6/2a00:1450:4002:803::1000_115 </li> + <li> ip6/2a00:1450:4002:803::1000_114 </li> + <li> ip6/2a00:1450:4002:803::1000_113 </li> + <li> ip6/2a00:1450:4002:803::_112 </li> + <li> ip6/2a00:1450:4002:803::_111 </li> + </ul> + and so on, down to: + <ul> + <li> ip6/2a00::_11 </li> + <li> ip6/2800::_10 </li> + <li> ip6/2800::_9 </li> + <li> ip6/2000::_8 </li> + <li> ip6/2000::_7 </li> + <li> ip6/2000::_6 </li> + <li> ip6/2000::_5 </li> + <li> ip6/2000::_4 </li> + <li> ip6/2000::_3 </li> + <li> ip6/::_2 </li> + <li> ip6/::_1 </li> + <li> ip6/::_0 </li> + </ul> + Note that the <tt>ip6/::_0</tt> string is a catch-all key that +matches everything. </li> + <li> +<a name="ip46" /> + <tt>s6_accessrules_keycheck_ip46</tt> interprets <em>key</em> as a pointer to an +<a href="http://skarnet.org/software/skalibs/libstddjb/ip46.html">ip46_t</a>, and +behaves either as s6_accessrules_keycheck_ip6 or s6_accessrules_keycheck_ip4, +depending on the type of address *<em>key</em> contains. </li> +</ul> + +<h2> Ready-to-use functions </h2> + + Those functions are mostly macros; they're built by associating a frontend +function with a backend function. + +<p> +<code> s6_accessrules_result_t s6_accessrules_uidgid_cdb +(unsigned int u, unsigned int g, struct cdb *c, +s6_accessrules_params_t *params) </code> <br /> +Checks the *<em>c</em> CDB database for an authorization for uid <em>u</em> +and gid <em>g</em>. If the result is S6_ACCESSRULES_ALLOW, additional +information may be stored into <em>params</em>. +</p> + +<p> +<code> s6_accessrules_result_t s6_accessrules_uidgid_fs +(unsigned int u, unsigned int g, char const *dir, +s6_accessrules_params_t *params) </code> <br /> +Checks the <em>dir</em> base directory for an authorization for uid <em>u</em> +and gid <em>g</em>. If the result is S6_ACCESSRULES_ALLOW, additional +information may be stored into <em>params</em>. +</p> + +<p> +<code> s6_accessrules_result_t s6_accessrules_reversedns_cdb +(char const *name, struct cdb *c, +s6_accessrules_params_t *params) </code> <br /> +Checks the *<em>c</em> CDB database for an authorization for the +<em>name</em> FQDN. If the result is S6_ACCESSRULES_ALLOW, additional +information may be stored into <em>params</em>. +</p> + +<p> +<code> s6_accessrules_result_t s6_accessrules_reversedns_fs +(char const *name, char const *dir, +s6_accessrules_params_t *params) </code> <br /> +Checks the <em>dir</em> base directory for an authorization for the +<em>name</em> FQDN. If the result is S6_ACCESSRULES_ALLOW, additional +information may be stored into <em>params</em>. +</p> + +<p> +<code> s6_accessrules_result_t s6_accessrules_ip4_cdb +(char const *ip4, struct cdb *c, +s6_accessrules_params_t *params) </code> <br /> +Checks the *<em>c</em> CDB database for an authorization for the +<em>ip4</em> IPv4 address (4 network byte order characters). +If the result is S6_ACCESSRULES_ALLOW, additional +information may be stored into <em>params</em>. +</p> + +<p> +<code> s6_accessrules_result_t s6_accessrules_ip4_fs +(char const *ip4, char const *dir, +s6_accessrules_params_t *params) </code> <br /> +Checks the <em>dir</em> base directory for an authorization for the +<em>ip4</em> IPv4 address (4 network byte order characters). +If the result is S6_ACCESSRULES_ALLOW, additional +information may be stored into <em>params</em>. +</p> + +<p> +<code> s6_accessrules_result_t s6_accessrules_ip6_cdb +(char const *ip6, struct cdb *c, +s6_accessrules_params_t *params) </code> <br /> +Checks the *<em>c</em> CDB database for an authorization for the +<em>ip6</em> IPv6 address (16 network byte order characters). +If the result is S6_ACCESSRULES_ALLOW, additional +information may be stored into <em>params</em>. +</p> + +<p> +<code> s6_accessrules_result_t s6_accessrules_ip6_fs +(char const *ip6, char const *dir, +s6_accessrules_params_t *params) </code> <br /> +Checks the <em>dir</em> base directory for an authorization for the +<em>ip6</em> IPv6 address (16 network byte order characters). +If the result is S6_ACCESSRULES_ALLOW, additional +information may be stored into <em>params</em>. +</p> + +<p> +<code> s6_accessrules_result_t s6_accessrules_ip46_cdb +(ip46_t *ip, struct cdb *c, +s6_accessrules_params_t *params) </code> <br /> +Checks the *<em>c</em> CDB database for an authorization for the +<em>ip</em> IP address. +If the result is S6_ACCESSRULES_ALLOW, additional +information may be stored into <em>params</em>. +</p> + +<p> +<code> s6_accessrules_result_t s6_accessrules_ip46_fs +(ip46_t const *ip, char const *dir, +s6_accessrules_params_t *params) </code> <br /> +Checks the <em>dir</em> base directory for an authorization for the +<em>ip</em> IP address. +If the result is S6_ACCESSRULES_ALLOW, additional +information may be stored into <em>params</em>. +</p> + +</body> +</html> diff --git a/doc/libs6/ftrigr.html b/doc/libs6/ftrigr.html @@ -0,0 +1,268 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the ftrigr library interface</title> + <meta name="Description" content="s6: the ftrigr library interface" /> + <meta name="Keywords" content="s6 ftrig notification subscriber listener libftrigr ftrigr library interface" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">libs6</a><br /> +<a href="../">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>ftrigr</tt> library interface </h1> + +<p> + The <tt>ftrigr</tt> library provides an API for listeners, i.e. +programs that want to subscribe to fifodirs and be instantly +notified when the proper sequence of events happens. +</p> + +<h2> Programming </h2> + +<p> + Check the <tt>s6/ftrigr.h</tt> header for the +exact function prototypes. +</p> + +<p> + Make sure your application is not disturbed by children it doesn't +know it has. This means paying some attention to the SIGCHLD handler, +if any, and to the way you perform <tt>waitpid()</tt>s. The best +practice is to use a +<a href="http://skarnet.org/software/skalibs/libstddjb/selfpipe.html">self-pipe</a> +to handle SIGCHLD (as well as other signals the application needs to trap), +and to <em>always</em> use <tt>wait_nohang()</tt> to reap children, +simply ignoring pids you don't know. +</p> + +<p> + If your (badly programmed) application has trouble handling unknown +children, consider using a ftrigrd service. +</p> + +<h3> A programming example </h3> + +<p> + The <tt>src/pipe-tools/s6-ftrig-listen1.c</tt> and +<tt>src/supervision/s6-svwait.c</tt> files in the s6 package, +for instance, illustrate how to use the ftrigr library. +</p> + + +<h3> Synchronous functions with a specified maximum execution time </h3> + +<ul> + <li> Synchronous functions take a <tt>tain_t const *</tt> +(<em>deadline</em>) parameter and a <tt>tain_t *</tt> (<em>stamp</em>) +parameter. Those are pointers to tain_t structures containing absolute times; +the former represents a deadline (in most cases, this time will be in the +future) and the latter must be an accurate enough timestamp. These +structures can be filled using the <tt>tain_</tt> primitives declared in +<a href="http://skarnet.org/software/skalibs/libstddjb/tai.html">skalibs/tai.h</a>. </li> + <li> ("Accurate enough" means that <strong>no blocking system call must have +been made</strong> since the last time <em>stamp</em> was updated (by +<tt>tain_now(&amp;stamp)</tt>). It's a good policy to always update +<em>stamp</em> right after a (potentially) blocking system call like +<tt>select()</tt> returns. And unless the application is extremely CPU-intensive +(think calculus for physicists or astronomers) updating <em>stamp</em> more +frequently is unnecessary.) </li> + <li> If such a synchronous function still hasn't returned when the deadline +occurs, then it will immediately return a failure code and set errno to ETIMEDOUT. +It is possible to pass null pointers to the function instead of pointers to +tain_t structures, in which case the function will never timeout. </li> + <li> If a timeout occurs, the library does not guarantee proper interprocess +communication later on; the application should either die, or at least close +the communication channel and open a new one. </li> + <li> If any waiting occurred, the <em>stamp</em> structure is automatically +updated by the called function, so it always represents an accurate enough estimation +of the current time. This allows the programmer to call several such functions +in a sequence without modifying the <em>deadline</em> and <em>stamp</em> +parameters: then the whole sequence is bound in execution time. </li> + <li> This is a general safety mechanism implemented in +<a href="http://skarnet.org/software/skalibs/libunixonacid/">libunixonacid</a>: +in interprocess communication, purely synchronous primitives are dangerous +because they make the calling process rely on proper behaviour of the called +process. Giving synchronous primitives the ability to timeout allows developers +to write reliable programs even when interacting with software they have no +control on. </li> +</ul> + + +<h3> Starting and ending a session </h3> + +<pre> +ftrigr_t a = FTRIGR_ZERO ; +tain_t deadline, stamp ; + +tain_now(&amp;stamp) ; +tain_addsec(&amp;deadline, &amp;stamp, 2) + +// char const *path = FTRIGR_IPCPATH ; +// ftrigr_start(&amp;a, path, &amp;deadline, &amp;stamp) ; +ftrigr_startf(&amp;a, &amp;deadline, &amp;stamp) ; +</pre> + +<p> +<tt>ftrigr_start</tt> starts a session with a ftrigrd service listening on +<em>path</em>. <br /> +<tt>ftrigr_startf</tt> starts a session with a ftrigrd process as a child +(which is the simplest usage). <br /> +<tt>a</tt> is a ftrigr_t structure that must be declared in the stack and +initialized to FTRIGR_ZERO. +<tt>stamp</tt> must be an accurate enough timestamp. <br /> +If the session initialization fails, the function returns 0 and errno is set; +else the function returns 1. +</p> +<p> +If the absolute time <tt>deadline</tt> is reached and the function +has not returned yet, it immediately returns 0 with errno set to ETIMEDOUT. + +Only local interprocess communications are involved; unless your system is +heavily overloaded, the function should return near-instantly. One or two +seconds of delay between <tt>stamp</tt> and <tt>deadline</tt> should be +enough: if the function takes more than that to return, then there is a +problem with the underlying processes. +</p> + +<p> + You can have more than one session open in parallel, by declaring +several distinct <tt>ftrigr_t</tt> structures and calling +<tt>ftrigr_startf</tt> (or <tt>ftrigr_start</tt>) more than once. +However, this is useless, since one single session can handle +virtually as many concurrent fifodirs as your application needs. +</p> + +<pre> +ftrigr_end(&amp;a) ; +</pre> + +<p> +<tt>ftrigr_end</tt> frees all the resources used by the session. The +<tt>a</tt> structure is then reusable for another session. +</p> + +<h3> Subscribing to a fifodir </h3> + +<pre> +char const *path = "/var/lib/myservice/fifodir" ; +char const *re = "a.*b|c*d" ; +uint32 options = 0 ; + +uint16 id = ftrigr_subscribe (&amp;a, path, re, options, &amp;deadline, &amp;stamp) ; +</pre> + +<p> +<tt>ftrigr_subscribe</tt> instructs the +<a href="s6-ftrigrd.html">s6-ftrigrd daemon</a>, related to the open +session represented by the <tt>a</tt> structure, to subscribe to the +<tt>path</tt> fifodir, and to notify the application when it receives +a series of events that matches the <tt>re</tt> regexp. +<tt>options</tt> can be 0 or FTRIGR_REPEAT. If it is 0, the daemon will +automatically unsubscribe from <tt>path</tt> once <tt>re</tt> has been +matched by a series of events. If it is FTRIGR_REPEAT, it will remain +subscribed until told otherwise. +</p> + +<p> + <tt>ftrigr_subscribe()</tt> returns 0 and sets errno in case of failure, or +a nonzero 16-bit number identifying the subscription in case of success. +</p> + +<p> +<tt>ftrigr_subscribe</tt> should return near-instantly, but if +<em>deadline</em> is reached, it will return 0 ETIMEDOUT. If +<tt>ftrigr_subscribe</tt> returns successfully, then the +s6-ftrigrd daemon is guaranteed to be listening on <tt>path</tt>, +and events can be sent without the risk of a race condition. +</p> + +<h3> Synchronously waiting for events </h3> + +<pre> +uint16 list[1] ; +unsigned int n = 1 ; +char trigger ; +list[0] = id ; + +// r = ftrigr_wait_and(&amp;a, list, n, &amp;deadline) ; +r = ftrigr_wait_or(&amp;a, list, n, &amp;deadline, &amp;trigger) ; +</pre> + +<p> + <tt>ftrigr_wait_and()</tt> waits for <em>all</em> the <tt>n</tt> fifodirs +whose ids are listed in <tt>list</tt> to receive an event. It returns -1 +in case of error or timeout, or a non-negative integer in case of success. <br /> + <tt>ftrigr_wait_or()</tt> waits for <em>one</em> of the <tt>n</tt> fifodirs +whose ids are listed in <tt>list</tt> to receive an event. It returns -1 +in case of error or timeout; if it succeeds, the return value is the +position in <tt>list</tt>, starting at 0, of the identifier that received +an event; and <tt>trigger</tt> is set to the character that triggered that +event, i.e. the last character of a sequence that matched the regular +expression <tt>re</tt> used in the subscription. +</p> + +<h3> Asynchronously waiting for events </h3> + +<p> +<em> (from now on, the functions are listed with their prototypes instead +of usage examples.) </em> +</p> + +<pre> +int ftrigr_fd (ftrigr_t const *a) +</pre> + +<p> + Returns a file descriptor to select on for reading. Do not +<tt>read()</tt> it though. +</p> + +<pre> +int ftrigr_update (ftrigr_t *a) +</pre> + +<p> + Call this function whenever the fd checks readability: it will +update <em>a</em>'s internal structures with information from the +<a href="s6-ftrigrd.html">s6-ftrigrd</a> daemon. It returns -1 if an error +occurs; in case of success, it returns the number of identifiers for +which something happened. +</p> + +<p> + When <tt>ftrigr_update</tt> returns, +<tt>genalloc_s(uint16, &amp;a-&gt;list)</tt> points to an array of +<tt>genalloc_len(uint16, &amp;a-&gt;list)</tt> 16-bit unsigned +integers. Those integers are ids waiting to be passed to +<tt>ftrigr_check</tt>. +</p> + +<pre> +int ftrigr_check (ftrigr_t *a, uint16 id, char *what) +</pre> + +<p> + Checks whether an event happened to <em>id</em>. Use after a +call to <tt>ftrigr_update()</tt>. +</p> + +<ul> + <li> If an error occurred, returns -1 and sets errno. The error +number may have been transmitted from +<a href="s6-ftrigrd.html">s6-ftrigrd</a>. </li> + <li> If no notification happened yet, returns 0. </li> + <li> If something happened, writes the character that triggered the +latest notification into <em>what</em> and returns the number of +times that an event happened to this identifier since the last +call to <tt>ftrigr_check()</tt>. </li> +</ul> + +</body> +</html> diff --git a/doc/libs6/ftrigw.html b/doc/libs6/ftrigw.html @@ -0,0 +1,100 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the ftrigw library interface</title> + <meta name="Description" content="s6: the ftrigw library interface" /> + <meta name="Keywords" content="s6 ftrig notification notifier writer libftrigw ftrigw library interface" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">libs6</a><br /> +<a href="../">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>ftrigw</tt> library interface </h1> + +<p> + The <tt>ftrigw</tt> library provides an API for notifiers, i.e. +programs that want to regularly announce what they're doing. +</p> + +<p> + Notifiers should create a fifodir in a hardcoded place in the +filesystem, and document its location. Listeners will then be +able to subscribe to that fifodir, and receive the events. +</p> + +<h2> Programming </h2> + +<p> + Check the <tt>s6/ftrigw.h</tt> header for the +exact function prototypes. +</p> + +<h3> Creating a fifodir </h3> + +<pre> +char const *path = "/var/lib/myservice/fifodir" ; +int gid = -1 ; +int forceperms = 0 ; +int r = ftrigw_fifodir_make(path, gid, forceperms) ; +</pre> + +<p> +<tt>ftrigw_fifodir_make</tt> creates a fifodir at the <tt>path</tt> location. +It returns 0, and sets errno, if an error occurs. +It returns 1 if it succeeds. <br /> +If a fifodir, owned by the user, already exists at <tt>path</tt>, and +<tt>forceperms</tt> is zero, then <tt>ftrigw_fifodir_make</tt> immediately +returns a success. If <tt>forceperms</tt> is nonzero, then +it tries to adjust <tt>path</tt>'s permissions before returning. +</p> + +<p> +If <tt>gid</tt> is negative, then <tt>path</tt> is created "public". +Any listener will be able to subscribe to <tt>path</tt>. +If <tt>gid</tt> is nonnegative, then <tt>path</tt> is created "private". +Only processes belonging to group <tt>gid</tt> will be able to +subscribe to <tt>path</tt>. +</p> + +<h3> Sending an event </h3> + +<pre> +char event = 'a' ; +r = ftrigw_notify(path, event) ; +</pre> + +<p> +<tt>ftrigw_notify</tt> sends <tt>event</tt> to all the processes that are +currently subscribed to <tt>path</tt>. +It returns -1 if an error occurs, or the number of successfully notified +processes. +</p> + +<h3> Cleaning up </h3> + +<p> +When stray KILL signals hit <a href="s6-ftrigrd.html">s6-ftrigrd</a> processes, +1. it's a sign of incorrect system administration, 2. they can leave +unused named pipes in the fifodir. It's the fifodir's owner's job, i.e. +the notifier's job, to periodically do some housecleaning and remove +those unused pipes. +</p> + +<pre> +r = ftrigw_clean(path) ; +</pre> + +<p> +<tt>ftrigw_clean</tt> cleans <tt>path</tt>. It returns 0, and sets errno, +if it encounters an error. It returns 1 if it succeeds. +</p> + +</body> +</html> diff --git a/doc/libs6/index.html b/doc/libs6/index.html @@ -0,0 +1,72 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6 library interface</title> + <meta name="Description" content="s6: the s6 library interface" /> + <meta name="Keywords" content="s6 s6 libs6 library libs6net" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="../">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6</tt> library interface </h1> + +<h2> General information </h2> + +<p> + <tt>libs6</tt> is a collection of utility +C interfaces, used in the s6 executables. +</p> + +<h2> Compiling </h2> + +<ul> + <li> Make sure the s6 headers, as well as the skalibs headers, +are visible in your header search path. </li> + <li> Use <tt>#include &lt;s6/s6.h&gt;</tt> </li> +</ul> + +<h2> Linking </h2> + +<ul> + <li> Make sure the s6 libraries, as well as the skalibs +libraries, are visible in your library search path. </li> + <li> Link against <tt>-ls6</tt> and <tt>-lskarnet</tt>. +If you're using socket functions (which is the case with +<a href="ftrigr.html">libftrigr</a>, for instance, add +<tt>`cat $sysdeps/socket.lib`</tt> to your command line. +If you're using timed functions involving TAI timestamps +(which is also the case with <a href="ftrigr.html">libftrigr</a> +for instance), add +<tt>`cat $sysdeps/tainnow.lib`</tt>. <tt>$sysdeps</tt> +stands for your skalibs sysdeps directory. </li> +</ul> + +<h2> Programming </h2> + +<p> + The <tt>s6/s6.h</tt> header is actually a +concatenation of other headers: +the libs6net is separated into several modules, each of them with its +own header. +</p> + +<ul> + <li> The <a href="accessrules.html">s6/accessrules.h</a> header +provides functions to check credentials against configuration files. </li> + <li> The <a href="ftrigr.html">s6/ftrigr.h</a> header provides +functions to subscribe to fifodirs and be notified of events. </li> + <li> The <a href="ftrigw.html">s6/ftrigw.h</a> header provides +functions to manage fifodirs and send notifications to them. </li> + <li> The <a href="s6lock.html">s6/s6lock.h</a> header provides +functions to acquire locks with a timeout. </li> +</ul> + +</body> +</html> diff --git a/doc/libs6/s6-ftrigrd.html b/doc/libs6/s6-ftrigrd.html @@ -0,0 +1,80 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-ftrigrd program</title> + <meta name="Description" content="s6: the s6-ftrigrd program" /> + <meta name="Keywords" content="s6 command s6-ftrigrd program internal libexec fifodir regexp subscribe notification listener" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">libs6</a><br /> +<a href="../">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The s6-ftrigrd program </h1> + +<p> +s6-ftrigrd is a helper program that manages a set of subscriptions to fifodirs as well +as regular expressions on events. It takes orders from its client program that controls +it via the <a href="ftrigr.html">ftrigr library</a>, and notifies it when desired +events happen. +</p> + +<h2> Interface </h2> + +<p> + s6-ftrigrd is not meant to be called directly. +</p> + +<ul> + <li> If the client program uses <tt>ftrigr_startf()</tt>, it spawns an instance of +s6-ftrigrd as a child. s6-ftrigrd's stdin is a pipe reading from the client; its +stdout is a pipe writing to the client; its stderr is the same as the client's; and +there's an additional pipe from s6-ftrigrd to the client, used for asynchronous +notifications. </li> + <li> If the client program uses <tt>ftrigr_start()</tt>, then it tries to connect +to a Unix domain socket. A ftrigrd <a href="../localservice.html">local service</a> should be listening to that +socket, i.e. a Unix domain superserver such as +<a href="s6-ipcserver.html">s6-ipcserver</a> +spawning a s6-ftrigrd program on every connection. Then a s6-ftrigrd instance is created +for the client. </li> + <li> When the client uses <tt>ftrigr_end()</tt>, or closes s6-ftrigrd's stdin in +any way, s6-ftrigrd exits 0. </li> +</ul> + +<p> + s6-ftrigrd handles the grunt work of creating fifos in a +<a href="fifodir.html">fifodir</a> for a subscriber. It also wakes up on every +event, and compares the chain of events it received on a given fifodir with the +client-provided regexp. If the chain of events matches the regexp, it notifies +the client. +</p> + +<h2> Notes </h2> + +<p> + The connection management between the client and s6-ftrigrd is entirely done +by the <a href="http://skarnet.org/software/skalibs/libunixonacid/skaclient.html">skaclient</a> +library. +</p> + +<p> + s6-ftrigrd is entirely asynchronous. It stores unread notifications into heap +memory; it can grow in size if there are a lot of events and the client fails +to read them. To avoid uncontrolled growth, make sure your client calls +<tt>ftrigr_update()</tt> as soon as <tt>ftrigr_fd()</tt> becomes readable. +</p> + +<p> + A s6-ftrigrd instance can only handle up to FTRIGRD_MAX (defined in <tt>s6/ftrigr.h</tt>) +subscriptions at once. By default, this number is 1000, which is more than enough for +any reasonable system. +</p> + +</body> +</html> diff --git a/doc/libs6/s6lock.html b/doc/libs6/s6lock.html @@ -0,0 +1,238 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6lock library interface</title> + <meta name="Description" content="s6: the s6lock library interface" /> + <meta name="Keywords" content="s6 timed lock s6lock libs6 library interface" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">libs6</a><br /> +<a href="../">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6lock</tt> library interface </h1> + +<h2> General information </h2> + +<p> + <tt>s6lock</tt> is a C interface to timed locks. Unix natively provides +locks, but the locking primitives are synchronous, so either they are +unbounded in execution time or they require polling. s6lock provides +poll-free locks that can timeout during attempted acquisition. +</p> + +<h2> Programming </h2> + +<ul> + <li> Check the <tt>s6/s6lock.h</tt> header +for the prototypes. The functions documented here are +often simplified macros, for instance relying on the STAMP global variable +to hold the current time. Fully reentrant functions with more control +options are usually available. </li> + <li> Given the nature of the s6lock library, it makes sense to use a +<a href="../localservice.html">s6lockd service</a> concurrently +accessed by several applications using such locks to gate shared +resources. </li> + <li> If you're not using a s6lockd service, +make sure your application is not disturbed by children it doesn't +know it has. Using nonblocking waits, ignoring pids you don't know, and +using a +<a href="http://skarnet.org/software/skalibs/libstddjb/selfpipe.html">self-pipe</a> +if your application is built around an event loop, are good programming +practices. </li> +</ul> + +<h3> Starting and ending a session </h3> + +<pre> +s6lock_t a = S6LOCK_ZERO ; +tain_t deadline ; + +tain_now_g() ; +tain_addsec_g(&amp;deadline, 2) + +char const *path = S6LOCK_IPCPATH ; +s6lock_start_g(&amp;a, path, &amp;deadline) ; +// char const *lockdir = "/tmp/lock" ; +// s6lock_startf_g(&amp;a, lockdir, &amp;deadline) ; +</pre> + +<p> +<tt>s6lock_start_g</tt> starts a session by connecting to a s6lockd service +listening on <em>path</em>. The working directory is set by the administrator +of the service. <br /> +<tt>s6lock_startf_g</tt> starts a session with a s6lockd process as a child, +using <em>lockdir</em> as its working directory. +<br /> +<tt>a</tt> is a s6lock_t structure that must be declared in the stack and +initialized to S6LOCK_ZERO. +If the session initialization fails, the function returns 0 and errno is set; +else the function returns 1. +</p> +<p> +If the absolute time <tt>deadline</tt> is reached and the function +has not returned yet, it immediately returns 0 with errno set to ETIMEDOUT. + +Only local interprocess communications are involved; unless your system is +heavily overloaded, the function should return near-instantly. One or two +seconds of delay between the current time and <tt>deadline</tt> should be +enough: if the function takes more than that to return, then there is a +problem with the underlying processes. +</p> + +<p> + You can have more than one session open in parallel, by declaring +several distinct <tt>s6lock_t</tt> structures and calling +<tt>s6lock_startf_g</tt> (or <tt>s6lock_start_g</tt>) more than once. +However, one single session can handle +virtually as many concurrent locks as your application needs, so +opening several sessions is only useful if you need to acquire locks +in various distinct lock directories. +</p> + +<pre> +s6lock_end(&amp;a) ; +</pre> + +<p> +<tt>s6lock_end</tt> frees all the resources used by the session. The +<tt>a</tt> structure is then reusable for another session. +</p> + +<h3> Acquiring and releasing locks </h3> + +<pre> +uint16 id ; +char const *file = "lockfile" ; +tain_t limit ; +tain_t deadline ; + +int r = s6lock_acquire_sh_g (&amp;a, &amp;id, file, &amp;limit, &amp;deadline) ; +/* int r = s6lock_acquire_ex_g (&amp;a, &amp;id, file, &amp;limit, &amp;deadline) ; */ +r = s6lock_release_g(&amp;a, id, &amp;deadline) ; +</pre> + +<p> +<tt>s6lock_acquire_sh_g</tt> instructs the +<a href="s6lockd.html">s6lockd daemon</a>, related to the open +session represented by the <tt>a</tt> handle, to try and acquire a +shared lock on the +<em>file</em> file located under that daemon's working directory +(typically <tt>/var/lock</tt>). <em>file</em> will be interpreted as +relative to the daemon's working directory even if it starts with a +slash; however, slashes in the middle of <em>file</em> are likely to +result in an error. +</p> + +<p> +<em>limit</em> and <em>deadline</em> are two absolute dates. +<em>deadline</em> is a deadline for the execution of the +function: if by <em>deadline</em> the function has not returned, +then it instantly returns 0 and sets errno to ETIMEDOUT. The +function is normally near-instantaneous, so <em>deadline</em> can +be very close in the future and serves only as a protection against +malicious servers. <em>limit</em> is the acquisition deadline: if +by <em>limit</em> the daemon still has not been able to acquire a lock +on <em>file</em>, then it will report a timeout to the client. +</p> + +<p> +The function returns 1 in case of success, or 0 if an error occurs, +with errno set to a suitable value. If it succeeds, then a 16-bit +number is stored into *<em>id</em>; this number serves as an identifier +for this lock. +</p> + +<p> +<tt>s6lock_acquire_ex_g</tt> works just like <tt>s6lock_acquire_sh_g</tt>, +except that the daemon tries to acquire an exclusive lock. +</p> + +<p> +<tt>s6lock_release_g</tt> releases the lock identified by <em>id</em>. +It normally returns 1. It can return 0 with errno set to a suitable +value if it fails. <em>id</em> is not valid after the corresponding +lock has been released. The function normally returns instantly, with +<em>deadline</em> as a safeguard. +</p> + +<h3> Asynchronously waiting for locks </h3> + +<p> +<em> (from now on, the functions are listed with their prototypes instead +of usage examples.) </em> +</p> + +<pre> +int s6lock_fd (s6lock_t const *a) +</pre> + +<p> + Returns a file descriptor to select on for reading. Do not +<tt>read()</tt> it though. +</p> + +<pre> +int s6lock_update (s6lock_t *a) +</pre> + +<p> + Call this function whenever the fd checks readability: it will +update <em>a</em>'s internal structures with information from the +<a href="s6lockd.html">s6lockd</a> daemon. It returns -1 if an error +occurs; in case of success, it returns the number of identifiers for +which something happened. +</p> + +<p> + When <tt>s6lock_update</tt> returns, +<tt>genalloc_s(uint16, &amp;a-&gt;list)</tt> points to an array of +<tt>genalloc_len(uint16, &amp;a-&gt;list)</tt> 16-bit unsigned +integers. Those integers are ids waiting to be passed to +<tt>s6lock_check</tt>. +</p> + +<pre> +int s6lock_check (s6lock_t *a, uint16 id, char *what) +</pre> + +<p> + Checks whether the lock identified by <em>id</em> has +been acquired. Use after a call to <tt>s6lock_update()</tt>. +</p> + +<ul> + <li> If an error occurred, returns -1 and sets errno. The error +number may have been transmitted from +<a href="s6lockd.html">s6lockd</a>. </li> + <li> If the lock has not been acquired yet, returns 0. </li> + <li> If the lock has been acquired, returns 1. </li> +</ul> + +<h3> Synchronously waiting for locks </h3> + +<p> +<code> int s6lock_wait_or_g (s6lock_t *a, uint16 const *idlist, unsigned int n, tain_t const *deadline) </code> <br /> +Synchronously waits for <em>one</em> of the locks represented by the array pointed to +by <em>idlist</em> of length <em>n</em> to be acquired. Returns -1 if it fails, +or a nonnegative number on success, which is the index in <em>idlist</em> of the +acquired lock's id. If no result has been obtained by <em>deadline</em>, the +function returns -1 ETIMEDOUT. +</p> + +<p> +<code> int s6lock_wait_and_g (s6lock_t *a, uint16 const *idlist, unsigned int n, tain_t const *deadline) </code> <br /> +Synchronously waits for <em>all</em> of the locks represented by the array pointed to +by <em>idlist</em> of length <em>n</em> to be acquired. Returns -1 if it fails and +0 if it succeeds. If no result has been obtained by <em>deadline</em>, the +function returns -1 ETIMEDOUT. +</p> + +</body> +</html> diff --git a/doc/libs6/s6lockd-helper.html b/doc/libs6/s6lockd-helper.html @@ -0,0 +1,52 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6lockd-helper internal program</title> + <meta name="Description" content="s6: the s6lockd-helper internal program" /> + <meta name="Keywords" content="s6 s6lockd-helper lockd asynchronous timed lock daemon helper" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<a href="index.html">libs6</a><br /> +<a href="../">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a><p /> + +<h1> The <tt>s6lockd-helper</tt> program </h1> + +<p> +<tt>s6lockd-helper</tt> is a helper program for the s6lock daemon. +It just acquires a lock and holds it until it is killed or told to +exit by its parent daemon. +</p> + +<h2> Interface </h2> + +<p> + s6lockd-helper is not meant to be invoked directly by the user: +it will be spawned by the +<a href="s6lockd.html">s6lockd</a> program. +</p> + +<h2> Notes </h2> + +<ul> + <li> s6lockd-helper blocks on lock acquisition until it succeeds. It then +notifies its parent. It exits when its parent tells him to (i.e. when the +client asks for lock release). During the lock acquisition phase, it can +be killed if its parent detects a timeout. </li> + <li> One s6lockd-helper process per lock is the only way (apart from +threads) to implement timed lock acquisition. This can lead to a lot of +s6lockd-helper processes, but this is not a problem: + <ul> + <li> Processes are not a scarce resource. Today's schedulers work in O(1), +i.e. a sleeping process takes no scheduling time at all. </li> + <li> s6lockd-helper is extremely tiny. Every instance should use up at +most one or two pages of non-sharable memory. </li> + </ul> </li> +</ul> + +</body> +</html> diff --git a/doc/libs6/s6lockd.html b/doc/libs6/s6lockd.html @@ -0,0 +1,73 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6lockd internal program</title> + <meta name="Description" content="s6: the s6lockd internal program" /> + <meta name="Keywords" content="s6 s6lockd lockd asynchronous timed lock daemon" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<a href="index.html">libs6</a><br /> +<a href="../">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a><p /> + +<h1> The <tt>s6lockd</tt> program </h1> + +<p> +<tt>s6lockd</tt> is the s6lock daemon. It is a program that manages +a set of lock files in a given directory, and associated timeouts. +</p> + +<h2> Interface </h2> + +<p> + s6lockd does not fork, does not background itself automatically, +and does not use syslog. It is not meant to be run directly by the +user: it will be spawned by the +<a href="s6lock.html">s6lock client library</a>. +</p> + +<p> + There are 2 ways to use s6lockd: +</p> + +<ol> + <li> Use the <tt>s6lock_startf()</tt> library call. +A <tt>s6lockd</tt> child will then be spawned from your +calling process, and automatically reaped when you call +<tt>s6lock_end()</tt>. It requires care with applications that +trap SIGCHLD. It also requires care with lock file permissions: +a s6lockd instance might not be able +to open a lock file created by a former instance run by another +client with different permissions. </li> + <li> Use the <tt>s6lock_start()</tt> library call, together with a +<a href="../localservice.html">s6lockd service</a>. +For once, <em>this is the recommended setup</em>: s6lockd creates empty +lock files, and having all s6lockd instances run under the same user +simplifies permissions management considerably. </li> + </li> +</ol> + +<p> +When run as a service, s6lockd has no "standalone" mode: it is +designed to work with a Unix +domain superserver, like +<a href="../s6-ipcserver.html">s6-ipcserver</a>. +s6lockd follows the <a href="http://cr.yp.to/proto/ucspi.txt">UCSPI</a> +interface, it can be directly executed from the superserver. +</p> + +<h2> Notes </h2> + +<ul> + <li> Unix does not natively provide a way to stop blocking on a lock +acquisition after a timeout. To emulate such behaviour, s6lockd actually +spawns a <a href="s6lockd-helper.html">s6lockd-helper</a> child per +requested lock. </li> +</ul> + +</body> +</html> diff --git a/doc/libs6lock/index.html b/doc/libs6lock/index.html @@ -1,256 +0,0 @@ -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <meta http-equiv="Content-Language" content="en" /> - <title>s6: the s6lock library interface</title> - <meta name="Description" content="s6: the s6lock library interface" /> - <meta name="Keywords" content="s6 timed lock s6lock libs6lock library interface" /> - <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> - </head> -<body> - -<p> -<a href="../">s6</a><br /> -<a href="http://skarnet.org/software/">Software</a><br /> -<a href="http://skarnet.org/">skarnet.org</a> -</p> - -<h1> The <tt>s6lock</tt> library interface </h1> - -<h2> General information </h2> - -<p> - <tt>libs6lock</tt> is a C interface to timed locks. Unix natively provides -locks, but the locking primitives are synchronous, so either they are -unbounded in execution time or they require polling. libs6lock provides -poll-free locks that can timeout during attempted acquisition. -</p> - -<h2> Compiling </h2> - -<ul> - <li> Make sure the s6 headers, as well as the skalibs headers, -are visible in your header search path. </li> - <li> Use <tt>#include &lt;s6/s6lock.h&gt;</tt> </li> -</ul> - -<h2> Linking </h2> - -<ul> - <li> Make sure the s6 libraries, as well as the skalibs libraries, -are visible in your library search path. </li> - <li> Link against <tt>-ls6</tt>, <tt>-lskarnet</tt>, -<tt>`cat $sysdeps/socket.lib`</tt>, and -<tt>`cat $sysdeps/tainnow.lib`</tt>, -if <tt>$sysdeps</tt> is your skalibs installation's sysdeps directory. </li> -</ul> - -<h2> Programming </h2> - -<ul> - <li> Check the <tt>s6/s6lock.h</tt> header -for the prototypes. The functions documented here are -often simplified macros, for instance relying on the STAMP global variable -to hold the current time. Fully reentrant functions with more control -options are usually available. </li> - <li> Given the nature of the s6lock library, it makes sense to use a -<a href="http://skarnet.org/software/s6-networking/localservice.html">s6lockd service</a> concurrently -accessed by several applications using such locks to gate shared -resources. </li> - <li> If you're not using a s6lockd service, -make sure your application is not disturbed by children it doesn't -know it has. Using nonblocking waits, ignoring pids you don't know, and -using a -<a href="http://skarnet.org/software/skalibs/libstddjb/selfpipe.html">self-pipe</a> -if your application is built around an event loop, are good programming -practices. </li> -</ul> - -<h3> Starting and ending a session </h3> - -<pre> -s6lock_t a = S6LOCK_ZERO ; -tain_t deadline ; - -tain_now_g() ; -tain_addsec_g(&amp;deadline, 2) - -char const *path = S6LOCK_IPCPATH ; -s6lock_start_g(&amp;a, path, &amp;deadline) ; -// char const *lockdir = "/tmp/lock" ; -// s6lock_startf_g(&amp;a, lockdir, &amp;deadline) ; -</pre> - -<p> -<tt>s6lock_start_g</tt> starts a session by connecting to a s6lockd service -listening on <em>path</em>. The working directory is set by the administrator -of the service. <br /> -<tt>s6lock_startf_g</tt> starts a session with a s6lockd process as a child, -using <em>lockdir</em> as its working directory. -<br /> -<tt>a</tt> is a s6lock_t structure that must be declared in the stack and -initialized to S6LOCK_ZERO. -If the session initialization fails, the function returns 0 and errno is set; -else the function returns 1. -</p> -<p> -If the absolute time <tt>deadline</tt> is reached and the function -has not returned yet, it immediately returns 0 with errno set to ETIMEDOUT. - -Only local interprocess communications are involved; unless your system is -heavily overloaded, the function should return near-instantly. One or two -seconds of delay between the current time and <tt>deadline</tt> should be -enough: if the function takes more than that to return, then there is a -problem with the underlying processes. -</p> - -<p> - You can have more than one session open in parallel, by declaring -several distinct <tt>s6lock_t</tt> structures and calling -<tt>s6lock_startf_g</tt> (or <tt>s6lock_start_g</tt>) more than once. -However, one single session can handle -virtually as many concurrent locks as your application needs, so -opening several sessions is only useful if you need to acquire locks -in various distinct lock directories. -</p> - -<pre> -s6lock_end(&amp;a) ; -</pre> - -<p> -<tt>s6lock_end</tt> frees all the resources used by the session. The -<tt>a</tt> structure is then reusable for another session. -</p> - -<h3> Acquiring and releasing locks </h3> - -<pre> -uint16 id ; -char const *file = "lockfile" ; -tain_t limit ; -tain_t deadline ; - -int r = s6lock_acquire_sh_g (&amp;a, &amp;id, file, &amp;limit, &amp;deadline) ; -/* int r = s6lock_acquire_ex_g (&amp;a, &amp;id, file, &amp;limit, &amp;deadline) ; */ -r = s6lock_release_g(&amp;a, id, &amp;deadline) ; -</pre> - -<p> -<tt>s6lock_acquire_sh_g</tt> instructs the -<a href="s6lockd.html">s6lockd daemon</a>, related to the open -session represented by the <tt>a</tt> handle, to try and acquire a -shared lock on the -<em>file</em> file located under that daemon's working directory -(typically <tt>/var/lock</tt>). <em>file</em> will be interpreted as -relative to the daemon's working directory even if it starts with a -slash; however, slashes in the middle of <em>file</em> are likely to -result in an error. -</p> - -<p> -<em>limit</em> and <em>deadline</em> are two absolute dates. -<em>deadline</em> is a deadline for the execution of the -function: if by <em>deadline</em> the function has not returned, -then it instantly returns 0 and sets errno to ETIMEDOUT. The -function is normally near-instantaneous, so <em>deadline</em> can -be very close in the future and serves only as a protection against -malicious servers. <em>limit</em> is the acquisition deadline: if -by <em>limit</em> the daemon still has not been able to acquire a lock -on <em>file</em>, then it will report a timeout to the client. -</p> - -<p> -The function returns 1 in case of success, or 0 if an error occurs, -with errno set to a suitable value. If it succeeds, then a 16-bit -number is stored into *<em>id</em>; this number serves as an identifier -for this lock. -</p> - -<p> -<tt>s6lock_acquire_ex_g</tt> works just like <tt>s6lock_acquire_sh_g</tt>, -except that the daemon tries to acquire an exclusive lock. -</p> - -<p> -<tt>s6lock_release_g</tt> releases the lock identified by <em>id</em>. -It normally returns 1. It can return 0 with errno set to a suitable -value if it fails. <em>id</em> is not valid after the corresponding -lock has been released. The function normally returns instantly, with -<em>deadline</em> as a safeguard. -</p> - -<h3> Asynchronously waiting for locks </h3> - -<p> -<em> (from now on, the functions are listed with their prototypes instead -of usage examples.) </em> -</p> - -<pre> -int s6lock_fd (s6lock_t const *a) -</pre> - -<p> - Returns a file descriptor to select on for reading. Do not -<tt>read()</tt> it though. -</p> - -<pre> -int s6lock_update (s6lock_t *a) -</pre> - -<p> - Call this function whenever the fd checks readability: it will -update <em>a</em>'s internal structures with information from the -<a href="s6lockd.html">s6lockd</a> daemon. It returns -1 if an error -occurs; in case of success, it returns the number of identifiers for -which something happened. -</p> - -<p> - When <tt>s6lock_update</tt> returns, -<tt>genalloc_s(uint16, &amp;a-&gt;list)</tt> points to an array of -<tt>genalloc_len(uint16, &amp;a-&gt;list)</tt> 16-bit unsigned -integers. Those integers are ids waiting to be passed to -<tt>s6lock_check</tt>. -</p> - -<pre> -int s6lock_check (s6lock_t *a, uint16 id, char *what) -</pre> - -<p> - Checks whether the lock identified by <em>id</em> has -been acquired. Use after a call to <tt>s6lock_update()</tt>. -</p> - -<ul> - <li> If an error occurred, returns -1 and sets errno. The error -number may have been transmitted from -<a href="s6lockd.html">s6lockd</a>. </li> - <li> If the lock has not been acquired yet, returns 0. </li> - <li> If the lock has been acquired, returns 1. </li> -</ul> - -<h3> Synchronously waiting for locks </h3> - -<p> -<code> int s6lock_wait_or_g (s6lock_t *a, uint16 const *idlist, unsigned int n, tain_t const *deadline) </code> <br /> -Synchronously waits for <em>one</em> of the locks represented by the array pointed to -by <em>idlist</em> of length <em>n</em> to be acquired. Returns -1 if it fails, -or a nonnegative number on success, which is the index in <em>idlist</em> of the -acquired lock's id. If no result has been obtained by <em>deadline</em>, the -function returns -1 ETIMEDOUT. -</p> - -<p> -<code> int s6lock_wait_and_g (s6lock_t *a, uint16 const *idlist, unsigned int n, tain_t const *deadline) </code> <br /> -Synchronously waits for <em>all</em> of the locks represented by the array pointed to -by <em>idlist</em> of length <em>n</em> to be acquired. Returns -1 if it fails and -0 if it succeeds. If no result has been obtained by <em>deadline</em>, the -function returns -1 ETIMEDOUT. -</p> - -</body> -</html> diff --git a/doc/libs6lock/s6lockd-helper.html b/doc/libs6lock/s6lockd-helper.html @@ -1,52 +0,0 @@ -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <meta http-equiv="Content-Language" content="en" /> - <title>s6: the s6lockd-helper internal program</title> - <meta name="Description" content="s6: the s6lockd-helper internal program" /> - <meta name="Keywords" content="s6 s6lockd-helper lockd asynchronous timed lock daemon helper" /> - <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> - </head> -<body> - -<a href="index.html">libs6lock</a><br /> -<a href="../">s6</a><br /> -<a href="http://skarnet.org/software/">Software</a><br /> -<a href="http://skarnet.org/">skarnet.org</a><p /> - -<h1> The <tt>s6lockd-helper</tt> program </h1> - -<p> -<tt>s6lockd-helper</tt> is a helper program for the s6lock daemon. -It just acquires a lock and holds it until it is killed or told to -exit by its parent daemon. -</p> - -<h2> Interface </h2> - -<p> - s6lockd-helper is not meant to be invoked directly by the user: -it will be spawned by the -<a href="s6lockd.html">s6lockd</a> program. -</p> - -<h2> Notes </h2> - -<ul> - <li> s6lockd-helper blocks on lock acquisition until it succeeds. It then -notifies its parent. It exits when its parent tells him to (i.e. when the -client asks for lock release). During the lock acquisition phase, it can -be killed if its parent detects a timeout. </li> - <li> One s6lockd-helper process per lock is the only way (apart from -threads) to implement timed lock acquisition. This can lead to a lot of -s6lockd-helper processes, but this is not a problem: - <ul> - <li> Processes are not a scarce resource. Today's schedulers work in O(1), -i.e. a sleeping process takes no scheduling time at all. </li> - <li> s6lockd-helper is extremely tiny. Every instance should use up at -most one or two pages of non-sharable memory. </li> - </ul> </li> -</ul> - -</body> -</html> diff --git a/doc/libs6lock/s6lockd.html b/doc/libs6lock/s6lockd.html @@ -1,73 +0,0 @@ -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <meta http-equiv="Content-Language" content="en" /> - <title>s6: the s6lockd internal program</title> - <meta name="Description" content="s6: the s6lockd internal program" /> - <meta name="Keywords" content="s6 s6lockd lockd asynchronous timed lock daemon" /> - <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> - </head> -<body> - -<a href="index.html">libs6lock</a><br /> -<a href="../">s6</a><br /> -<a href="http://skarnet.org/software/">Software</a><br /> -<a href="http://skarnet.org/">skarnet.org</a><p /> - -<h1> The <tt>s6lockd</tt> program </h1> - -<p> -<tt>s6lockd</tt> is the s6lock daemon. It is a program that manages -a set of lock files in a given directory, and associated timeouts. -</p> - -<h2> Interface </h2> - -<p> - s6lockd does not fork, does not background itself automatically, -and does not use syslog. It is not meant to be run directly by the -user: it will be spawned by the -<a href="index.html">s6lock client library</a>. -</p> - -<p> - There are 2 ways to use s6lockd: -</p> - -<ol> - <li> Use the <tt>s6lock_startf()</tt> library call. -A <tt>s6lockd</tt> child will then be spawned from your -calling process, and automatically reaped when you call -<tt>s6lock_end()</tt>. It requires care with applications that -trap SIGCHLD. It also requires care with lock file permissions: -a s6lockd instance might not be able -to open a lock file created by a former instance run by another -client with different permissions. </li> - <li> Use the <tt>s6lock_start()</tt> library call, together with a -<a href="http://skarnet.org/software/s6-networking/localservice.html">s6lockd service</a>. -For once, <em>this is the recommended setup</em>: s6lockd creates empty -lock files, and having all s6lockd instances run under the same user -simplifies permissions management considerably. </li> - </li> -</ol> - -<p> -When run as a service, s6lockd has no "standalone" mode: it is -designed to work with a Unix -domain superserver, like -<a href="http://skarnet.org/software/s6-networking/s6-ipcserver.html">s6-ipcserver</a>. -s6lockd follows the <a href="http://cr.yp.to/proto/ucspi.txt">UCSPI</a> -interface, it can be directly executed from the superserver. -</p> - -<h2> Notes </h2> - -<ul> - <li> Unix does not natively provide a way to stop blocking on a lock -acquisition after a timeout. To emulate such behaviour, s6lockd actually -spawns a <a href="s6lockd-helper.html">s6lockd-helper</a> child per -requested lock. </li> -</ul> - -</body> -</html> diff --git a/doc/localservice.html b/doc/localservice.html @@ -0,0 +1,153 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: what is a local service</title> + <meta name="Description" content="s6: what is a local service" /> + <meta name="Keywords" content="s6 local service s6-ipcserver" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> Local services </h1> + +<p> + A <em>local service</em> is a daemon that listens to incoming connections +on a Unix domain socket. Clients of the service are programs connecting to +this socket: the daemon performs operations on their behalf. +</p> + +<p> + The service is called <em>local</em> because it is not accessible to +clients from the network. +</p> + +<p> + A widely known example of a local service is the <tt>syslogd</tt> daemon. +On most implementations, it listens to the <tt>/dev/log</tt> socket. +Its clients connect to it and send their logs via the socket. The +<tt>openlog()</tt> function is just a wrapper arround the <tt>connect()</tt> +system call, the <tt>syslog()</tt> function a wrapper around <tt>write()</tt>, +and so on. +</p> + +<h2> Benefits </h2> + +<h3> Privileges </h3> + +<p> + The most important benefit of a local service is that it permits +<strong>controlled privilege gains without using setuid programs</strong>. +The daemon is run as user S; a client running as user C and connecting to +the daemon asks it to perform operations: those will be done as user S. +</p> + +<p> + Standard Unix permissions on the listening socket can be used to implement +some basic access control: to restrict access to clients belonging to group +G, change the socket to user S and group G, and give it 0420 permissions. +This is functionally equivalent to the basic access control for setuid +programs: a program having user S, group G and permissions 4750 will be +executable by group G and run with S rights. +</p> + +<p> + But modern systems implement the +<a href="http://www.superscript.com/ucspi-ipc/getpeereid.html">getpeereid()</a> +system call or library function. This function allows the server to know the +client's credentials: so fine-grained access control is possible. On those +systems, <strong>local services can do as much authentication as setuid programs, +in a much more controlled environment</strong>. +</p> + +<h3> fd-passing </h3> + +<p> + The most obvious difference between a local service and a network service +is that a local service does not serve network clients. But local services +have another nice perk: while network services usually only provide you +with a single channel (a TCP or UDP socket) of communication between the +client and the server, forcing you to multiplex your data into that +channel, local services allow you to have as many +communication channels as you want. +</p> + +<p> +(The SCTP transport layer provides a way for network services to use +several communication channels. Unfortunately, it is not widely deployed +yet, and a lot of network services still depend on TCP.) +</p> + +<p> + The <em>fd-passing</em> mechanism is Unix domain socket black magic +that allows one peer of the socket to send open file descriptors to +the other peer. So, if the server opens a pipe and sends one end of +this pipe to a client via this mechanism, there is effectively a +socket <em>and</em> a pipe between the client and the server. +</p> + +<h2> UCSPI </h2> + +<p> + The <a href="http://cr.yp.to/proto/ucspi.txt">UCSPI</a> protocol +is an easy way of abstracting clients and servers from the network. +A server written as a UCSPI server, just as it can be run +under inetd or +<a href="http://skarnet.org/software/s6-networking/s6-tcpserver.html">s6-tcpserver</a>, +can be run under +<a href="s6-ipcserver.html">s6-ipcserver</a>: choose a socket +location and you have a local service. +</p> + +<p> + Fine-grained access control can be added by inserting +<a href="s6-ipcserver-access.html">s6-ipcserver-access</a> in +your server command line after s6-ipcserver. +</p> + +<p> + A client written as an UCSPI client, i.e. assuming it has descriptor +6 (resp. 7) open and reading from (resp. writing to) the server socket, +can be run under <a href="s6-ipcclient.html">s6-ipcclient</a>. +</p> + +<h2> Use in skarnet.org software </h2> + +<p> + skarnet.org libraries often use a separate process to handle +asynchronicity and background work in a way that's invisible to +the user. Among them are: +</p> + +<ul> + <li> <a href="libs6/s6-ftrigrd.html">s6-ftrigrd</a>, +managing the reception of notifications and only waking up the client process +when the notification pattern matches a regular expression. </li> + <li> <a href="libs6/s6lockd.html">s6lockd</a>, +handling time-constrained lock acquisition on client behalf. </li> + <li> <a href="http://skarnet.org/software/s6-dns/skadns/skadnsd.html">skadnsd</a>, +performing asynchronous DNS queries and only waking up the client process +when an answer arrives. </li> +</ul> + +<p> + Those processes are usually spawned from a client, via the corresponding +<tt>*_startf*()</tt> library call. But they can also be spawned from a +s6-ipcserver program in a local service configuration. In both cases, they +need an additional control channel to be passed from the server to +the client: the main socket is used for synchronous commands from the client +to the server and their answers, whereas the additional channel, which is +now implemented as a socket as well (but created by the server on-demand +and not bound to a local path), is used for asynchronous +notifications from the server to the client. The fd-passing mechanism +is used to transfer the additional channel from the server to the client. +</p> + +</body> +</html> diff --git a/doc/s6-accessrules-cdb-from-fs.html b/doc/s6-accessrules-cdb-from-fs.html @@ -0,0 +1,141 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-accessrules-cdb-from-fs program</title> + <meta name="Description" content="s6: the s6-accessrules-cdb-from-fs program" /> + <meta name="Keywords" content="s6 s6-accessrules-cdb-from-fs tcp unix access control ipcrules tcprules cdb filesystem" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-accessrules-cdb-from-fs</tt> program </h1> + +<p> +<tt>s6-accessrules-cdb-from-fs</tt> compiles a directory +containing a ruleset suitable for +<a href="s6-ipcserver-access.html">s6-ipcserver-access<a> or +<a href="http://skarnet.org/software/s6-networking/s6-tcpserver-access.html">s6-tcpserver-access<a> into a +<a href="http://en.wikipedia.org/wiki/Cdb_(software)">CDB file</a>. +</p> + +<h2> Interface </h2> + +<pre> + s6-accessrules-cdb-from-fs <em>cdbfile</em> <em>dir</em> +</pre> + +<ul> + <li> s6-accessrules-cdb-from-fs compiles the <em>dir</em> +directory containing a ruleset into a +<a href="http://en.wikipedia.org/wiki/Cdb_(software)">CDB file</a> +<em>cdbfile</em> then exits 0. </li> +</ul> + +<h2> Ruleset directory format </h2> + +<p> + To be understood by s6-accessrules-cdb-from-fs, +<a href="s6-ipcserver-access.html">s6-ipcserver-access<a>, or +<a href="http://skarnet.org/software/s6-networking/s6-tcpserver-access.html">s6-tcpserver-access<a>, +<em>dir</em> must have a specific format. +</p> + +<p> + <em>dir</em> contains a series of directories: +</p> + +<ul> + <li> <tt>ip4</tt> for rules on IPv4 addresses </li> + <li> <tt>ip6</tt> for rules on IPv6 addresses </li> + <li> <tt>reversedns</tt> for rules on host names </li> + <li> <tt>uid</tt> for rules on user IDs </li> + <li> <tt>gid</tt> for rules on group IDs </li> +</ul> + +<p> +Depending on the application, other directories can appear in <em>dir</em> +and be compiled into <em>cdbfile</em>, but +<a href="http://skarnet.org/software/s6-networking/s6-tcpserver-access.html">s6-tcpserver-access<a> only +uses the first three, and +<a href="s6-ipcserver-access.html">s6-ipcserver-access<a> only +uses the last two. +</p> + +<p> + Each of those directories contains a set of rules. A rule is +a subdirectory named after the set of keys it matches, and containing +actions that will be executed if the rule is the first matching rule +for the tested key. +</p> + +<p> + The syntax for the rule name is dependent on the nature of keys, and +fully documented on the +<a href="libs6/accessrules.html">accessrules</a> +library page. For instance, a subdirectory named <tt>192.168.0.0_27</tt> +in the <tt>ip4</tt> directory will match every IPv4 address in the +192.168.0.0/27 network that does not match a more precise rule. +</p> + +<p> + The syntax for the actions, however, is the same for every type of key. +A rule subdirectory can contain the following elements: +</p> + +<ul> + <li> a file (that can be empty) named <tt>allow</tt>. If such a file exists, +a key matching this rule will be immediately accepted. </li> + <li> a file (that can be empty) named <tt>deny</tt>. If such a file exists and +no <tt>allow</tt> file exists, a key matching this rule will be immediately +denied. </li> + <li> a subdirectory named <tt>env</tt>. If such a directory exists along +with an <tt>allow</tt> file, then its contents represent environment +modifications that will be applied after accepting the connection and +before executing the next program in the chain, as if the +<a href="s6-envdir.html">s6-envdir</a> +program, without options, was applied to <tt>env</tt>. <tt>env</tt> +has exactly the same format as a directory suitable for s6-envdir; +however, if the modifications take up more than 4096 bytes when +compiled into <em>cdbfile</em>, then s6-accessrules-cdb-from-fs will +complain and exit 100. </li> + <li> a file named <tt>exec</tt>. If such a file exists along with an +<tt>allow</tt> file, then its contents represent a command line that, +interpreted by the +<a href="http://skarnet.org/software/execline/execlineb.html">execlineb</a> +launcher, will be executed after accepting the connection, totally bypassing the +original command line. s6-accessrules-cdb-from-fs truncates the <tt>exec</tt> +file to 4096 bytes max when embedding it into <em>cdbfile</em>, so make +sure it is not larger than that. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> <em>cdbfile</em> can exist prior to, and during, the compilation, +which actually works in a temporary file in the same directory as +<em>cdbfile</em> and performs an atomic replacement when it is done. +So it is not necessary to interrupt a running service during the +compilation. </li> + <li> If s6-accessrules-cdb-from-fs fails at some point, the temporary +file is removed. However, this doesn't happen if +s6-accessrules-cdb-from-fs is interrupted by a signal. </li> + <li> After the program successfully completes, if <em>dir</em> +was a suitable candidate for the <tt>-i</tt> option of +<a href="s6-ipcserver-access.html">s6-ipcserver-access</a> or +<a href="http://skarnet.org/software/s6-networking/s6-tcpserver-access.html">s6-tcpserver-access</a>, then +<em>cdbfile</em> will be a suitable candidate for the <tt>-x</tt> option +of the same program, implementing the same ruleset. </li> + <li> <em>cdbfile</em> can be decompiled by the +<a href="s6-accessrules-fs-from-cdb.html">s6-accessrules-fs-from-cdb</a> +program. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-accessrules-fs-from-cdb.html b/doc/s6-accessrules-fs-from-cdb.html @@ -0,0 +1,60 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-accessrules-fs-from-cdb program</title> + <meta name="Description" content="s6: the s6-accessrules-fs-from-cdb program" /> + <meta name="Keywords" content="s6 s6-accessrules-fs-from-cdb tcp unix access control ipcrules tcprules cdb filesystem" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-accessrules-fs-from-cdb</tt> program </h1> + +<p> +<tt>s6-accessrules-fs-from-cdb</tt> decompiles a CDB database +containing a ruleset suitable for +<a href="s6-ipcserver-access.html">s6-ipcserver-access<a> or +<a href="http://skarnet.org/software/s6-networking/s6-tcpserver-access.html">s6-tcpserver-access<a> and +that has been compiled with +<a href="s6-accessrules-cdb-from-fs.html">s6-accessrules-cdb-from-fs<a>. +</p> + +<h2> Interface </h2> + +<pre> + s6-accessrules-fs-from-cdb <em>dir</em> <em>cdbfile</em> +</pre> + +<ul> + <li> s6-accessrules-fs-from-cdb decompiles the +<a href="http://en.wikipedia.org/wiki/Cdb_(software)">CDB file</a> +<em>cdbfile</em> into the directory <em>dir</em>, then exits 0. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> <em>dir</em> must not exist prior to the decompilation. </li> + <li> <em>dir</em> must be considered a work in progress as long as +s6-accessrules-fs-from-cdb is running. It is only safe to use <em>dir</em> +as a ruleset once the program has exited. </li> + <li> If s6-accessrules-fs-from-cdb fails at some point, the partial +arborescence at <em>dir</em> is removed. However, this doesn't happen if +s6-accessrules-fs-from-cdb is interrupted by a signal. </li> + <li> After the program successfully completes, if <em>cdbfile</em> +was a suitable candidate for the <tt>-x</tt> option of +<a href="s6-ipcserver-access.html">s6-ipcserver-access</a> or +<a href="s6-tcpserver-access.html">s6-tcpserver-access</a>, then +<em>dir</em> will be a suitable candidate for the <tt>-i</tt> option +of the same program, implementing the same ruleset. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-cleanfifodir.html b/doc/s6-cleanfifodir.html @@ -34,7 +34,7 @@ That means it removes all stale FIFOs in <em>fifodir</em>. <p> In normal use, it is not necessary to call s6-cleanfifodir. However, stale -FIFOs can be left by <a href="s6-ftrigrd.html">s6-ftrigrd</a> processes that +FIFOs can be left by <a href="libs6/s6-ftrigrd.html">s6-ftrigrd</a> processes that were violently killed, so it's good practice to regularly clean up fifodirs. </p> diff --git a/doc/s6-connlimit.html b/doc/s6-connlimit.html @@ -0,0 +1,107 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-connlimit program</title> + <meta name="Description" content="s6: the s6-connlimit program" /> + <meta name="Keywords" content="s6 connection limit s6-connlimit" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-connlimit</tt> program </h1> + +<p> +<tt>s6-connlimit</tt> is a small utility to perform IP-based +control on the number of client connections to a TCP socket, and +uid-based control on the number of client connections to a Unix +domain socket. +</p> + +<h2> Interface </h2> + +<pre> + s6-connlimit <em>prog...</em> +</pre> + +<ul> + <li> <tt>s6-connlimit</tt> reads its environment for the PROTO +environment variable, and then for ${PROTO}CONNNUM and ${PROTO}CONNMAX, +which must contain integers. </li> + <li> If the value of ${PROTO}CONNNUM is superior or equal to the value +of ${PROTO}CONNMAX, s6-connlimit exits 1 with an error message. </li> + <li> Else it execs into <em>prog...</em>. </li> + <li> If ${PROTO}CONNMAX is unset, s6-connlimit directly execs into +<em>prog...</em> without performing any check: +no maximum number of connections has been defined. </li> +</ul> + +<h2> Usage </h2> + +<p> + The <a href="http://skarnet.org/software/s6-networking/s6-tcpserver4.html">s6-tcpserver4</a> and +<a href="http://skarnet.org/software/s6-networking/s6-tcpserver6.html">s6-tcpserver6</a> define the PROTO environment +variable to "TCP", and spawn every child server with the TCPCONNNUM environment +variable set to the number of connections from the same IP address. + The <a href="http://skarnet.org/software/s6-networking/s6-tcpserver-access.html">s6-tcpserver-access</a> program +can set environment variables depending on the client's IP address. If the +s6-tcpserver-access database is configured to set the TCPCONNMAX environment +variable for a given set of IP addresses, and s6-tcpserver-access execs into +s6-connlimit, then s6-connlimit will drop connections if there already are +${TCPCONNMAX} connections from the same client IP address. +</p> + +<p> + The <a href="s6-ipcserver.html">s6-ipcserver</a> and +<a href="s6-ipcserver-access.html">s6-ipcserver-access</a> programs can +be used the same way, with "IPC" instead of "TCP", to limit the number +of client connections by UID. +</p> + +<h2> Example </h2> + +<p> + The following command line: +</p> + +<pre> + s6-tcpserver4 -v2 -c1000 -C40 1.2.3.4 80 \ + s6-tcpserver-access -v2 -RHl0 -i <em>dir</em> \ + s6-connlimit \ + <em>prog...</em> +</pre> + +<p> + will run a server listening to IPv4 address 1.2.3.4, on port 80, +serving up to 1000 concurrent connections, and up to 40 concurrent +connections from the same IP address, no matter what the IP address. +For every client connection, it will look up the database set up +in <em>dir</em>; if the connection is accepted, it will run <em>prog...</em>. +</p> + +<p> + If the <tt><em>dir</em>/ip4/5.6.7.8_32/env/TCPCONNMAX</tt> file +exists and contains the string <tt>30</tt>, then at most 30 concurrent +connections from 5.6.7.8 will execute <em>prog...</em>, instead of the +default of 40. +</p> + +<h2> Notes </h2> + +<ul> + <li> The s6-connlimit utility was once part of the +<a href=""http://skarnet.org/software/s6-networking/">s6-networking</a> +suite, and is mostly useful with TCP connections, which is why the +examples here involve TCP. Nevertheless, it can be used with connections +across Unix domain sockets, and that is why it has been moved to the s6 +package. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-ftrig-notify.html b/doc/s6-ftrig-notify.html @@ -37,7 +37,7 @@ with all the characters in <em>message</em>. <p> s6-ftrig-notify cannot be used to send the null character (event 0x00). If you need to send the null character, use the C API: -<a href="libftrigw.html">ftrigw_notify()</a>. +<a href="libs6/ftrigw.html">ftrigw_notify()</a>. </p> </body> diff --git a/doc/s6-ftrigrd.html b/doc/s6-ftrigrd.html @@ -1,79 +0,0 @@ -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <meta http-equiv="Content-Language" content="en" /> - <title>s6: the s6-ftrigrd program</title> - <meta name="Description" content="s6: the s6-ftrigrd program" /> - <meta name="Keywords" content="s6 command s6-ftrigrd program internal libexec fifodir regexp subscribe notification listener" /> - <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> - </head> -<body> - -<p> -<a href="index.html">s6</a><br /> -<a href="http://skarnet.org/software/">Software</a><br /> -<a href="http://skarnet.org/">skarnet.org</a> -</p> - -<h1> The s6-ftrigrd program </h1> - -<p> -s6-ftrigrd is a helper program that manages a set of subscriptions to fifodirs as well -as regular expressions on events. It takes orders from its client program that controls -it via the <a href="libftrigr.html">ftrigr library</a>, and notifies it when desired -events happen. -</p> - -<h2> Interface </h2> - -<p> - s6-ftrigrd is not meant to be called directly. -</p> - -<ul> - <li> If the client program uses <tt>ftrigr_startf()</tt>, it spawns an instance of -s6-ftrigrd as a child. s6-ftrigrd's stdin is a pipe reading from the client; its -stdout is a pipe writing to the client; its stderr is the same as the client's; and -there's an additional pipe from s6-ftrigrd to the client, used for asynchronous -notifications. </li> - <li> If the client program uses <tt>ftrigr_start()</tt>, then it tries to connect -to a Unix domain socket. A <em>ftrigrd service</em> should be listening to that -socket, i.e. a Unix domain superserver such as -<a href="http://www.skarnet.org/software/s6-networking/s6-ipcserver.html">s6-ipcserver</a> -spawning a s6-ftrigrd program on every connection. Then a s6-ftrigrd instance is created -for the client. </li> - <li> When the client uses <tt>ftrigr_end()</tt>, or closes s6-ftrigrd's stdin in -any way, s6-ftrigrd exits 0. </li> -</ul> - -<p> - s6-ftrigrd handles the grunt work of creating fifos in a -<a href="fifodir.html">fifodir</a> for a subscriber. It also wakes up on every -event, and compares the chain of events it received on a given fifodir with the -client-provided regexp. If the chain of events matches the regexp, it notifies -the client. -</p> - -<h2> Notes </h2> - -<p> - The connection management between the client and s6-ftrigrd is entirely done -by the <a href="http://www.skarnet.org/software/skalibs/libunixonacid/skaclient.html">skaclient</a> -library. -</p> - -<p> - s6-ftrigrd is entirely asynchronous. It stores unread notifications into heap -memory; it can grow in size if there are a lot of events and the client fails -to read them. To avoid uncontrolled growth, make sure your client calls -<tt>ftrigr_update()</tt> as soon as <tt>ftrigr_fd()</tt> becomes readable. -</p> - -<p> - A s6-ftrigrd instance can only handle up to FTRIGRD_MAX (defined in <tt>s6/ftrigr.h</tt>) -subscriptions at once. By default, this number is 1000, which is more than enough for -any reasonable system. -</p> - -</body> -</html> diff --git a/doc/s6-ioconnect.html b/doc/s6-ioconnect.html @@ -0,0 +1,90 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-ioconnect program</title> + <meta name="Description" content="s6: the s6-ioconnect program" /> + <meta name="Keywords" content="s6 ioconnect ucspi tcpconnect ipcconnect" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-ioconnect</tt> program </h1> + +<p> +<tt>s6-ioconnect</tt> performs full-duplex data transmission +between two sets of open file descriptors. +</p> + +<h2> Interface </h2> + +<pre> + s6-ioconnect [ -t <em>millisecs</em> ] [ -r <em>fdr</em> ] [ -w <em>fdw</em> ] [ -0 ] [ -1 ] [ -6 ] [ -7 ] +</pre> + +<ul> + <li> s6-ioconnect reads data from its stdin and writes it as is to +file descriptor 7, which is assumed to be open. </li> + <li> It also reads data from its file descriptor 6, which is assumed +to be open, and writes it as is to its stdout. </li> + <li> When both sides have transmitted EOF and s6-ioconnect has +flushed its buffers, it exits 0. </li> +</ul> + +<h2> Options </h2> + +<ul> + <li> <tt>-t&nbsp;<em>millisecs</em></tt>&nbsp;: if no activity on +either side happens for <em>millisecs</em> milliseconds, s6-ioconnect +closes the connection on both ends and exits 1. By default, +<em>millisecs</em> is 0, which means no such timeout. </li> + <li> <tt>-r&nbsp;<em>fdr</em></tt>&nbsp;: Use fd <em>fdr</em> for +"remote" reading instead of fd 6. </li> + <li> <tt>-w&nbsp;<em>fdw</em></tt>&nbsp;: Use fd <em>fdw</em> for +"remote" writing instead of fd 7. </li> + <li> <tt>-0</tt>: assume stdin is a socket and needs to be shut down +for reading after an EOF. </li> + <li> <tt>-1</tt>: assume stdout is a socket and needs to be shut down +for writing to correctly transmit an EOF. </li> + <li> <tt>-6</tt>: assume the remote reading fd is a socket and needs to be shut down +for reading after an EOF. </li> + <li> <tt>-7</tt>: assume the remote writing fd is a socket and needs to be shut down +for writing to correctly transmit an EOF. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> Transmitting EOF across full-duplex sockets +<a href="http://cr.yp.to/tcpip/twofd.html">is ugly</a>. The right thing +in every case cannot be automatically determined, so it is up to the user +to mention that a socket must be shut down. Most of the time, though, +shutting down sockets after EOF <em>is</em> the right thing to do, so +<tt>s6-ioconnect -67</tt> should be the common use case. </li> + <li> The point of s6-ioconnect is to be used together with +<a href="http://skarnet.org/software/s6-networking/s6-tcpclient.html">s6-tcpclient</a> or +<a href="s6-ipcclient.html">s6-ipcclient</a> to establish a full- +duplex connection between the client and the server, for instance +for testing purposes. <tt>s6-ioconnect</tt> is to s6-tcpclient as +<tt>cat</tt> is to s6-tcpserver: a program that will just echo +what it gets. </li> + <li> On modern Linux systems, s6-ioconnect will perform zero-copy +data transmission, via the +<a href="http://man7.org/linux/man-pages/man2/splice.2.html">splice</a> +system call. </li> + <li> The s6-ioconnect utility was once part of the +<a href=""http://skarnet.org/software/s6-networking/">s6-networking</a> +suite, which is why the +examples here involve TCP. Nevertheless, it can be used with connections +across Unix domain sockets as well, and has its place in the s6 +package. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-ipcclient.html b/doc/s6-ipcclient.html @@ -0,0 +1,74 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-ipcclient program</title> + <meta name="Description" content="s6: the s6-ipcclient program" /> + <meta name="Keywords" content="s6 s6-ipcclient ipcclient ucspi unix client" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-ipcclient</tt> program </h1> + +<p> +<tt>s6-ipcclient</tt> is an +<a href="http://cr.yp.to/proto/ucspi.txt">UCSPI client tool</a> for +Unix domain sockets. It connects to a socket, then executes into +a program. +</p> + +<h2> Interface </h2> + +<pre> + s6-ipcclient [ -q | -Q | -v ] [ -p bindpath ] [ -l localname ] <em>path</em> <em>prog...</em> +</pre> + +<ul> + <li> s6-ipcclient connects to a Unix domain socket on <em>path</em>. </li> + <li> It executes into <em>prog...</em> with descriptor 6 reading from +the socket and descriptor 7 writing to it. </li> +</ul> + +<h2> Environment variables </h2> + +<p> + <em>prog...</em> is run with +the following variables set: +</p> + +<ul> + <li> PROTO: always set to IPC </li> + <li> IPCLOCALPATH: set to the path associated with the local socket, +if any. Be aware that it may contain arbitrary characters. </li> +</ul> + +<h2> Options </h2> + +<ul> + <li> <tt>-q</tt>&nbsp;: be quiet. </li> + <li> <tt>-Q</tt>&nbsp;: be normally verbose. This is the default. </li> + <li> <tt>-v</tt>&nbsp;: be verbose. </li> + <li> <tt>-p&nbsp;<em>localpath</em></tt>&nbsp;: bind the local +socket to <em>localpath</em> before connecting to <em>path</em>. </li> + <li> <tt>-l&nbsp;<em>localname</em></tt>&nbsp;: use <em>localname</em> +as the value of the IPCLOCALPATH environment variable. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> s6-ipcclient is mostly used to connect a client to a +<a href="localservice.html">local service</a> without having +to implement networking in the client. For instance, the +<a href="s6-sudo">s6-sudo</a> program does this. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-ipcserver-access.html b/doc/s6-ipcserver-access.html @@ -0,0 +1,172 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-ipcserver-access program</title> + <meta name="Description" content="s6: the s6-ipcserver-access program" /> + <meta name="Keywords" content="s6 s6-ipcserver-access unix access control ipcrules" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-ipcserver-access</tt> program </h1> + +<p> +<tt>s6-ipcserver-access</tt> is a command-line access +control tool for Unix domain sockets on systems where the +<a href="http://www.superscript.com/ucspi-ipc/getpeereid.html">getpeereid()</a> system call can be implemented. +It is meant to be run after +<a href="s6-ipcserverd.html">s6-ipcserverd</a> and before +the application program on the s6-ipcserver command line. +</p> + +<h2> Interface </h2> + +<pre> + s6-ipcserver-access [ -v <em>verbosity</em> ] [ -E | -e ] [ -l <em>localname</em> ] [ -i <em>rulesdir</em> | -x <em>rulesfile</em> ] <em>prog...</em> +</pre> + +<ul> + <li> s6-ipcserver-access checks it is run under a UCSPI server tool +such as <a href="s6-ipcserver.html">s6-ipcserver</a>. + <li> It checks that the remote end of the connection fits the +accepted criteria defined by the database contained in <em>rulesdir</em> +or <em>rulesfile</em>. If the database tells it to reject the connection, +the program exits 1. </li> + <li> It sets up a few additional environment variables. </li> + <li> It executes into <em>prog...</em>, +unless the first matching rule in the rule database +includes instructions to override <em>prog...</em>. </li> +</ul> + +<h2> Environment variables </h2> + +<p> +s6-ipcserver-access expects to inherit some environment variables from +its parent: +</p> + +<ul> + <li> PROTO: normally IPC, but could be anything else, like UNIX. </li> + <li> ${PROTO}REMOTEEUID: the effective UID of the client program connecting to the socket. </li> + <li> ${PROTO}REMOTEEGID: the effective GID of the client program connecting to the socket. </li> +</ul> + +<p> + Additionally, it exports the following variables before executing into +<em>prog...</em>: +</p> + +<ul> + <li> ${PROTO}LOCALPATH: set to the local "address" of the socket, as +reported by the +<a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html">getsockname()</a> +system call, truncated to 99 characters max. </li> +</ul> + +<p> + Also, the access rules database can instruct s6-ipcserver-access to set +up, or unset, more environment variables, depending on the client address. +</p> + +<h2> Options </h2> + +<ul> + <li> <tt>-v&nbsp;<em>verbosity</em></tt>&nbsp;: be more or less verbose, i.e. +print more or less information to stderr: + <ul> + <li> 0: only log error messages. </li> + <li> 1: only log error and warning messages, and accepted connections. +This is the default. </li> + <li> 2: also log rejected connections and more warning messages. </li> + </ul> </li> + <li> <tt>-E</tt>&nbsp;: no environment. All environment variables potentially +set by s6-ipcserver-access, as well as those set by +<a href="s6-ipcserver.html">s6-ipcserver</a>, will be unset instead. </li> + <li> <tt>-e</tt>&nbsp;: set up environment variables normally. +This is the default. </li> + <li> <tt>-l&nbsp;<em>localname</em></tt>&nbsp;: use <em>localname</em> +as the value for the ${PROTO}LOCALPATH environment variable, instead of +looking it up via getsockname(). </li> + <li> <tt>-i&nbsp;<em>rulesdir</em></tt>&nbsp;: check client credentials +against a filesystem-based database in the <em>rulesdir</em> directory. </li> + <li> <tt>-x&nbsp;<em>rulesfile</em></tt>&nbsp;: check client credentials +against a <a href="http://en.wikipedia.org/wiki/Cdb_(software)">cdb</a> +database in the <em>rulesfile</em> file. <tt>-i</tt> and <tt>-x</tt> are +mutually exclusive. If none of those options is given, no credential checking will be +performed, and a warning will be emitted on every connection if +<em>verbosity</em> is 2 or more. </li> +</ul> + +<h2> Access rule checking </h2> + +<p> + s6-ipcserver-access checks its client connection against +a ruleset. This ruleset can be implemented: +</p> + +<ul> + <li> either in the filesystem as an arborescence of directories and files, +if the <tt>-i</tt> option has been given. This option is the most flexible +one: the directory format is simple enough for scripts to understand and +modify it, and the ruleset can be changed dynamically. This is practical, +for instance, for roaming users. </li> +<li> or in a <a href="http://en.wikipedia.org/wiki/Cdb_(software)">CDB +file</a>, if the <tt>-x</tt> option has been given. This option is the most +efficient one if the ruleset is static enough: a lot less system calls are +needed to perform searches in a CDB than in the filesystem. </li> +</ul> + +<p> + The exact format of the ruleset is described on the +<a href="s6-accessrules-cdb-from-fs.html">s6-accessrules-cdb-from-fs</a> page. +</p> + +<p> +s6-ipcserver-access first reads the client UID <em>uid</em> and +GID <em>gid</em> from the +${PROTO}REMOTEEUID and ${PROTO}REMOTEEGID environment variables, and checks +them with the +<a href="libs6/accessrules.html#uidgid">s6_accessrules_keycheck_uidgid()</a> +function. In other words, it tries to match: + +<ul> + <li> <tt>uid/</tt><em>uid</em> </li> + <li> <tt>gid/</tt><em>gid</em> </li> + <li> <tt>uid/default</tt> </li> +</ul> + +<p> + in that order. If no S6_ACCESSRULES_ALLOW result can be obtained, +the connection is denied. +</p> + +<h2> Environment and executable modifications </h2> + +<p> + s6-ipcserver-access interprets non-empty <tt>env</tt> subdirectories +and <tt>exec</tt> files +it finds in the first matching rule of the ruleset, as explained +in the <a href="s6-accessrules-cdb-from-fs.html">s6-accessrules-cdb-from-fs</a> +page. +</p> + +<ul> + <li> An <tt>env</tt> subdirectory is interpreted as if the +<a href="http://skarnet.org/software/s6/s6-envdir.html">s6-envdir</a> +command had been called before executing <em>prog</em>: the environment +is modified according to the contents of <tt>env</tt>. </li> + <li> An <tt>exec</tt> file containing <em>newprog</em> completely +bypasses the rest of s6-ipcserver-access' command line. After +environment modifications, if any, s6-ipcserver-access execs into +<tt><a href="http://skarnet.org/software/execline/execlineb.html">execlineb</a> -c <em>newprog</em></tt>. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-ipcserver-socketbinder.html b/doc/s6-ipcserver-socketbinder.html @@ -0,0 +1,72 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-ipcserver-socketbinder program</title> + <meta name="Description" content="s6: the s6-ipcserver-socketbinder program" /> + <meta name="Keywords" content="s6 s6-ipcserver-socketbinder ipcserver ucspi socket bind listen" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-ipcserver-socketbinder</tt> program </h1> + +<p> +<tt>s6-ipcserver-socketbinder</tt> binds a Unix domain +socket, then executes a program. +</p> + +<h2> Interface </h2> + +<pre> + s6-ipcserver-socketbinder [ -d | -D ] [ -b <em>backlog</em> ] <em>path</em> <em>prog...</em> +</pre> + +<ul> + <li> s6-ipcserver-socketbinder creates a Unix domain socket of type SOCK_STREAM +and binds it to <em>path</em>. It prepares the socket to accept +connections by calling +<a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html">listen()</a>. </li> + <li> It then execs into <em>prog...</em> with the open socket +as its standard input. </li> +</ul> + +<h2> Options </h2> + +<ul> + <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>-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> +</ul> + +<h2> Notes </h2> + +<ul> + <li> s6-ipcserver-socketbinder is part of a set of basic blocks used to +build a flexible Unix super-server. It normally should be given a +command line crafted to make it execute into +<a href="s6-ipcserverd.html">s6-ipcserverd</a> to accept connections +from clients, or into a program such as +<a href="s6-applyuidgid.html">s6-applyuidgid</a> +to drop privileges before doing so. </li> + <li> The <a href="s6-ipcserver.html">s6-ipcserver</a> program does +exactly this. It implements +a full Unix super-server by building a command line starting with +s6-ipcserver-socketbinder and ending with s6-ipcserverd followed by the +application program, and executing into it. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-ipcserver.html b/doc/s6-ipcserver.html @@ -0,0 +1,173 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-ipcserver program</title> + <meta name="Description" content="s6: the s6-ipcserver program" /> + <meta name="Keywords" content="s6 s6-ipcserver ipcserver ucspi unix server super-server" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-ipcserver</tt> program </h1> + +<p> +<tt>s6-ipcserver</tt> is an +<a href="http://cr.yp.to/proto/ucspi.txt">UCSPI server tool</a> for +Unix domain sockets, i.e. a super-server. +It accepts connections from clients, and forks a +program to handle each connection. +</p> + +<h2> Interface </h2> + +<pre> + s6-ipcserver [ -1 ] [ -q | -Q | -v ] [ -d | -D ] [ -P | -p ] [ -c <em>maxconn</em> ] [ -C <em>localmaxconn</em> ] [ -b <em>backlog</em> ] [ -G <em>gidlist</em> ] [ -g <em>gid</em> ] [ -u <em>uid</em> ] [ -U ] <em>path</em> <em>prog...</em> +</pre> + +<ul> + <li> s6-ipcserver binds a Unix domain socket to <em>path</em>. </li> + <li> It can drop its root privileges. </li> + <li> It closes its stdin and stdout. </li> + <li> For every client connection to this socket, it +forks. The child sets some environment variables, then +executes <em>prog...</em> with stdin reading from the socket and +stdout writing to it. </li> + <li> Depending on the verbosity level, it logs what it does to stderr. </li> + <li> It runs until killed by a signal. Depending on the received +signal, it may kill its children before exiting. </li> + <li> s6-ipcserver actually doesn't do any of this itself. It is +a wrapper, rewriting the command line and executing into a chain +of programs that perform those duties. </li> +</ul> + +<h2> Implementation </h2> + +<ul> + <li> s6-ipcserver parses the options and arguments it is given, and +builds a new command line with them. It then executes into that new +command line. </li> + <li> The first program s6-ipcserver executes into is +<a href="s6-ipcserver-socketbinder.html">s6-ipcserver-socketbinder</a>. +It will create and bind a Unix domain socket to <em>path</em>, then +execute into the rest of the command line. </li> + <li> If a privilege-dropping operation has been requested, the +program that s6-ipcserver-socketbinder executes into is +<a href="s6-applyuidgid.html">s6-applyuidgid</a>. +It will drop the root privileges, then execute into the rest of the +command line. </li> + <li> The next program in the chain is +<a href="s6-ipcserverd.html">s6-ipcserverd</a>. It is executed into +by s6-applyuidgid, or directly by s6-ipcserver-socketbinder if no +privilege-dropping operation has been requested. s6-ipcserverd is +the long-lived process, the "daemon" itself, accepting connections +from clients. </li> + <li> For every client, s6-ipcserverd will spawn an instance of +<em>prog...</em>, the remainder of the command line. </li> +</ul> + + +<h2> Options </h2> + +<ul> + <li> <tt>-1</tt>&nbsp;: write <em>path</em>, followed by 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>-q</tt>&nbsp;: be quiet. </li> + <li> <tt>-Q</tt>&nbsp;: be normally verbose. This is the default. </li> + <li> <tt>-v</tt>&nbsp;: be verbose. </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>-P</tt>&nbsp;: disable client credentials lookups. The +IPCREMOTEEUID and IPCREMOTEEGID environment variables will be unset +in every instance of <em>prog...</em>. This is the portable option, +because not every system supports credential lookup across Unix domain +sockets; but it is not as secure. </li> + <li> <tt>-p</tt>&nbsp;: enable client credentials lookups. This +is the default; it works at least on Linux, Solaris, and +*BSD systems. On systems that do not support it, every connection +attempt will fail with a warning message. </li> + <li> <tt>-c&nbsp;<em>maxconn</em></tt>&nbsp;: accept at most +<em>maxconn</em> concurrent connections. Default is 40. It is +impossible to set it higher than 1000. </li> + <li> <tt>-C&nbsp;<em>localmaxconn</em></tt>&nbsp;: accept at most +<em>localmaxconn</em> connections from the same user ID. +Default is 40. It is impossible to set it higher than <em>maxconn</em>. </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 s6-ipcserver'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 s6-ipcserver'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 s6-ipcserver'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 s6-ipcserver'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> +</ul> + +<h2> Implementation </h2> + +<ul> + <li> s6-ipcserver parses the options and arguments it is given, and +builds a new command line with them. It then executes into that new +command line. </li> + <li> The first program s6-ipcserver executes into is +<a href="s6-ipcserver-socketbinder.html">s6-ipcserver-socketbinder</a>. +It will create and bind a Unix domain socket to <em>path</em>, then +execute into the rest of the command line. </li> + <li> If a privilege-dropping operation has been requested, the +program that s6-ipcserver-socketbinder executes into is +<a href="s6-applyuidgid.html">s6-applyuidgid</a>. +It will drop the root privileges, then execute into the rest of the +command line. </li> + <li> The next program in the chain is +<a href="s6-ipcserverd.html">s6-ipcserverd</a>. It is executed into +by s6-applyuidgid, or directly by s6-ipcserver-socketbinder if no +privilege-dropping operation has been requested. s6-ipcserverd is +the long-lived process, the "daemon" itself, accepting connections +from clients. </li> + <li> For every client, s6-ipcserverd will spawn an instance of +<em>prog...</em>, the remainder of the command line. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> s6-ipcserver does not interpret its options itself. It just +dispatches them to the appropriate program on the command line that +it builds. </li> + <li> Previous versions of s6-ipcserver were +monolithic: it did the work of s6-ipcserver-socketbinder, +s6-applyuidgid and s6-ipcserverd itself. The functionality has now +been split into several different programs because some service startup +schemes require the daemon to get its socket from an external +program instead of creating and binding it itself. The most obvious +application of this is upgrading a long-lived process without +losing existing connections. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-ipcserverd.html b/doc/s6-ipcserverd.html @@ -0,0 +1,131 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-ipcserverd program</title> + <meta name="Description" content="s6: the s6-ipcserverd program" /> + <meta name="Keywords" content="s6 s6-ipcserverd ipcserver ucspi unix server super-server" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-ipcserverd</tt> program </h1> + +<p> +<tt>s6-ipcserverd</tt> is the serving part of the +<a href="s6-ipcserver.html">s6-ipcserver</a> super-server. +It assumes that its stdin is a bound and listening Unix +domain socket, and +it accepts connections from clients connecting to it, forking a +program to handle each connection. +</p> + +<h2> Interface </h2> + +<pre> + s6-ipcserverd [ -1 ] [ -v verbosity ] [ -P | -p ] [ -c <em>maxconn</em> ] [ -C <em>localmaxconn</em> ] <em>prog...</em> +</pre> + +<ul> + <li> s6-ipcserverd accepts connections from clients to an already +bound and listening SOCK_STREAM Unix domain socket which is its +standard input. </li> + <li> For every client connection to this socket, it +forks. The child sets some environment variables, then +executes <em>prog...</em> with stdin reading from the socket and +stdout writing to it. </li> + <li> Depending on the verbosity level, it logs what it does to stderr. </li> + <li> It runs until killed by a signal. Depending on the received +signal, it may kill its children before exiting. </li> +</ul> + +<h2> Environment variables </h2> + +<p> + For each connection, an instance of <em>prog...</em> is spawned with +the following variables set: +</p> + +<ul> + <li> PROTO: always set to IPC </li> + <li> IPCREMOTEEUID: set to the effective UID of the client, +unless credentials lookups have been disabled </li> + <li> IPCREMOTEEGID: set to the effective GID of the client, +unless credentials lookups have been disabled </li> + <li> IPCREMOTEPATH: set to the path associated with the remote socket, +if any. Be aware that it may contain arbitrary characters. </li> + <li> IPCCONNNUM: set to the number of connections originating from +the same user (i.e. same uid) </li> +</ul> + +<p> + If client credentials lookup has been disabled, IPCREMOTEEUID and +IPCREMOTEEUID will be set, but empty. +</p> + + +<h2> Options </h2> + +<ul> + <li> <tt>-1</tt>&nbsp;: write a newline to stdout, and close stdout, +right before entering the client-accepting loop. +If stdout is suitably redirected, this can be used by monitoring +programs to check when the server is accepting connections. +The <a href="s6-notifywhenup.html">s6-notifywhenup</a> +program can be used before the s6-ipcserver +invocation to notify listeners when the server is ready. </li> + <li> <tt>-v&nbsp;<em>verbosity</em></tt>&nbsp;: be more or less +verbose. <em>verbosity</em> can be 0 (quiet), 1 (normal), or 2 +(verbose). </li> + <li> <tt>-P</tt>&nbsp;: disable client credentials lookups. The +IPCREMOTEEUID and IPCREMOTEEGID environment variables will be unset +in every instance of <em>prog...</em>. This is the portable option, +because not every system supports credential lookup across Unix domain +sockets; but it is not as secure. </li> + <li> <tt>-p</tt>&nbsp;: enable client credentials lookups. This +is the default; it works at least on Linux, Solaris, and +*BSD systems. On systems that do not support it, every connection +attempt will fail with a warning message. </li> + <li> <tt>-c&nbsp;<em>maxconn</em></tt>&nbsp;: accept at most +<em>maxconn</em> concurrent connections. Default is 40. It is +impossible to set it higher than 1000. </li> + <li> <tt>-C&nbsp;<em>localmaxconn</em></tt>&nbsp;: accept at most +<em>localmaxconn</em> connections from the same user ID. +Default is 40. It is impossible to set it higher than <em>maxconn</em>. </li> +</ul> + +<h2> Signals </h2> + +<ul> + <li> SIGTERM: exit. </li> + <li> SIGHUP: send a SIGTERM and a SIGCONT to all children. </li> + <li> SIGQUIT: send a SIGTERM and a SIGCONT to all children, then exit. </li> + <li> SIGABRT: send a SIGKILL to all children, then exit. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> Unlike his close cousin +<a href="http://www.superscript.com/ucspi-ipc/ipcserver.html">ipcserver</a>, +s6-ipcserverd does not perform operations such as access control. Those are +delegated to the +<a href="s6-ipcserver-access.html">s6-ipcserver-access</a> program. </li> + <li> s6-ipcserverd can be used to set up +<a href="localservice.html">local services</a>. </li> + <li> s6-ipcserverd is meant to be execve'd into by a program that gets +the listening socket. That program is normally +<a href="s6-ipcserver-socketbinder.html">s6-ipcserver-socketbinder</a>, +which creates the socket itself; but it can be a different one if the +socket is to be retrieved by another means, for instance by fd-passing +from a fd-holding daemon (some people call this "socket activation"). </li> +</ul> + +</body> +</html> diff --git a/doc/s6-notifywhenup.html b/doc/s6-notifywhenup.html @@ -68,11 +68,11 @@ Default is 0, meaning infinite. </li> for <em>prog</em> to keep the same pid, which is vital for supervised processes. </li> <li> s6-notifywhenup can be used, for instance, with -<a href="http://skarnet.org/software/s6-networking/s6-tcpserver.html">s6-tcpserver</a> +<a href="s6-ipcserver.html">s6-ipcserver</a> and its <tt>-1</tt> option, so that reliable startup notification is -achieved. <tt>s6-notifywhenup -f s6-tcpserver -1 <em>args</em></tt> will -send a 'U' event to <tt>./event</tt> when s6-tcpserver is actually -listening to its network socket. </li> +achieved. <tt>s6-notifywhenup -f s6-ipcserver -1 <em>args</em></tt> will +send a 'U' event to <tt>./event</tt> when s6-ipcserver is actually +listening to its socket. </li> <li> The <a href="s6-svwait.html">s6-svwait</a> program can be used to wait for 'U' events. </li> </ul> diff --git a/doc/s6-setlock.html b/doc/s6-setlock.html @@ -55,7 +55,7 @@ execution. This is intended: the fd holds the lock, which is released when <em>prog</em> exits. <em>prog</em> must not touch fds it does not know about. </li> <li> If the timed lock option is chosen, s6-setlock does not acquire the lock -itself. Instead, it spawns a <a href="libs6lock/s6lockd-helper.html">s6lockd-helper</a> +itself. Instead, it spawns a <a href="libs6/s6lockd-helper.html">s6lockd-helper</a> process that acquires the lock while s6-setlock controls the timeout; the s6lockd-helper process then holds the lock and lives as long as <em>prog</em>. </li> diff --git a/doc/s6-sudo.html b/doc/s6-sudo.html @@ -0,0 +1,59 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-sudo program</title> + <meta name="Description" content="s6: the s6-sudo program" /> + <meta name="Keywords" content="s6 s6-sudo sudo setuid suid unix privilege gain getpeereid" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-sudo</tt> program </h1> + +<p> +<tt>s6-sudo</tt> connects to a Unix domain socket and passes +its standard file descriptors, command-line arguments and +environment to a program running on the server side, potentially +with different privileges. +</p> + +<h2> Interface </h2> + +<pre> + s6-sudo [ -q | -Q | -v ] [ -p <em>bindpath</em> ] [ -l <em>localname</em> ] [ -e ] [ -t <em>timeoutconn</em> ] [ -T <em>timeoutrun</em> ] <em>path</em> [ <em>args...</em> ] +</pre> + +<ul> + <li> s6-sudo executes into <tt><a href="s6-ipcclient.html">s6-ipcclient</a> <em>path</em> +<a href="s6-sudoc.html">s6-sudoc</a> args...</tt> It does nothing else: it is just a +convenience program. The <a href="s6-ipcclient.html">s6-ipcclient</a> program connects +to a Unix socket at <em>path</em>, and the +<a href="s6-sudoc.html">s6-sudoc program</a> transmits the desired elements over the +socket. </li> + <li> It should be used to connect to a +<a href="localservice.html">local service</a> running the +<a href="s6-sudod.html">s6-sudod</a> program, which will run a server program on the +client's behalf. </li> +</ul> + +<h2> Options </h2> + +<ul> + <li> The <tt>-q</tt>, <tt>-Q</tt>, <tt>-v</tt>, <tt>-p</tt> and </tt>-l</tt> +options are passed to <a href="s6-ipcclient.html">s6-ipcclient</a>. </li> + <li> The <tt>-e</tt>, <tt>-t</tt> and <tt>-T</tt> options are passed to +<a href="s6-sudoc.html">s6-sudoc</a>. </li> + <li> Command-line arguments, if any, are also passed to +<a href="s6-sudoc.html">s6-sudoc</a>, which will transmit them to +<a href="s6-sudod.html">s6-sudod</a> over the socket. +</ul> + +</body> +</html> diff --git a/doc/s6-sudoc.html b/doc/s6-sudoc.html @@ -0,0 +1,80 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-sudoc program</title> + <meta name="Description" content="s6: the s6-sudoc program" /> + <meta name="Keywords" content="s6 s6-sudoc sudo setuid suid unix privilege gain getpeereid client" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-sudoc</tt> program </h1> + +<p> +<tt>s6-sudoc</tt> talks to a peer <a href="s6-sudod.html">s6-sudod</a> +program over a Unix socket, passing it command-line arguments, environment +variables and standard descriptors. +</p> + +<h2> Interface </h2> + +<pre> + s6-sudoc [ -e ] [ -t <em>timeoutconn</em> ] [ -T <em>timeoutrun</em> ] [ <em>args...</em> ] +</pre> + +<ul> + <li> s6-sudoc transmits its standard input, standard output and standard error +via fd-passing over a Unix socket that must be open on its descriptors 6 and 7. + It expects a <a href="s6-sudod.html">s6-sudod</a> process to be receiving them +on the other side. </li> +<li> It also transmits its command-line arguments <em>args</em>, and also its +environment by default. Note that s6-sudod will not necessarily accept all the +environment variables that s6-sudoc tries to transmit. </li> + <li> s6-sudoc waits for the server program run by s6-sudod to finish. It exits +the same exit code as the server program. If the server program is killed by a +signal, s6-sudoc kills itself with the same signal. </li> +</ul> + +<h2> Options </h2> + +<ul> + <li> <tt>-e</tt>&nbsp;: do not attempt to transmit any environment variables +to <a href="s6-sudod.html">s6-sudod</a>. </li> + <li> <tt>-t&nbsp;<em>timeoutconn</em></tt>&nbsp;: if s6-sudod has not +managed to process the given information and start the server program after +<em>timeoutconn</em> milliseconds, give up. By default, <em>timeoutconn</em> +is 0, meaning infinite. Note that there is no reason to set up a nonzero +<em>timeoutconn</em> with a large value: s6-sudod is not supposed to block. +The option is only there to protect against ill-written services. </li> + <li> <tt>-T&nbsp;<em>timeoutrun</em></tt>&nbsp;: if the server program +has not exited after <em>timeoutrun</em> milliseconds, give up. By +default, <em>timeoutrun</em> is 0, meaning infinite. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> If s6-sudoc is killed, or exits after <em>timeoutrun</em> milliseconds, +while the server program is still running, s6-sudod will send a SIGTERM and a +SIGCONT to the server program - but this does not guarantee that it will die. +If the server program keeps running, it might still read from the file that +was s6-sudoc's stdin, or write to the files that were s6-sudoc's stdout or +stderr. <strong>This is a potential security risk</strong>. +Administrators should audit their server programs to make sure this does not +happen. </li> + <li> More generally, anything using signals or terminals will not be +handled transparently by the s6-sudoc + s6-sudod mechanism. The mechanism +was designed to allow programs to gain privileges in specific situations: +short-lived, simple, noninteractive processes. It was not designed to emulate +the full suid functionality and will not go out of its way to do so. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-sudod.html b/doc/s6-sudod.html @@ -0,0 +1,170 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-sudod program</title> + <meta name="Description" content="s6: the s6-sudod program" /> + <meta name="Keywords" content="s6 s6-sudod sudo setuid suid unix privilege gain getpeereid server" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-sudod</tt> program </h1> + +<p> +<tt>s6-sudod</tt> receives command-line arguments, environment variables +and standard descriptors from a peer <a href="s6-sudoc.html">s6-sudoc</a> +program over a Unix socket, then forks another program. +</p> + +<h2> Interface </h2> + +<pre> + s6-sudod [ -0 ] [ -1 ] [ -2 ] [ -s ] [ -t <em>timeout</em> ] [ <em>sargv...</em> ] +</pre> + +<ul> + <li> s6-sudod gets 3 file descriptors via fd-passing over a Unix socket that +must be open on its descriptors 0 and 1. (The received descriptors will be the +stdin, stdout and stderr of the server program.) It expects a +<a href="s6-sudoc.html">s6-sudoc</a> process to be sending them on the +client side. </li> + <li> It also receives a list of command-line arguments <em>cargv...</em>, and +an environment <em>clientenv</em>. </li> + <li> s6-sudod forks and executes <em>sargv...</em> <em>cargv</em>... +The client command line is appended to the server command line. </li> + <li> s6-sudod waits for its child to exit and transmits its exit code +to the peer <a href="s6-sudoc.html">s6-sudoc</a> process. It then exits 0. </li> +</ul> + +<h2> Environment </h2> + +<p> +s6-sudod transmits its own environment to its child, plus the environment sent +by <a href="s6-sudoc.html">s6-sudoc</a>, filtered in the following manner: +for every variable sent by <a href="s6-sudoc.html">s6-sudoc</a>, if the +variable is <strong>present but empty</strong> in s6-sudod's environment, then +its value is overriden by the value given by s6-sudoc. A variable that is +already nonempty, or that doesn't exist, in s6-sudod's environment, will not +be transmitted to the child. +</p> + +<h2> Options </h2> + +<ul> + <li> <tt>-0</tt>&nbsp;: do not inherit stdin from s6-sudoc. The child will be +run with its stdin pointing to <tt>/dev/null</tt> instead. </li> + <li> <tt>-1</tt>&nbsp;: do not inherit stdout from s6-sudoc. The child will be +run with its stdout pointing to <tt>/dev/null</tt> instead. </li> + <li> <tt>-2</tt>&nbsp;: do not inherit stderr from s6-sudoc. The child will be +run with its stderr being a copy of s6-sudod's stderr instead. (This is useful +to still log the child's error messages without sending them to the client.) </li> + <li> <tt>-t&nbsp;<em>timeout</em></tt>&nbsp;: if s6-sudod has not +received all the needed data from the client after <em>timeout</em> +milliseconds, it will exit without spawning a child. By default, <em>timeout</em> +is 0, meaning infinite. This mechanism exists to protect the server from +malicious or buggy clients that would uselessly consume resources. </li> +</ul> + +<h2> Usage example </h2> + +<p> + The typical use of s6-sudod is in a +<a href="localservice.html">local service</a> with a +<a href="s6-ipcserver.html">s6-ipcserver</a> process listening on a Unix +socket, a <a href="s6-ipcserver-access.html">s6-ipcserver-access</a> process +performing client authentication and access control, and possibly a +<a href="s6-envdir.html">s6-envdir</a> +process setting up the environment variables that will be accepted by +s6-sudod. The following script, meant to be a <em>run script</em> in a +<a href="servicedir.html">service directory</a>, +will set up a privileged program: +</p> + +<pre> +#!/command/execlineb -P +fdmove -c 2 1 +s6-envuidgid serveruser +s6-notifywhenup -f +s6-ipcserver -U -1 -- serversocket +s6-ipcserver-access -v2 -l0 -i rules -- +exec -c +s6-envdir env +s6-sudod +sargv +</pre> + +<ul> + <li> <a href="http://skarnet.org/software/execline/execlineb.html">execlineb</a> +executes the script. </li> + <li> <a href="http://skarnet.org/software/execline/fdmove.html">fdmove</a> makes +sure the script's error messages are sent to the service's logger. </li> + <li> <a href="s6-envuidgid.html">s6-envuidgid</a> +sets the UID, GID and GIDLIST environment variables for s6-ipcserver to interpret. </li> + <li> <a href="s6-notifywhenup.html">s6-notifywhenup</a> primes the +service for readiness notification (and the +<tt>-1</tt> option to s6-ipcserver tells the daemon to actually +notify when it's ready). </li> + <li> <a href="s6-ipcserver.html">s6-ipcserver</a> binds to <em>serversocket</em>, +drops its privileges to those of <em>serveruser</em>, and announces its +readiness. Then, for every client connecting to <em>serversocket</em>: + <ul> + <li> <a href="s6-ipcserver-access.html">s6-ipcserver-access</a> checks the +client's credentials according to the rules in directory <em>rules</em>. + <li> <a href="http://skarnet.org/software/execline/exec.html">exec -c</a> +clears the environment. </li> + <li> <a href="http://skarnet.org/software/s6/s6-envdir.html">s6-envdir</a> +sets environment variables according to the directory <em>env</em>. You can +make sure that a variable VAR will be present but empty by performing +<tt>echo > env/VAR</tt>. (A single newline is interpreted by s6-envdir as +an empty variable; whereas if <tt>env/VAR</tt> is totally empty, then the +VAR variable will be removed from the environment.) </li> + <li> s6-sudod reads a command line <em>cargv</em>, a client environment +and file descriptors over the socket. </li> + <li> s6-sudod spawns <tt>sargv cargv</tt>. </li> + </ul> + (Actually, <a href="s6-ipcserver.html">s6-ipcserver</a> does not do this +itself: it executes into other programs that each do one of the tasks. But for +our example, it does not matter.) </li> +</ul> + +<p> + This means that user <em>clientuser</em> running +<tt><a href="s6-sudo.html">s6-sudo</a> serversocket cargv</tt> will be +able, if authorized by the configuration in <em>rules</em>, to run +<tt>sargv cargv</tt> as user <em>serveruser</em>, with stdin, +stdout, stderr and the environment variables properly listed in <em>env</em> +transmitted to <em>sargv</em>. +</p> + +<h2> Notes </h2> + +<ul> + <li> If s6-sudoc is killed, or exits after <em>timeoutrun</em> milliseconds, +while the server program is still running, s6-sudod will send a SIGTERM and a +SIGCONT to its child, then exit 1. However, sending a SIGTERM to the child +does not guarantee that it will die; and +if it keeps running, it might still read from the file that +was s6-sudoc's stdin, or write to the files that were s6-sudoc's stdout or +stderr. <strong>This is a potential security risk</strong>. +Administrators should audit their server programs to make sure this does not +happen. </li> + <li> More generally, anything using signals or terminals will not be +handled transparently by the s6-sudoc + s6-sudod mechanism. The mechanism +was designed to allow programs to gain privileges in specific situations: +short-lived, simple, noninteractive processes. It was not designed to emulate +the full suid functionality and will not go out of its way to do so. </li> + <li> <em>sargv</em> may be empty. In that case, the client is in complete +control of the command line executed as <em>serveruser</em>. This setup is +permitted by s6-sudod, but it is very dangerous, and extreme attention should +be paid to the construction of the s6-ipcserver-access rules. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-supervise.html b/doc/s6-supervise.html @@ -39,7 +39,7 @@ being a leaf. s6-supervise creates it and allows subscriptions to it from processes having the same effective group id as the s6-supervise process. If it already exists, it uses it as is, without modifying the subscription rights. </li> - <li> It <a href="libftrigw.html">sends</a> a <tt>'s'</tt> event to <tt>./event</tt>. </li> + <li> It <a href="libs6/ftrigw.html">sends</a> a <tt>'s'</tt> event to <tt>./event</tt>. </li> <li> If the default service state is up, s6-supervise spawns <tt>./run</tt>. </li> <li> s6-supervise sends a <tt>'u'</tt> event to <tt>./event</tt> whenever it successfully spawns <tt>./run</tt>. </li> diff --git a/doc/s6-svc.html b/doc/s6-svc.html @@ -57,7 +57,7 @@ a SIGTERM and a SIGCONT. Do not restart it. </li> <li> <tt>-u</tt>&nbsp;: up. If the supervised process is down, start it. Automatically restart it when it dies. </li> <li> <tt>-x</tt>&nbsp;: exit. When the service is asked to be down and -the supervised process dies, supervise will exit too. This command should +the supervised process dies, s6-supervise will exit too. This command should normally never be used on a working system. </li> <li> <tt>-O</tt>&nbsp;: Once at most. Do not restart the supervised process when it dies. If it is down when the command is received, do not even start diff --git a/doc/s6-svwait.html b/doc/s6-svwait.html @@ -55,7 +55,7 @@ See the explanation on <a href="notifywhenup.html">this page</a>. </li> given services comes up or down. </li> <li> <tt>-a</tt>&nbsp;: and. s6-svwait will wait until <em>all</em> of the given services come up or down. This is the default. </li> - <li> <tt>-t <em>timeout</em></tt>&nbsp;: if the requested events have not + <li> <tt>-t&nbsp;<em>timeout</em></tt>&nbsp;: if the requested events have not happened after <em>timeout</em> milliseconds, s6-svwait will print a message to stderr and exit 1. By default, <em>timeout</em> is 0, which means no time limit. </li> @@ -64,7 +64,7 @@ limit. </li> <h2> Internals </h2> <p> -s6-svwait spawns a <a href="s6-ftrigrd.html">s6-ftrigrd</a> child to +s6-svwait spawns a <a href="libs6/s6-ftrigrd.html">s6-ftrigrd</a> child to listen to notifications sent by <a href="s6-supervise.html">s6-supervise</a>. It also checks <tt>supervise/status</tt> files, as well as the <tt>supervise/ready</tt> files if necessary, to get the current service diff --git a/doc/upgrade.html b/doc/upgrade.html @@ -17,6 +17,14 @@ <h1> What has changed in s6 </h1> +<h2> in 2.0.2.0 </h2> + +<ul> + <li> Unix domain socket utilities moved from the +<a href="http://skarnet.org/software/s6-networking/">s6-networking</a> +package to s6. </li> +</ul> + <h2> in 2.0.1.0 </h2> <ul> diff --git a/doc/why.html b/doc/why.html @@ -143,8 +143,8 @@ provided. </li> themselves. Again, this is not good design: service management has nothing to do with init or process supervision, and should be implemented on top of it, not as a part of it. </li> - <li> s6 comes with <a href="libftrig.html">libftrig</a>, an event notification -library, and command-line tools based on this library, thus providing a simple + <li> s6 comes with <a href="ftrig.html">an event notification +library</a>, and command-line tools based on this library, thus providing a simple API for future service management tools to build upon. </li> </ul> diff --git a/package/deps.mak b/package/deps.mak @@ -3,8 +3,20 @@ # src/include/s6/ftrigr.h: src/include/s6/config.h -src/include/s6/s6.h: src/include/s6/ftrigr.h src/include/s6/ftrigw.h src/include/s6/s6-supervise.h src/include/s6/s6lock.h +src/include/s6/s6.h: src/include/s6/accessrules.h src/include/s6/ftrigr.h src/include/s6/ftrigw.h src/include/s6/s6-supervise.h src/include/s6/s6lock.h src/include/s6/s6lock.h: src/include/s6/config.h +src/conn-tools/s6-accessrules-cdb-from-fs.o src/conn-tools/s6-accessrules-cdb-from-fs.lo: src/conn-tools/s6-accessrules-cdb-from-fs.c +src/conn-tools/s6-accessrules-fs-from-cdb.o src/conn-tools/s6-accessrules-fs-from-cdb.lo: src/conn-tools/s6-accessrules-fs-from-cdb.c +src/conn-tools/s6-connlimit.o src/conn-tools/s6-connlimit.lo: src/conn-tools/s6-connlimit.c +src/conn-tools/s6-ioconnect.o src/conn-tools/s6-ioconnect.lo: src/conn-tools/s6-ioconnect.c +src/conn-tools/s6-ipcclient.o src/conn-tools/s6-ipcclient.lo: src/conn-tools/s6-ipcclient.c +src/conn-tools/s6-ipcserver-access.o src/conn-tools/s6-ipcserver-access.lo: src/conn-tools/s6-ipcserver-access.c src/include/s6/accessrules.h +src/conn-tools/s6-ipcserver-socketbinder.o src/conn-tools/s6-ipcserver-socketbinder.lo: src/conn-tools/s6-ipcserver-socketbinder.c +src/conn-tools/s6-ipcserver.o src/conn-tools/s6-ipcserver.lo: src/conn-tools/s6-ipcserver.c src/include/s6/config.h +src/conn-tools/s6-ipcserverd.o src/conn-tools/s6-ipcserverd.lo: src/conn-tools/s6-ipcserverd.c +src/conn-tools/s6-sudo.o src/conn-tools/s6-sudo.lo: src/conn-tools/s6-sudo.c src/include/s6/config.h +src/conn-tools/s6-sudoc.o src/conn-tools/s6-sudoc.lo: src/conn-tools/s6-sudoc.c src/conn-tools/s6-sudo.h +src/conn-tools/s6-sudod.o src/conn-tools/s6-sudod.lo: src/conn-tools/s6-sudod.c src/conn-tools/s6-sudo.h src/daemontools-extras/s6-applyuidgid.o src/daemontools-extras/s6-applyuidgid.lo: src/daemontools-extras/s6-applyuidgid.c src/daemontools-extras/s6-envdir.o src/daemontools-extras/s6-envdir.lo: src/daemontools-extras/s6-envdir.c src/daemontools-extras/s6-envuidgid.o src/daemontools-extras/s6-envuidgid.lo: src/daemontools-extras/s6-envuidgid.c @@ -36,6 +48,14 @@ src/libs6/ftrigw_fifodir_make.o src/libs6/ftrigw_fifodir_make.lo: src/libs6/ftri src/libs6/ftrigw_notify.o src/libs6/ftrigw_notify.lo: src/libs6/ftrigw_notify.c src/include/s6/ftrigw.h src/libs6/ftrigw_notifyb.o src/libs6/ftrigw_notifyb.lo: src/libs6/ftrigw_notifyb.c src/libs6/ftrig1.h src/include/s6/ftrigw.h src/libs6/s6-ftrigrd.o src/libs6/s6-ftrigrd.lo: src/libs6/s6-ftrigrd.c src/libs6/ftrig1.h src/include/s6/ftrigr.h +src/libs6/s6_accessrules_backend_cdb.o src/libs6/s6_accessrules_backend_cdb.lo: src/libs6/s6_accessrules_backend_cdb.c src/include/s6/accessrules.h +src/libs6/s6_accessrules_backend_fs.o src/libs6/s6_accessrules_backend_fs.lo: src/libs6/s6_accessrules_backend_fs.c src/include/s6/accessrules.h +src/libs6/s6_accessrules_keycheck_ip4.o src/libs6/s6_accessrules_keycheck_ip4.lo: src/libs6/s6_accessrules_keycheck_ip4.c src/include/s6/accessrules.h +src/libs6/s6_accessrules_keycheck_ip6.o src/libs6/s6_accessrules_keycheck_ip6.lo: src/libs6/s6_accessrules_keycheck_ip6.c src/include/s6/accessrules.h +src/libs6/s6_accessrules_keycheck_reversedns.o src/libs6/s6_accessrules_keycheck_reversedns.lo: src/libs6/s6_accessrules_keycheck_reversedns.c src/include/s6/accessrules.h +src/libs6/s6_accessrules_keycheck_uidgid.o src/libs6/s6_accessrules_keycheck_uidgid.lo: src/libs6/s6_accessrules_keycheck_uidgid.c src/include/s6/accessrules.h +src/libs6/s6_accessrules_uidgid_cdb.o src/libs6/s6_accessrules_uidgid_cdb.lo: src/libs6/s6_accessrules_uidgid_cdb.c src/include/s6/accessrules.h +src/libs6/s6_accessrules_uidgid_fs.o src/libs6/s6_accessrules_uidgid_fs.lo: src/libs6/s6_accessrules_uidgid_fs.c src/include/s6/accessrules.h src/libs6/s6_supervise_lock.o src/libs6/s6_supervise_lock.lo: src/libs6/s6_supervise_lock.c src/include/s6/s6-supervise.h src/libs6/s6_supervise_lock_mode.o src/libs6/s6_supervise_lock_mode.lo: src/libs6/s6_supervise_lock_mode.c src/include/s6/s6-supervise.h src/libs6/s6_svc_main.o src/libs6/s6_svc_main.lo: src/libs6/s6_svc_main.c src/include/s6/s6-supervise.h @@ -70,6 +90,30 @@ src/supervision/s6-svscanctl.o src/supervision/s6-svscanctl.lo: src/supervision/ src/supervision/s6-svstat.o src/supervision/s6-svstat.lo: src/supervision/s6-svstat.c src/include/s6/s6-supervise.h src/supervision/s6-svwait.o src/supervision/s6-svwait.lo: src/supervision/s6-svwait.c src/include/s6/ftrigr.h src/include/s6/s6-supervise.h +s6-accessrules-cdb-from-fs: private EXTRA_LIBS := ${SOCKET_LIB} ${TAINNOW_LIB} +s6-accessrules-cdb-from-fs: src/conn-tools/s6-accessrules-cdb-from-fs.o -lskarnet +s6-accessrules-fs-from-cdb: private EXTRA_LIBS := +s6-accessrules-fs-from-cdb: src/conn-tools/s6-accessrules-fs-from-cdb.o -lskarnet +s6-connlimit: private EXTRA_LIBS := +s6-connlimit: src/conn-tools/s6-connlimit.o -lskarnet +s6-ioconnect: private EXTRA_LIBS := ${SOCKET_LIB} ${TAINNOW_LIB} +s6-ioconnect: src/conn-tools/s6-ioconnect.o -lskarnet +s6-ipcclient: private EXTRA_LIBS := ${SOCKET_LIB} +s6-ipcclient: src/conn-tools/s6-ipcclient.o -lskarnet +s6-ipcserver: private EXTRA_LIBS := +s6-ipcserver: src/conn-tools/s6-ipcserver.o -lskarnet +s6-ipcserver-access: private EXTRA_LIBS := ${SOCKET_LIB} +s6-ipcserver-access: src/conn-tools/s6-ipcserver-access.o ${LIBS6} -lskarnet +s6-ipcserver-socketbinder: private EXTRA_LIBS := ${SOCKET_LIB} +s6-ipcserver-socketbinder: src/conn-tools/s6-ipcserver-socketbinder.o -lskarnet +s6-ipcserverd: private EXTRA_LIBS := ${SOCKET_LIB} +s6-ipcserverd: src/conn-tools/s6-ipcserverd.o -lskarnet +s6-sudo: private EXTRA_LIBS := +s6-sudo: src/conn-tools/s6-sudo.o -lskarnet +s6-sudoc: private EXTRA_LIBS := ${SOCKET_LIB} ${TAINNOW_LIB} +s6-sudoc: src/conn-tools/s6-sudoc.o -lskarnet +s6-sudod: private EXTRA_LIBS := ${SOCKET_LIB} ${TAINNOW_LIB} +s6-sudod: src/conn-tools/s6-sudod.o -lskarnet s6-applyuidgid: private EXTRA_LIBS := s6-applyuidgid: src/daemontools-extras/s6-applyuidgid.o -lskarnet s6-envdir: private EXTRA_LIBS := @@ -96,8 +140,8 @@ s6-tai64nlocal: private EXTRA_LIBS := s6-tai64nlocal: src/daemontools-extras/s6-tai64nlocal.o -lskarnet ucspilogd: private EXTRA_LIBS := ucspilogd: src/daemontools-extras/ucspilogd.o -lskarnet -libs6.a: src/libs6/ftrigr1_zero.o src/libs6/ftrigr_check.o src/libs6/ftrigr_end.o src/libs6/ftrigr_start.o src/libs6/ftrigr_startf.o src/libs6/ftrigr_subscribe.o src/libs6/ftrigr_unsubscribe.o src/libs6/ftrigr_update.o src/libs6/ftrigr_wait_and.o src/libs6/ftrigr_wait_or.o src/libs6/ftrigr_zero.o src/libs6/ftrigw_clean.o src/libs6/ftrigw_fifodir_make.o src/libs6/ftrigw_notify.o src/libs6/ftrigw_notifyb.o src/libs6/s6_supervise_lock.o src/libs6/s6_supervise_lock_mode.o src/libs6/s6_svc_main.o src/libs6/s6_svc_write.o src/libs6/s6_svstatus_pack.o src/libs6/s6_svstatus_read.o src/libs6/s6_svstatus_unpack.o src/libs6/s6_svstatus_write.o src/libs6/s6lock_acquire.o src/libs6/s6lock_check.o src/libs6/s6lock_end.o src/libs6/s6lock_release.o src/libs6/s6lock_start.o src/libs6/s6lock_startf.o src/libs6/s6lock_update.o src/libs6/s6lock_wait_and.o src/libs6/s6lock_wait_or.o src/libs6/s6lock_zero.o -libs6.so: src/libs6/ftrigr1_zero.lo src/libs6/ftrigr_check.lo src/libs6/ftrigr_end.lo src/libs6/ftrigr_start.lo src/libs6/ftrigr_startf.lo src/libs6/ftrigr_subscribe.lo src/libs6/ftrigr_unsubscribe.lo src/libs6/ftrigr_update.lo src/libs6/ftrigr_wait_and.lo src/libs6/ftrigr_wait_or.lo src/libs6/ftrigr_zero.lo src/libs6/ftrigw_clean.lo src/libs6/ftrigw_fifodir_make.lo src/libs6/ftrigw_notify.lo src/libs6/ftrigw_notifyb.lo src/libs6/s6_supervise_lock.lo src/libs6/s6_supervise_lock_mode.lo src/libs6/s6_svc_main.lo src/libs6/s6_svc_write.lo src/libs6/s6_svstatus_pack.lo src/libs6/s6_svstatus_read.lo src/libs6/s6_svstatus_unpack.lo src/libs6/s6_svstatus_write.lo src/libs6/s6lock_acquire.lo src/libs6/s6lock_check.lo src/libs6/s6lock_end.lo src/libs6/s6lock_release.lo src/libs6/s6lock_start.lo src/libs6/s6lock_startf.lo src/libs6/s6lock_update.lo src/libs6/s6lock_wait_and.lo src/libs6/s6lock_wait_or.lo src/libs6/s6lock_zero.lo +libs6.a: src/libs6/ftrigr1_zero.o src/libs6/ftrigr_check.o src/libs6/ftrigr_end.o src/libs6/ftrigr_start.o src/libs6/ftrigr_startf.o src/libs6/ftrigr_subscribe.o src/libs6/ftrigr_unsubscribe.o src/libs6/ftrigr_update.o src/libs6/ftrigr_wait_and.o src/libs6/ftrigr_wait_or.o src/libs6/ftrigr_zero.o src/libs6/ftrigw_clean.o src/libs6/ftrigw_fifodir_make.o src/libs6/ftrigw_notify.o src/libs6/ftrigw_notifyb.o src/libs6/s6_accessrules_backend_cdb.o src/libs6/s6_accessrules_backend_fs.o src/libs6/s6_accessrules_keycheck_ip4.o src/libs6/s6_accessrules_keycheck_ip6.o src/libs6/s6_accessrules_keycheck_reversedns.o src/libs6/s6_accessrules_keycheck_uidgid.o src/libs6/s6_accessrules_uidgid_cdb.o src/libs6/s6_accessrules_uidgid_fs.o src/libs6/s6_supervise_lock.o src/libs6/s6_supervise_lock_mode.o src/libs6/s6_svc_main.o src/libs6/s6_svc_write.o src/libs6/s6_svstatus_pack.o src/libs6/s6_svstatus_read.o src/libs6/s6_svstatus_unpack.o src/libs6/s6_svstatus_write.o src/libs6/s6lock_acquire.o src/libs6/s6lock_check.o src/libs6/s6lock_end.o src/libs6/s6lock_release.o src/libs6/s6lock_start.o src/libs6/s6lock_startf.o src/libs6/s6lock_update.o src/libs6/s6lock_wait_and.o src/libs6/s6lock_wait_or.o src/libs6/s6lock_zero.o +libs6.so: src/libs6/ftrigr1_zero.lo src/libs6/ftrigr_check.lo src/libs6/ftrigr_end.lo src/libs6/ftrigr_start.lo src/libs6/ftrigr_startf.lo src/libs6/ftrigr_subscribe.lo src/libs6/ftrigr_unsubscribe.lo src/libs6/ftrigr_update.lo src/libs6/ftrigr_wait_and.lo src/libs6/ftrigr_wait_or.lo src/libs6/ftrigr_zero.lo src/libs6/ftrigw_clean.lo src/libs6/ftrigw_fifodir_make.lo src/libs6/ftrigw_notify.lo src/libs6/ftrigw_notifyb.lo src/libs6/s6_accessrules_backend_cdb.lo src/libs6/s6_accessrules_backend_fs.lo src/libs6/s6_accessrules_keycheck_ip4.lo src/libs6/s6_accessrules_keycheck_ip6.lo src/libs6/s6_accessrules_keycheck_reversedns.lo src/libs6/s6_accessrules_keycheck_uidgid.lo src/libs6/s6_accessrules_uidgid_cdb.lo src/libs6/s6_accessrules_uidgid_fs.lo src/libs6/s6_supervise_lock.lo src/libs6/s6_supervise_lock_mode.lo src/libs6/s6_svc_main.lo src/libs6/s6_svc_write.lo src/libs6/s6_svstatus_pack.lo src/libs6/s6_svstatus_read.lo src/libs6/s6_svstatus_unpack.lo src/libs6/s6_svstatus_write.lo src/libs6/s6lock_acquire.lo src/libs6/s6lock_check.lo src/libs6/s6lock_end.lo src/libs6/s6lock_release.lo src/libs6/s6lock_start.lo src/libs6/s6lock_startf.lo src/libs6/s6lock_update.lo src/libs6/s6lock_wait_and.lo src/libs6/s6lock_wait_or.lo src/libs6/s6lock_zero.lo s6-ftrigrd: private EXTRA_LIBS := ${SOCKET_LIB} ${TAINNOW_LIB} s6-ftrigrd: src/libs6/s6-ftrigrd.o src/libs6/ftrig1_free.o src/libs6/ftrig1_make.o -lskarnet s6lockd: private EXTRA_LIBS := ${SOCKET_LIB} ${TAINNOW_LIB} diff --git a/package/info b/package/info @@ -1,4 +1,4 @@ package=s6 -version=2.0.1.0 +version=2.0.2.0 category=admin package_macro_name=S6 diff --git a/package/modes b/package/modes @@ -1,29 +1,41 @@ -s6-ftrigrd 0755 -s6-ftrig-listen1 0755 -s6-ftrig-listen 0755 -s6-ftrig-notify 0755 -s6-ftrig-wait 0755 -s6lockd 0755 -s6lockd-helper 0755 -s6-cleanfifodir 0755 -s6-mkfifodir 0755 -s6-notifywhenup 0755 -s6-svscan 0755 -s6-supervise 0755 -s6-svc 0755 -s6-svscanctl 0755 -s6-svok 0755 -s6-svstat 0755 -s6-svwait 0755 -s6-applyuidgid 0700 -s6-envdir 0755 -s6-envuidgid 0755 -s6-fghack 0755 -s6-log 0755 -s6-setlock 0755 -s6-setsid 0755 -s6-setuidgid 0700 -s6-softlimit 0755 -s6-tai64n 0755 -s6-tai64nlocal 0755 -ucspilogd 0755 +s6-ftrigrd 0755 +s6-ftrig-listen1 0755 +s6-ftrig-listen 0755 +s6-ftrig-notify 0755 +s6-ftrig-wait 0755 +s6lockd 0755 +s6lockd-helper 0755 +s6-cleanfifodir 0755 +s6-mkfifodir 0755 +s6-notifywhenup 0755 +s6-svscan 0755 +s6-supervise 0755 +s6-svc 0755 +s6-svscanctl 0755 +s6-svok 0755 +s6-svstat 0755 +s6-svwait 0755 +s6-applyuidgid 0700 +s6-envdir 0755 +s6-envuidgid 0755 +s6-fghack 0755 +s6-log 0755 +s6-setlock 0755 +s6-setsid 0755 +s6-setuidgid 0700 +s6-softlimit 0755 +s6-tai64n 0755 +s6-tai64nlocal 0755 +s6-accessrules-cdb-from-fs 0755 +s6-accessrules-fs-from-cdb 0755 +s6-connlimit 0755 +s6-ioconnect 0755 +s6-ipcclient 0755 +s6-ipcserver-access 0755 +s6-ipcserver-socketbinder 0755 +s6-ipcserver 0755 +s6-ipcserverd 0755 +s6-sudo 0755 +s6-sudoc 0755 +s6-sudod 0755 +ucspilogd 0755 diff --git a/package/targets.mak b/package/targets.mak @@ -24,6 +24,18 @@ s6-setsid \ s6-softlimit \ s6-tai64n \ s6-tai64nlocal \ +s6-accessrules-cdb-from-fs \ +s6-accessrules-fs-from-cdb \ +s6-connlimit \ +s6-ioconnect \ +s6-ipcclient \ +s6-ipcserver-access \ +s6-ipcserver-socketbinder \ +s6-ipcserver \ +s6-ipcserverd \ +s6-sudo \ +s6-sudoc \ +s6-sudod \ ucspilogd SBIN_TARGETS := \ diff --git a/src/conn-tools/deps-exe/s6-accessrules-cdb-from-fs b/src/conn-tools/deps-exe/s6-accessrules-cdb-from-fs @@ -0,0 +1,3 @@ +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/conn-tools/deps-exe/s6-accessrules-fs-from-cdb b/src/conn-tools/deps-exe/s6-accessrules-fs-from-cdb @@ -0,0 +1 @@ +-lskarnet diff --git a/src/conn-tools/deps-exe/s6-connlimit b/src/conn-tools/deps-exe/s6-connlimit @@ -0,0 +1 @@ +-lskarnet diff --git a/src/conn-tools/deps-exe/s6-ioconnect b/src/conn-tools/deps-exe/s6-ioconnect @@ -0,0 +1,3 @@ +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/conn-tools/deps-exe/s6-ipcclient b/src/conn-tools/deps-exe/s6-ipcclient @@ -0,0 +1,2 @@ +-lskarnet +${SOCKET_LIB} diff --git a/src/conn-tools/deps-exe/s6-ipcserver b/src/conn-tools/deps-exe/s6-ipcserver @@ -0,0 +1 @@ +-lskarnet diff --git a/src/conn-tools/deps-exe/s6-ipcserver-access b/src/conn-tools/deps-exe/s6-ipcserver-access @@ -0,0 +1,3 @@ +${LIBS6} +-lskarnet +${SOCKET_LIB} diff --git a/src/conn-tools/deps-exe/s6-ipcserver-socketbinder b/src/conn-tools/deps-exe/s6-ipcserver-socketbinder @@ -0,0 +1,2 @@ +-lskarnet +${SOCKET_LIB} diff --git a/src/conn-tools/deps-exe/s6-ipcserverd b/src/conn-tools/deps-exe/s6-ipcserverd @@ -0,0 +1,2 @@ +-lskarnet +${SOCKET_LIB} diff --git a/src/conn-tools/deps-exe/s6-sudo b/src/conn-tools/deps-exe/s6-sudo @@ -0,0 +1 @@ +-lskarnet diff --git a/src/conn-tools/deps-exe/s6-sudoc b/src/conn-tools/deps-exe/s6-sudoc @@ -0,0 +1,3 @@ +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/conn-tools/deps-exe/s6-sudod b/src/conn-tools/deps-exe/s6-sudod @@ -0,0 +1,3 @@ +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/conn-tools/s6-accessrules-cdb-from-fs.c b/src/conn-tools/s6-accessrules-cdb-from-fs.c @@ -0,0 +1,195 @@ +/* ISC license. */ + +#include <unistd.h> +#include <errno.h> +#include <stdio.h> /* for rename() */ +#include <skalibs/bytestr.h> +#include <skalibs/uint16.h> +#include <skalibs/fmtscan.h> +#include <skalibs/cdb_make.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/direntry.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> +#include <skalibs/random.h> + +#define USAGE "s6-accessrules-cdb-from-fs cdbfile dir" + +static stralloc tmp = STRALLOC_ZERO ; + +static void cleanup (void) +{ + register int e = errno ; + unlink(tmp.s) ; + errno = e ; +} + +static void dienomem (void) +{ + cleanup() ; + strerr_diefu1sys(111, "stralloc_catb") ; +} + +static void doit (struct cdb_make *c, stralloc *sa, unsigned int start) +{ + unsigned int tmpbase = tmp.len ; + unsigned int k = sa->len ; + if (!stralloc_readyplus(sa, 10)) dienomem() ; + stralloc_catb(sa, "/allow", 7) ; + tmp.s[tmpbase] = 0 ; + if (access(sa->s, F_OK) < 0) + { + if ((errno != ENOENT) && (errno != EACCES)) + { + cleanup() ; + strerr_diefu2sys(111, "access ", sa->s) ; + } + sa->len = k+1 ; + stralloc_catb(sa, "deny", 5) ; + if (access(sa->s, F_OK) < 0) + if ((errno != ENOENT) && (errno != EACCES)) + { + cleanup() ; + strerr_diefu2sys(111, "access ", sa->s) ; + } + else return ; + else if (cdb_make_add(c, sa->s + start, k - start, "D", 1) < 0) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_make_add") ; + } + } + else + { + uint16 envlen = 0 ; + uint16 execlen = 0 ; + register int r ; + tmp.s[tmpbase] = 'A' ; + sa->len = k+1 ; + stralloc_catb(sa, "env", 4) ; + tmp.len = tmpbase + 3 ; + if ((envdir(sa->s, &tmp) < 0) && (errno != ENOENT)) + { + cleanup() ; + strerr_diefu2sys(111, "envdir ", sa->s) ; + } + if (tmp.len > tmpbase + 4103) + { + cleanup() ; + strerr_diefu2sys(100, sa->s, "too big") ; + } + envlen = tmp.len - tmpbase - 3 ; + tmp.len = tmpbase ; + uint16_pack_big(tmp.s + tmpbase + 1, envlen) ; + sa->len = k+1 ; + stralloc_catb(sa, "exec", 5) ; + r = openreadnclose(sa->s, tmp.s + tmpbase + 5 + envlen, 4096) ; + if ((r < 0) && (errno != ENOENT)) + { + cleanup() ; + strerr_diefu2sys(111, "openreadnclose ", sa->s) ; + } + if (r > 0) execlen = r ; + if (execlen == 4096) strerr_warnw2x("possibly truncated file ", sa->s) ; + uint16_pack_big(tmp.s + tmpbase + 3 + envlen, execlen) ; + if (cdb_make_add(c, sa->s + start, k - start, tmp.s + tmpbase, 5 + envlen + execlen) < 0) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_make_add") ; + } + } +} + +int main (int argc, char const *const *argv) +{ + stralloc sa = STRALLOC_ZERO ; + struct cdb_make c = CDB_MAKE_ZERO ; + DIR *dir ; + unsigned int start ; + int fd ; + PROG = "s6-accessrules-cdb-from-fs" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (!stralloc_cats(&tmp, argv[1])) return 0 ; + if (random_sauniquename(&tmp, 8) < 0) + strerr_diefu1sys(111, "random_sauniquename") ; + if (!stralloc_readyplus(&tmp, 8210)) + strerr_diefu1sys(111, "stralloc_catb") ; + stralloc_0(&tmp) ; + fd = open_trunc(tmp.s) ; + if (fd < 0) strerr_diefu2sys(111, "open_trunc ", tmp.s) ; + if (cdb_make_start(&c, fd) < 0) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_make_start") ; + } + dir = opendir(argv[2]) ; + if (!dir) + { + cleanup() ; + strerr_diefu2sys(111, "opendir ", argv[2]) ; + } + if (!stralloc_cats(&sa, argv[2]) || !stralloc_catb(&sa, "/", 1)) dienomem() ; + start = sa.len ; + + for (;;) + { + DIR *subdir ; + direntry *d ; + unsigned int base ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + sa.len = start ; + if (!stralloc_cats(&sa, d->d_name) || !stralloc_0(&sa)) dienomem() ; + base = sa.len ; + subdir = opendir(sa.s) ; + if (!subdir) + { + cleanup() ; + strerr_diefu2sys(111, "opendir ", sa.s) ; + } + sa.s[base-1] = '/' ; + for (;;) + { + errno = 0 ; + d = readdir(subdir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + sa.len = base ; + if (!stralloc_cats(&sa, d->d_name)) dienomem() ; + doit(&c, &sa, start) ; + } + if (errno) + { + sa.s[base-1] = 0 ; + cleanup() ; + strerr_diefu2sys(111, "readdir ", sa.s) ; + } + dir_close(subdir) ; + } + if (errno) + { + cleanup() ; + strerr_diefu2sys(111, "readdir ", argv[2]) ; + } + dir_close(dir) ; + if (cdb_make_finish(&c) < 0) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_make_finish") ; + } + if (fd_sync(fd) < 0) + { + cleanup() ; + strerr_diefu1sys(111, "fd_sync") ; + } + fd_close(fd) ; + if (rename(tmp.s, argv[1]) < 0) + { + cleanup() ; + strerr_diefu4sys(111, "rename ", tmp.s, " to ", argv[1]) ; + } + return 0 ; +} diff --git a/src/conn-tools/s6-accessrules-fs-from-cdb.c b/src/conn-tools/s6-accessrules-fs-from-cdb.c @@ -0,0 +1,177 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <unistd.h> +#include <skalibs/bytestr.h> +#include <skalibs/uint16.h> +#include <skalibs/uint32.h> +#include <skalibs/cdb.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#define USAGE "s6-accessrules-fs-from-cdb dir cdbfile" + +static char const *basedir ; +unsigned int basedirlen ; + +static void cleanup () +{ + int e = errno ; + rm_rf(basedir) ; + errno = e ; +} + +static int domkdir (char const *s) +{ + return mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_ISGID) < 0 ? (errno == EEXIST) : 1 ; +} + +static void mkdirp (char *s) +{ + mode_t m = umask(0) ; + unsigned int len = str_len(s) ; + register unsigned int i = basedirlen + 1 ; + for (; i < len ; i++) if (s[i] == '/') + { + s[i] = 0 ; + if (!domkdir(s)) goto err ; + s[i] = '/' ; + } + if (!domkdir(s)) goto err ; + umask(m) ; + return ; + + err: + cleanup() ; + strerr_diefu2sys(111, "mkdir ", s) ; +} + +static void touchtrunc (char const *file) +{ + register int fd = open_trunc(file) ; + if (fd < 0) strerr_diefu2sys(111, "open_trunc ", file) ; + fd_close(fd) ; +} + +static int doenv (char const *dir, unsigned int dirlen, char *env, unsigned int envlen) +{ + mode_t m = umask(0) ; + unsigned int i = 0 ; + if (!domkdir(dir)) + { + cleanup() ; + strerr_diefu2sys(111, "mkdir ", dir) ; + } + umask(m) ; + while (i < envlen) + { + unsigned int n = byte_chr(env + i, envlen - i, 0) ; + if (i + n >= envlen) return 0 ; + { + unsigned int p = byte_chr(env + i, n, '=') ; + char tmp[dirlen + p + 2] ; + byte_copy(tmp, dirlen, dir) ; + tmp[dirlen] = '/' ; + byte_copy(tmp + dirlen + 1, p, env + i) ; + tmp[dirlen + p + 1] = 0 ; + if (p < n) + { + env[i+n] = '\n' ; + if (!openwritenclose_unsafe(tmp, env + i + p + 1, n - p)) + { + cleanup() ; + strerr_diefu2sys(111, "openwritenclose_unsafe ", tmp) ; + } + } + else touchtrunc(tmp) ; + } + i += n + 1 ; + } + return 1 ; +} + +static int doit (struct cdb *c) +{ + unsigned int klen = cdb_keylen(c) ; + unsigned int dlen = cdb_datalen(c) ; + { + uint16 envlen, execlen ; + char name[basedirlen + klen + 8] ; + char data[dlen] ; + byte_copy(name, basedirlen, basedir) ; + name[basedirlen] = '/' ; + if (!dlen || (dlen > 8201)) return (errno = EINVAL, 0) ; + if ((cdb_read(c, name+basedirlen+1, klen, cdb_keypos(c)) < 0) + || (cdb_read(c, data, dlen, cdb_datapos(c)) < 0)) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_read") ; + } + name[basedirlen + klen + 1] = 0 ; + mkdirp(name) ; + name[basedirlen + klen + 1] = '/' ; + if (data[0] == 'A') + { + byte_copy(name + basedirlen + klen + 2, 6, "allow") ; + touchtrunc(name) ; + } + else if (data[0] == 'D') + { + byte_copy(name + basedirlen + klen + 2, 5, "deny") ; + touchtrunc(name) ; + } + if (dlen < 3) return 1 ; + uint16_unpack_big(data + 1, &envlen) ; + if ((envlen > 4096U) || (3U + envlen > dlen)) return (errno = EINVAL, 0) ; + uint16_unpack_big(data + 3 + envlen, &execlen) ; + if ((execlen > 4096U) || (5U + envlen + execlen != dlen)) return (errno = EINVAL, 0) ; + if (envlen) + { + byte_copy(name + basedirlen + klen + 2, 4, "env") ; + if (!doenv(name, basedirlen + klen + 5, data + 3, envlen)) return (errno = EINVAL, 0) ; + } + byte_copy(name + basedirlen + klen + 2, 5, "exec") ; + if (execlen && !openwritenclose_unsafe(name, data + 5 + envlen, execlen)) + { + cleanup() ; + strerr_diefu2sys(111, "openwritenclose_unsafe ", name) ; + } + } + return 1 ; +} + +int main (int argc, char const *const *argv) +{ + struct cdb c = CDB_ZERO ; + uint32 kpos ; + PROG = "s6-accessrules-fs-from-cdb" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (cdb_mapfile(&c, argv[2]) < 0) strerr_diefu1sys(111, "cdb_mapfile") ; + basedir = argv[1] ; + basedirlen = str_len(argv[1]) ; + { + mode_t m = umask(0) ; + if (mkdir(basedir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_ISGID) < 0) + strerr_diefu2sys(111, "mkdir ", basedir) ; + umask(m) ; + } + cdb_traverse_init(&c, &kpos) ; + for (;;) + { + register int r = cdb_nextkey(&c, &kpos) ; + if (r < 0) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_nextkey") ; + } + else if (!r) break ; + else if (!doit(&c)) + { + cleanup() ; + strerr_diefu1sys(111, "handle key") ; + } + } + return 0 ; +} diff --git a/src/conn-tools/s6-connlimit.c b/src/conn-tools/s6-connlimit.c @@ -0,0 +1,39 @@ +/* ISC license. */ + +#include <skalibs/uint.h> +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> + +int main (int argc, char const *const *argv, char const *const *envp) +{ + char const *x ; + unsigned int protolen ; + PROG = "s6-connlimit" ; + x = env_get2(envp, "PROTO") ; + if (!x) strerr_dienotset(100, "PROTO") ; + protolen = str_len(x) ; + if (!protolen) strerr_dief1x(100, "empty PROTO") ; + { + unsigned int num ; + char s[protolen + 8] ; + byte_copy(s, protolen, x) ; + byte_copy(s + protolen, 8, "CONNNUM") ; + x = env_get2(envp, s) ; + if (!x) strerr_dienotset(100, s) ; + if (!uint0_scan(x, &num)) strerr_dief2x(100, "invalid ", s) ; + byte_copy(s + protolen + 4, 4, "MAX") ; + x = env_get2(envp, s) ; + if (x) + { + unsigned int max ; + if (!uint0_scan(x, &max)) strerr_dief2x(100, "invalid ", s) ; + if (num > max) + strerr_dief2x(1, "number of connections from this client limited to ", x) ; + } + } + pathexec0_run(argv+1, envp) ; + (void)argc ; + strerr_dieexec(111, argv[1]) ; +} diff --git a/src/conn-tools/s6-ioconnect.c b/src/conn-tools/s6-ioconnect.c @@ -0,0 +1,187 @@ +/* ISC license. */ + +#include <skalibs/nonposix.h> +#include <sys/socket.h> +#include <errno.h> +#include <signal.h> +#include <skalibs/uint.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/sgetopt.h> +#include <skalibs/error.h> +#include <skalibs/iobuffer.h> +#include <skalibs/sig.h> +#include <skalibs/selfpipe.h> +#include <skalibs/strerr2.h> +#include <skalibs/tai.h> +#include <skalibs/iopause.h> +#include <skalibs/djbunix.h> + +#define USAGE "s6-ioconnect [ -t timeout ] [ -r fdr ] [ -w fdw ] [ -0 ] [ -1 ] [ -6 ] [ -7 ]" +#define dieusage() strerr_dieusage(100, USAGE) + + +typedef struct ioblah_s ioblah_t, *ioblah_t_ref ; +struct ioblah_s +{ + unsigned int fd ; + unsigned int xindex ; + unsigned int flagsocket : 1 ; + unsigned int flagopen : 1 ; +} ; + +static ioblah_t a[2][2] = { { { 0, 4, 0, 1 }, { 7, 4, 0, 1 } }, { { 6, 4, 0, 1 }, { 1, 4, 0, 1 } } } ; +static iobuffer b[2] ; +static iopause_fd x[5] = { { -1, IOPAUSE_READ, 0 } } ; + +static void closeit (unsigned int i, unsigned int j) +{ + if (a[i][j].flagsocket) + { + if ((shutdown(a[i][j].fd, j) < 0) && (errno != ENOTSOCK) && (errno != ENOTCONN)) + strerr_warnwu4sys("shutdown ", i ? "incoming" : "outgoing", " socket for ", j ? "writing" : "reading") ; + } + fd_close(a[i][j].fd) ; + a[i][j].flagopen = 0 ; + a[i][j].xindex = 5 ; +} + +static inline void finishit (unsigned int i) +{ + closeit(i, 1) ; + iobuffer_finish(&b[i]) ; +} + +static void handle_signals (void) +{ + for (;;) + { + char c = selfpipe_read() ; + switch (c) + { + case -1 : strerr_diefu1sys(111, "selfpipe_read") ; + case 0 : return ; + case SIGTERM : + { + if (a[0][0].xindex < 5) x[a[0][0].xindex].revents |= IOPAUSE_EXCEPT ; + if (a[1][0].xindex < 5) x[a[1][0].xindex].revents |= IOPAUSE_EXCEPT ; + break ; + } + default : + strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ; + } + } +} + +int main (int argc, char const *const *argv) +{ + tain_t tto ; + register unsigned int i, j ; + PROG = "s6-ioconnect" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int t = 0 ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "0167t:r:w:", &l) ; + if (opt < 0) break ; + switch (opt) + { + case '0' : a[0][0].flagsocket = 1 ; break ; + case '1' : a[1][1].flagsocket = 1 ; break ; + case '6' : a[1][0].flagsocket = 1 ; break ; + case '7' : a[0][1].flagsocket = 1 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + case 'r' : if (!uint0_scan(l.arg, &a[1][0].fd)) dieusage() ; break ; + case 'w' : if (!uint0_scan(l.arg, &a[0][1].fd)) dieusage() ; break ; + default : dieusage() ; + } + } + if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ; + argc -= l.ind ; argv += l.ind ; + } + if ((a[0][1].fd < 3) || (a[1][0].fd < 3)) dieusage() ; + for (i = 0 ; i < 2 ; i++) + { + for (j = 0 ; j < 2 ; j++) + if (ndelay_on(a[i][j].fd) == -1) strerr_diefu1sys(111, "ndelay_on") ; + if (!iobuffer_init(&b[i], a[i][0].fd, a[i][1].fd) < 0) strerr_diefu1sys(111, "iobuffer_init") ; + } + if (sig_ignore(SIGPIPE) == -1) strerr_diefu1sys(111, "sig_ignore") ; + tain_now_g() ; + x[0].fd = selfpipe_init() ; + if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + if (selfpipe_trap(SIGTERM) < 0) + strerr_diefu1sys(111, "trap SIGTERM") ; + + for (;;) + { + tain_t deadline ; + unsigned int xlen = 1 ; + int r ; + + tain_add_g(&deadline, iobuffer_isempty(&b[0]) && iobuffer_isempty(&b[1]) ? &tto : &tain_infinite_relative) ; + for (i = 0 ; i < 2 ; i++) + { + a[i][0].xindex = 5 ; + if (a[i][0].flagopen && iobuffer_isreadable(&b[i])) + { + x[xlen].fd = a[i][0].fd ; + x[xlen].events = IOPAUSE_READ ; + a[i][0].xindex = xlen++ ; + } + a[i][1].xindex = 5 ; + if (a[i][1].flagopen) + { + x[xlen].fd = a[i][1].fd ; + x[xlen].events = IOPAUSE_EXCEPT | (iobuffer_isempty(&b[i]) ? 0 : IOPAUSE_WRITE) ; + a[i][1].xindex = xlen++ ; + } + } + if (xlen <= 1) break ; + + r = iopause_g(x, xlen, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + else if (!r) return 1 ; + + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + + for (i = 0 ; i < 2 ; i++) if (a[i][1].xindex < 5) + { + if (x[a[i][1].xindex].revents & IOPAUSE_WRITE) + { + if (!iobuffer_flush(&b[i])) + { + if (!error_isagain(errno)) x[a[i][1].xindex].revents |= IOPAUSE_EXCEPT ; + } + else if (!a[i][0].flagopen) finishit(i) ; + } + if (x[a[i][1].xindex].revents & IOPAUSE_EXCEPT) + { + if (!iobuffer_isempty(&b[i])) + { + iobuffer_flush(&b[i]) ; /* sets errno */ + strerr_warnwu3sys("write ", i ? "incoming" : "outgoing", " data") ; + } + closeit(i, 0) ; finishit(i) ; + } + } + + for (i = 0 ; i < 2 ; i++) if (a[i][0].xindex < 5) + { + if (x[a[i][0].xindex].revents & IOPAUSE_READ) + { + if (sanitize_read(iobuffer_fill(&b[i])) < 0) + { + if (errno != EPIPE) strerr_warnwu3sys("read ", i ? "incoming" : "outgoing", " data") ; + x[a[i][0].xindex].revents |= IOPAUSE_EXCEPT ; + } + } + if (x[a[i][0].xindex].revents & IOPAUSE_EXCEPT) + { + closeit(i, 0) ; + if (iobuffer_isempty(&b[i])) finishit(i) ; + } + } + } + return 0 ; +} diff --git a/src/conn-tools/s6-ipcclient.c b/src/conn-tools/s6-ipcclient.c @@ -0,0 +1,66 @@ +/* ISC license. */ + +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <skalibs/webipc.h> + +#define USAGE "s6-ipcclient [ -q | -Q | -v ] [ -p bindpath ] [ -l localname ] path prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + char const *bindpath = 0 ; + char const *localname = 0 ; + unsigned int verbosity = 1 ; + PROG = "s6-ipcclient" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "qQvp:l:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'q' : if (verbosity) verbosity-- ; break ; + case 'Q' : verbosity = 1 ; break ; + case 'v' : verbosity++ ; break ; + case 'p' : bindpath = l.arg ; break ; + case 'l' : localname = l.arg ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc < 2) strerr_dieusage(100, USAGE) ; + { + char modif[24 + IPCPATH_MAX] = "PROTO=IPC\0IPCLOCALPATH=" ; + unsigned int i = 23 ; + int s = ipc_stream() ; + if (s < 0) strerr_diefu1sys(111, "create socket") ; + if (bindpath && (ipc_bind(s, bindpath) == -1)) + strerr_diefu2sys(111, "bind socket to ", bindpath) ; + if (!ipc_connect(s, argv[0])) + strerr_diefu2sys(111, "connect to ", argv[0]) ; + if (verbosity >= 2) strerr_warn3x(PROG, ": connected to ", argv[0]) ; + if (localname) + { + register unsigned int n = str_len(localname) ; + if (n > IPCPATH_MAX) n = IPCPATH_MAX ; + byte_copy(modif + i, n, localname) ; + i += n ; modif[i++] = 0 ; + } + else + { + int dummy ; + if (ipc_local(s, modif + i, IPCPATH_MAX, &dummy) < 0) modif[--i] = 0 ; + else i += str_len(modif + i) + 1 ; + } + if (fd_move(6, s) < 0) + strerr_diefu2sys(111, "set up fd ", "6") ; + if (fd_copy(7, 6) < 0) + strerr_diefu2sys(111, "set up fd ", "7") ; + pathexec_r(argv+1, envp, env_len(envp), modif, i) ; + } + strerr_dieexec(111, argv[1]) ; +} diff --git a/src/conn-tools/s6-ipcserver-access.c b/src/conn-tools/s6-ipcserver-access.c @@ -0,0 +1,211 @@ +/* ISC license. */ + +#include <unistd.h> +#include <errno.h> +#include <skalibs/gccattributes.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/sgetopt.h> +#include <skalibs/cdb.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <skalibs/webipc.h> +#include <execline/config.h> +#include <s6/accessrules.h> + +#define USAGE "s6-ipcserver-access [ -v verbosity ] [ -e | -E ] [ -l localname ] [ -i rulesdir | -x rulesfile ] prog..." + +static unsigned int verbosity = 1 ; + + /* Utility functions */ + +static inline void dieusage (void) gccattr_noreturn ; +static inline void dieusage () +{ + strerr_dieusage(100, USAGE) ; +} + +static inline void dienomem (void) gccattr_noreturn ; +static inline void dienomem () +{ + strerr_diefu1sys(111, "update environment") ; +} + +static inline void X (void) gccattr_noreturn ; +static inline void X () +{ + strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ; +} + + + /* Logging */ + +static void logit (unsigned int pid, unsigned int uid, unsigned int gid, int h) +{ + char fmtpid[UINT_FMT] ; + char fmtuid[UINT_FMT] ; + char fmtgid[UINT_FMT] ; + fmtpid[uint_fmt(fmtpid, pid)] = 0 ; + fmtuid[uint_fmt(fmtuid, uid)] = 0 ; + fmtgid[uint_fmt(fmtgid, gid)] = 0 ; + if (h) strerr_warni7x("allow", " pid ", fmtpid, " uid ", fmtuid, " gid ", fmtgid) ; + else strerr_warni7sys("deny", " pid ", fmtpid, " uid ", fmtuid, " gid ", fmtgid) ; +} + +static inline void log_accept (unsigned int pid, unsigned int uid, unsigned int gid) +{ + logit(pid, uid, gid, 1) ; +} + +static inline void log_deny (unsigned int pid, unsigned int uid, unsigned int gid) +{ + logit(pid, uid, gid, 0) ; +} + + + /* Checking */ + +static s6_accessrules_result_t check_cdb (unsigned int uid, unsigned int gid, char const *file, s6_accessrules_params_t *params) +{ + struct cdb c = CDB_ZERO ; + int fd = open_readb(file) ; + register s6_accessrules_result_t r ; + if (fd < 0) return -1 ; + if (cdb_init(&c, fd) < 0) strerr_diefu2sys(111, "cdb_init ", file) ; + r = s6_accessrules_uidgid_cdb(uid, gid, &c, params) ; + cdb_free(&c) ; + fd_close(fd) ; + return r ; +} + +static inline int check (s6_accessrules_params_t *params, char const *rules, unsigned int rulestype, unsigned int uid, unsigned int gid) +{ + char const *x = "" ; + s6_accessrules_result_t r ; + switch (rulestype) + { + case 0 : + if (verbosity >= 2) strerr_warnw1x("invoked without a ruleset!") ; + return 1 ; + case 1 : + r = s6_accessrules_uidgid_fs(uid, gid, rules, params) ; + x = "fs" ; + break ; + case 2 : + r = check_cdb(uid, gid, rules, params) ; + x = "cdb" ; + break ; + default : X() ; + } + switch (r) + { + case S6_ACCESSRULES_ERROR : strerr_diefu4sys(111, "check ", x, " ruleset in ", rules) ; + case S6_ACCESSRULES_ALLOW : return 1 ; + case S6_ACCESSRULES_DENY : return (errno = EACCES, 0) ; + case S6_ACCESSRULES_NOTFOUND : return (errno = ENOENT, 0) ; + default : X() ; + } +} + + +int main (int argc, char const *const *argv, char const *const *envp) +{ + s6_accessrules_params_t params = S6_ACCESSRULES_PARAMS_ZERO ; + char const *rules = 0 ; + char const *localname = 0 ; + char const *proto ; + unsigned int protolen ; + unsigned int uid = 0, gid = 0 ; + unsigned int rulestype = 0 ; + int doenv = 1 ; + PROG = "s6-ipcserver-access" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "v:Eel:i:x:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'E' : doenv = 0 ; break ; + case 'e' : doenv = 1 ; break ; + case 'l' : localname = l.arg ; break ; + case 'i' : rules = l.arg ; rulestype = 1 ; break ; + case 'x' : rules = l.arg ; rulestype = 2 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) dieusage() ; + if (!*argv[0]) dieusage() ; + + proto = env_get2(envp, "PROTO") ; + if (!proto) strerr_dienotset(100, "PROTO") ; + protolen = str_len(proto) ; + + { + char const *x ; + char tmp[protolen + 11] ; + byte_copy(tmp, protolen, proto) ; + byte_copy(tmp + protolen, 11, "REMOTEEUID") ; + x = env_get2(envp, tmp) ; + if (!x) strerr_dienotset(100, tmp) ; + if (!uint0_scan(x, &uid)) strerr_dieinvalid(100, tmp) ; + tmp[protolen + 7] = 'G' ; + x = env_get2(envp, tmp) ; + if (!x) strerr_dienotset(100, tmp) ; + if (!uint0_scan(x, &gid)) strerr_dieinvalid(100, tmp) ; + } + + if (!check(&params, rules, rulestype, uid, gid)) + { + if (verbosity >= 2) log_deny(getpid(), uid, gid) ; + return 1 ; + } + if (verbosity) log_accept(getpid(), uid, gid) ; + + if (doenv) + { + char tmp[protolen + 10] ; + byte_copy(tmp, protolen, proto) ; + byte_copy(tmp + protolen, 10, "LOCALPATH") ; + if (localname) + { + if (!env_addmodif(&params.env, tmp, localname)) dienomem() ; + } + else + { + char curname[IPCPATH_MAX+1] ; + int dummy ; + if (ipc_local(0, curname, IPCPATH_MAX+1, &dummy) < 0) + strerr_diefu1sys(111, "ipc_local") ; + if (!env_addmodif(&params.env, tmp, curname)) dienomem() ; + } + } + else + { + char tmp[protolen + 11] ; + byte_copy(tmp, protolen, proto) ; + byte_copy(tmp + protolen, 11, "REMOTEEUID") ; + if (!env_addmodif(&params.env, "PROTO", 0)) dienomem() ; + if (!env_addmodif(&params.env, tmp, 0)) dienomem() ; + tmp[protolen + 7] = 'G' ; + if (!env_addmodif(&params.env, tmp, 0)) dienomem() ; + byte_copy(tmp + protolen + 6, 5, "PATH") ; + if (!env_addmodif(&params.env, tmp, 0)) dienomem() ; + byte_copy(tmp + protolen, 10, "LOCALPATH") ; + if (!env_addmodif(&params.env, tmp, 0)) dienomem() ; + } + + if (params.exec.len) + { + char *specialargv[4] = { EXECLINE_EXTBINPREFIX "execlineb", "-c", params.exec.s, 0 } ; + pathexec_r((char const *const *)specialargv, envp, env_len(envp), params.env.s, params.env.len) ; + strerr_dieexec(111, specialargv[0]) ; + } + + pathexec_r(argv, envp, env_len(envp), params.env.s, params.env.len) ; + strerr_dieexec(111, argv[0]) ; +} diff --git a/src/conn-tools/s6-ipcserver-socketbinder.c b/src/conn-tools/s6-ipcserver-socketbinder.c @@ -0,0 +1,49 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <skalibs/uint.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <skalibs/webipc.h> + +#define USAGE "s6-ipcserver-socketbinder [ -d | -D ] [ -b backlog ] path prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int backlog = 20 ; + int flagreuse = 1 ; + PROG = "s6-ipcserver-socketbinder" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "Ddb:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'D' : flagreuse = 0 ; break ; + case 'd' : flagreuse = 1 ; break ; + case 'b' : if (!uint0_scan(l.arg, &backlog)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc < 2) dieusage() ; + close(0) ; + if (ipc_stream()) strerr_diefu1sys(111, "create socket") ; + { + mode_t m = umask(0) ; + if ((flagreuse ? ipc_bind_reuse(0, argv[0]) : ipc_bind(0, argv[0])) < 0) + strerr_diefu2sys(111, "bind to ", argv[0]) ; + umask(m) ; + } + if (ipc_listen(0, backlog) < 0) strerr_diefu2sys(111, "listen to ", argv[0]) ; + + pathexec_run(argv[1], argv + 1, envp) ; + strerr_dieexec(111, argv[1]) ; +} diff --git a/src/conn-tools/s6-ipcserver.c b/src/conn-tools/s6-ipcserver.c @@ -0,0 +1,127 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <limits.h> +#include <skalibs/uint.h> +#include <skalibs/gidstuff.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <s6/config.h> + +#define USAGE "s6-ipcserver [ -q | -Q | -v ] [ -d | -D ] [ -P | -p ] [ -1 ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -G gid,gid,... ] [ -g gid ] [ -u uid ] [ -U ] path prog..." +#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 flaglookup = 1 ; + int flagreuse = 1 ; + unsigned int uid = 0, gid = 0 ; + gid_t gids[NGROUPS_MAX] ; + unsigned int gidn = (unsigned int)-1 ; + unsigned int maxconn = 0 ; + unsigned int localmaxconn = 0 ; + unsigned int backlog = (unsigned int)-1 ; + PROG = "s6-ipcserver" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "qQvDd1UPpc:C:b:u:g:G:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'q' : verbosity = 0 ; break ; + case 'Q' : verbosity = 1 ; break ; + case 'v' : verbosity = 2 ; break ; + case 'D' : flagreuse = 0 ; break ; + case 'd' : flagreuse = 1 ; break ; + case 'P' : flaglookup = 0 ; break ; + case 'p' : flaglookup = 1 ; break ; + case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; if (!maxconn) maxconn = 1 ; break ; + case 'C' : if (!uint0_scan(l.arg, &localmaxconn)) dieusage() ; if (!localmaxconn) localmaxconn = 1 ; break ; + case 'b' : if (!uint0_scan(l.arg, &backlog)) dieusage() ; break ; + case 'u' : if (!uint0_scan(l.arg, &uid)) dieusage() ; break ; + case 'g' : if (!uint0_scan(l.arg, &gid)) dieusage() ; break ; + case 'G' : if (!gid_scanlist(gids, NGROUPS_MAX, l.arg, &gidn) && *l.arg) dieusage() ; break ; + case '1' : flag1 = 1 ; break ; + case 'U' : flagU = 1 ; uid = 0 ; gid = 0 ; gidn = (unsigned int)-1 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (argc < 2) dieusage() ; + } + + { + unsigned int m = 0 ; + unsigned int pos = 0 ; + char fmt[UINT_FMT * 5 + GID_FMT * NGROUPS_MAX] ; + char const *newargv[24 + argc] ; + newargv[m++] = S6_BINPREFIX "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 != (unsigned int)-1) + { + newargv[m++] = S6_BINPREFIX "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 != (unsigned int)-1) + { + newargv[m++] = "-G" ; + newargv[m++] = fmt + pos ; + pos += gid_fmtlist(fmt + pos, gids, gidn) ; + fmt[pos++] = 0 ; + } + newargv[m++] = "--" ; + } + newargv[m++] = S6_BINPREFIX "s6-ipcserverd" ; + if (!verbosity) newargv[m++] = "-v0" ; + else if (verbosity == 2) newargv[m++] = "-v2" ; + if (flag1) newargv[m++] = "-1" ; + if (!flaglookup) newargv[m++] = "-P" ; + if (maxconn) + { + newargv[m++] = "-c" ; + newargv[m++] = fmt + pos ; + pos += uint_fmt(fmt + pos, maxconn) ; + fmt[pos++] = 0 ; + } + if (localmaxconn) + { + newargv[m++] = "-C" ; + newargv[m++] = fmt + pos ; + pos += uint_fmt(fmt + pos, localmaxconn) ; + fmt[pos++] = 0 ; + } + newargv[m++] = "--" ; + while (*argv) newargv[m++] = *argv++ ; + newargv[m++] = 0 ; + pathexec_run(newargv[0], newargv, envp) ; + strerr_dieexec(111, newargv[0]) ; + } +} diff --git a/src/conn-tools/s6-ipcserverd.c b/src/conn-tools/s6-ipcserverd.c @@ -0,0 +1,399 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <skalibs/uint.h> +#include <skalibs/gccattributes.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/diuint.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <skalibs/sig.h> +#include <skalibs/selfpipe.h> +#include <skalibs/iopause.h> +#include <skalibs/webipc.h> + +#define USAGE "s6-ipcserverd [ -v verbosity ] [ -1 ] [ -P | -p ] [ -c maxconn ] [ -C localmaxconn ] prog..." + +#define ABSOLUTE_MAXCONN 1000 + +static unsigned int maxconn = 40 ; +static unsigned int localmaxconn = 40 ; +static char fmtmaxconn[UINT_FMT+1] = "/" ; +static char fmtlocalmaxconn[UINT_FMT+1] = "/" ; +static int flaglookup = 1 ; +static unsigned int verbosity = 1 ; +static int cont = 1 ; + +static diuint *piduid ; +static unsigned int numconn = 0 ; +static diuint *uidnum ; +static unsigned int uidlen = 0 ; + + + /* Utility functions */ + +static inline void dieusage () +{ + strerr_dieusage(100, USAGE) ; +} + +static inline void X (void) +{ + strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ; +} + + + /* Lookup primitives */ + +static unsigned int lookup_diuint (diuint const *tab, unsigned int tablen, unsigned int key) +{ + register unsigned int i = 0 ; + for (; i < tablen ; i++) if (key == tab[i].left) break ; + return i ; +} + +static inline unsigned int lookup_pid (unsigned int pid) +{ + return lookup_diuint(piduid, numconn, pid) ; +} + +static inline unsigned int lookup_uid (unsigned int uid) +{ + return lookup_diuint(uidnum, uidlen, uid) ; +} + + + /* Logging */ + +static inline void log_start (void) +{ + strerr_warni1x("starting") ; +} + +static inline void log_exit (void) +{ + strerr_warni1x("exiting") ; +} + +static void log_status (void) +{ + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, numconn)] = 0 ; + strerr_warni3x("status: ", fmt, fmtmaxconn) ; +} + +static void log_deny (unsigned int uid, unsigned int gid, unsigned int num) +{ + char fmtuid[UINT_FMT] = "?" ; + char fmtgid[UINT_FMT] = "?" ; + char fmtnum[UINT_FMT] = "?" ; + if (flaglookup) + { + fmtuid[uint_fmt(fmtuid, uid)] = 0 ; + fmtgid[uint_fmt(fmtgid, gid)] = 0 ; + fmtnum[uint_fmt(fmtnum, num)] = 0 ; + } + strerr_warni7sys("deny ", fmtuid, ":", fmtgid, " count ", fmtnum, fmtlocalmaxconn) ; +} + +static void log_accept (unsigned int pid, unsigned int uid, unsigned int gid, unsigned int num) +{ + char fmtuidgid[UINT_FMT * 2 + 1] = "?:?" ; + char fmtpid[UINT_FMT] ; + char fmtnum[UINT_FMT] = "?" ; + if (flaglookup) + { + register unsigned int n = uint_fmt(fmtuidgid, uid) ; + fmtuidgid[n++] = ':' ; + n += uint_fmt(fmtuidgid + n, gid) ; + fmtuidgid[n] = 0 ; + fmtnum[uint_fmt(fmtnum, num)] = 0 ; + } + fmtpid[uint_fmt(fmtpid, pid)] = 0 ; + strerr_warni7x("allow ", fmtuidgid, " pid ", fmtpid, " count ", fmtnum, fmtlocalmaxconn) ; +} + +static void log_close (unsigned int pid, unsigned int uid, int w) +{ + char fmtpid[UINT_FMT] ; + char fmtuid[UINT_FMT] = "?" ; + char fmtw[UINT_FMT] ; + fmtpid[uint_fmt(fmtpid, pid)] = 0 ; + if (flaglookup) fmtuid[uint_fmt(fmtuid, uid)] = 0 ; + fmtw[uint_fmt(fmtw, WIFSIGNALED(w) ? WTERMSIG(w) : WEXITSTATUS(w))] = 0 ; + strerr_warni6x("end pid ", fmtpid, " uid ", fmtuid, WIFSIGNALED(w) ? " signal " : " exitcode ", fmtw) ; +} + + + /* Signal handling */ + +static void killthem (int sig) +{ + register unsigned int i = 0 ; + for (; i < numconn ; i++) kill(piduid[i].left, sig) ; +} + +static void wait_children (void) +{ + for (;;) + { + unsigned int i ; + int w ; + register pid_t pid = wait_nohang(&w) ; + if (pid < 0) + if (errno != ECHILD) strerr_diefu1sys(111, "wait_nohang") ; + else break ; + else if (!pid) break ; + i = lookup_pid(pid) ; + if (i < numconn) + { + unsigned int uid = piduid[i].right ; + register unsigned int j = lookup_uid(uid) ; + if (j >= uidlen) X() ; + if (!--uidnum[j].right) uidnum[j] = uidnum[--uidlen] ; + piduid[i] = piduid[--numconn] ; + if (verbosity >= 2) + { + log_close(pid, uid, w) ; + log_status() ; + } + } + } +} + +static void handle_signals (void) +{ + for (;;) switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "read selfpipe") ; + case 0 : return ; + case SIGCHLD : wait_children() ; break ; + case SIGTERM : + { + if (verbosity >= 2) + strerr_warni3x("received ", "SIGTERM,", " quitting") ; + cont = 0 ; + break ; + } + case SIGHUP : + { + if (verbosity >= 2) + strerr_warni5x("received ", "SIGHUP,", " sending ", "SIGTERM+SIGCONT", " to all connections") ; + killthem(SIGTERM) ; + killthem(SIGCONT) ; + break ; + } + case SIGQUIT : + { + if (verbosity >= 2) + strerr_warni6x("received ", "SIGQUIT,", " sending ", "SIGTERM+SIGCONT", " to all connections", " and quitting") ; + cont = 0 ; + killthem(SIGTERM) ; + killthem(SIGCONT) ; + break ; + } + case SIGABRT : + { + if (verbosity >= 2) + strerr_warni6x("received ", "SIGABRT,", " sending ", "SIGKILL", " to all connections", " and quitting") ; + cont = 0 ; + killthem(SIGKILL) ; + break ; + } + default : X() ; + } +} + + + /* New connection handling */ + +static void run_child (int, unsigned int, unsigned int, unsigned int, char const *, char const *const *, char const *const *) gccattr_noreturn ; +static void run_child (int s, unsigned int uid, unsigned int gid, unsigned int num, char const *remotepath, char const *const *argv, char const *const *envp) +{ + unsigned int rplen = str_len(remotepath) + 1 ; + unsigned int n = 0 ; + char fmt[65 + UINT_FMT * 3 + rplen] ; + PROG = "s6-ipcserver (child)" ; + if ((fd_move(0, s) < 0) || (fd_copy(1, 0) < 0)) + strerr_diefu1sys(111, "move fds") ; + byte_copy(fmt+n, 23, "PROTO=IPC\0IPCREMOTEEUID") ; n += 23 ; + if (flaglookup) + { + fmt[n++] = '=' ; + n += uint_fmt(fmt+n, uid) ; + } + fmt[n++] = 0 ; + byte_copy(fmt+n, 13, "IPCREMOTEEGID") ; n += 13 ; + if (flaglookup) + { + fmt[n++] = '=' ; + n += uint_fmt(fmt+n, gid) ; + } + fmt[n++] = 0 ; + byte_copy(fmt+n, 11, "IPCCONNNUM=") ; n += 11 ; + if (flaglookup) n += uint_fmt(fmt+n, num) ; + fmt[n++] = 0 ; + byte_copy(fmt+n, 14, "IPCREMOTEPATH=") ; n += 14 ; + byte_copy(fmt+n, rplen, remotepath) ; n += rplen ; + pathexec_r(argv, envp, env_len(envp), fmt, n) ; + strerr_dieexec(111, argv[0]) ; +} + +static void new_connection (int s, char const *remotepath, char const *const *argv, char const *const *envp) +{ + unsigned int uid = 0, gid = 0 ; + unsigned int num, i ; + register pid_t pid ; + if (flaglookup && (ipc_eid(s, &uid, &gid) < 0)) + { + if (verbosity) strerr_warnwu1sys("ipc_eid") ; + return ; + } + i = lookup_uid(uid) ; + num = (i < uidlen) ? uidnum[i].right : 0 ; + if (num >= localmaxconn) + { + log_deny(uid, gid, num) ; + return ; + } + pid = fork() ; + if (pid < 0) + { + if (verbosity) strerr_warnwu1sys("fork") ; + return ; + } + else if (!pid) + { + selfpipe_finish() ; + run_child(s, uid, gid, num+1, remotepath, argv, envp) ; + } + + if (i < uidlen) uidnum[i].right = num + 1 ; + else + { + uidnum[uidlen].left = uid ; + uidnum[uidlen++].right = 1 ; + } + piduid[numconn].left = (unsigned int)pid ; + piduid[numconn++].right = uid ; + if (verbosity >= 2) + { + log_accept((unsigned int)pid, uid, gid, uidnum[i].right) ; + log_status() ; + } +} + + + /* And the main */ + +int main (int argc, char const *const *argv, char const *const *envp) +{ + iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .fd = 0, .events = IOPAUSE_READ | IOPAUSE_EXCEPT } } ; + PROG = "s6-ipcserverd" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + int flag1 = 0 ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "Pp1c:C:v:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'P' : flaglookup = 0 ; break ; + case 'p' : flaglookup = 1 ; break ; + case '1' : flag1 = 1 ; break ; + case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ; + case 'C' : if (!uint0_scan(l.arg, &localmaxconn)) dieusage() ; break ; + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (!argc || !*argv[0]) dieusage() ; + { + 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 (coe(0) < 0) strerr_diefu1sys(111, "make socket close-on-exec") ; + if (flag1) + { + if (fcntl(1, F_GETFD) < 0) + strerr_dief1sys(100, "called with option -1 but stdout said") ; + } + else close(1) ; + if (!maxconn) maxconn = 1 ; + if (maxconn > ABSOLUTE_MAXCONN) maxconn = ABSOLUTE_MAXCONN ; + if (!flaglookup || (localmaxconn > maxconn)) localmaxconn = maxconn ; + + x[0].fd = selfpipe_init() ; + if (x[0].fd == -1) strerr_diefu1sys(111, "create selfpipe") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGCHLD) ; + sigaddset(&set, SIGTERM) ; + sigaddset(&set, SIGHUP) ; + sigaddset(&set, SIGQUIT) ; + sigaddset(&set, SIGABRT) ; + if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ; + } + + fmtlocalmaxconn[1+uint_fmt(fmtlocalmaxconn+1, localmaxconn)] = 0 ; + if (verbosity >= 2) + { + fmtmaxconn[1+uint_fmt(fmtmaxconn+1, maxconn)] = 0 ; + log_start() ; + log_status() ; + } + if (flag1) + { + fd_write(1, "\n", 1) ; + fd_close(1) ; + } + } + + { + diuint inyostack[maxconn + (flaglookup ? maxconn : 1)] ; + piduid = inyostack ; uidnum = inyostack + maxconn ; + + while (cont) + { + if (iopause_g(x, 1 + (numconn < maxconn), 0) < 0) + strerr_diefu1sys(111, "iopause") ; + + if (x[0].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with selfpipe") ; + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + if (numconn < maxconn) + { + if (x[1].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with socket") ; + if (x[1].revents & IOPAUSE_READ) + { + int dummy ; + char remotepath[IPCPATH_MAX+1] ; + register int s = ipc_accept(x[1].fd, remotepath, IPCPATH_MAX+1, &dummy) ; + if (s < 0) + { + if (verbosity) strerr_warnwu1sys("accept") ; + } + else + { + new_connection(s, remotepath, argv, envp) ; + fd_close(s) ; + } + } + } + } + } + if (verbosity >= 2) log_exit() ; + return 0 ; +} diff --git a/src/conn-tools/s6-sudo.c b/src/conn-tools/s6-sudo.c @@ -0,0 +1,67 @@ +/* ISC license. */ + +#include <skalibs/uint.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <s6/config.h> + +#define USAGE "s6-sudo [ -q | -Q | -v ] [ -p bindpath ] [ -l localname ] [ -e ] [ -t timeout ] [ -T timeoutrun ] path [ args... ]" +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int verbosity = 1, t = 0, T = 0 ; + char const *bindpath = 0 ; + char const *localname = 0 ; + int nodoenv = 0 ; + PROG = "s6-sudo" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "qQvp:l:et:T:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'q' : if (verbosity) verbosity-- ; break ; + case 'Q' : verbosity = 1 ; break ; + case 'v' : verbosity++ ; break ; + case 'p' : bindpath = l.arg ; break ; + case 'l' : localname = l.arg ; break ; + case 'e' : nodoenv = 1 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) dieusage() ; + { + char const *eargv[9 + argc + ((verbosity < 2 ? 1 : verbosity-1)) + ((!!bindpath + !!localname) << 1) + nodoenv] ; + char fmt1[UINT_FMT] ; + char fmt2[UINT_FMT] ; + unsigned int n = 0 ; + eargv[n++] = S6_BINPREFIX "s6-ipcclient" ; + if (!verbosity) eargv[n++] = "-Q" ; + else while (--verbosity) eargv[n++] = "-v" ; + if (bindpath) { eargv[n++] = "-p" ; eargv[n++] = bindpath ; } + if (localname) { eargv[n++] = "-l" ; eargv[n++] = localname ; } + eargv[n++] = "--" ; + eargv[n++] = *argv++ ; argc-- ; + eargv[n++] = S6_BINPREFIX "s6-sudoc" ; + if (nodoenv) eargv[n++] = "-e" ; + eargv[n++] = "-t" ; + fmt1[uint_fmt(fmt1, t)] = 0 ; + eargv[n++] = fmt1 ; + eargv[n++] = "-T" ; + fmt2[uint_fmt(fmt2, T)] = 0 ; + eargv[n++] = fmt2 ; + eargv[n++] = "--" ; + while (argc--) eargv[n++] = *argv++ ; + eargv[n++] = 0 ; + pathexec_run(eargv[0], eargv, envp) ; + } + strerr_dieexec(111, S6_BINPREFIX "s6-ipcclient") ; +} diff --git a/src/conn-tools/s6-sudo.h b/src/conn-tools/s6-sudo.h @@ -0,0 +1,11 @@ +/* ISC license. */ + +#ifndef S6_SUDO_H +#define S6_SUDO_H + +#define S6_SUDO_BANNERB "s6-sudo b v1.0\n" +#define S6_SUDO_BANNERB_LEN (sizeof(S6_SUDO_BANNERB) - 1) +#define S6_SUDO_BANNERA "s6-sudo a v1.0\n" +#define S6_SUDO_BANNERA_LEN (sizeof(S6_SUDO_BANNERA) - 1) + +#endif diff --git a/src/conn-tools/s6-sudoc.c b/src/conn-tools/s6-sudoc.c @@ -0,0 +1,115 @@ +/* ISC license. */ + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/wait.h> +#include <skalibs/uint.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/stralloc.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <skalibs/tai.h> +#include <skalibs/env.h> +#include <skalibs/unix-timed.h> +#include <skalibs/unixmessage.h> +#include "s6-sudo.h" + +#define USAGE "s6-sudoc [ -e ] [ -t timeoutconn ] [ -T timeoutrun ] [ args... ]" +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") + +int main (int argc, char const *const *argv, char const *const *envp) +{ + char buf6[64] ; + buffer b6 = BUFFER_INIT(&buffer_read, 6, buf6, 64) ; + unixmessage_sender_t b7 = UNIXMESSAGE_SENDER_INIT(7) ; + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int t = 0, T = 0 ; + int doenv = 1 ; + + tain_t deadline = TAIN_INFINITE_RELATIVE ; + PROG = "s6-sudoc" ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "et:T:", &l) ; + if (opt < 0) break ; + switch (opt) + { + case 'e' : doenv = 0 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&deadline, t) ; + if ((ndelay_on(6) < 0) || (ndelay_on(7) < 0)) + strerr_diefu1sys(111, "make socket non-blocking") ; + if (!fd_sanitize() || !fd_ensure_open(2, 1)) + strerr_diefu1sys(111, "sanitize stdin/stdout/stderr") ; + + tain_now_g() ; + tain_add_g(&deadline, &deadline) ; + { + char tmp[S6_SUDO_BANNERB_LEN] ; + if (buffer_timed_get_g(&b6, tmp, S6_SUDO_BANNERB_LEN, &deadline) < S6_SUDO_BANNERB_LEN) + strerr_diefu1sys(111, "read banner from s6-sudod") ; + if (str_diffn(tmp, S6_SUDO_BANNERB, S6_SUDO_BANNERB_LEN)) + strerr_dief1x(100, "wrong server banner") ; + } + { + int fds[3] = { 0, 1, 2 } ; + char pack[16] ; + siovec_t v[4] = { + { .s = S6_SUDO_BANNERA, .len = S6_SUDO_BANNERA_LEN }, + { .s = pack, .len = 16 }, + { .s = 0, .len = 0 }, + { .s = 0, .len = 0 } } ; + unixmessage_v_t mv = { .v = v, .vlen = 4, .fds = fds, .nfds = 3 } ; + stralloc sa = STRALLOC_ZERO ; + unsigned int envlen = doenv ? env_len(envp) : 0 ; + uint32_pack_big(pack, (uint32)argc) ; + uint32_pack_big(pack + 4, (uint32)envlen) ; + if (!env_string(&sa, argv, argc)) dienomem() ; + v[2].len = sa.len ; + uint32_pack_big(pack + 8, (uint32)v[2].len) ; + if (doenv) + { + if (!env_string(&sa, envp, envlen)) dienomem() ; + v[3].len = sa.len - v[2].len ; + } + uint32_pack_big(pack + 12, (uint32)v[3].len) ; + v[2].s = sa.s ; + v[3].s = sa.s + v[2].len ; + if (!unixmessage_putv_and_close(&b7, &mv, (unsigned char const *)"\003")) + strerr_diefu1sys(111, "unixmessage_putv") ; + stralloc_free(&sa) ; + } + if (!unixmessage_sender_timed_flush_g(&b7, &deadline)) + strerr_diefu1sys(111, "send args to server") ; + unixmessage_sender_free(&b7) ; + { + char c ; + if (buffer_timed_get_g(&b6, &c, 1, &deadline) < 1) + strerr_diefu1sys(111, "get confirmation from server") ; + if (c) + { + errno = c ; + strerr_diefu1sys(111, "start privileged program: server answered: ") ; + } + } + + if (T) tain_from_millisecs(&deadline, T) ; else deadline = tain_infinite_relative ; + tain_add_g(&deadline, &deadline) ; + { + char pack[UINT_PACK] ; + if (buffer_timed_get_g(&b6, pack, UINT_PACK, &deadline) < UINT_PACK) + strerr_diefu1sys(111, "get exit status from server") ; + uint_unpack_big(pack, &t) ; + } + if (WIFSIGNALED(t)) raise(WTERMSIG(t)) ; + return WEXITSTATUS(t) ; +} diff --git a/src/conn-tools/s6-sudod.c b/src/conn-tools/s6-sudod.c @@ -0,0 +1,233 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <skalibs/uint.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/stralloc.h> +#include <skalibs/strerr2.h> +#include <skalibs/tai.h> +#include <skalibs/iopause.h> +#include <skalibs/selfpipe.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <skalibs/unix-timed.h> +#include <skalibs/unixmessage.h> +#include "s6-sudo.h" + +#define USAGE "s6-sudod [ -0 ] [ -1 ] [ -2 ] [ -t timeout ] args..." +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") + +int main (int argc, char const *const *argv, char const *const *envp) +{ + subgetopt_t l = SUBGETOPT_ZERO ; + unixmessage_t m ; + unsigned int nullfds = 0, t = 2000 ; + pid_t pid ; + uint32 envc = env_len(envp) ; + uint32 cargc, cenvc, carglen, cenvlen ; + int spfd ; + tain_t deadline = TAIN_INFINITE_RELATIVE ; + PROG = "s6-sudod" ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "012t:", &l) ; + if (opt < 0) break ; + switch (opt) + { + case '0' : nullfds |= 1 ; break ; + case '1' : nullfds |= 2 ; break ; + case '2' : nullfds |= 4 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&deadline, t) ; + if ((ndelay_on(0) < 0) || (ndelay_on(1) < 0)) + strerr_diefu1sys(111, "make socket non-blocking") ; + + tain_now_g() ; + tain_add_g(&deadline, &deadline) ; + buffer_putnoflush(buffer_1small, S6_SUDO_BANNERB, S6_SUDO_BANNERB_LEN) ; + if (!buffer_timed_flush_g(buffer_1small, &deadline)) + strerr_diefu1sys(111, "write banner to client") ; + if (unixmessage_timed_receive_g(unixmessage_receiver_0, &m, &deadline) <= 0) + strerr_diefu1sys(111, "read message from client") ; + if (m.nfds != 3) + strerr_dief1x(100, "client did not send 3 fds") ; + if (m.len < 16 + S6_SUDO_BANNERA_LEN) + strerr_dief1x(100, "wrong client message") ; + if (str_diffn(m.s, S6_SUDO_BANNERA, S6_SUDO_BANNERA_LEN)) + strerr_dief1x(100, "wrong client banner") ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN, &cargc) ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 4, &cenvc) ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 8, &carglen) ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 12, &cenvlen) ; + if (S6_SUDO_BANNERA_LEN + 16 + carglen + cenvlen != m.len) + strerr_dief1x(100, "wrong client argc/envlen") ; + if ((cargc > 131072) || (cenvc > 131072)) + strerr_dief1x(100, "too many args/envvars from client") ; + + if (nullfds & 1) + { + close(m.fds[0]) ; + m.fds[0] = open2("/dev/null", O_RDONLY) ; + if (m.fds[0] < 0) strerr_diefu2sys(111, "open /dev/null for ", "reading") ; + } + if (nullfds & 2) + { + close(m.fds[1]) ; + m.fds[1] = open2("/dev/null", O_WRONLY) ; + if (m.fds[1] < 0) strerr_diefu2sys(111, "open /dev/null for ", "writing") ; + } + if (nullfds & 4) + { + close(m.fds[2]) ; + m.fds[2] = 2 ; + } + + { + char const *targv[argc + 1 + cargc] ; + char const *tenvp[envc + 1 + cenvc] ; + int p[2] ; + register unsigned int i = 0 ; + for (; i < (unsigned int)argc ; i++) targv[i] = argv[i] ; + for (i = 0 ; i <= envc ; i++) tenvp[i] = envp[i] ; + if (!env_make(targv + argc, cargc, m.s + S6_SUDO_BANNERA_LEN + 16, carglen)) + { + char c = errno ; + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + errno = c ; + strerr_diefu1sys(111, "make child argv") ; + } + if (!env_make(tenvp + envc + 1, cenvc, m.s + S6_SUDO_BANNERA_LEN + 16 + carglen, cenvlen)) + { + char c = errno ; + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + errno = c ; + strerr_diefu1sys(111, "make child envp") ; + } + targv[argc + cargc] = 0 ; + + for (i = 0 ; i < cenvc ; i++) + { + char const *var = tenvp[envc + 1 + i] ; + register unsigned int j = 0 ; + register unsigned int len = str_chr(var, '=') ; + if (!var[len]) + { + char c = EINVAL ; + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + strerr_dief1x(100, "bad environment from client") ; + } + for (; j < envc ; j++) if (!str_diffn(var, tenvp[j], len+1)) break ; + if ((j < envc) && !tenvp[j][len+1]) tenvp[j] = var ; + } + + spfd = selfpipe_init() ; + if (spfd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "trap SIGCHLD") ; + if (pipe(p) < 0) strerr_diefu1sys(111, "pipe") ; + if (coe(p[1]) < 0) strerr_diefu1sys(111, "coe pipe") ; + pid = fork() ; + if (pid < 0) strerr_diefu1sys(111, "fork") ; + if (!pid) + { + PROG = "s6-sudod (child)" ; + fd_close(p[0]) ; + if ((fd_move(2, m.fds[2]) < 0) + || (fd_move(1, m.fds[1]) < 0) + || (fd_move(0, m.fds[0]) < 0)) + { + char c = errno ; + fd_write(p[1], &c, 1) ; + strerr_diefu1sys(111, "move fds") ; + } + selfpipe_finish() ; + pathexec0_run(targv, tenvp) ; + { + char c = errno ; + fd_write(p[1], &c, 1) ; + } + strerr_dieexec(111, targv[0]) ; + } + fd_close(p[1]) ; + { + char c ; + register int r = fd_read(p[0], &c, 1) ; + if (r < 0) strerr_diefu1sys(111, "read from child") ; + if (r) + { + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + return 111 ; + } + } + fd_close(p[0]) ; + } + + fd_close(m.fds[0]) ; + fd_close(m.fds[1]) ; + if (!(nullfds & 4)) fd_close(m.fds[2]) ; + unixmessage_receiver_free(unixmessage_receiver_0) ; + buffer_putnoflush(buffer_1small, "", 1) ; + if (!buffer_timed_flush_g(buffer_1small, &deadline)) + strerr_diefu1sys(111, "send confirmation to client") ; + + { + iopause_fd x[2] = { { .fd = 0, .events = 0 }, { .fd = spfd, .events = IOPAUSE_READ } } ; + int cont = 1 ; + while (cont) + { + if (iopause_g(x, 2, 0) < 0) strerr_diefu1sys(111, "iopause") ; + if (x[1].revents) + { + for (;;) + { + int c = selfpipe_read() ; + if (c < 0) strerr_diefu1sys(111, "read from selfpipe") ; + else if (!c) break ; + else if (c == SIGCHLD) + { + int wstat ; + register int r = wait_pid_nohang(pid, &wstat) ; + if ((r < 0) && (errno != ECHILD)) + strerr_diefu1sys(111, "wait_pid_nohang") ; + else if (r > 0) + { + char pack[UINT_PACK] ; + uint_pack_big(pack, (unsigned int)wstat) ; + buffer_putnoflush(buffer_1small, pack, UINT_PACK) ; + cont = 0 ; + } + } + else + strerr_dief1sys(101, "internal inconsistency, please submit a bug-report") ; + } + } + if (x[0].revents && cont) + { + kill(pid, SIGTERM) ; + kill(pid, SIGCONT) ; + x[0].fd = -1 ; + return 1 ; + } + } + } + if (ndelay_off(1) < 0) + strerr_diefu1sys(111, "set stdout blocking") ; + if (!buffer_flush(buffer_1small)) + strerr_diefu1sys(111, "write status to client") ; + return 0 ; +} diff --git a/src/include/s6/accessrules.h b/src/include/s6/accessrules.h @@ -0,0 +1,53 @@ +/* ISC license. */ + +#ifndef S6_ACCESSRULES_H +#define S6_ACCESSRULES_H + +#include <skalibs/cdb.h> +#include <skalibs/stralloc.h> +#include <skalibs/ip46.h> + +typedef struct s6_accessrules_params_s s6_accessrules_params_t, *s6_accessrules_params_t_ref ; +struct s6_accessrules_params_s +{ + stralloc env ; + stralloc exec ; +} ; +#define S6_ACCESSRULES_PARAMS_ZERO { .env = STRALLOC_ZERO, .exec = STRALLOC_ZERO } + +typedef enum s6_accessrules_result_e s6_accessrules_result_t, *s6_accessrules_result_t_ref ; +enum s6_accessrules_result_e +{ + S6_ACCESSRULES_ERROR = -1, + S6_ACCESSRULES_DENY = 0, + S6_ACCESSRULES_ALLOW = 1, + S6_ACCESSRULES_NOTFOUND = 2 +} ; + +typedef s6_accessrules_result_t s6_accessrules_backend_func_t (char const *, unsigned int, void *, s6_accessrules_params_t *) ; +typedef s6_accessrules_backend_func_t *s6_accessrules_backend_func_t_ref ; + +extern s6_accessrules_backend_func_t s6_accessrules_backend_fs ; +extern s6_accessrules_backend_func_t s6_accessrules_backend_cdb ; + +typedef s6_accessrules_result_t s6_accessrules_keycheck_func_t (void const *, void *, s6_accessrules_params_t *, s6_accessrules_backend_func_t_ref) ; +typedef s6_accessrules_keycheck_func_t *s6_accessrules_keycheck_func_t_ref ; + +extern s6_accessrules_keycheck_func_t s6_accessrules_keycheck_uidgid ; +extern s6_accessrules_keycheck_func_t s6_accessrules_keycheck_ip4 ; +extern s6_accessrules_keycheck_func_t s6_accessrules_keycheck_ip6 ; +extern s6_accessrules_keycheck_func_t s6_accessrules_keycheck_reversedns ; +#define s6_accessrules_keycheck_ip46(key, data, params, f) (ip46_is6((ip46_t const *)(key)) ? s6_accessrules_keycheck_ip6(((ip46_t const *)(key))->ip, data, params, f) : s6_accessrules_keycheck_ip4(((ip46_t const *)(key))->ip, data, params, f)) + +extern s6_accessrules_result_t s6_accessrules_uidgid_cdb (unsigned int, unsigned int, struct cdb *, s6_accessrules_params_t *) ; +extern s6_accessrules_result_t s6_accessrules_uidgid_fs (unsigned int, unsigned int, char const *, s6_accessrules_params_t *) ; +#define s6_accessrules_ip4_cdb(ip4, c, params) s6_accessrules_keycheck_ip4(ip4, c, (params), &s6_accessrules_backend_cdb) +#define s6_accessrules_ip4_fs(ip4, rulesdir, params) s6_accessrules_keycheck_ip4(ip4, rulesdir, (params), &s6_accessrules_backend_fs) +#define s6_accessrules_ip6_cdb(ip6, c, params) s6_accessrules_keycheck_ip6(ip6, c, (params), &s6_accessrules_backend_cdb) +#define s6_accessrules_ip6_fs(ip6, rulesdir, params) s6_accessrules_keycheck_ip6(ip6, rulesdir, (params), &s6_accessrules_backend_fs) +#define s6_accessrules_ip46_cdb(ip, c, params) s6_accessrules_keycheck_ip46(ip, c, (params), &s6_accessrules_backend_cdb) +#define s6_accessrules_ip46_fs(ip, rulesdir, params) s6_accessrules_keycheck_ip46(ip, rulesdir, (params), &s6_accessrules_backend_fs) +#define s6_accessrules_reversedns_cdb(name, c, params) s6_accessrules_keycheck_reversedns(name, c, (params), &s6_accessrules_backend_cdb) +#define s6_accessrules_reversedns_fs(name, c, params) s6_accessrules_keycheck_reversedns(name, c, (params), &s6_accessrules_backend_fs) + +#endif diff --git a/src/include/s6/s6.h b/src/include/s6/s6.h @@ -6,6 +6,7 @@ #include <s6/s6-supervise.h> #include <s6/ftrigr.h> #include <s6/ftrigw.h> +#include <s6/accessrules.h> #include <s6/s6lock.h> #endif diff --git a/src/libs6/deps-lib/s6 b/src/libs6/deps-lib/s6 @@ -13,6 +13,14 @@ ftrigw_clean.o ftrigw_fifodir_make.o ftrigw_notify.o ftrigw_notifyb.o +s6_accessrules_backend_cdb.o +s6_accessrules_backend_fs.o +s6_accessrules_keycheck_ip4.o +s6_accessrules_keycheck_ip6.o +s6_accessrules_keycheck_reversedns.o +s6_accessrules_keycheck_uidgid.o +s6_accessrules_uidgid_cdb.o +s6_accessrules_uidgid_fs.o s6_supervise_lock.o s6_supervise_lock_mode.o s6_svc_main.o diff --git a/src/libs6/s6_accessrules_backend_cdb.c b/src/libs6/s6_accessrules_backend_cdb.c @@ -0,0 +1,38 @@ +/* ISC license. */ + +#include <unistd.h> +#include <errno.h> +#include <skalibs/bytestr.h> +#include <skalibs/uint16.h> +#include <skalibs/cdb.h> +#include <skalibs/stralloc.h> +#include <s6/accessrules.h> + +s6_accessrules_result_t s6_accessrules_backend_cdb (char const *key, unsigned int keylen, void *data, s6_accessrules_params_t *params) +{ + struct cdb *c = data ; + unsigned int execbase, n ; + uint16 envlen, execlen ; + register int r = cdb_find(c, key, keylen) ; + if (r < 0) return S6_ACCESSRULES_ERROR ; + else if (!r) return S6_ACCESSRULES_NOTFOUND ; + n = cdb_datalen(c) ; + if ((n < 5U) || (n > 8197U)) return (errno = EINVAL, S6_ACCESSRULES_ERROR) ; + if (!stralloc_readyplus(&params->exec, n)) return S6_ACCESSRULES_ERROR ; + execbase = params->exec.len ; + if (cdb_read(c, params->exec.s + execbase, n, cdb_datapos(c)) < 0) return S6_ACCESSRULES_ERROR ; + if (params->exec.s[execbase] == 'D') return S6_ACCESSRULES_DENY ; + else if (params->exec.s[execbase] != 'A') return S6_ACCESSRULES_NOTFOUND ; + uint16_unpack_big(params->exec.s + execbase + 1U, &envlen) ; + if ((envlen > 4096U) || (envlen+5U > n)) return (errno = EINVAL, S6_ACCESSRULES_ERROR) ; + uint16_unpack_big(params->exec.s + execbase + 3 + envlen, &execlen) ; + if ((execlen > 4096U) || (5U + envlen + execlen != n)) return (errno = EINVAL, S6_ACCESSRULES_ERROR) ; + if (!stralloc_catb(&params->env, params->exec.s + execbase + 3U, envlen)) return S6_ACCESSRULES_ERROR ; + byte_copy(params->exec.s + execbase, execlen, params->exec.s + execbase + 5U + envlen) ; + if (execlen) + { + params->exec.len += execlen ; + params->exec.s[params->exec.len++] = 0 ; + } + return S6_ACCESSRULES_ALLOW ; +} diff --git a/src/libs6/s6_accessrules_backend_fs.c b/src/libs6/s6_accessrules_backend_fs.c @@ -0,0 +1,58 @@ +/* ISC license. */ + +#include <unistd.h> +#include <errno.h> +#include <skalibs/bytestr.h> +#include <skalibs/fmtscan.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> +#include <s6/accessrules.h> + +s6_accessrules_result_t s6_accessrules_backend_fs (char const *key, unsigned int keylen, void *data, s6_accessrules_params_t *params) +{ + char *dir = data ; + unsigned int dirlen = str_len(dir) ; + unsigned int envbase = params->env.len ; + int wasnull = !params->env.s ; + { + char tmp[dirlen + keylen + 10] ; + byte_copy(tmp, dirlen, dir) ; + tmp[dirlen] = '/' ; + byte_copy(tmp + dirlen + 1, keylen, key) ; + byte_copy(tmp + dirlen + keylen + 1, 7, "/allow") ; + if (access(tmp, R_OK) < 0) + { + if ((errno != EACCES) && (errno != ENOENT)) + return S6_ACCESSRULES_ERROR ; + byte_copy(tmp + dirlen + keylen + 2, 5, "deny") ; + return (access(tmp, R_OK) == 0) ? S6_ACCESSRULES_DENY : + (errno != EACCES) && (errno != ENOENT) ? S6_ACCESSRULES_ERROR : + S6_ACCESSRULES_NOTFOUND ; + } + byte_copy(tmp + dirlen + keylen + 2, 4, "env") ; + if ((envdir(tmp, &params->env) < 0) && (errno != ENOENT)) + return S6_ACCESSRULES_ERROR ; + if (!stralloc_readyplus(&params->exec, 4097)) + { + if (wasnull) stralloc_free(&params->env) ; + else params->env.len = envbase ; + return S6_ACCESSRULES_ERROR ; + } + byte_copy(tmp + dirlen + keylen + 2, 5, "exec") ; + { + register int r = openreadnclose(tmp, params->exec.s + params->exec.len, 4096) ; + if ((r < 0) && (errno != EACCES) && (errno != ENOENT)) + { + if (wasnull) stralloc_free(&params->env) ; + else params->env.len = envbase ; + return S6_ACCESSRULES_ERROR ; + } + if (r > 0) + { + params->exec.len += r ; + params->exec.s[params->exec.len++] = 0 ; + } + } + } + return S6_ACCESSRULES_ALLOW ; +} diff --git a/src/libs6/s6_accessrules_keycheck_ip4.c b/src/libs6/s6_accessrules_keycheck_ip4.c @@ -0,0 +1,24 @@ +/* ISC license. */ + +#include <skalibs/uint32.h> +#include <skalibs/uint.h> +#include <skalibs/fmtscan.h> +#include <s6/accessrules.h> + +s6_accessrules_result_t s6_accessrules_keycheck_ip4 (void const *key, void *data, s6_accessrules_params_t *params, s6_accessrules_backend_func_t_ref check1) +{ + char fmt[IP4_FMT + UINT_FMT + 6] = "ip4/" ; + uint32 ip ; + unsigned int i = 0 ; + uint32_unpack_big((char const *)key, &ip) ; + for (; i <= 32 ; i++) + { + register s6_accessrules_result_t r ; + register unsigned int len = 4 + ip4_fmtu32(fmt+4, (i == 32) ? 0 : ip & ~((1U << i) - 1)) ; + fmt[len++] = '_' ; + len += uint_fmt(fmt + len, 32 - i) ; + r = (*check1)(fmt, len, data, params) ; + if (r != S6_ACCESSRULES_NOTFOUND) return r ; + } + return S6_ACCESSRULES_NOTFOUND ; +} diff --git a/src/libs6/s6_accessrules_keycheck_ip6.c b/src/libs6/s6_accessrules_keycheck_ip6.c @@ -0,0 +1,27 @@ +/* ISC license. */ + +#include <skalibs/uint.h> +#include <skalibs/bytestr.h> +#include <skalibs/bitarray.h> +#include <skalibs/fmtscan.h> +#include <s6/accessrules.h> + +s6_accessrules_result_t s6_accessrules_keycheck_ip6 (void const *key, void *data, s6_accessrules_params_t *params, s6_accessrules_backend_func_t_ref check1) +{ + char fmt[IP6_FMT + UINT_FMT + 6] = "ip6/" ; + char ip6[16] ; + unsigned int i = 0 ; + byte_copy(ip6, 16, (char const *)key) ; + for (; i <= 128 ; i++) + { + unsigned int len ; + register s6_accessrules_result_t r ; + if (i) bitarray_clear(ip6, 128 - i) ; + len = 4 + ip6_fmt(fmt+4, ip6) ; + fmt[len++] = '_' ; + len += uint_fmt(fmt + len, 128 - i) ; + r = (*check1)(fmt, len, data, params) ; + if (r != S6_ACCESSRULES_NOTFOUND) return r ; + } + return S6_ACCESSRULES_NOTFOUND ; +} diff --git a/src/libs6/s6_accessrules_keycheck_reversedns.c b/src/libs6/s6_accessrules_keycheck_reversedns.c @@ -0,0 +1,27 @@ +/* ISC license. */ + +#include <errno.h> +#include <skalibs/bytestr.h> +#include <s6/accessrules.h> + +s6_accessrules_result_t s6_accessrules_keycheck_reversedns (void const *key, void *data, s6_accessrules_params_t *params, s6_accessrules_backend_func_t_ref check1) +{ + char const *name = key ; + unsigned int len = str_len(name) ; + if (!len) return (errno = EINVAL, S6_ACCESSRULES_ERROR) ; + if (name[len-1] == '.') len-- ; + { + unsigned int i = 0 ; + char tmp[len + 11] ; + byte_copy(tmp, 11, "reversedns/") ; + while (i < len) + { + register s6_accessrules_result_t r ; + byte_copy(tmp+11, len-i, name+i) ; + r = (*check1)(tmp, 11+len-i, data, params) ; + if (r != S6_ACCESSRULES_NOTFOUND) return r ; + i += byte_chr(name+i, len-i, '.') + 1 ; + } + } + return (*check1)("reversedns/@", 12, data, params) ; +} diff --git a/src/libs6/s6_accessrules_keycheck_uidgid.c b/src/libs6/s6_accessrules_keycheck_uidgid.c @@ -0,0 +1,16 @@ +/* ISC license. */ + +#include <skalibs/uint.h> +#include <skalibs/diuint.h> +#include <s6/accessrules.h> + +s6_accessrules_result_t s6_accessrules_keycheck_uidgid (void const *key, void *data, s6_accessrules_params_t *params, s6_accessrules_backend_func_t_ref check1) +{ + char fmt[4 + UINT_FMT] = "uid/" ; + register s6_accessrules_result_t r = (*check1)(fmt, 4 + uint_fmt(fmt+4, ((diuint const *)key)->left), data, params) ; + if (r != S6_ACCESSRULES_NOTFOUND) return r ; + fmt[0] = 'g' ; + r = (*check1)(fmt, 4 + uint_fmt(fmt+4, ((diuint const *)key)->right), data, params) ; + return (r != S6_ACCESSRULES_NOTFOUND) ? r : + (*check1)("uid/default", 11, data, params) ; +} diff --git a/src/libs6/s6_accessrules_uidgid_cdb.c b/src/libs6/s6_accessrules_uidgid_cdb.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include <skalibs/diuint.h> +#include <skalibs/cdb.h> +#include <s6/accessrules.h> + +s6_accessrules_result_t s6_accessrules_uidgid_cdb (unsigned int uid, unsigned int gid, struct cdb *c, s6_accessrules_params_t *params) +{ + diuint uidgid = { .left = uid, .right = gid } ; + return s6_accessrules_keycheck_uidgid(&uidgid, c, params, &s6_accessrules_backend_cdb) ; +} diff --git a/src/libs6/s6_accessrules_uidgid_fs.c b/src/libs6/s6_accessrules_uidgid_fs.c @@ -0,0 +1,10 @@ +/* ISC license. */ + +#include <skalibs/diuint.h> +#include <s6/accessrules.h> + +s6_accessrules_result_t s6_accessrules_uidgid_fs (unsigned int uid, unsigned int gid, char const *rulesdir, s6_accessrules_params_t *params) +{ + diuint uidgid = { .left = uid, .right = gid } ; + return s6_accessrules_keycheck_uidgid(&uidgid, (void *)rulesdir, params, &s6_accessrules_backend_fs) ; +}