diff --git a/README.md b/README.md
index 7bf00c3..f87fbb0 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,7 @@
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) support -
the ability to change resolution
on the fly by source signal | ✔ | ☹ Partially yes 1 |
| Option to skip frames when streaming
static images by HTTP to save the traffic | ✔ 2 | ✘ |
| Streaming via UNIX domain socket | ✔ | ✘ |
+| Systemd socket activation | ✔ | ✘ |
| Debug logs without recompiling,
performance statistics log,
access to HTTP streaming parameters | ✔ | ✘ |
| Option to serve files
with a built-in HTTP server | ✔ | ☹ Regular files only |
| Signaling about the stream state
on GPIO using [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) | ✔ | ✘ |
diff --git a/README.ru.md b/README.ru.md
index 189af67..efd88e2 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -17,6 +17,7 @@
| Поддержка [DV-таймингов](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) - возможности
изменения параметров разрешения
трансляции на лету по сигналу
источника (устройства видеозахвата) | ✔ | ☹ Условно есть 1 |
| Возможность пропуска фреймов при передаче
статического изображения по HTTP
для экономии трафика | ✔ 2 | ✘ |
| Стрим через UNIX domain socket | ✔ | ✘ |
+| Systemd socket activation | ✔ | ✘ |
| Дебаг-логи без перекомпиляции,
логгирование статистики производительности,
возможность получения параметров
трансляции по HTTP | ✔ | ✘ |
| Возможность сервить файлы встроенным
HTTP-сервером | ✔ | ☹ Нет каталогов |
| Вывод сигналов о состоянии стрима на GPIO
с помощью [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) | ✔ | ✘ |
diff --git a/man/ustreamer.1 b/man/ustreamer.1
index 72f23e3..7e70197 100644
--- a/man/ustreamer.1
+++ b/man/ustreamer.1
@@ -173,6 +173,9 @@ Try to remove old unix socket file before binding. default: disabled.
.BR \-M\ \fImode ", " \-\-unix\-mode\ \fImode
Set UNIX socket file permissions (like 777). Default: disabled.
.TP
+.BR \-\-systemd
+Bind to systemd socket for socket activation. Required \fBWITH_SYSTEMD\fR feature. Default: disabled.
+.TP
.BR \-\-user\ \fIname
HTTP basic auth user. Default: disabled.
.TP
@@ -189,7 +192,7 @@ Don't send identical frames to clients, but no more than specified number. It ca
Override image resolution for the /state. Default: disabled.
.TP
.BR \-\-tcp\-nodelay
-Set TCP_NODELAY flag to the client /stream socket. Ignored for \-\-unix.
+Set TCP_NODELAY flag to the client /stream socket. Only for TCP socket.
Default: disabled.
.TP
.BR \-\-allow\-origin\ \fIstr
diff --git a/pkg/arch/PKGBUILD b/pkg/arch/PKGBUILD
index d6b9959..1c020f4 100644
--- a/pkg/arch/PKGBUILD
+++ b/pkg/arch/PKGBUILD
@@ -31,6 +31,11 @@ if [ -e /usr/include/janus/plugins/plugin.h ];then
makedepends+=(janus-gateway-pikvm)
_options="$_options WITH_JANUS=1"
fi
+if [ -e /usr/include/systemd/sd-daemon.h ];then
+ depends+=(systemd)
+ makedepends+=(systemd)
+ _options="$_options WITH_SYSTEMD=1"
+fi
# LD does not link mmal with this option
diff --git a/src/Makefile b/src/Makefile
index f1636fc..c311e2e 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -59,6 +59,13 @@ _USTR_SRCS += $(shell ls ustreamer/gpio/*.c)
endif
+ifneq ($(call optbool,$(WITH_SYSTEMD)),)
+_USTR_LIBS += -lsystemd
+override _CFLAGS += -DWITH_SYSTEMD
+_USTR_SRCS += $(shell ls ustreamer/http/systemd/*.c)
+endif
+
+
WITH_PTHREAD_NP ?= 1
ifneq ($(call optbool,$(WITH_PTHREAD_NP)),)
override _CFLAGS += -DWITH_PTHREAD_NP
diff --git a/src/ustreamer/http/server.c b/src/ustreamer/http/server.c
index 532f744..b02049c 100644
--- a/src/ustreamer/http/server.c
+++ b/src/ustreamer/http/server.c
@@ -86,8 +86,8 @@ void server_destroy(server_s *server) {
}
evhttp_free(RUN(http));
- if (RUN(unix_fd)) {
- close(RUN(unix_fd));
+ if (RUN(ext_fd)) {
+ close(RUN(ext_fd));
}
event_base_free(RUN(base));
@@ -160,7 +160,7 @@ int server_listen(server_s *server) {
if (server->unix_path[0] != '\0') {
LOG_DEBUG("Binding HTTP to UNIX socket '%s' ...", server->unix_path);
- if ((RUN(unix_fd) = evhttp_my_bind_unix(
+ if ((RUN(ext_fd) = evhttp_my_bind_unix(
RUN(http),
server->unix_path,
server->unix_rm,
@@ -169,9 +169,16 @@ int server_listen(server_s *server) {
return -1;
}
LOG_INFO("Listening HTTP on UNIX socket '%s'", server->unix_path);
- if (server->tcp_nodelay) {
- LOG_ERROR("TCP_NODELAY flag can't be used with UNIX socket and will be ignored");
+
+# ifdef WITH_SYSTEMD
+ } else if (server->systemd) {
+ LOG_DEBUG("Binding HTTP to systemd socket ...");
+ if ((RUN(ext_fd) = evhttp_my_bind_systemd(RUN(http))) < 0) {
+ return -1;
}
+ LOG_INFO("Listening systemd socket ...");
+# endif
+
} else {
LOG_DEBUG("Binding HTTP to [%s]:%u ...", server->host, server->port);
if (evhttp_bind_socket(RUN(http), server->host, server->port) < 0) {
@@ -546,7 +553,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
client->hostport, client->id, RUN(stream_clients_count));
struct bufferevent *buf_event = evhttp_connection_get_bufferevent(conn);
- if (server->tcp_nodelay && !RUN(unix_fd)) {
+ if (server->tcp_nodelay && !RUN(ext_fd)) {
evutil_socket_t fd;
int on = 1;
diff --git a/src/ustreamer/http/server.h b/src/ustreamer/http/server.h
index 10ac48a..d0b298e 100644
--- a/src/ustreamer/http/server.h
+++ b/src/ustreamer/http/server.h
@@ -71,6 +71,9 @@
#include "uri.h"
#include "mime.h"
#include "static.h"
+#ifdef WITH_SYSTEMD
+# include "systemd/systemd.h"
+#endif
typedef struct stream_client_sx {
@@ -112,7 +115,7 @@ typedef struct {
typedef struct {
struct event_base *base;
struct evhttp *http;
- evutil_socket_t unix_fd;
+ evutil_socket_t ext_fd; // Unix or socket activation
char *auth_token;
struct event *refresh;
stream_s *stream;
@@ -124,9 +127,15 @@ typedef struct {
typedef struct server_sx {
char *host;
unsigned port;
+
char *unix_path;
bool unix_rm;
mode_t unix_mode;
+
+# ifdef WITH_SYSTEMD
+ bool systemd;
+# endif
+
bool tcp_nodelay;
unsigned timeout;
diff --git a/src/ustreamer/http/systemd/systemd.c b/src/ustreamer/http/systemd/systemd.c
new file mode 100644
index 0000000..d006ff5
--- /dev/null
+++ b/src/ustreamer/http/systemd/systemd.c
@@ -0,0 +1,46 @@
+/*****************************************************************************
+# #
+# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
+# #
+# Copyright (C) 2018-2022 Maxim Devaev #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program 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 General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see . #
+# #
+*****************************************************************************/
+
+
+#include "systemd.h"
+
+
+evutil_socket_t evhttp_my_bind_systemd(struct evhttp *http) {
+ int fds = sd_listen_fds(1);
+ if (fds < 1) {
+ LOG_ERROR("No available systemd sockets");
+ return -1;
+ }
+
+ int fd;
+ for (fd = 1; fd < fds; ++fd) {
+ close(SD_LISTEN_FDS_START + fd);
+ }
+ fd = SD_LISTEN_FDS_START;
+
+ assert(!evutil_make_socket_nonblocking(fd));
+
+ if (evhttp_accept_socket(http, fd) < 0) {
+ LOG_PERROR("Can't evhttp_accept_socket() systemd socket");
+ return -1;
+ }
+ return fd;
+}
diff --git a/src/ustreamer/http/systemd/systemd.h b/src/ustreamer/http/systemd/systemd.h
new file mode 100644
index 0000000..6494cb3
--- /dev/null
+++ b/src/ustreamer/http/systemd/systemd.h
@@ -0,0 +1,37 @@
+/*****************************************************************************
+# #
+# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
+# #
+# Copyright (C) 2018-2022 Maxim Devaev #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program 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 General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see . #
+# #
+*****************************************************************************/
+
+
+#pragma once
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include "../../../libs/tools.h"
+#include "../../../libs/logging.h"
+
+
+evutil_socket_t evhttp_my_bind_systemd(struct evhttp *http);
diff --git a/src/ustreamer/options.c b/src/ustreamer/options.c
index a36ecf6..2749c57 100644
--- a/src/ustreamer/options.c
+++ b/src/ustreamer/options.c
@@ -50,6 +50,9 @@ enum _OPT_VALUES {
_O_UNIX = 'U',
_O_UNIX_RM = 'D',
_O_UNIX_MODE = 'M',
+# ifdef WITH_SYSTEMD
+ _O_SYSTEMD = 'S',
+# endif
_O_DROP_SAME_FRAMES = 'e',
_O_FAKE_RESOLUTION = 'R',
@@ -168,6 +171,9 @@ static const struct option _LONG_OPTS[] = {
{"unix", required_argument, NULL, _O_UNIX},
{"unix-rm", no_argument, NULL, _O_UNIX_RM},
{"unix-mode", required_argument, NULL, _O_UNIX_MODE},
+# ifdef WITH_SYSTEMD
+ {"systemd", no_argument, NULL, _O_SYSTEMD},
+# endif
{"user", required_argument, NULL, _O_USER},
{"passwd", required_argument, NULL, _O_PASSWD},
{"static", required_argument, NULL, _O_STATIC},
@@ -419,6 +425,9 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
case _O_UNIX: OPT_SET(server->unix_path, optarg);
case _O_UNIX_RM: OPT_SET(server->unix_rm, true);
case _O_UNIX_MODE: OPT_NUMBER("--unix-mode", server->unix_mode, INT_MIN, INT_MAX, 8);
+# ifdef WITH_SYSTEMD
+ case _O_SYSTEMD: OPT_SET(server->systemd, true);
+# endif
case _O_USER: OPT_SET(server->user, optarg);
case _O_PASSWD: OPT_SET(server->passwd, optarg);
case _O_STATIC: OPT_SET(server->static_path, optarg);
@@ -551,6 +560,12 @@ static void _features(void) {
puts("- WITH_GPIO");
# endif
+# ifdef WITH_SYSTEMD
+ puts("+ WITH_SYSTEMD");
+# else
+ puts("- WITH_SYSTEMD");
+# endif
+
# ifdef WITH_PTHREAD_NP
puts("+ WITH_PTHREAD_NP");
# else
@@ -652,6 +667,9 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
SAY(" -U|--unix ─────────── Bind to UNIX domain socket. Default: disabled.\n");
SAY(" -D|--unix-rm ─────────────── Try to remove old UNIX socket file before binding. Default: disabled.\n");
SAY(" -M|--unix-mode ────── Set UNIX socket file permissions (like 777). Default: disabled.\n");
+# ifdef WITH_SYSTEMD
+ SAY(" -S|--systemd ─────────────── Bind to systemd socket for socket activation.\n");
+# endif
SAY(" --user ────────────── HTTP basic auth user. Default: disabled.\n");
SAY(" --passwd ───────────── HTTP basic auth passwd. Default: empty.\n");
SAY(" --static ───────────── Path to dir with static files instead of embedded root index page.");
@@ -661,7 +679,7 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
SAY(" the CPU loading. Don't use this option with analog signal sources");
SAY(" or webcams, it's useless. Default: disabled.\n");
SAY(" -R|--fake-resolution ─ Override image resolution for the /state. Default: disabled.\n");
- SAY(" --tcp-nodelay ────────────── Set TCP_NODELAY flag to the client /stream socket. Ignored for --unix.");
+ SAY(" --tcp-nodelay ────────────── Set TCP_NODELAY flag to the client /stream socket. Only for TCP socket.");
SAY(" Default: disabled.\n");
SAY(" --allow-origin ─────── Set Access-Control-Allow-Origin header. Default: disabled.\n");
SAY(" --server-timeout ───── Timeout for client connections. Default: %u.\n", server->timeout);