diff --git a/configuration.c b/configuration.c
index abf59d4..7fce1d5 100644
--- a/configuration.c
+++ b/configuration.c
@@ -77,6 +77,7 @@ static int readConfigTime(xmlTextReaderPtr reader, time_t * value, const char *
static int configListen(WebdavdConfiguration * config, xmlTextReaderPtr reader, const char * configFile) {
//80localhostdisabled
+ //http
int index = config->daemonCount++;
config->daemons = reallocSafe(config->daemons, sizeof(*config->daemons) * config->daemonCount);
memset(&config->daemons[index], 0, sizeof(config->daemons[index]));
@@ -90,6 +91,8 @@ static int configListen(WebdavdConfiguration * config, xmlTextReaderPtr reader,
result = readConfigInt(reader, &config->daemons[index].port, configFile);
} else if (!strcmp(xmlTextReaderConstLocalName(reader), "host")) {
result = readConfigString(reader, &config->daemons[index].host);
+ } else if (!strcmp(xmlTextReaderConstLocalName(reader), "name")) {
+ result = readConfigString(reader, &config->daemons[index].name);
} else if (!strcmp(xmlTextReaderConstLocalName(reader), "encryption")) {
const char * encryptionString;
result = stepOverText(reader, &encryptionString);
@@ -148,7 +151,7 @@ static int configListen(WebdavdConfiguration * config, xmlTextReaderPtr reader,
result = stepOver(reader);
}
}
- if (config->daemons[index].port == -1) {
+ if (!config->socketActivation && config->daemons[index].port == -1) {
stdLogError(0, "port not specified for listen in %s", configFile);
exit(1);
}
@@ -282,6 +285,17 @@ static int configUnprotectOptions(WebdavdConfiguration * config, xmlTextReaderPt
return result;
}
+static int configSocketActivation(WebdavdConfiguration * config, xmlTextReaderPtr reader, const char * configFile) {
+ //
+#ifdef HAVE_SYSTEMD
+ config->socketActivation = true;
+ return stepOver(reader);
+#else
+ stdLogError(0, "Socket activation support not enable at build time");
+ return false;
+#endif
+}
+
///////////////////////////
// End Handler Functions //
///////////////////////////
@@ -314,6 +328,7 @@ static const ConfigurationFunction configFunctions[] = {
{ .nodeName = "rap-timeout", .func = &configRapTimeout }, //
{ .nodeName = "restricted", .func = &configRestricted }, //
{ .nodeName = "session-timeout", .func = &configSessionTimeout }, //
+ { .nodeName = "socket-activation", .func = &configSocketActivation }, //
{ .nodeName = "ssl-cert", .func = &configConfigSSLCert }, //
{ .nodeName = "static-response-dir", .func = &configResponseDir }, //
{ .nodeName = "unprotect-options", .func = &configUnprotectOptions } //
diff --git a/configuration.h b/configuration.h
index 881a02c..704e26c 100644
--- a/configuration.h
+++ b/configuration.h
@@ -2,6 +2,7 @@
#define WEBDAV_CONFIGURATION_H
#include
+#include
//////////////////////////////////////
// Webdavd Configuration Structures //
@@ -9,7 +10,8 @@
typedef struct DaemonConfig {
int port;
- const char * host;
+ const char * host; // For opening socket ourselves
+ const char * name; // For socket activation, i.e. open socket passed to daemon
int sslEnabled;
int forwardToIsEncrypted;
int forwardToPort;
@@ -55,6 +57,9 @@ typedef struct WebdavdConfiguration {
// OPTIONS Requests
int unprotectOptions;
+ // Use systemd/xinetd style socket based activation
+ bool socketActivation;
+
} WebdavdConfiguration;
extern WebdavdConfiguration config;
diff --git a/makefile b/makefile
index 83a9dc5..c12a713 100644
--- a/makefile
+++ b/makefile
@@ -1,17 +1,22 @@
CFLAGS=-O3 -s
STATIC_FLAGS=-Werror -Wall -Wno-pointer-sign -Wno-unused-result -std=gnu99 -pthread
+ifdef HAVE_SYSTEMD
+DEFS+=-DHAVE_SYSTEMD=1
+LIBSYSTEMD=-lsystemd
+endif
+
all: build/rap build/webdavd
ls -lh $^
build/webdavd: build/webdavd.o build/shared.o build/configuration.o build/xml.o
- gcc ${CFLAGS} ${STATIC_FLAGS} -o $@ $(filter %.o,$^) -lmicrohttpd -lxml2 -lgnutls -luuid
+ gcc ${CFLAGS} ${STATIC_FLAGS} -o $@ $(filter %.o,$^) -lmicrohttpd -lxml2 -lgnutls -luuid ${LIBSYSTEMD}
build/rap: build/rap.o build/shared.o build/xml.o
gcc ${CFLAGS} ${STATIC_FLAGS} -o $@ $(filter %.o,$^) -lpam -lxml2
build/%.o: %.c makefile | build
- gcc ${CFLAGS} ${STATIC_FLAGS} -MMD -o $@ $(filter %.c,$^) -I/usr/include/libxml2 -c
+ gcc ${CFLAGS} ${STATIC_FLAGS} ${DEFS} -MMD -o $@ $(filter %.c,$^) -I/usr/include/libxml2 -c
build:
mkdir $@
diff --git a/package-control/webdavd.spec b/package-control/webdavd.spec
index fc356e5..430ccf4 100644
--- a/package-control/webdavd.spec
+++ b/package-control/webdavd.spec
@@ -13,6 +13,7 @@ BuildRequires: libxml2-devel
BuildRequires: pam-devel
BuildRequires: libuuid-devel
BuildRequires: make
+BuildRequires: systemd-devel
Requires: gnutls
Requires: libmicrohttpd
@@ -36,7 +37,7 @@ webdavd is a WebDAV server designed to be a replace for SMBA providing access to
%setup -n WebDAV-Daemon-%{version}
%build
-%make_build
+%make_build HAVE_SYSTEMD=1
%install
install -Dpm 755 build/webdavd %{buildroot}%{_sbindir}/webdavd
@@ -46,6 +47,7 @@ install -Dpm 644 package-with/conf.xml %{buildroot}%{_sysconfdir}/webdavd
install -d %{buildroot}%{_datadir}/webdavd
install -Dpm 644 package-with/share/* %{buildroot}%{_datadir}/webdavd
install -Dpm 644 package-with/systemd.service %{buildroot}%{_prefix}/lib/systemd/system/webdavd.service
+install -Dpm 644 package-with/webdavd-*.socket %{buildroot}%{_prefix}/lib/systemd/system
install -Dpm 644 package-with/logrotate.conf %{buildroot}%{_sysconfdir}/logrotate.d/webdavd
@@ -57,6 +59,7 @@ install -Dpm 644 package-with/logrotate.conf %{buildroot}%{_sysconfdir}/logrotat
%{_prefix}/lib/webdavd/webdav-worker
%{_datadir}/webdavd/*
%{_prefix}/lib/systemd/system/webdavd.service
+%{_prefix}/lib/systemd/system/webdavd-*.socket
%config %{_sysconfdir}/pam.d/webdavd
%config %{_sysconfdir}/webdavd
%config %{_sysconfdir}/logrotate.d/webdavd
diff --git a/package-with/conf.xml b/package-with/conf.xml
index b7bce47..2555f92 100644
--- a/package-with/conf.xml
+++ b/package-with/conf.xml
@@ -9,6 +9,18 @@
default port. Default host "any ip". Default encryption is "none". The following
will listening on all ips and be unencrypted -->
+
+
+
80
@@ -17,6 +29,11 @@
be convertied to an IP before binding. -->
+
+ http
+
none
+ https
+
diff --git a/package-with/webdavd-http.socket b/package-with/webdavd-http.socket
new file mode 100644
index 0000000..c446cbb
--- /dev/null
+++ b/package-with/webdavd-http.socket
@@ -0,0 +1,14 @@
+[Unit]
+Description=WebDAV server on HTTP socket
+
+[Socket]
+Accept=no
+ListenStream=80
+FileDescriptorName=http
+DeferAcceptSec=1
+Service=webdavd.service
+# Add this to only allow connections over localhost
+# BindToDevice=lo
+
+[Install]
+WantedBy=sockets.target
diff --git a/package-with/webdavd-https.socket b/package-with/webdavd-https.socket
new file mode 100644
index 0000000..6ea5108
--- /dev/null
+++ b/package-with/webdavd-https.socket
@@ -0,0 +1,14 @@
+[Unit]
+Description=WebDAV server on HTTPS socket
+
+[Socket]
+Accept=no
+ListenStream=443
+FileDescriptorName=https
+DeferAcceptSec=1
+Service=webdavd.service
+# Add this to only allow connections over localhost
+# BindToDevice=lo
+
+[Install]
+WantedBy=sockets.target
diff --git a/webdavd.c b/webdavd.c
index e9c288c..8315e71 100644
--- a/webdavd.c
+++ b/webdavd.c
@@ -20,6 +20,10 @@
#include
#include
+#if HAVE_SYSTEMD
+#include
+#endif
+
////////////////
// Structures //
////////////////
@@ -90,6 +94,16 @@ typedef struct FDResponseData {
RAP * session;
} FDResponseData;
+// Sockets passed to us by systemd
+typedef struct PassedSockets {
+ int count;
+ struct {
+ int fd;
+ char * name;
+ bool used; // For us to mark socket as in use
+ } socket[];
+} PassedSockets;
+
////////////////////
// End Structures //
////////////////////
@@ -1912,6 +1926,61 @@ static void initializeEnvVariables() {
else unsetenv("WEBDAVD_CHROOT_PATH");
}
+static void freePassedSockets(PassedSockets * ps) {
+ if (ps) {
+ for (int i = 0; i < ps->count; i++)
+ free(ps->socket[i].name); // Allocated by libsystemd
+ freeSafe(ps); // Allocated by us
+ }
+}
+
+#if HAVE_SYSTEMD
+static void initializeSocketActivation(PassedSockets ** socketsRet) {
+ if (!config.socketActivation) {
+ *socketsRet = NULL;
+ return;
+ }
+
+ char **names;
+ // This will set close on exec on all sockets
+ int n = sd_listen_fds_with_names(1, &names);
+ if (n <= 0) {
+ *socketsRet = NULL;
+ return;
+ }
+ PassedSockets * ps = mallocSafe(sizeof(*ps) + sizeof(*ps->socket) * n);
+ ps->count = n;
+
+ bool error = false;
+ for (int i = 0; i < n; i++) {
+ ps->socket[i].fd = SD_LISTEN_FDS_START + i;
+ ps->socket[i].name = names[i]; // Take ownership of string, need to free on exit
+ ps->socket[i].used = false;
+ int ret = sd_is_socket(ps->socket[i].fd, AF_UNSPEC, SOCK_STREAM, true);
+ if (ret <= 0) {
+ stdLogError(-ret, "Socket #%d '%s' unstuitable for WebDAV", i, names[i]);
+ error = true;
+ }
+ }
+ free(names);
+
+ if (error) {
+ // Just clean up stuct now and refuse to use any sockets
+ freePassedSockets(ps);
+ ps = NULL;
+ }
+
+ *socketsRet = ps;
+ return;
+}
+#else
+// Only supported with systemd. The old inetd system on stdin could work too,
+// but would need some more code to support it.
+static void initializeSocketActivation(PassedSockets ** socketsRet) {
+ *socketsRet = NULL;
+}
+#endif
+
////////////////////////
// End Initialisation //
////////////////////////
@@ -1960,6 +2029,7 @@ static void runServer() {
if (!lockToUser(config.restrictedUser, NULL)) {
exit(1);
}
+ PassedSockets *passedSocks;
initializeLogs();
initializeStaticResponses();
@@ -1967,50 +2037,92 @@ static void runServer() {
initializeLockDB();
initializeSSL();
initializeEnvVariables();
+ initializeSocketActivation(&passedSocks);
+
+ if (config.socketActivation && !passedSocks) {
+ stdLogError(0, "Configured to use systemd-socket activation but no sockets passed");
+ return;
+ }
// Start up the daemons
daemons = mallocSafe(sizeof(*daemons) * config.daemonCount);
for (int i = 0; i < config.daemonCount; i++) {
- struct sockaddr_in6 address;
- if (getBindAddress(&address, &config.daemons[i])) {
- MHD_AccessHandlerCallback callback;
- if (config.daemons[i].forwardToPort) {
- callback = (MHD_AccessHandlerCallback) &answerForwardToRequest;
- } else {
- callback = (MHD_AccessHandlerCallback) &answerToRequest;
- }
-
- if (config.daemons[i].sslEnabled) {
- // https
- if (sslCertificateCount == 0) {
- stdLogError(0, "No certificates available for ssl %s:%d",
- config.daemons[i].host ? config.daemons[i].host : "", config.daemons[i].port);
+ unsigned int flags = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DUAL_STACK | MHD_USE_PEDANTIC_CHECKS;
+ // Addtional options of MHD_start_daemon. Needs terminating MHD_OPTION_END entry.
+ struct MHD_OptionItem ops[3];
+ int nops = 0;
+
+ if (config.socketActivation) {
+ int listenFd = -1;
+ // Socket should have been passed to us already
+ if (config.daemons[i].name) {
+ // Find by name
+ for (int j = 0; j < passedSocks->count; j++) {
+ if (!strcmp(passedSocks->socket[j].name, config.daemons[i].name)) {
+ if (passedSocks->socket[j].used) {
+ stdLogError(0, "Socket '%s' for listening port #%d has already been used", config.daemons[i].name, i);
+ continue;
+ }
+ listenFd = passedSocks->socket[j].fd;
+ passedSocks->socket[j].used = true;
+ }
+ }
+ if (listenFd == -1) {
+ stdLogError(0, "No socket named '%s' was passed to webdavd by systemd", config.daemons[i].name);
continue;
}
- daemons[i] = MHD_start_daemon(
- MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DUAL_STACK | MHD_USE_PEDANTIC_CHECKS
- | MHD_USE_SSL, 0 /* ignored */, NULL, NULL, //
- callback, &config.daemons[i], //
- MHD_OPTION_SOCK_ADDR, &address, // Specifies both host and port
- MHD_OPTION_HTTPS_CERT_CALLBACK, &sslSNICallback, // enable ssl
- MHD_OPTION_PER_IP_CONNECTION_LIMIT, config.maxConnectionsPerIp, //
- MHD_OPTION_END);
} else {
- // http
- daemons[i] = MHD_start_daemon(
- MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DUAL_STACK | MHD_USE_PEDANTIC_CHECKS,
- 0 /* ignored */,
- NULL, NULL, //
- callback, &config.daemons[i], //
- MHD_OPTION_SOCK_ADDR, &address, // Specifies both host and port
- MHD_OPTION_PER_IP_CONNECTION_LIMIT, config.maxConnectionsPerIp, //
- MHD_OPTION_END);
+ // Assign sockets to daemons in order listed
+ if (i >= passedSocks->count || passedSocks->socket[i].used) {
+ stdLogError(0, "Socket for listening port #%d not passed via socket activation", i);
+ continue;
+ }
+ listenFd = passedSocks->socket[i].fd;
+ passedSocks->socket[i].used = true;
+ }
+ ops[nops++] = (struct MHD_OptionItem){ MHD_OPTION_LISTEN_SOCKET, (MHD_socket)listenFd };
+ } else {
+ struct sockaddr_in6 address;
+ if (!getBindAddress(&address, &config.daemons[i])) {
+ // Already printed error message in getBindAddress
+ continue;
}
- if (!daemons[i]) {
- stdLogError(errno, "Unable to initialise daemon on port %d", config.daemons[i].port);
+ // Specifies both host and port
+ ops[nops++] = (struct MHD_OptionItem){ MHD_OPTION_SOCK_ADDR, 0, &address };
+ }
+
+ MHD_AccessHandlerCallback callback;
+ if (config.daemons[i].forwardToPort) {
+ callback = (MHD_AccessHandlerCallback) &answerForwardToRequest;
+ } else {
+ callback = (MHD_AccessHandlerCallback) &answerToRequest;
+ }
+
+ if (config.daemons[i].sslEnabled) {
+ // https
+ if (sslCertificateCount == 0) {
+ stdLogError(0, "No certificates available for ssl %s:%d",
+ config.daemons[i].name ? config.daemons[i].name :
+ config.daemons[i].host ? config.daemons[i].host : "",
+ config.daemons[i].port);
+ continue;
}
+ flags |= MHD_USE_SSL;
+ ops[nops++] = (struct MHD_OptionItem){ MHD_OPTION_HTTPS_CERT_CALLBACK, 0, &sslSNICallback };
+ }
+
+ ops[nops++] = (struct MHD_OptionItem){ MHD_OPTION_END };
+ daemons[i] = MHD_start_daemon(flags, 0 /* ignored */, NULL, NULL,
+ callback, &config.daemons[i], //
+ MHD_OPTION_PER_IP_CONNECTION_LIMIT, config.maxConnectionsPerIp, //
+ MHD_OPTION_ARRAY, ops,
+ MHD_OPTION_END);
+ if (!daemons[i]) {
+ stdLogError(errno, "Unable to initialise daemon on port %d", config.daemons[i].port);
}
}
+
+ freePassedSockets(passedSocks);
}
int main(int argCount, char ** args) {