Add version info adn damon support
This commit is contained in:
@@ -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
585
nwwebui.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user