Add version info adn damon support

This commit is contained in:
Mario Fetka
2026-04-21 15:19:26 +02:00
parent 8e7c97ff7f
commit a4dfee99e2
3 changed files with 461 additions and 160 deletions

View File

@@ -1,25 +1,31 @@
#ifndef NWWEBUI_CONFIG_H
#define NWWEBUI_CONFIG_H
#define DEFAULT_SMART_CONF "@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf"
#define DEFAULT_SMART_PERL "@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/smart"
#define NWWEBUI_NAME "nwwebui"
#define NWWEBUI_VERSION "@MARS_NWE_VERSION@"
#define LOG_PATH_DEFAULT "@MARS_NWE_LOG_DIR@/nwwebui.log"
#define DEFAULT_SMART_CONF "@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf"
#define DEFAULT_SMART_PERL "@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/smart"
#define LOG_LEVEL_ERROR 0
#define LOG_LEVEL_INFO 1
#define LOG_LEVEL_DEBUG 2
#define LOG_LEVEL_DEFAULT LOG_LEVEL_INFO
#define LOG_PATH_DEFAULT "@MARS_NWE_LOG_DIR@/nwwebui.log"
#define LOG_LEVEL_ERROR 0
#define LOG_LEVEL_INFO 1
#define LOG_LEVEL_DEBUG 2
#define LOG_LEVEL_DEFAULT LOG_LEVEL_INFO
#define DEFAULT_BIND_IP "0.0.0.0"
#define DEFAULT_SSL_ENABLE 0
#define DEFAULT_SSL_ENABLE 1
#define DEFAULT_HTTP_PORT 9080
#define DEFAULT_HTTPS_PORT 9443
#define DEFAULT_CERT_FILE "@MARS_NWE_INSTALL_FULL_CONFDIR@/server.crt"
#define DEFAULT_KEY_FILE "@MARS_NWE_INSTALL_FULL_CONFDIR@/server.key"
#define DEFAULT_CERT_FILE "@MARS_NWE_INSTALL_FULL_CONFDIR@/server.crt"
#define DEFAULT_KEY_FILE "@MARS_NWE_INSTALL_FULL_CONFDIR@/server.key"
#define DEFAULT_PID_FILE "@MARS_NWE_PID_DIR@/nwwebui.pid"
#define DEFAULT_DAEMONIZE 0
#define NW_BACKLOG 64
#define NW_BUF_SZ 16384
#endif
#endif

585
nwwebui.c
View File

