[systemd-commits] .gitignore Makefile.am Makefile-man.am man/systemd-sabridge.xml man/systemd-saproxy.xml src/sabridge src/saproxy TODO

David Strauss straussd at kemper.freedesktop.org
Tue Oct 15 17:00:24 PDT 2013


 .gitignore               |    2 
 Makefile-man.am          |    2 
 Makefile.am              |   10 
 TODO                     |    8 
 man/systemd-sabridge.xml |  254 --------------------
 man/systemd-saproxy.xml  |  254 ++++++++++++++++++++
 src/sabridge/Makefile    |   28 --
 src/sabridge/sabridge.c  |  575 -----------------------------------------------
 src/saproxy/Makefile     |   28 ++
 src/saproxy/saproxy.c    |  575 +++++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 868 insertions(+), 868 deletions(-)

New commits:
commit d1b38fac57d82f0249b9e581eb0d18175f6aa74a
Author: David Strauss <david at davidstrauss.net>
Date:   Tue Oct 15 17:00:18 2013 -0700

    Rename sabridge to saproxy to be less cryptic

diff --git a/.gitignore b/.gitignore
index d2d5da5..22485b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -71,7 +71,7 @@
 /systemd-reply-password
 /systemd-rfkill
 /systemd-run
-/systemd-sabridge
+/systemd-saproxy
 /systemd-shutdown
 /systemd-shutdownd
 /systemd-sleep
diff --git a/Makefile-man.am b/Makefile-man.am
index e78a8a2..6b286bc 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -66,7 +66,7 @@ MANPAGES += \
 	man/systemd-nspawn.1 \
 	man/systemd-remount-fs.service.8 \
 	man/systemd-run.1 \
-	man/systemd-sabridge.1 \
+	man/systemd-saproxy.1 \
 	man/systemd-shutdownd.service.8 \
 	man/systemd-sleep.conf.5 \
 	man/systemd-suspend.service.8 \
diff --git a/Makefile.am b/Makefile.am
index 2877184..59c5174 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -300,7 +300,7 @@ bin_PROGRAMS = \
 	systemd-delta \
 	systemd-analyze \
 	systemd-run \
-	systemd-sabridge
+	systemd-saproxy
 
 dist_bin_SCRIPTS = \
 	src/kernel-install/kernel-install
@@ -3210,10 +3210,10 @@ EXTRA_DIST += \
 
 # ------------------------------------------------------------------------------
 
-systemd_sabridge_SOURCES = \
-	src/sabridge/sabridge.c
+systemd_saproxy_SOURCES = \
+	src/saproxy/saproxy.c
 
-systemd_sabridge_LDADD = \
+systemd_saproxy_LDADD = \
 	libsystemd-shared.la \
 	libsystemd-logs.la \
 	libsystemd-journal-internal.la \
@@ -3221,7 +3221,7 @@ systemd_sabridge_LDADD = \
 	libsystemd-daemon.la \
 	libsystemd-bus.la
 
-systemd_sabridge_CFLAGS = \
+systemd_saproxy_CFLAGS = \
 	$(AM_CFLAGS)
 
 # ------------------------------------------------------------------------------
diff --git a/TODO b/TODO
index a047f62..a3ab09e 100644
--- a/TODO
+++ b/TODO
@@ -48,13 +48,13 @@ CGroup Rework Completion:
 
 Features:
 
-* sabridge: Support multiple inherited sockets mapped using different proxies
+* saproxy: Support multiple inherited sockets mapped using different proxies
 
-* sabridge: Use a nonblocking alternative to getaddrinfo
+* saproxy: Use a nonblocking alternative to getaddrinfo
 
-* sabridge: Until we can start daemons directly, find a less ugly, less racy alternative than shell scripts for the second man page example.
+* saproxy: Until we can start daemons directly, find a less ugly, less racy alternative than shell scripts for the second man page example.
 
-* sabridge: Support starting daemons directly without requiring a shell script; update man pages
+* saproxy: Support starting daemons directly without requiring a shell script; update man pages
 
 * "systemctl cat" or "systemctl view" command or or so, that cats the backing unit file of a service, plus its drop-ins and shows them in a pager
 
