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);