@@ -15,6 +15,7 @@
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
@@ -28,9 +29,11 @@ typedef struct {
int ssl_enable;
int http_port;
int https_port;
int daemonize;
char cert_file[512];
char key_file[512];
char pid_file[512];
char smart_conf[512];
char smart_perl_path[512];
@@ -39,6 +42,27 @@ typedef struct {
static FILE *g_log_fp = NULL;
static int g_log_level = LOG_LEVEL_DEFAULT;
static volatile sig_atomic_t g_terminate = 0;
static volatile sig_atomic_t g_got_signal = 0;
static int g_pid_file_written = 0;
static char g_pid_file_path[512] = { 0 };
/* ------------------------------------------------------------ */
/* Console lifecycle output */
/* ------------------------------------------------------------ */
static void console_msg(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
fflush(stderr);
}
/* ------------------------------------------------------------ */
/* Logging */
/* ------------------------------------------------------------ */
@@ -61,12 +85,13 @@ static void log_msg(int level, const char *fmt, ...) {
time_t now = time(NULL);
struct tm tm_now;
localtime_r(&now, &tm_now);
char tbuf[64];
const char *lvl = "INFO";
va_list ap;
localtime_r(&now, &tm_now);
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", &tm_now);
const char *lvl = "INFO";
if (level == LOG_LEVEL_ERROR) {
lvl = "ERROR";
} else if (level == LOG_LEVEL_DEBUG) {
@@ -75,7 +100,6 @@ static void log_msg(int level, const char *fmt, ...) {
fprintf(g_log_fp, "[%s] [%s] ", tbuf, lvl);
va_list ap;
va_start(ap, fmt);
vfprintf(g_log_fp, fmt, ap);
va_end(ap);
@@ -101,14 +125,17 @@ static void ssl_die(const char *msg) {
static void trim(char *s) {
char *p = s;
size_t len;
while (*p && isspace((unsigned char)*p)) {
p++;
}
if (p != s) {
memmove(s, p, strlen(p) + 1);
}
size_t len = strlen(s);
len = strlen(s);
while (len > 0 && isspace((unsigned char)s[len - 1])) {
s[len - 1] = '\0';
len--;
@@ -117,6 +144,7 @@ static void trim(char *s) {
static void strip_quotes(char *s) {
size_t len = strlen(s);
if (len >= 2) {
if ((s[0] == '\'' && s[len - 1] == '\'') ||
(s[0] == '"' && s[len - 1] == '"')) {
@@ -127,12 +155,9 @@ static void strip_quotes(char *s) {
}
static int parse_perl_assignment(const char *line, char *key, size_t ksz, char *val, size_t vsz) {
/* Expected format:
$variable = 'value';
$variable = 1234;
*/
const char *p = line;
size_t ki = 0;
size_t vi = 0;
while (*p && isspace((unsigned char)*p)) {
p++;
@@ -142,7 +167,6 @@ static int parse_perl_assignment(const char *line, char *key, size_t ksz, char *
}
p++;
size_t ki = 0;
while (*p && (isalnum((unsigned char)*p) || *p == '_')) {
if (ki + 1 < ksz) {
key[ki++] = *p;
@@ -163,7 +187,6 @@ static int parse_perl_assignment(const char *line, char *key, size_t ksz, char *
p++;
}
size_t vi = 0;
while (*p && *p != ';' && *p != '\n' && *p != '\r') {
if (vi + 1 < vsz) {
val[vi++] = *p;
@@ -194,6 +217,180 @@ static int set_nonblocking(int fd) {
return 0;
}
static void print_usage(const char *progname) {
fprintf(stdout,
"%s %s\n"
"\n"
"Usage: %s [-h] [-d] [-s] [-c <smart.conf>] [-p <pidfile>]\n"
"\n"
"Options:\n"
" -h, --help Show this help text and exit\n"
" -d, --daemon Run in daemon mode\n"
" -s, --stop Stop the running nwwebui instance\n"
" -c, --config <file> Use an alternate smart.conf path\n"
" -p, --pidfile <file> Override PID file path\n",
NWWEBUI_NAME, NWWEBUI_VERSION, progname);
}
/* ------------------------------------------------------------ */
/* PID file handling */
/* ------------------------------------------------------------ */
static void remove_pid_file(void) {
if (g_pid_file_written) {
unlink(g_pid_file_path);
g_pid_file_written = 0;
}
}
static void write_pid_file(const char *path) {
FILE *fp = fopen(path, "w");
if (!fp) {
die("fopen pid file");
}
fprintf(fp, "%ld\n", (long)getpid());
fclose(fp);
snprintf(g_pid_file_path, sizeof(g_pid_file_path), "%s", path);
g_pid_file_written = 1;
}
static pid_t read_pid_file(const char *path) {
FILE *fp;
long pid = -1;
fp = fopen(path, "r");
if (!fp) {
return -1;
}
if (fscanf(fp, "%ld", &pid) != 1) {
fclose(fp);
return -1;
}
fclose(fp);
if (pid <= 0) {
return -1;
}
return (pid_t)pid;
}
static int process_exists(pid_t pid) {
if (pid <= 0) {
return 0;
}
if (kill(pid, 0) == 0) {
return 1;
}
if (errno == EPERM) {
return 1;
}
return 0;
}
static int pid_is_nwwebui(pid_t pid) {
char path[128];
char name[256];
FILE *fp;
snprintf(path, sizeof(path), "/proc/%ld/comm", (long)pid);
fp = fopen(path, "r");
if (!fp) {
return 0;
}
if (!fgets(name, sizeof(name), fp)) {
fclose(fp);
return 0;
}
fclose(fp);
name[strcspn(name, "\r\n")] = '\0';
return strcmp(name, "nwwebui") == 0;
}
static int stop_running_instance(const char *pid_file) {
pid_t pid = read_pid_file(pid_file);
if (pid <= 0) {
fprintf(stderr, "Could not read PID file: %s\n", pid_file);
return EXIT_FAILURE;
}
if (!process_exists(pid)) {
fprintf(stderr, "No running process found for PID %ld\n", (long)pid);
unlink(pid_file);
return EXIT_FAILURE;
}
if (!pid_is_nwwebui(pid)) {
fprintf(stderr, "PID %ld is not a nwwebui process\n", (long)pid);
return EXIT_FAILURE;
}
if (kill(pid, SIGTERM) < 0) {
fprintf(stderr, "Failed to stop nwwebui with PID %ld: %s\n",
(long)pid, strerror(errno));
return EXIT_FAILURE;
}
fprintf(stderr, "Sent SIGTERM to %s %s with PID %ld\n",
NWWEBUI_NAME, NWWEBUI_VERSION, (long)pid);
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------ */
/* Daemonization */
/* ------------------------------------------------------------ */
static void daemonize_process(void) {
pid_t pid;
pid = fork();
if (pid < 0) {
die("fork");
}
if (pid > 0) {
exit(EXIT_SUCCESS);
}
if (setsid() < 0) {
die("setsid");
}
pid = fork();
if (pid < 0) {
die("fork");
}
if (pid > 0) {
exit(EXIT_SUCCESS);
}
if (chdir("/") < 0) {
die("chdir");
}
umask(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
open("/dev/null", O_RDONLY);
open("/dev/null", O_WRONLY);
open("/dev/null", O_WRONLY);
}
/* ------------------------------------------------------------ */
/* smart.conf loading */
/* ------------------------------------------------------------ */
@@ -206,9 +403,11 @@ static void init_defaults(nw_config_t *cfg, const char *smart_conf_path) {
cfg->ssl_enable = DEFAULT_SSL_ENABLE;
cfg->http_port = DEFAULT_HTTP_PORT;
cfg->https_port = DEFAULT_HTTPS_PORT;
cfg->daemonize = DEFAULT_DAEMONIZE;
snprintf(cfg->cert_file, sizeof(cfg->cert_file), "%s", DEFAULT_CERT_FILE);
snprintf(cfg->key_file, sizeof(cfg->key_file), "%s", DEFAULT_KEY_FILE);
snprintf(cfg->pid_file, sizeof(cfg->pid_file), "%s", DEFAULT_PID_FILE);
snprintf(cfg->smart_perl_path, sizeof(cfg->smart_perl_path), "%s", DEFAULT_SMART_PERL);
snprintf(cfg->smart_conf, sizeof(cfg->smart_conf), "%s", smart_conf_path);
@@ -216,12 +415,13 @@ static void init_defaults(nw_config_t *cfg, const char *smart_conf_path) {
static void load_smart_conf(nw_config_t *cfg) {
FILE *fp = fopen(cfg->smart_conf, "r");
char line[2048];
if (!fp) {
log_msg(LOG_LEVEL_ERROR, "Could not open smart.conf: %s", cfg->smart_conf);
die("fopen smart.conf");
}
char line[2048];
while (fgets(line, sizeof(line), fp)) {
char key[256];
char val[1024];
@@ -240,6 +440,10 @@ static void load_smart_conf(nw_config_t *cfg) {
cfg->http_port = atoi(val);
} else if (strcmp(key, "nw_https_port") == 0) {
cfg->https_port = atoi(val);
} else if (strcmp(key, "nw_daemonize") == 0) {
cfg->daemonize = atoi(val);
} else if (strcmp(key, "nw_pid_file") == 0) {
snprintf(cfg->pid_file, sizeof(cfg->pid_file), "%s", val);
} else if (strcmp(key, "nw_cert_file") == 0) {
snprintf(cfg->cert_file, sizeof(cfg->cert_file), "%s", val);
} else if (strcmp(key, "nw_key_file") == 0) {
@@ -272,17 +476,18 @@ static void load_smart_conf(nw_config_t *cfg) {
static int create_listener(const char *bind_ip, int port) {
int fd = socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
struct sockaddr_in addr;
if (fd < 0) {
die("socket");
}
int yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
close(fd);
die("setsockopt(SO_REUSEADDR)");
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons((uint16_t)port);
@@ -358,6 +563,7 @@ static ssize_t send_all_ssl(SSL *ssl, const unsigned char *buf, size_t len) {
static pid_t spawn_smart_perl(const nw_config_t *cfg, int *child_stdin_fd, int *child_stdout_fd) {
int inpipe[2] = { -1, -1 };
int outpipe[2] = { -1, -1 };
pid_t pid;
if (pipe(inpipe) < 0) {
die("pipe stdin");
@@ -369,7 +575,7 @@ static pid_t spawn_smart_perl(const nw_config_t *cfg, int *child_stdin_fd, int *
die("pipe stdout");
}
pid_t pid = fork();
pid = fork();
if (pid < 0) {
close(inpipe[0]);
close(inpipe[1]);
@@ -379,11 +585,6 @@ static pid_t spawn_smart_perl(const nw_config_t *cfg, int *child_stdin_fd, int *
}
if (pid == 0) {
/* Child process:
stdin <- parent writes client request data here
stdout -> parent reads response data here
stderr -> merged into stdout for logging/debugging
*/
if (dup2(inpipe[0], STDIN_FILENO) < 0) {
perror("dup2 stdin");
_exit(127);
@@ -471,6 +672,8 @@ static void handle_connection_plain(int client_fd, const nw_config_t *cfg) {
for (;;) {
struct pollfd pfds[2];
int pr;
pfds[0].fd = client_fd;
pfds[0].events = POLLIN | POLLHUP | POLLERR;
pfds[0].revents = 0;
@@ -479,7 +682,7 @@ static void handle_connection_plain(int client_fd, const nw_config_t *cfg) {
pfds[1].events = POLLIN | POLLHUP | POLLERR;
pfds[1].revents = 0;
int pr = poll(pfds, 2, -1);
pr = poll(pfds, 2, -1);
if (pr < 0) {
if (errno == EINTR) {
continue;
@@ -488,7 +691,6 @@ static void handle_connection_plain(int client_fd, const nw_config_t *cfg) {
break;
}
/* Client -> Perl */
if (pfds[0].revents & (POLLIN | POLLHUP | POLLERR)) {
ssize_t n = read(client_fd, buf, sizeof(buf));
if (n > 0) {
@@ -504,7 +706,6 @@ static void handle_connection_plain(int client_fd, const nw_config_t *cfg) {
}
}
/* Perl -> Client */
if (pfds[1].revents & (POLLIN | POLLHUP | POLLERR)) {
ssize_t rn = read(child_stdout_fd, buf, sizeof(buf));
if (rn > 0) {
@@ -575,6 +776,8 @@ static void handle_connection_tls(SSL_CTX *ctx, int client_fd, const nw_config_t
for (;;) {
struct pollfd pfds[2];
int pr;
pfds[0].fd = client_fd;
pfds[0].events = POLLIN | POLLHUP | POLLERR;
pfds[0].revents = 0;
@@ -583,7 +786,7 @@ static void handle_connection_tls(SSL_CTX *ctx, int client_fd, const nw_config_t
pfds[1].events = POLLIN | POLLHUP | POLLERR;
pfds[1].revents = 0;
int pr = poll(pfds, 2, -1);
pr = poll(pfds, 2, -1);
if (pr < 0) {
if (errno == EINTR) {
continue;
@@ -592,7 +795,6 @@ static void handle_connection_tls(SSL_CTX *ctx, int client_fd, const nw_config_t
break;
}
/* TLS client -> Perl */
if ((pfds[0].revents & (POLLIN | POLLHUP | POLLERR)) || SSL_pending(ssl) > 0) {
int n = SSL_read(ssl, buf, sizeof(buf));
if (n > 0) {
@@ -612,7 +814,6 @@ static void handle_connection_tls(SSL_CTX *ctx, int client_fd, const nw_config_t
}
}
/* Perl -> TLS client */
if (pfds[1].revents & (POLLIN | POLLHUP | POLLERR)) {
ssize_t rn = read(child_stdout_fd, buf, sizeof(buf));
if (rn > 0) {
@@ -649,7 +850,7 @@ cleanup:
}
/* ------------------------------------------------------------ */
/* SIGCHLD handler */
/* Signal handlers */
/* ------------------------------------------------------------ */
static void reap_children(int sig) {
@@ -663,18 +864,42 @@ static void reap_children(int sig) {
}
}
static void term_handler(int sig) {
g_got_signal = sig;
g_terminate = 1;
}
/* ------------------------------------------------------------ */
/* Main */
/* ------------------------------------------------------------ */
int main(int argc, char **argv) {
const char *smart_conf_path = DEFAULT_SMART_CONF;
const char *pidfile_override = NULL;
int http_fd = -1;
int https_fd = -1;
SSL_CTX *ctx = NULL;
int force_daemon = 0;
int stop_mode = 0;
int i;
if (argc > 1) {
smart_conf_path = argv[1];
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
print_usage(argv[0]);
return EXIT_SUCCESS;
} else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--daemon") == 0) {
force_daemon = 1;
} else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--stop") == 0) {
stop_mode = 1;
} else if ((strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--config") == 0) && i + 1 < argc) {
smart_conf_path = argv[++i];
} else if ((strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--pidfile") == 0) && i + 1 < argc) {
pidfile_override = argv[++i];
} else {
fprintf(stderr, "Unknown argument: %s\n", argv[i]);
print_usage(argv[0]);
return EXIT_FAILURE;
}
}
{
@@ -689,154 +914,215 @@ int main(int argc, char **argv) {
}
}
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = term_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGTERM, &sa, NULL) < 0) {
die("sigaction(SIGTERM)");
}
if (sigaction(SIGINT, &sa, NULL) < 0) {
die("sigaction(SIGINT)");
}
if (sigaction(SIGHUP, &sa, NULL) < 0) {
die("sigaction(SIGHUP)");
}
}
signal(SIGPIPE, SIG_IGN);
log_open();
log_msg(LOG_LEVEL_INFO, "nwwebui starting");
console_msg("%s %s starting", NWWEBUI_NAME, NWWEBUI_VERSION);
nw_config_t cfg;
init_defaults(&cfg, smart_conf_path);
load_smart_conf(&cfg);
{
nw_config_t cfg;
init_defaults(&cfg, smart_conf_path);
load_smart_conf(&cfg);
if (!file_exists_and_executable(cfg.smart_perl_path)) {
log_msg(LOG_LEVEL_ERROR,
"SMArT Perl program is missing or not executable: %s",
cfg.smart_perl_path);
return EXIT_FAILURE;
}
log_msg(LOG_LEVEL_INFO, "Using SMArT Perl path: %s", cfg.smart_perl_path);
log_msg(LOG_LEVEL_INFO,
"Config loaded: bind=%s log_level=%d ssl_enable=%d http_port=%d https_port=%d cert=%s key=%s smart.conf=%s",
cfg.bind_ip,
g_log_level,
cfg.ssl_enable,
cfg.http_port,
cfg.https_port,
cfg.cert_file,
cfg.key_file,
cfg.smart_conf);
if (cfg.ssl_enable) {
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
ctx = SSL_CTX_new(TLS_server_method());
if (!ctx) {
ssl_die("SSL_CTX_new failed");
if (force_daemon) {
cfg.daemonize = 1;
}
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
if (SSL_CTX_use_certificate_file(ctx, cfg.cert_file, SSL_FILETYPE_PEM) <= 0) {
ssl_die("Could not load certificate file");
if (pidfile_override) {
snprintf(cfg.pid_file, sizeof(cfg.pid_file), "%s", pidfile_override);
}
if (SSL_CTX_use_PrivateKey_file(ctx, cfg.key_file, SSL_FILETYPE_PEM) <= 0) {
ssl_die("Could not load private key file");
if (stop_mode) {
return stop_running_instance(cfg.pid_file);
}
if (!SSL_CTX_check_private_key(ctx)) {
ssl_die("Private key does not match certificate");
if (!file_exists_and_executable(cfg.smart_perl_path)) {
log_msg(LOG_LEVEL_ERROR,
"SMArT Perl program is missing or not executable: %s",
cfg.smart_perl_path);
return EXIT_FAILURE;
}
}
if (cfg.http_port > 0) {
http_fd = create_listener(cfg.bind_ip, cfg.http_port);
log_msg(LOG_LEVEL_INFO, "%s version %s starting", NWWEBUI_NAME, NWWEBUI_VERSION);
log_msg(LOG_LEVEL_INFO, "Using SMArT Perl path: %s", cfg.smart_perl_path);
log_msg(LOG_LEVEL_INFO,
"HTTP listener active on %s:%d",
"%s version %s config loaded: bind=%s log_level=%d ssl_enable=%d http_port=%d https_port=%d daemonize=%d cert=%s key=%s pid_file=%s smart.conf=%s",
NWWEBUI_NAME,
NWWEBUI_VERSION,
cfg.bind_ip,
cfg.http_port);
}
g_log_level,
cfg.ssl_enable,
cfg.http_port,
cfg.https_port,
cfg.daemonize,
cfg.cert_file,
cfg.key_file,
cfg.pid_file,
cfg.smart_conf);
if (cfg.ssl_enable && cfg.https_port > 0) {
https_fd = create_listener(cfg.bind_ip, cfg.https_port);
log_msg(LOG_LEVEL_INFO,
"HTTPS listener active on %s:%d",
cfg.bind_ip,
cfg.https_port);
}
if (http_fd < 0 && https_fd < 0) {
log_msg(LOG_LEVEL_ERROR, "No listener is active");
if (ctx) {
SSL_CTX_free(ctx);
}
return EXIT_FAILURE;
}
for (;;) {
struct pollfd pfds[2];
int nfds = 0;
if (http_fd >= 0) {
pfds[nfds].fd = http_fd;
pfds[nfds].events = POLLIN;
pfds[nfds].revents = 0;
nfds++;
if (cfg.daemonize) {
console_msg("%s %s switching to daemon mode", NWWEBUI_NAME, NWWEBUI_VERSION);
daemonize_process();
}
if (https_fd >= 0) {
pfds[nfds].fd = https_fd;
pfds[nfds].events = POLLIN;
pfds[nfds].revents = 0;
nfds++;
}
write_pid_file(cfg.pid_file);
atexit(remove_pid_file);
log_msg(LOG_LEVEL_INFO, "Wrote PID file: %s", cfg.pid_file);
int pr = poll(pfds, nfds, -1);
if (pr < 0) {
if (errno == EINTR) {
continue;
}
log_msg(LOG_LEVEL_ERROR, "poll failed: %s", strerror(errno));
continue;
}
if (cfg.ssl_enable) {
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
for (int i = 0; i < nfds; i++) {
if (!(pfds[i].revents & POLLIN)) {
continue;
ctx = SSL_CTX_new(TLS_server_method());
if (!ctx) {
ssl_die("SSL_CTX_new failed");
}
struct sockaddr_in peer;
socklen_t peerlen = sizeof(peer);
int client_fd = accept(pfds[i].fd, (struct sockaddr *)&peer, &peerlen);
if (client_fd < 0) {
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
if (SSL_CTX_use_certificate_file(ctx, cfg.cert_file, SSL_FILETYPE_PEM) <= 0) {
ssl_die("Could not load certificate file");
}
if (SSL_CTX_use_PrivateKey_file(ctx, cfg.key_file, SSL_FILETYPE_PEM) <= 0) {
ssl_die("Could not load private key file");
}
if (!SSL_CTX_check_private_key(ctx)) {
ssl_die("Private key does not match certificate");
}
}
if (cfg.http_port > 0) {
http_fd = create_listener(cfg.bind_ip, cfg.http_port);
log_msg(LOG_LEVEL_INFO,
"HTTP listener active on %s:%d",
cfg.bind_ip,
cfg.http_port);
}
if (cfg.ssl_enable && cfg.https_port > 0) {
https_fd = create_listener(cfg.bind_ip, cfg.https_port);
log_msg(LOG_LEVEL_INFO,
"HTTPS listener active on %s:%d",
cfg.bind_ip,
cfg.https_port);
}
if (http_fd < 0 && https_fd < 0) {
log_msg(LOG_LEVEL_ERROR, "No listener is active");
if (ctx) {
SSL_CTX_free(ctx);
}
return EXIT_FAILURE;
}
console_msg("%s %s started", NWWEBUI_NAME, NWWEBUI_VERSION);
for (;;) {
struct pollfd pfds[2];
int nfds = 0;
int pr;
if (g_terminate) {
console_msg("%s received signal %d, shutting down", NWWEBUI_NAME, g_got_signal);
log_msg(LOG_LEVEL_INFO, "Termination requested by signal %d", g_got_signal);
break;
}
if (http_fd >= 0) {
pfds[nfds].fd = http_fd;
pfds[nfds].events = POLLIN;
pfds[nfds].revents = 0;
nfds++;
}
if (https_fd >= 0) {
pfds[nfds].fd = https_fd;
pfds[nfds].events = POLLIN;
pfds[nfds].revents = 0;
nfds++;
}
pr = poll(pfds, nfds, 1000);
if (pr < 0) {
if (errno == EINTR) {
continue;
}
log_msg(LOG_LEVEL_ERROR, "accept failed: %s", strerror(errno));
log_msg(LOG_LEVEL_ERROR, "poll failed: %s", strerror(errno));
continue;
}
pid_t pid = fork();
if (pid < 0) {
log_msg(LOG_LEVEL_ERROR, "fork failed: %s", strerror(errno));
if (pr == 0) {
continue;
}
for (i = 0; i < nfds; i++) {
struct sockaddr_in peer;
socklen_t peerlen = sizeof(peer);
int client_fd;
pid_t pid;
if (!(pfds[i].revents & POLLIN)) {
continue;
}
client_fd = accept(pfds[i].fd, (struct sockaddr *)&peer, &peerlen);
if (client_fd < 0) {
if (errno == EINTR) {
continue;
}
log_msg(LOG_LEVEL_ERROR, "accept failed: %s", strerror(errno));
continue;
}
pid = fork();
if (pid < 0) {
log_msg(LOG_LEVEL_ERROR, "fork failed: %s", strerror(errno));
close(client_fd);
continue;
}
if (pid == 0) {
if (http_fd >= 0) {
close(http_fd);
}
if (https_fd >= 0) {
close(https_fd);
}
if (pfds[i].fd == https_fd) {
handle_connection_tls(ctx, client_fd, &cfg);
} else {
handle_connection_plain(client_fd, &cfg);
}
if (ctx) {
SSL_CTX_free(ctx);
}
_exit(0);
}
close(client_fd);
continue;
}
if (pid == 0) {
if (http_fd >= 0) {
close(http_fd);
}
if (https_fd >= 0) {
close(https_fd);
}
if (pfds[i].fd == https_fd) {
handle_connection_tls(ctx, client_fd, &cfg);
} else {
handle_connection_plain(client_fd, &cfg);
}
if (ctx) {
SSL_CTX_free(ctx);
}
_exit(0);
}
close(client_fd);
}
}
@@ -850,5 +1136,6 @@ int main(int argc, char **argv) {
SSL_CTX_free(ctx);
}
console_msg("%s stopped", NWWEBUI_NAME);
return 0;
}

View File

@@ -84,6 +84,14 @@ $nw_bind_ip = '0.0.0.0';
# 2 = debug messages
$nw_log_level = 1;
# Run nwwebui in daemon mode by default.
# 0 = stay in foreground
# 1 = detach into background
$nw_daemonize = 0;
# PID file written by nwwebui.
$nw_pid_file = '@MARS_NWE_PID_DIR@/nwwebui.pid';
# Enable or disable TLS/SSL support.
# 1 = enable HTTPS listener
# 0 = disable HTTPS listener