mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-03-12 02:23:43 +00:00
systemd socket activation
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal | ✔ | ☹ Partially yes <sup>1</sup> |
|
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal | ✔ | ☹ Partially yes <sup>1</sup> |
|
||||||
| Option to skip frames when streaming<br>static images by HTTP to save the traffic | ✔ <sup>2</sup> | ✘ |
|
| Option to skip frames when streaming<br>static images by HTTP to save the traffic | ✔ <sup>2</sup> | ✘ |
|
||||||
| Streaming via UNIX domain socket | ✔ | ✘ |
|
| Streaming via UNIX domain socket | ✔ | ✘ |
|
||||||
|
| Systemd socket activation | ✔ | ✘ |
|
||||||
| Debug logs without recompiling,<br>performance statistics log,<br>access to HTTP streaming parameters | ✔ | ✘ |
|
| Debug logs without recompiling,<br>performance statistics log,<br>access to HTTP streaming parameters | ✔ | ✘ |
|
||||||
| Option to serve files<br>with a built-in HTTP server | ✔ | ☹ Regular files only |
|
| Option to serve files<br>with a built-in HTTP server | ✔ | ☹ Regular files only |
|
||||||
| Signaling about the stream state<br>on GPIO using [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) | ✔ | ✘ |
|
| Signaling about the stream state<br>on GPIO using [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) | ✔ | ✘ |
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
| Поддержка [DV-таймингов](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) - возможности<br>изменения параметров разрешения<br>трансляции на лету по сигналу<br>источника (устройства видеозахвата) | ✔ | ☹ Условно есть <sup>1</sup> |
|
| Поддержка [DV-таймингов](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) - возможности<br>изменения параметров разрешения<br>трансляции на лету по сигналу<br>источника (устройства видеозахвата) | ✔ | ☹ Условно есть <sup>1</sup> |
|
||||||
| Возможность пропуска фреймов при передаче<br>статического изображения по HTTP<br>для экономии трафика | ✔ <sup>2</sup> | ✘ |
|
| Возможность пропуска фреймов при передаче<br>статического изображения по HTTP<br>для экономии трафика | ✔ <sup>2</sup> | ✘ |
|
||||||
| Стрим через UNIX domain socket | ✔ | ✘ |
|
| Стрим через UNIX domain socket | ✔ | ✘ |
|
||||||
|
| Systemd socket activation | ✔ | ✘ |
|
||||||
| Дебаг-логи без перекомпиляции,<br>логгирование статистики производительности,<br>возможность получения параметров<br>трансляции по HTTP | ✔ | ✘ |
|
| Дебаг-логи без перекомпиляции,<br>логгирование статистики производительности,<br>возможность получения параметров<br>трансляции по HTTP | ✔ | ✘ |
|
||||||
| Возможность сервить файлы встроенным<br>HTTP-сервером | ✔ | ☹ Нет каталогов |
|
| Возможность сервить файлы встроенным<br>HTTP-сервером | ✔ | ☹ Нет каталогов |
|
||||||
| Вывод сигналов о состоянии стрима на GPIO<br>с помощью [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) | ✔ | ✘ |
|
| Вывод сигналов о состоянии стрима на GPIO<br>с помощью [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) | ✔ | ✘ |
|
||||||
|
|||||||
@@ -173,6 +173,9 @@ Try to remove old unix socket file before binding. default: disabled.
|
|||||||
.BR \-M\ \fImode ", " \-\-unix\-mode\ \fImode
|
.BR \-M\ \fImode ", " \-\-unix\-mode\ \fImode
|
||||||
Set UNIX socket file permissions (like 777). Default: disabled.
|
Set UNIX socket file permissions (like 777). Default: disabled.
|
||||||
.TP
|
.TP
|
||||||
|
.BR \-\-systemd
|
||||||
|
Bind to systemd socket for socket activation. Required \fBWITH_SYSTEMD\fR feature. Default: disabled.
|
||||||
|
.TP
|
||||||
.BR \-\-user\ \fIname
|
.BR \-\-user\ \fIname
|
||||||
HTTP basic auth user. Default: disabled.
|
HTTP basic auth user. Default: disabled.
|
||||||
.TP
|
.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.
|
Override image resolution for the /state. Default: disabled.
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-tcp\-nodelay
|
.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.
|
Default: disabled.
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-allow\-origin\ \fIstr
|
.BR \-\-allow\-origin\ \fIstr
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ if [ -e /usr/include/janus/plugins/plugin.h ];then
|
|||||||
makedepends+=(janus-gateway-pikvm)
|
makedepends+=(janus-gateway-pikvm)
|
||||||
_options="$_options WITH_JANUS=1"
|
_options="$_options WITH_JANUS=1"
|
||||||
fi
|
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
|
# LD does not link mmal with this option
|
||||||
|
|||||||
@@ -59,6 +59,13 @@ _USTR_SRCS += $(shell ls ustreamer/gpio/*.c)
|
|||||||
endif
|
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
|
WITH_PTHREAD_NP ?= 1
|
||||||
ifneq ($(call optbool,$(WITH_PTHREAD_NP)),)
|
ifneq ($(call optbool,$(WITH_PTHREAD_NP)),)
|
||||||
override _CFLAGS += -DWITH_PTHREAD_NP
|
override _CFLAGS += -DWITH_PTHREAD_NP
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ void server_destroy(server_s *server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
evhttp_free(RUN(http));
|
evhttp_free(RUN(http));
|
||||||
if (RUN(unix_fd)) {
|
if (RUN(ext_fd)) {
|
||||||
close(RUN(unix_fd));
|
close(RUN(ext_fd));
|
||||||
}
|
}
|
||||||
event_base_free(RUN(base));
|
event_base_free(RUN(base));
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ int server_listen(server_s *server) {
|
|||||||
|
|
||||||
if (server->unix_path[0] != '\0') {
|
if (server->unix_path[0] != '\0') {
|
||||||
LOG_DEBUG("Binding HTTP to UNIX socket '%s' ...", server->unix_path);
|
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),
|
RUN(http),
|
||||||
server->unix_path,
|
server->unix_path,
|
||||||
server->unix_rm,
|
server->unix_rm,
|
||||||
@@ -169,9 +169,16 @@ int server_listen(server_s *server) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
LOG_INFO("Listening HTTP on UNIX socket '%s'", server->unix_path);
|
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 {
|
} else {
|
||||||
LOG_DEBUG("Binding HTTP to [%s]:%u ...", server->host, server->port);
|
LOG_DEBUG("Binding HTTP to [%s]:%u ...", server->host, server->port);
|
||||||
if (evhttp_bind_socket(RUN(http), server->host, server->port) < 0) {
|
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));
|
client->hostport, client->id, RUN(stream_clients_count));
|
||||||
|
|
||||||
struct bufferevent *buf_event = evhttp_connection_get_bufferevent(conn);
|
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;
|
evutil_socket_t fd;
|
||||||
int on = 1;
|
int on = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,9 @@
|
|||||||
#include "uri.h"
|
#include "uri.h"
|
||||||
#include "mime.h"
|
#include "mime.h"
|
||||||
#include "static.h"
|
#include "static.h"
|
||||||
|
#ifdef WITH_SYSTEMD
|
||||||
|
# include "systemd/systemd.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef struct stream_client_sx {
|
typedef struct stream_client_sx {
|
||||||
@@ -112,7 +115,7 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
struct event_base *base;
|
struct event_base *base;
|
||||||
struct evhttp *http;
|
struct evhttp *http;
|
||||||
evutil_socket_t unix_fd;
|
evutil_socket_t ext_fd; // Unix or socket activation
|
||||||
char *auth_token;
|
char *auth_token;
|
||||||
struct event *refresh;
|
struct event *refresh;
|
||||||
stream_s *stream;
|
stream_s *stream;
|
||||||
@@ -124,9 +127,15 @@ typedef struct {
|
|||||||
typedef struct server_sx {
|
typedef struct server_sx {
|
||||||
char *host;
|
char *host;
|
||||||
unsigned port;
|
unsigned port;
|
||||||
|
|
||||||
char *unix_path;
|
char *unix_path;
|
||||||
bool unix_rm;
|
bool unix_rm;
|
||||||
mode_t unix_mode;
|
mode_t unix_mode;
|
||||||
|
|
||||||
|
# ifdef WITH_SYSTEMD
|
||||||
|
bool systemd;
|
||||||
|
# endif
|
||||||
|
|
||||||
bool tcp_nodelay;
|
bool tcp_nodelay;
|
||||||
unsigned timeout;
|
unsigned timeout;
|
||||||
|
|
||||||
|
|||||||
46
src/ustreamer/http/systemd/systemd.c
Normal file
46
src/ustreamer/http/systemd/systemd.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2022 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
37
src/ustreamer/http/systemd/systemd.h
Normal file
37
src/ustreamer/http/systemd/systemd.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2022 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <event2/http.h>
|
||||||
|
#include <event2/util.h>
|
||||||
|
|
||||||
|
#include <systemd/sd-daemon.h>
|
||||||
|
|
||||||
|
#include "../../../libs/tools.h"
|
||||||
|
#include "../../../libs/logging.h"
|
||||||
|
|
||||||
|
|
||||||
|
evutil_socket_t evhttp_my_bind_systemd(struct evhttp *http);
|
||||||
@@ -50,6 +50,9 @@ enum _OPT_VALUES {
|
|||||||
_O_UNIX = 'U',
|
_O_UNIX = 'U',
|
||||||
_O_UNIX_RM = 'D',
|
_O_UNIX_RM = 'D',
|
||||||
_O_UNIX_MODE = 'M',
|
_O_UNIX_MODE = 'M',
|
||||||
|
# ifdef WITH_SYSTEMD
|
||||||
|
_O_SYSTEMD = 'S',
|
||||||
|
# endif
|
||||||
_O_DROP_SAME_FRAMES = 'e',
|
_O_DROP_SAME_FRAMES = 'e',
|
||||||
_O_FAKE_RESOLUTION = 'R',
|
_O_FAKE_RESOLUTION = 'R',
|
||||||
|
|
||||||
@@ -168,6 +171,9 @@ static const struct option _LONG_OPTS[] = {
|
|||||||
{"unix", required_argument, NULL, _O_UNIX},
|
{"unix", required_argument, NULL, _O_UNIX},
|
||||||
{"unix-rm", no_argument, NULL, _O_UNIX_RM},
|
{"unix-rm", no_argument, NULL, _O_UNIX_RM},
|
||||||
{"unix-mode", required_argument, NULL, _O_UNIX_MODE},
|
{"unix-mode", required_argument, NULL, _O_UNIX_MODE},
|
||||||
|
# ifdef WITH_SYSTEMD
|
||||||
|
{"systemd", no_argument, NULL, _O_SYSTEMD},
|
||||||
|
# endif
|
||||||
{"user", required_argument, NULL, _O_USER},
|
{"user", required_argument, NULL, _O_USER},
|
||||||
{"passwd", required_argument, NULL, _O_PASSWD},
|
{"passwd", required_argument, NULL, _O_PASSWD},
|
||||||
{"static", required_argument, NULL, _O_STATIC},
|
{"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: OPT_SET(server->unix_path, optarg);
|
||||||
case _O_UNIX_RM: OPT_SET(server->unix_rm, true);
|
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);
|
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_USER: OPT_SET(server->user, optarg);
|
||||||
case _O_PASSWD: OPT_SET(server->passwd, optarg);
|
case _O_PASSWD: OPT_SET(server->passwd, optarg);
|
||||||
case _O_STATIC: OPT_SET(server->static_path, optarg);
|
case _O_STATIC: OPT_SET(server->static_path, optarg);
|
||||||
@@ -551,6 +560,12 @@ static void _features(void) {
|
|||||||
puts("- WITH_GPIO");
|
puts("- WITH_GPIO");
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
# ifdef WITH_SYSTEMD
|
||||||
|
puts("+ WITH_SYSTEMD");
|
||||||
|
# else
|
||||||
|
puts("- WITH_SYSTEMD");
|
||||||
|
# endif
|
||||||
|
|
||||||
# ifdef WITH_PTHREAD_NP
|
# ifdef WITH_PTHREAD_NP
|
||||||
puts("+ WITH_PTHREAD_NP");
|
puts("+ WITH_PTHREAD_NP");
|
||||||
# else
|
# else
|
||||||
@@ -652,6 +667,9 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
|
|||||||
SAY(" -U|--unix <path> ─────────── Bind to UNIX domain socket. Default: disabled.\n");
|
SAY(" -U|--unix <path> ─────────── 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(" -D|--unix-rm ─────────────── Try to remove old UNIX socket file before binding. Default: disabled.\n");
|
||||||
SAY(" -M|--unix-mode <mode> ────── Set UNIX socket file permissions (like 777). Default: disabled.\n");
|
SAY(" -M|--unix-mode <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 <name> ────────────── HTTP basic auth user. Default: disabled.\n");
|
SAY(" --user <name> ────────────── HTTP basic auth user. Default: disabled.\n");
|
||||||
SAY(" --passwd <str> ───────────── HTTP basic auth passwd. Default: empty.\n");
|
SAY(" --passwd <str> ───────────── HTTP basic auth passwd. Default: empty.\n");
|
||||||
SAY(" --static <path> ───────────── Path to dir with static files instead of embedded root index page.");
|
SAY(" --static <path> ───────────── 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(" the CPU loading. Don't use this option with analog signal sources");
|
||||||
SAY(" or webcams, it's useless. Default: disabled.\n");
|
SAY(" or webcams, it's useless. Default: disabled.\n");
|
||||||
SAY(" -R|--fake-resolution <WxH> ─ Override image resolution for the /state. Default: disabled.\n");
|
SAY(" -R|--fake-resolution <WxH> ─ 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(" Default: disabled.\n");
|
||||||
SAY(" --allow-origin <str> ─────── Set Access-Control-Allow-Origin header. Default: disabled.\n");
|
SAY(" --allow-origin <str> ─────── Set Access-Control-Allow-Origin header. Default: disabled.\n");
|
||||||
SAY(" --server-timeout <sec> ───── Timeout for client connections. Default: %u.\n", server->timeout);
|
SAY(" --server-timeout <sec> ───── Timeout for client connections. Default: %u.\n", server->timeout);
|
||||||
|
|||||||
Reference in New Issue
Block a user