diff --git a/man/systemd-sabridge.xml b/man/systemd-sabridge.xml
deleted file mode 100644
index abeb1a5..0000000
--- a/man/systemd-sabridge.xml
+++ /dev/null
@@ -1,254 +0,0 @@
-<?xml version="1.0"?>
-<!--*-nxml-*-->
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-     "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-<!--
-  This file is part of systemd.
-
-  Copyright 2013 David Strauss
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
--->
-<refentry id="systemd-sabridge">
-        <refentryinfo>
-                <title>systemd-sabridge</title>
-                <productname>systemd</productname>
-                <authorgroup>
-                        <author>
-                                <contrib>Developer</contrib>
-                                <firstname>David</firstname>
-                                <surname>Strauss</surname>
-                                <email>david at davidstrauss.net</email>
-                        </author>
-                </authorgroup>
-        </refentryinfo>
-        <refmeta>
-                <refentrytitle>systemd-sabridge</refentrytitle>
-                <manvolnum>1</manvolnum>
-        </refmeta>
-        <refnamediv>
-                <refname>systemd-sabridge</refname>
-                <refpurpose>Inherit a socket. Bidirectionally
-                proxy.</refpurpose>
-        </refnamediv>
-        <refsynopsisdiv>
-                <cmdsynopsis>
-                        <command>systemd-sabridge</command>
-                        <arg choice="opt" rep="repeat">OPTIONS</arg>
-                        <arg choice="plain"><replaceable>HOSTNAME-OR-IP</replaceable></arg>
-                        <arg choice="plain"><replaceable>PORT-OR-SERVICE</replaceable></arg>
-                </cmdsynopsis>
-                <cmdsynopsis>
-                        <command>systemd-sabridge</command>
-                        <arg choice="opt" rep="repeat">OPTIONS</arg>
-                        <arg choice="plain"><replaceable>UNIX-DOMAIN-SOCKET-PATH</replaceable>
-                        </arg>
-                </cmdsynopsis>
-        </refsynopsisdiv>
-        <refsect1>
-                <title>Description</title>
-                <para>
-                <command>systemd-sabridge</command>provides a proxy
-                to socket-activate services that do not yet support
-                native socket activation. On behalf of the daemon,
-                the proxy inherits the socket from systemd, accepts
-                each client connection, opens a connection to the server
-                for each client, and then bidirectionally forwards
-                data between the two.</para>
-                <para>This utility's behavior is similar to
-                <citerefentry><refentrytitle>socat</refentrytitle><manvolnum>1</manvolnum> </citerefentry>.
-                The main differences for <command>systemd-sabridge</command>
-                are support for socket activation with
-                <literal>Accept=false</literal> and an event-driven
-                design that scales better with the number of
-                connections.</para>
-        </refsect1>
-        <refsect1>
-                <title>Options</title>
-                <para>The following options are understood:</para>
-                <variablelist>
-                        <varlistentry>
-                                <term><option>-h</option></term>
-                                <term><option>--help</option></term>
-                                <listitem>
-                                        <para>Prints a short help
-                                        text and exits.</para>
-                                </listitem>
-                        </varlistentry>
-                        <varlistentry>
-                                <term><option>--version</option></term>
-                                <listitem>
-                                        <para>Prints a version
-                                        string and exits.</para>
-                                </listitem>
-                        </varlistentry>
-                        <varlistentry>
-                                <term><option>--ignore-env</option></term>
-                                <listitem>
-                                        <para>Skips verification of
-                                        the expected PID and file
-                                        descriptor numbers. Use if
-                                        invoked indirectly, for
-                                        example with a shell script
-                                        rather than with
-                                        <option>ExecStart=/usr/bin/systemd-sabridge</option>
-                                        </para>
-                                </listitem>
-                        </varlistentry>
-                </variablelist>
-        </refsect1>
-        <refsect1>
-                <title>Exit status</title>
-                <para>On success 0 is returned, a non-zero failure
-                code otherwise.</para>
-        </refsect1>
-        <refsect1>
-                <title>Examples</title>
-                <refsect2>
-                        <title>Direct-Use Example</title>
-                        <para>Use two services with a dependency
-                        and no namespace isolation.</para>
-                        <example label="bridge socket unit">
-                                <title>/etc/systemd/system/bridge-to-nginx.socket</title>
-                                <programlisting>
-<![CDATA[[Socket]
-ListenStream=80
-
-[Install]
-WantedBy=socket.target]]>
-</programlisting>
-                        </example>
-                        <example label="bridge service unit">
-                                <title>/etc/systemd/system/bridge-to-nginx.service</title>
-                                <programlisting>
-<![CDATA[[Unit]
-After=nginx.service
-Requires=nginx.service
-
-[Service]
-ExecStart=/usr/bin/systemd-sabridge /tmp/nginx.sock
-PrivateTmp=true
-PrivateNetwork=true]]>
-</programlisting>
-                        </example>
-                        <example label="nginx configuration">
-                                <title>/etc/nginx/nginx.conf</title>
-                                <programlisting>
-<![CDATA[[...]
-server {
-    listen       unix:/tmp/nginx.sock;
-    [...]]]>
-</programlisting>
-                        </example>
-                        <example label="commands">
-                                <programlisting>
-<![CDATA[$ sudo systemctl --system daemon-reload
-$ sudo systemctl start bridge-to-nginx.socket
-$ sudo systemctl enable bridge-to-nginx.socket
-$ curl http://localhost:80/]]>
-</programlisting>
-                        </example>
-                </refsect2>
-                <refsect2>
-                        <title>Indirect-Use Example</title>
-                        <para>Use a shell script to isolate the
-                        service and bridge into the same namespace.
-                        This is particularly useful for running
-                        TCP-only daemons without the daemon
-                        affecting ports on regular
-                        interfaces.</para>
-                        <example label="combined bridge and nginx socket unit">
-
-                                <title>
-                                /etc/systemd/system/bridge-with-nginx.socket</title>
-                                <programlisting>
-<![CDATA[[Socket]
-ListenStream=80
-
-[Install]
-WantedBy=socket.target]]>
-</programlisting>
-                        </example>
-                        <example label="combined bridge and nginx service unit">
-
-                                <title>
-                                /etc/systemd/system/bridge-with-nginx.service</title>
-                                <programlisting>
-<![CDATA[[Unit]
-After=syslog.target remote-fs.target nss-lookup.target
-
-[Service]
-ExecStartPre=/usr/sbin/nginx -t
-ExecStart=/usr/bin/sabridge-nginx.sh
-PrivateTmp=true
-PrivateNetwork=true]]>
-</programlisting>
-                        </example>
-                        <example label="shell script">
-                                <title>
-                                /usr/bin/sabridge-nginx.sh</title>
-                                <programlisting>
-<![CDATA[#!/bin/sh
-/usr/sbin/nginx
-while [ ! -f /tmp/nginx.pid ]
-  do
-     /usr/bin/inotifywait /tmp/nginx.pid
-  done
-/usr/bin/systemd-sabridge --ignore-env localhost 8080]]>
-</programlisting>
-                        </example>
-                        <example label="nginx configuration">
-                                <title>
-                                /etc/nginx/nginx.conf</title>
-                                <programlisting>
-<![CDATA[[...]
-server {
-    listen       8080;
-    listen       unix:/tmp/nginx.sock;
-    [...]]]>
-</programlisting>
-                        </example>
-                        <example label="commands">
-                                <programlisting>
-<![CDATA[$ sudo systemctl --system daemon-reload
-$ sudo systemctl start bridge-with-nginx.socket
-$ sudo systemctl enable bridge-with-nginx.socket
-$ curl http://localhost:80/]]>
-</programlisting>
-                        </example>
-                </refsect2>
-        </refsect1>
-        <refsect1>
-                <title>See Also</title>
-                <para>
-                <citerefentry>
-                        <refentrytitle>
-                        systemd.service</refentrytitle>
-                        <manvolnum>5</manvolnum>
-                </citerefentry>,
-                <citerefentry>
-                        <refentrytitle>
-                        systemd.socket</refentrytitle>
-                        <manvolnum>5</manvolnum>
-                </citerefentry>,
-                <citerefentry>
-                        <refentrytitle>systemctl</refentrytitle>
-                        <manvolnum>1</manvolnum>
-                </citerefentry>,
-                <citerefentry>
-                        <refentrytitle>socat</refentrytitle>
-                        <manvolnum>1</manvolnum>
-                </citerefentry></para>
-        </refsect1>
-</refentry>
diff --git a/man/systemd-saproxy.xml b/man/systemd-saproxy.xml
new file mode 100644
index 0000000..1314b31
--- /dev/null
+++ b/man/systemd-saproxy.xml
@@ -0,0 +1,254 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+     "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!--
+  This file is part of systemd.
+
+  Copyright 2013 David Strauss
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+<refentry id="systemd-saproxy">
+        <refentryinfo>
+                <title>systemd-saproxy</title>
+                <productname>systemd</productname>
+                <authorgroup>
+                        <author>
+                                <contrib>Developer</contrib>
+                                <firstname>David</firstname>
+                                <surname>Strauss</surname>
+                                <email>david at davidstrauss.net</email>
+                        </author>
+                </authorgroup>
+        </refentryinfo>
+        <refmeta>
+                <refentrytitle>systemd-saproxy</refentrytitle>
+                <manvolnum>1</manvolnum>
+        </refmeta>
+        <refnamediv>
+                <refname>systemd-saproxy</refname>
+                <refpurpose>Inherit a socket. Bidirectionally
+                proxy.</refpurpose>
+        </refnamediv>
+        <refsynopsisdiv>
+                <cmdsynopsis>
+                        <command>systemd-saproxy</command>
+                        <arg choice="opt" rep="repeat">OPTIONS</arg>
+                        <arg choice="plain"><replaceable>HOSTNAME-OR-IP</replaceable></arg>
+                        <arg choice="plain"><replaceable>PORT-OR-SERVICE</replaceable></arg>
+                </cmdsynopsis>
+                <cmdsynopsis>
+                        <command>systemd-saproxy</command>
+                        <arg choice="opt" rep="repeat">OPTIONS</arg>
+                        <arg choice="plain"><replaceable>UNIX-DOMAIN-SOCKET-PATH</replaceable>
+                        </arg>
+                </cmdsynopsis>
+        </refsynopsisdiv>
+        <refsect1>
+                <title>Description</title>
+                <para>
+                <command>systemd-saproxy</command>provides a proxy
+                to socket-activate services that do not yet support
+                native socket activation. On behalf of the daemon,
+                the proxy inherits the socket from systemd, accepts
+                each client connection, opens a connection to the server
+                for each client, and then bidirectionally forwards
+                data between the two.</para>
+                <para>This utility's behavior is similar to
+                <citerefentry><refentrytitle>socat</refentrytitle><manvolnum>1</manvolnum> </citerefentry>.
+                The main differences for <command>systemd-saproxy</command>
+                are support for socket activation with
+                <literal>Accept=false</literal> and an event-driven
+                design that scales better with the number of
+                connections.</para>
+        </refsect1>
+        <refsect1>
+                <title>Options</title>
+                <para>The following options are understood:</para>
+                <variablelist>
+                        <varlistentry>
+                                <term><option>-h</option></term>
+                                <term><option>--help</option></term>
+                                <listitem>
+                                        <para>Prints a short help
+                                        text and exits.</para>
+                                </listitem>
+                        </varlistentry>
+                        <varlistentry>
+                                <term><option>--version</option></term>
+                                <listitem>
+                                        <para>Prints a version
+                                        string and exits.</para>
+                                </listitem>
+                        </varlistentry>
+                        <varlistentry>
+                                <term><option>--ignore-env</option></term>
+                                <listitem>
+                                        <para>Skips verification of
+                                        the expected PID and file
+                                        descriptor numbers. Use if
+                                        invoked indirectly, for
+                                        example with a shell script
+                                        rather than with
+                                        <option>ExecStart=/usr/bin/systemd-saproxy</option>
+                                        </para>
+                                </listitem>
+                        </varlistentry>
+                </variablelist>
+        </refsect1>
+        <refsect1>
+                <title>Exit status</title>
+                <para>On success 0 is returned, a non-zero failure
+                code otherwise.</para>
+        </refsect1>
+        <refsect1>
+                <title>Examples</title>
+                <refsect2>
+                        <title>Direct-Use Example</title>
+                        <para>Use two services with a dependency
+                        and no namespace isolation.</para>
+                        <example label="bridge socket unit">
+                                <title>/etc/systemd/system/bridge-to-nginx.socket</title>
+                                <programlisting>
+<![CDATA[[Socket]
+ListenStream=80
+
+[Install]
+WantedBy=socket.target]]>
+</programlisting>
+                        </example>
+                        <example label="bridge service unit">
+                                <title>/etc/systemd/system/bridge-to-nginx.service</title>
+                                <programlisting>
+<![CDATA[[Unit]
+After=nginx.service
+Requires=nginx.service
+
+[Service]
+ExecStart=/usr/bin/systemd-saproxy /tmp/nginx.sock
+PrivateTmp=true
+PrivateNetwork=true]]>
+</programlisting>
+                        </example>
+                        <example label="nginx configuration">
+                                <title>/etc/nginx/nginx.conf</title>
+                                <programlisting>
+<![CDATA[[...]
+server {
+    listen       unix:/tmp/nginx.sock;
+    [...]]]>
+</programlisting>
+                        </example>
+                        <example label="commands">
+                                <programlisting>
+<![CDATA[$ sudo systemctl --system daemon-reload
+$ sudo systemctl start bridge-to-nginx.socket
+$ sudo systemctl enable bridge-to-nginx.socket
+$ curl http://localhost:80/]]>
+</programlisting>
+                        </example>
+                </refsect2>
+                <refsect2>
+                        <title>Indirect-Use Example</title>
+                        <para>Use a shell script to isolate the
+                        service and bridge into the same namespace.
+                        This is particularly useful for running
+                        TCP-only daemons without the daemon
+                        affecting ports on regular
+                        interfaces.</para>
+                        <example label="combined bridge and nginx socket unit">
+
+                                <title>
+                                /etc/systemd/system/bridge-with-nginx.socket</title>
+                                <programlisting>
+<![CDATA[[Socket]
+ListenStream=80
+
+[Install]
+WantedBy=socket.target]]>
+</programlisting>
+                        </example>
+                        <example label="combined bridge and nginx service unit">
+
+                                <title>
+                                /etc/systemd/system/bridge-with-nginx.service</title>
+                                <programlisting>
+<![CDATA[[Unit]
+After=syslog.target remote-fs.target nss-lookup.target
+
+[Service]
+ExecStartPre=/usr/sbin/nginx -t
+ExecStart=/usr/bin/saproxy-nginx.sh
+PrivateTmp=true
+PrivateNetwork=true]]>
+</programlisting>
+                        </example>
+                        <example label="shell script">
+                                <title>
+                                /usr/bin/saproxy-nginx.sh</title>
+                                <programlisting>
+<![CDATA[#!/bin/sh
+/usr/sbin/nginx
+while [ ! -f /tmp/nginx.pid ]
+  do
+     /usr/bin/inotifywait /tmp/nginx.pid
+  done
+/usr/bin/systemd-saproxy --ignore-env localhost 8080]]>
+</programlisting>
+                        </example>
+                        <example label="nginx configuration">
+                                <title>
+                                /etc/nginx/nginx.conf</title>
+                                <programlisting>
+<![CDATA[[...]
+server {
+    listen       8080;
+    listen       unix:/tmp/nginx.sock;
+    [...]]]>
+</programlisting>
+                        </example>
+                        <example label="commands">
+                                <programlisting>
+<![CDATA[$ sudo systemctl --system daemon-reload
+$ sudo systemctl start bridge-with-nginx.socket
+$ sudo systemctl enable bridge-with-nginx.socket
+$ curl http://localhost:80/]]>
+</programlisting>
+                        </example>
+                </refsect2>
+        </refsect1>
+        <refsect1>
+                <title>See Also</title>
+                <para>
+                <citerefentry>
+                        <refentrytitle>
+                        systemd.service</refentrytitle>
+                        <manvolnum>5</manvolnum>
+                </citerefentry>,
+                <citerefentry>
+                        <refentrytitle>
+                        systemd.socket</refentrytitle>
+                        <manvolnum>5</manvolnum>
+                </citerefentry>,
+                <citerefentry>
+                        <refentrytitle>systemctl</refentrytitle>
+                        <manvolnum>1</manvolnum>
+                </citerefentry>,
+                <citerefentry>
+                        <refentrytitle>socat</refentrytitle>
+                        <manvolnum>1</manvolnum>
+                </citerefentry></para>
+        </refsect1>
+</refentry>
diff --git a/src/sabridge/Makefile b/src/sabridge/Makefile
deleted file mode 100644
index 9d07505..0000000
--- a/src/sabridge/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-#  This file is part of systemd.
-#
-#  Copyright 2010 Lennart Poettering
-#
-#  systemd is free software; you can redistribute it and/or modify it
-#  under the terms of the GNU Lesser General Public License as published by
-#  the Free Software Foundation; either version 2.1 of the License, or
-#  (at your option) any later version.
-#
-#  systemd is distributed in the hope that it will be useful, but
-#  WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public License
-#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-
-# This file is a dirty trick to simplify compilation from within
-# emacs. This file is not intended to be distributed. So, don't touch
-# it, even better ignore it!
-
-all:
-	$(MAKE) -C ..
-
-clean:
-	$(MAKE) -C .. clean
-
-.PHONY: all clean
diff --git a/src/sabridge/sabridge.c b/src/sabridge/sabridge.c
deleted file mode 100644
index 0589871..0000000
--- a/src/sabridge/sabridge.c
+++ /dev/null
@@ -1,575 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2013 David Strauss
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
- ***/
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <netdb.h>
-#include <sys/fcntl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "sd-daemon.h"
-#include "sd-event.h"
-#include "socket-util.h"
-#include "util.h"
-
-#define BUFFER_SIZE 4096
-#define _cleanup_freeaddrinfo_ _cleanup_(freeaddrinfop)
-
-unsigned int total_clients = 0;
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct addrinfo *, freeaddrinfo);
-
-struct proxy {
-        int listen_fd;
-        bool ignore_env;
-        bool remote_is_inet;
-        const char *remote_host;
-        const char *remote_service;
-};
-
-struct connection {
-        int fd;
-        sd_event_source *w_recv;
-        sd_event_source *w_send;
-        struct connection *c_destination;
-        size_t buffer_filled_len;
-        size_t buffer_sent_len;
-        char buffer[BUFFER_SIZE];
-};
-
-static void free_connection(struct connection *c) {
-        log_debug("Freeing fd=%d (conn %p).", c->fd, c);
-        sd_event_source_unref(c->w_recv);
-        sd_event_source_unref(c->w_send);
-        close(c->fd);
-        free(c);
-}
-
-static int send_buffer(struct connection *sender) {
-        struct connection *receiver = sender->c_destination;
-        ssize_t len;
-        int r = 0;
-
-        /* We cannot assume that even a partial send() indicates that
-         * the next send() will block. Loop until it does. */
-        while (sender->buffer_filled_len > sender->buffer_sent_len) {
-                len = send(receiver->fd, sender->buffer + sender->buffer_sent_len, sender->buffer_filled_len - sender->buffer_sent_len, 0);
-                log_debug("send(%d, ...)=%ld", receiver->fd, len);
-                if (len < 0) {
-                        if (errno != EWOULDBLOCK && errno != EAGAIN) {
-                                log_error("Error %d in send to fd=%d: %m", errno, receiver->fd);
-                                return -errno;
-                        }
-                        else {
-                                /* send() is in a blocking state. */
-                                break;
-                        }
-                }
-
-                /* len < 0 can't occur here. len == 0 is possible but
-                 * undefined behavior for nonblocking send(). */
-                assert(len > 0);
-                sender->buffer_sent_len += len;
-        }
-
-        log_debug("send(%d, ...) completed with %lu bytes still buffered.", receiver->fd, sender->buffer_filled_len - sender->buffer_sent_len);
-
-        /* Detect a would-block state or partial send. */
-        if (sender->buffer_filled_len > sender->buffer_sent_len) {
-
-                /* If the buffer is full, disable events coming for recv. */
-                if (sender->buffer_filled_len == BUFFER_SIZE) {
-                    r = sd_event_source_set_enabled(sender->w_recv, SD_EVENT_OFF);
-                    if (r < 0) {
-                            log_error("Error %d disabling recv for fd=%d: %s", r, sender->fd, strerror(-r));
-                            return r;
-                    }
-                }
-
-                /* Watch for when the recipient can be sent data again. */
-                r = sd_event_source_set_enabled(receiver->w_send, SD_EVENT_ON);
-                if (r < 0) {
-                        log_error("Error %d enabling send for fd=%d: %s", r, receiver->fd, strerror(-r));
-                        return r;
-                }
-                log_debug("Done with recv for fd=%d. Waiting on send for fd=%d.", sender->fd, receiver->fd);
-                return r;
-        }
-
-        /* If we sent everything without blocking, the buffer is now empty. */
-        sender->buffer_filled_len = 0;
-        sender->buffer_sent_len = 0;
-
-        /* Unmute the sender, in case the buffer was full. */
-        r = sd_event_source_set_enabled(sender->w_recv, SD_EVENT_ON);
-        if (r < 0) {
-                log_error("Error %d enabling recv for fd=%d: %s", r, sender->fd, strerror(-r));
-                return r;
-        }
-
-        /* Mute the recipient, as we have no data to send now. */
-        r = sd_event_source_set_enabled(receiver->w_send, SD_EVENT_OFF);
-        if (r < 0) {
-                log_error("Error %d disabling send for fd=%d: %s", r, receiver->fd, strerror(-r));
-                return r;
-        }
-
-        return 0;
-}
-
-static int transfer_data_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        struct connection *c = (struct connection *) userdata;
-        int r = 0;
-        ssize_t len;
-
-        assert(revents & (EPOLLIN | EPOLLOUT));
-        assert(fd == c->fd);
-
-        log_debug("Got event revents=%d from fd=%d (conn %p).", revents, fd, c);
-
-        if (revents & EPOLLIN) {
-                log_debug("About to recv up to %lu bytes from fd=%d (%lu/BUFFER_SIZE).", BUFFER_SIZE - c->buffer_filled_len, fd, c->buffer_filled_len);
-
-                assert(s == c->w_recv);
-
-                /* Receive until the buffer's full, there's no more data,
-                 * or the client/server disconnects. */
-                while (c->buffer_filled_len < BUFFER_SIZE) {
-                        len = recv(fd, c->buffer + c->buffer_filled_len, BUFFER_SIZE - c->buffer_filled_len, 0);
-                        log_debug("recv(%d, ...)=%ld", fd, len);
-                        if (len < 0) {
-                                if (errno != EWOULDBLOCK && errno != EAGAIN) {
-                                        log_error("Error %d in recv from fd=%d: %m", errno, fd);
-                                        return -errno;
-                                }
-                                else {
-                                        /* recv() is in a blocking state. */
-                                        break;
-                                }
-                        }
-                        else if (len == 0) {
-                                log_debug("Clean disconnection from fd=%d", fd);
-                                total_clients--;
-                                free_connection(c->c_destination);
-                                free_connection(c);
-                                return 0;
-                        }
-
-                        assert(len > 0);
-                        log_debug("Recording that the buffer got %ld more bytes full.", len);
-                        c->buffer_filled_len += len;
-                        log_debug("Buffer now has %ld bytes full.", c->buffer_filled_len);
-                }
-
-                /* Try sending the data immediately. */
-                return send_buffer(c);
-        }
-        else {
-                assert(s == c->w_send);
-                return send_buffer(c->c_destination);
-        }
-
-        return r;
-}
-
-/* Once sending to the server is unblocked, set up the real watchers. */
-static int connected_to_server_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        struct sd_event *e = NULL;
-        struct connection *c_server_to_client = (struct connection *) userdata;
-        struct connection *c_client_to_server = c_server_to_client->c_destination;
-        int r;
-
-        assert(revents & EPOLLOUT);
-
-        e = sd_event_get(s);
-
-        /* Cancel the initial write watcher for the server. */
-        sd_event_source_unref(s);
-
-        log_debug("Connected to server. Initializing watchers for receiving data.");
-
-        /* A disabled send watcher for the server. */
-        r = sd_event_add_io(e, c_server_to_client->fd, EPOLLOUT, transfer_data_cb, c_server_to_client, &c_server_to_client->w_send);
-        if (r < 0) {
-                log_error("Error %d creating send watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
-                goto fail;
-        }
-        r = sd_event_source_set_enabled(c_server_to_client->w_send, SD_EVENT_OFF);
-        if (r < 0) {
-                log_error("Error %d muting send for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
-                goto finish;
-        }
-
-        /* A recv watcher for the server. */
-        r = sd_event_add_io(e, c_server_to_client->fd, EPOLLIN, transfer_data_cb, c_server_to_client, &c_server_to_client->w_recv);
-        if (r < 0) {
-                log_error("Error %d creating recv watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
-                goto fail;
-        }
-
-        /* A disabled send watcher for the client. */
-        r = sd_event_add_io(e, c_client_to_server->fd, EPOLLOUT, transfer_data_cb, c_client_to_server, &c_client_to_server->w_send);
-        if (r < 0) {
-                log_error("Error %d creating send watcher for fd=%d: %s", r, c_client_to_server->fd, strerror(-r));
-                goto fail;
-        }
-        r = sd_event_source_set_enabled(c_client_to_server->w_send, SD_EVENT_OFF);
-        if (r < 0) {
-                log_error("Error %d muting send for fd=%d: %s", r, c_client_to_server->fd, strerror(-r));
-                goto finish;
-        }
-
-        /* A recv watcher for the client. */
-        r = sd_event_add_io(e, c_client_to_server->fd, EPOLLIN, transfer_data_cb, c_client_to_server, &c_client_to_server->w_recv);
-        if (r < 0) {
-                log_error("Error %d creating recv watcher for fd=%d: %s", r, c_client_to_server->fd, strerror(-r));
-                goto fail;
-        }
-
-goto finish;
-
-fail:
-        free_connection(c_client_to_server);
-        free_connection(c_server_to_client);
-
-finish:
-        return r;
-}
-
-static int get_server_connection_fd(const struct proxy *proxy) {
-        int server_fd;
-        int r = -EBADF;
-        int len;
-
-        if (proxy->remote_is_inet) {
-                int s;
-                _cleanup_freeaddrinfo_ struct addrinfo *result = NULL;
-                struct addrinfo hints = {.ai_family = AF_UNSPEC,
-                                         .ai_socktype = SOCK_STREAM,
-                                         .ai_flags = AI_PASSIVE};
-
-                log_debug("Looking up address info for %s:%s", proxy->remote_host, proxy->remote_service);
-                s = getaddrinfo(proxy->remote_host, proxy->remote_service, &hints, &result);
-                if (s != 0) {
-                        log_error("getaddrinfo error (%d): %s", s, gai_strerror(s));
-                        return r;
-                }
-
-                if (result == NULL) {
-                        log_error("getaddrinfo: no result");
-                        return r;
-                }
-
-                /* @TODO: Try connecting to all results instead of just the first. */
-                server_fd = socket(result->ai_family, result->ai_socktype | SOCK_NONBLOCK, result->ai_protocol);
-                if (server_fd < 0) {
-                        log_error("Error %d creating socket: %m", errno);
-                        return r;
-                }
-
-                r = connect(server_fd, result->ai_addr, result->ai_addrlen);
-                /* Ignore EINPROGRESS errors because they're expected for a nonblocking socket. */
-                if (r < 0 && errno != EINPROGRESS) {
-                        log_error("Error %d while connecting to socket %s:%s: %m", errno, proxy->remote_host, proxy->remote_service);
-                        return r;
-                }
-        }
-        else {
-                struct sockaddr_un remote;
-
-                server_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
-                if (server_fd < 0) {
-                        log_error("Error %d creating socket: %m", errno);
-                        return -EBADFD;
-                }
-
-                remote.sun_family = AF_UNIX;
-                strncpy(remote.sun_path, proxy->remote_host, sizeof(remote.sun_path));
-                len = strlen(remote.sun_path) + sizeof(remote.sun_family);
-                r = connect(server_fd, (struct sockaddr *) &remote, len);
-                if (r < 0 && errno != EINPROGRESS) {
-                        log_error("Error %d while connecting to Unix domain socket %s: %m", errno, proxy->remote_host);
-                        return -EBADFD;
-                }
-        }
-
-        log_debug("Server connection is fd=%d", server_fd);
-        return server_fd;
-}
-
-static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        struct proxy *proxy = (struct proxy *) userdata;
-        struct connection *c_server_to_client;
-        struct connection *c_client_to_server;
-        int r = 0;
-        union sockaddr_union sa;
-        socklen_t salen = sizeof(sa);
-
-        assert(revents & EPOLLIN);
-
-        c_server_to_client = malloc0(sizeof(struct connection));
-        if (c_server_to_client == NULL) {
-                log_oom();
-                goto fail;
-        }
-
-        c_client_to_server = malloc0(sizeof(struct connection));
-        if (c_client_to_server == NULL) {
-                log_oom();
-                goto fail;
-        }
-
-        c_server_to_client->fd = get_server_connection_fd(proxy);
-        if (c_server_to_client->fd < 0) {
-                log_error("Error initiating server connection.");
-                goto fail;
-        }
-
-        c_client_to_server->fd = accept(fd, (struct sockaddr *) &sa, &salen);
-        if (c_client_to_server->fd < 0) {
-                log_error("Error accepting client connection.");
-                goto fail;
-        }
-
-        /* Unlike on BSD, client sockets do not inherit nonblocking status
-         * from the listening socket. */
-        r = fd_nonblock(c_client_to_server->fd, true);
-        if (r < 0) {
-                log_error("Error %d marking client connection as nonblocking: %s", r, strerror(-r));
-                goto fail;
-        }
-
-        if (sa.sa.sa_family == AF_INET || sa.sa.sa_family == AF_INET6) {
-                char sa_str[INET6_ADDRSTRLEN];
-                const char *success;
-
-                success = inet_ntop(sa.sa.sa_family, &sa.in6.sin6_addr, sa_str, INET6_ADDRSTRLEN);
-                if (success == NULL)
-                        log_warning("Error %d calling inet_ntop: %m", errno);
-                else
-                        log_debug("Accepted client connection from %s as fd=%d", sa_str, c_client_to_server->fd);
-        }
-        else {
-                log_debug("Accepted client connection (non-IP) as fd=%d", c_client_to_server->fd);
-        }
-
-        total_clients++;
-        log_debug("Client fd=%d (conn %p) successfully connected. Total clients: %u", c_client_to_server->fd, c_client_to_server, total_clients);
-        log_debug("Server fd=%d (conn %p) successfully initialized.", c_server_to_client->fd, c_server_to_client);
-
-        /* Initialize watcher for send to server; this shows connectivity. */
-        r = sd_event_add_io(sd_event_get(s), c_server_to_client->fd, EPOLLOUT, connected_to_server_cb, c_server_to_client, &c_server_to_client->w_send);
-        if (r < 0) {
-                log_error("Error %d creating connectivity watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
-                goto fail;
-        }
-
-        /* Allow lookups of the opposite connection. */
-        c_server_to_client->c_destination = c_client_to_server;
-        c_client_to_server->c_destination = c_server_to_client;
-
-        goto finish;
-
-fail:
-        log_warning("Accepting a client connection or connecting to the server failed.");
-        free_connection(c_client_to_server);
-        free_connection(c_server_to_client);
-
-finish:
-        /* Preserve the main loop even if a single proxy setup fails. */
-        return 0;
-}
-
-static int run_main_loop(struct proxy *proxy) {
-        int r = EXIT_SUCCESS;
-        struct sd_event *e = NULL;
-        sd_event_source *w_accept = NULL;
-
-        r = sd_event_new(&e);
-        if (r < 0)
-                goto finish;
-
-        r = fd_nonblock(proxy->listen_fd, true);
-        if (r < 0)
-                goto finish;
-
-        log_debug("Initializing main listener fd=%d", proxy->listen_fd);
-
-        sd_event_add_io(e, proxy->listen_fd, EPOLLIN, accept_cb, proxy, &w_accept);
-
-        log_debug("Initialized main listener. Entering loop.");
-
-        sd_event_loop(e);
-
-finish:
-        sd_event_source_unref(w_accept);
-        sd_event_unref(e);
-
-        return r;
-}
-
-static int help(void) {
-
-        printf("%s hostname-or-ip port-or-service\n"
-               "%s unix-domain-socket-path\n\n"
-               "Inherit a socket. Bidirectionally proxy.\n\n"
-               "  -h --help       Show this help\n"
-               "  --version       Print version and exit\n"
-               "  --ignore-env    Ignore expected systemd environment\n",
-               program_invocation_short_name,
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static void version(void) {
-        puts(PACKAGE_STRING " sabridge");
-}
-
-static int parse_argv(int argc, char *argv[], struct proxy *p) {
-
-        enum {
-                ARG_VERSION = 0x100,
-                ARG_IGNORE_ENV
-        };
-
-        static const struct option options[] = {
-                { "help",       no_argument, NULL, 'h'           },
-                { "version",    no_argument, NULL, ARG_VERSION   },
-                { "ignore-env", no_argument, NULL, ARG_IGNORE_ENV},
-                { NULL,         0,           NULL, 0             }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case '?':
-                        return -EINVAL;
-
-                case ARG_VERSION:
-                        version();
-                        return 0;
-
-                case ARG_IGNORE_ENV:
-                        p->ignore_env = true;
-                        continue;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind + 1 != argc && optind + 2 != argc) {
-                log_error("Incorrect number of positional arguments.");
-                help();
-                return -EINVAL;
-        }
-
-        p->remote_host = argv[optind];
-        assert(p->remote_host);
-
-        p->remote_is_inet = p->remote_host[0] != '/';
-
-        if (optind == argc - 2) {
-                if (!p->remote_is_inet) {
-                        log_error("A port or service is not allowed for Unix socket destinations.");
-                        help();
-                        return -EINVAL;
-                }
-                p->remote_service = argv[optind + 1];
-                assert(p->remote_service);
-        } else if (p->remote_is_inet) {
-                log_error("A port or service is required for IP destinations.");
-                help();
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-int main(int argc, char *argv[]) {
-        struct proxy p = {};
-        int r;
-
-        log_parse_environment();
-        log_open();
-
-        r = parse_argv(argc, argv, &p);
-        if (r <= 0)
-                goto finish;
-
-        p.listen_fd = SD_LISTEN_FDS_START;
-
-        if (!p.ignore_env) {
-            int n;
-            n = sd_listen_fds(1);
-            if (n == 0) {
-                    log_error("Found zero inheritable sockets. Are you sure this is running as a socket-activated service?");
-                    r = EXIT_FAILURE;
-                    goto finish;
-            } else if (n < 0) {
-                    log_error("Error %d while finding inheritable sockets: %s", n, strerror(-n));
-                    r = EXIT_FAILURE;
-                    goto finish;
-            } else if (n > 1) {
-                    log_error("Can't listen on more than one socket.");
-                    r = EXIT_FAILURE;
-                    goto finish;
-            }
-        }
-
-        /* @TODO: Check if this proxy can work with datagram sockets. */
-        r = sd_is_socket(p.listen_fd, 0, SOCK_STREAM, 1);
-        if (r < 0) {
-                log_error("Error %d while checking inherited socket: %s", r, strerror(-r));
-                goto finish;
-        }
-
-        log_info("Starting the socket activation bridge with listener fd=%d.", p.listen_fd);
-
-        r = run_main_loop(&p);
-        if (r < 0) {
-                log_error("Error %d from main loop.", r);
-                goto finish;
-        }
-
-finish:
-        log_close();
-        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/saproxy/Makefile b/src/saproxy/Makefile
new file mode 100644
index 0000000..9d07505
--- /dev/null
+++ b/src/saproxy/Makefile
@@ -0,0 +1,28 @@
+#  This file is part of systemd.
+#
+#  Copyright 2010 Lennart Poettering
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+#
+#  systemd is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+# This file is a dirty trick to simplify compilation from within
+# emacs. This file is not intended to be distributed. So, don't touch
+# it, even better ignore it!
+
+all:
+	$(MAKE) -C ..
+
+clean:
+	$(MAKE) -C .. clean
+
+.PHONY: all clean
diff --git a/src/saproxy/saproxy.c b/src/saproxy/saproxy.c
new file mode 100644
index 0000000..ca75b40
--- /dev/null
+++ b/src/saproxy/saproxy.c
@@ -0,0 +1,575 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 David Strauss
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+ ***/
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "sd-daemon.h"
+#include "sd-event.h"
+#include "socket-util.h"
+#include "util.h"
+
+#define BUFFER_SIZE 4096
+#define _cleanup_freeaddrinfo_ _cleanup_(freeaddrinfop)
+
+unsigned int total_clients = 0;
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct addrinfo *, freeaddrinfo);
+
+struct proxy {
+        int listen_fd;
+        bool ignore_env;
+        bool remote_is_inet;
+        const char *remote_host;
+        const char *remote_service;
+};
+
+struct connection {
+        int fd;
+        sd_event_source *w_recv;
+        sd_event_source *w_send;
+        struct connection *c_destination;
+        size_t buffer_filled_len;
+        size_t buffer_sent_len;
+        char buffer[BUFFER_SIZE];
+};
+
+static void free_connection(struct connection *c) {
+        log_debug("Freeing fd=%d (conn %p).", c->fd, c);
+        sd_event_source_unref(c->w_recv);
+        sd_event_source_unref(c->w_send);
+        close(c->fd);
+        free(c);
+}
+
+static int send_buffer(struct connection *sender) {
+        struct connection *receiver = sender->c_destination;
+        ssize_t len;
+        int r = 0;
+
+        /* We cannot assume that even a partial send() indicates that
+         * the next send() will block. Loop until it does. */
+        while (sender->buffer_filled_len > sender->buffer_sent_len) {
+                len = send(receiver->fd, sender->buffer + sender->buffer_sent_len, sender->buffer_filled_len - sender->buffer_sent_len, 0);
+                log_debug("send(%d, ...)=%ld", receiver->fd, len);
+                if (len < 0) {
+                        if (errno != EWOULDBLOCK && errno != EAGAIN) {
+                                log_error("Error %d in send to fd=%d: %m", errno, receiver->fd);
+                                return -errno;
+                        }
+                        else {
+                                /* send() is in a blocking state. */
+                                break;
+                        }
+                }
+
+                /* len < 0 can't occur here. len == 0 is possible but
+                 * undefined behavior for nonblocking send(). */
+                assert(len > 0);
+                sender->buffer_sent_len += len;
+        }
+
+        log_debug("send(%d, ...) completed with %lu bytes still buffered.", receiver->fd, sender->buffer_filled_len - sender->buffer_sent_len);
+
+        /* Detect a would-block state or partial send. */
+        if (sender->buffer_filled_len > sender->buffer_sent_len) {
+
+                /* If the buffer is full, disable events coming for recv. */
+                if (sender->buffer_filled_len == BUFFER_SIZE) {
+                    r = sd_event_source_set_enabled(sender->w_recv, SD_EVENT_OFF);
+                    if (r < 0) {
+                            log_error("Error %d disabling recv for fd=%d: %s", r, sender->fd, strerror(-r));
+                            return r;
+                    }
+                }
+
+                /* Watch for when the recipient can be sent data again. */
+                r = sd_event_source_set_enabled(receiver->w_send, SD_EVENT_ON);
+                if (r < 0) {
+                        log_error("Error %d enabling send for fd=%d: %s", r, receiver->fd, strerror(-r));
+                        return r;
+                }
+                log_debug("Done with recv for fd=%d. Waiting on send for fd=%d.", sender->fd, receiver->fd);
+                return r;
+        }
+
+        /* If we sent everything without blocking, the buffer is now empty. */
+        sender->buffer_filled_len = 0;
+        sender->buffer_sent_len = 0;
+
+        /* Unmute the sender, in case the buffer was full. */
+        r = sd_event_source_set_enabled(sender->w_recv, SD_EVENT_ON);
+        if (r < 0) {
+                log_error("Error %d enabling recv for fd=%d: %s", r, sender->fd, strerror(-r));
+                return r;
+        }
+
+        /* Mute the recipient, as we have no data to send now. */
+        r = sd_event_source_set_enabled(receiver->w_send, SD_EVENT_OFF);
+        if (r < 0) {
+                log_error("Error %d disabling send for fd=%d: %s", r, receiver->fd, strerror(-r));
+                return r;
+        }
+
+        return 0;
+}
+
+static int transfer_data_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        struct connection *c = (struct connection *) userdata;
+        int r = 0;
+        ssize_t len;
+
+        assert(revents & (EPOLLIN | EPOLLOUT));
+        assert(fd == c->fd);
+
+        log_debug("Got event revents=%d from fd=%d (conn %p).", revents, fd, c);
+
+        if (revents & EPOLLIN) {
+                log_debug("About to recv up to %lu bytes from fd=%d (%lu/BUFFER_SIZE).", BUFFER_SIZE - c->buffer_filled_len, fd, c->buffer_filled_len);
+
+                assert(s == c->w_recv);
+
+                /* Receive until the buffer's full, there's no more data,
+                 * or the client/server disconnects. */
+                while (c->buffer_filled_len < BUFFER_SIZE) {
+                        len = recv(fd, c->buffer + c->buffer_filled_len, BUFFER_SIZE - c->buffer_filled_len, 0);
+                        log_debug("recv(%d, ...)=%ld", fd, len);
+                        if (len < 0) {
+                                if (errno != EWOULDBLOCK && errno != EAGAIN) {
+                                        log_error("Error %d in recv from fd=%d: %m", errno, fd);
+                                        return -errno;
+                                }
+                                else {
+                                        /* recv() is in a blocking state. */
+                                        break;
+                                }
+                        }
+                        else if (len == 0) {
+                                log_debug("Clean disconnection from fd=%d", fd);
+                                total_clients--;
+                                free_connection(c->c_destination);
+                                free_connection(c);
+                                return 0;
+                        }
+
+                        assert(len > 0);
+                        log_debug("Recording that the buffer got %ld more bytes full.", len);
+                        c->buffer_filled_len += len;
+                        log_debug("Buffer now has %ld bytes full.", c->buffer_filled_len);
+                }
+
+                /* Try sending the data immediately. */
+                return send_buffer(c);
+        }
+        else {
+                assert(s == c->w_send);
+                return send_buffer(c->c_destination);
+        }
+
+        return r;
+}
+
+/* Once sending to the server is unblocked, set up the real watchers. */
+static int connected_to_server_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        struct sd_event *e = NULL;
+        struct connection *c_server_to_client = (struct connection *) userdata;
+        struct connection *c_client_to_server = c_server_to_client->c_destination;
+        int r;
+
+        assert(revents & EPOLLOUT);
+
+        e = sd_event_get(s);
+
+        /* Cancel the initial write watcher for the server. */
+        sd_event_source_unref(s);
+
+        log_debug("Connected to server. Initializing watchers for receiving data.");
+
+        /* A disabled send watcher for the server. */
+        r = sd_event_add_io(e, c_server_to_client->fd, EPOLLOUT, transfer_data_cb, c_server_to_client, &c_server_to_client->w_send);
+        if (r < 0) {
+                log_error("Error %d creating send watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
+                goto fail;
+        }
+        r = sd_event_source_set_enabled(c_server_to_client->w_send, SD_EVENT_OFF);
+        if (r < 0) {
+                log_error("Error %d muting send for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
+                goto finish;
+        }
+
+        /* A recv watcher for the server. */
+        r = sd_event_add_io(e, c_server_to_client->fd, EPOLLIN, transfer_data_cb, c_server_to_client, &c_server_to_client->w_recv);
+        if (r < 0) {
+                log_error("Error %d creating recv watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
+                goto fail;
+        }
+
+        /* A disabled send watcher for the client. */
+        r = sd_event_add_io(e, c_client_to_server->fd, EPOLLOUT, transfer_data_cb, c_client_to_server, &c_client_to_server->w_send);
+        if (r < 0) {
+                log_error("Error %d creating send watcher for fd=%d: %s", r, c_client_to_server->fd, strerror(-r));
+                goto fail;
+        }
+        r = sd_event_source_set_enabled(c_client_to_server->w_send, SD_EVENT_OFF);
+        if (r < 0) {
+                log_error("Error %d muting send for fd=%d: %s", r, c_client_to_server->fd, strerror(-r));
+                goto finish;
+        }
+
+        /* A recv watcher for the client. */
+        r = sd_event_add_io(e, c_client_to_server->fd, EPOLLIN, transfer_data_cb, c_client_to_server, &c_client_to_server->w_recv);
+        if (r < 0) {
+                log_error("Error %d creating recv watcher for fd=%d: %s", r, c_client_to_server->fd, strerror(-r));
+                goto fail;
+        }
+
+goto finish;
+
+fail:
+        free_connection(c_client_to_server);
+        free_connection(c_server_to_client);
+
+finish:
+        return r;
+}
+
+static int get_server_connection_fd(const struct proxy *proxy) {
+        int server_fd;
+        int r = -EBADF;
+        int len;
+
+        if (proxy->remote_is_inet) {
+                int s;
+                _cleanup_freeaddrinfo_ struct addrinfo *result = NULL;
+                struct addrinfo hints = {.ai_family = AF_UNSPEC,
+                                         .ai_socktype = SOCK_STREAM,
+                                         .ai_flags = AI_PASSIVE};
+
+                log_debug("Looking up address info for %s:%s", proxy->remote_host, proxy->remote_service);
+                s = getaddrinfo(proxy->remote_host, proxy->remote_service, &hints, &result);
+                if (s != 0) {
+                        log_error("getaddrinfo error (%d): %s", s, gai_strerror(s));
+                        return r;
+                }
+
+                if (result == NULL) {
+                        log_error("getaddrinfo: no result");
+                        return r;
+                }
+
+                /* @TODO: Try connecting to all results instead of just the first. */
+                server_fd = socket(result->ai_family, result->ai_socktype | SOCK_NONBLOCK, result->ai_protocol);
+                if (server_fd < 0) {
+                        log_error("Error %d creating socket: %m", errno);
+                        return r;
+                }
+
+                r = connect(server_fd, result->ai_addr, result->ai_addrlen);
+                /* Ignore EINPROGRESS errors because they're expected for a nonblocking socket. */
+                if (r < 0 && errno != EINPROGRESS) {
+                        log_error("Error %d while connecting to socket %s:%s: %m", errno, proxy->remote_host, proxy->remote_service);
+                        return r;
+                }
+        }
+        else {
+                struct sockaddr_un remote;
+
+                server_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
+                if (server_fd < 0) {
+                        log_error("Error %d creating socket: %m", errno);
+                        return -EBADFD;
+                }
+
+                remote.sun_family = AF_UNIX;
+                strncpy(remote.sun_path, proxy->remote_host, sizeof(remote.sun_path));
+                len = strlen(remote.sun_path) + sizeof(remote.sun_family);
+                r = connect(server_fd, (struct sockaddr *) &remote, len);
+                if (r < 0 && errno != EINPROGRESS) {
+                        log_error("Error %d while connecting to Unix domain socket %s: %m", errno, proxy->remote_host);
+                        return -EBADFD;
+                }
+        }
+
+        log_debug("Server connection is fd=%d", server_fd);
+        return server_fd;
+}
+
+static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        struct proxy *proxy = (struct proxy *) userdata;
+        struct connection *c_server_to_client;
+        struct connection *c_client_to_server;
+        int r = 0;
+        union sockaddr_union sa;
+        socklen_t salen = sizeof(sa);
+
+        assert(revents & EPOLLIN);
+
+        c_server_to_client = malloc0(sizeof(struct connection));
+        if (c_server_to_client == NULL) {
+                log_oom();
+                goto fail;
+        }
+
+        c_client_to_server = malloc0(sizeof(struct connection));
+        if (c_client_to_server == NULL) {
+                log_oom();
+                goto fail;
+        }
+
+        c_server_to_client->fd = get_server_connection_fd(proxy);
+        if (c_server_to_client->fd < 0) {
+                log_error("Error initiating server connection.");
+                goto fail;
+        }
+
+        c_client_to_server->fd = accept(fd, (struct sockaddr *) &sa, &salen);
+        if (c_client_to_server->fd < 0) {
+                log_error("Error accepting client connection.");
+                goto fail;
+        }
+
+        /* Unlike on BSD, client sockets do not inherit nonblocking status
+         * from the listening socket. */
+        r = fd_nonblock(c_client_to_server->fd, true);
+        if (r < 0) {
+                log_error("Error %d marking client connection as nonblocking: %s", r, strerror(-r));
+                goto fail;
+        }
+
+        if (sa.sa.sa_family == AF_INET || sa.sa.sa_family == AF_INET6) {
+                char sa_str[INET6_ADDRSTRLEN];
+                const char *success;
+
+                success = inet_ntop(sa.sa.sa_family, &sa.in6.sin6_addr, sa_str, INET6_ADDRSTRLEN);
+                if (success == NULL)
+                        log_warning("Error %d calling inet_ntop: %m", errno);
+                else
+                        log_debug("Accepted client connection from %s as fd=%d", sa_str, c_client_to_server->fd);
+        }
+        else {
+                log_debug("Accepted client connection (non-IP) as fd=%d", c_client_to_server->fd);
+        }
+
+        total_clients++;
+        log_debug("Client fd=%d (conn %p) successfully connected. Total clients: %u", c_client_to_server->fd, c_client_to_server, total_clients);
+        log_debug("Server fd=%d (conn %p) successfully initialized.", c_server_to_client->fd, c_server_to_client);
+
+        /* Initialize watcher for send to server; this shows connectivity. */
+        r = sd_event_add_io(sd_event_get(s), c_server_to_client->fd, EPOLLOUT, connected_to_server_cb, c_server_to_client, &c_server_to_client->w_send);
+        if (r < 0) {
+                log_error("Error %d creating connectivity watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
+                goto fail;
+        }
+
+        /* Allow lookups of the opposite connection. */
+        c_server_to_client->c_destination = c_client_to_server;
+        c_client_to_server->c_destination = c_server_to_client;
+
+        goto finish;
+
+fail:
+        log_warning("Accepting a client connection or connecting to the server failed.");
+        free_connection(c_client_to_server);
+        free_connection(c_server_to_client);
+
+finish:
+        /* Preserve the main loop even if a single proxy setup fails. */
+        return 0;
+}
+
+static int run_main_loop(struct proxy *proxy) {
+        int r = EXIT_SUCCESS;
+        struct sd_event *e = NULL;
+        sd_event_source *w_accept = NULL;
+
+        r = sd_event_new(&e);
+        if (r < 0)
+                goto finish;
+
+        r = fd_nonblock(proxy->listen_fd, true);
+        if (r < 0)
+                goto finish;
+
+        log_debug("Initializing main listener fd=%d", proxy->listen_fd);
+
+        sd_event_add_io(e, proxy->listen_fd, EPOLLIN, accept_cb, proxy, &w_accept);
+
+        log_debug("Initialized main listener. Entering loop.");
+
+        sd_event_loop(e);
+
+finish:
+        sd_event_source_unref(w_accept);
+        sd_event_unref(e);
+
+        return r;
+}
+
+static int help(void) {
+
+        printf("%s hostname-or-ip port-or-service\n"
+               "%s unix-domain-socket-path\n\n"
+               "Inherit a socket. Bidirectionally proxy.\n\n"
+               "  -h --help       Show this help\n"
+               "  --version       Print version and exit\n"
+               "  --ignore-env    Ignore expected systemd environment\n",
+               program_invocation_short_name,
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static void version(void) {
+        puts(PACKAGE_STRING " saproxy");
+}
+
+static int parse_argv(int argc, char *argv[], struct proxy *p) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_IGNORE_ENV
+        };
+
+        static const struct option options[] = {
+                { "help",       no_argument, NULL, 'h'           },
+                { "version",    no_argument, NULL, ARG_VERSION   },
+                { "ignore-env", no_argument, NULL, ARG_IGNORE_ENV},
+                { NULL,         0,           NULL, 0             }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case '?':
+                        return -EINVAL;
+
+                case ARG_VERSION:
+                        version();
+                        return 0;
+
+                case ARG_IGNORE_ENV:
+                        p->ignore_env = true;
+                        continue;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind + 1 != argc && optind + 2 != argc) {
+                log_error("Incorrect number of positional arguments.");
+                help();
+                return -EINVAL;
+        }
+
+        p->remote_host = argv[optind];
+        assert(p->remote_host);
+
+        p->remote_is_inet = p->remote_host[0] != '/';
+
+        if (optind == argc - 2) {
+                if (!p->remote_is_inet) {
+                        log_error("A port or service is not allowed for Unix socket destinations.");
+                        help();
+                        return -EINVAL;
+                }
+                p->remote_service = argv[optind + 1];
+                assert(p->remote_service);
+        } else if (p->remote_is_inet) {
+                log_error("A port or service is required for IP destinations.");
+                help();
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        struct proxy p = {};
+        int r;
+
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv, &p);
+        if (r <= 0)
+                goto finish;
+
+        p.listen_fd = SD_LISTEN_FDS_START;
+
+        if (!p.ignore_env) {
+            int n;
+            n = sd_listen_fds(1);
+            if (n == 0) {
+                    log_error("Found zero inheritable sockets. Are you sure this is running as a socket-activated service?");
+                    r = EXIT_FAILURE;
+                    goto finish;
+            } else if (n < 0) {
+                    log_error("Error %d while finding inheritable sockets: %s", n, strerror(-n));
+                    r = EXIT_FAILURE;
+                    goto finish;
+            } else if (n > 1) {
+                    log_error("Can't listen on more than one socket.");
+                    r = EXIT_FAILURE;
+                    goto finish;
+            }
+        }
+
+        /* @TODO: Check if this proxy can work with datagram sockets. */
+        r = sd_is_socket(p.listen_fd, 0, SOCK_STREAM, 1);
+        if (r < 0) {
+                log_error("Error %d while checking inherited socket: %s", r, strerror(-r));
+                goto finish;
+        }
+
+        log_info("Starting the socket activation proxy with listener fd=%d.", p.listen_fd);
+
+        r = run_main_loop(&p);
+        if (r < 0) {
+                log_error("Error %d from main loop.", r);
+                goto finish;
+        }
+
+finish:
+        log_close();
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}



More information about the systemd-commits mailing list