Compare commits
15 Commits
master
...
8e7c97ff7f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e7c97ff7f | ||
|
|
b7206fc83a | ||
|
|
2ee152f543 | ||
|
|
829be767c9 | ||
|
|
a4d3ded77e | ||
|
|
0d3bbdec8e | ||
|
|
ed3acbe2d4 | ||
|
|
9670c4e749 | ||
|
|
a3f7460150 | ||
|
|
476c51ac10 | ||
|
|
5bbe69ac5f | ||
|
|
331fb1a746 | ||
|
|
bb9392b9b0 | ||
|
|
24f48e1f96 | ||
|
|
0a31d87196 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
error.log
|
||||
75
CMakeLists.txt
Normal file
75
CMakeLists.txt
Normal file
@@ -0,0 +1,75 @@
|
||||
#################################
|
||||
# Project
|
||||
##############
|
||||
|
||||
#################################
|
||||
# Dependencies
|
||||
##############
|
||||
|
||||
#################################
|
||||
# Generated files
|
||||
##############
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/config.h"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/smart.conf.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/smart.conf"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/smart.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/smart"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
#################################
|
||||
# Compiler Switches
|
||||
##############
|
||||
|
||||
INCLUDE_DIRECTORIES(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
)
|
||||
|
||||
#################################
|
||||
# Source Files
|
||||
##############
|
||||
|
||||
add_executable(nwwebui nwwebui.c)
|
||||
add_executable(check_login check_login.c)
|
||||
|
||||
#################################
|
||||
# Linking
|
||||
##############
|
||||
|
||||
target_link_libraries(nwwebui
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
)
|
||||
|
||||
target_link_libraries(check_login
|
||||
${PAM_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
)
|
||||
|
||||
#################################
|
||||
# Install Files
|
||||
##############
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/smart.conf DESTINATION ${MARS_NWE_INSTALL_FULL_CONFDIR})
|
||||
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/smart DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||
|
||||
install(PROGRAMS apply.pl DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||
install(PROGRAMS readconfig.pl DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||
install(PROGRAMS settings.pl DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||
install(PROGRAMS static.pl DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||
|
||||
install(DIRECTORY static/ DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||
|
||||
install(TARGETS check_login DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||
|
||||
install(TARGETS nwwebui DESTINATION ${CMAKE_INSTALL_SBINDIR})
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# mars-smart
|
||||
|
||||
SMArT is a web configuration tool for MARS_NWE, a Novell NetWare 3.x emulator for Linux and FreeBSD.
|
||||
8
apply.pl
8
apply.pl
@@ -138,17 +138,17 @@ sub handle_request()
|
||||
if( $p{mars_config} ne $mars_config )
|
||||
{
|
||||
# Just append the line. Messy but easy. ;)
|
||||
open( FILE, '>>' . $base . 'smart.conf' );
|
||||
open( FILE, '>>' . $smart_conf_path ) or die "Could not open $smart_conf_path: $!";
|
||||
print( FILE "\n" . '$mars_config = \'' . $p{mars_config} . '\';' . "\n" );
|
||||
close( FILE );
|
||||
}
|
||||
|
||||
open( FILE, '>' . $base . '.nwclient' );
|
||||
open( FILE, '>' . $smart_nwclient_path ) or die "Could not open $smart_nwclient_path: $!";
|
||||
print( FILE $p{bind_server} . '/' . $p{bind_user} . ' ' . $p{bind_pass } . "\n" );
|
||||
close( FILE );
|
||||
|
||||
chown( scalar( getpwnam( $nonroot_user ) ), 0, $base . '.nwclient' );
|
||||
chmod( 0600, $base . '.nwclient' );
|
||||
chown( scalar( getpwnam( $nonroot_user ) ), 0, $smart_nwclient_path );
|
||||
chmod( 0600, $smart_nwclient_path );
|
||||
}
|
||||
elsif( $c[1] eq 'users' )
|
||||
{
|
||||
|
||||
BIN
check_login
BIN
check_login
Binary file not shown.
@@ -23,6 +23,8 @@
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr);
|
||||
|
||||
|
||||
25
config.h.cmake
Normal file
25
config.h.cmake
Normal file
@@ -0,0 +1,25 @@
|
||||
#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 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_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 NW_BACKLOG 64
|
||||
#define NW_BUF_SZ 16384
|
||||
|
||||
#endif
|
||||
854
nwwebui.c
Normal file
854
nwwebui.c
Normal file
@@ -0,0 +1,854 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
typedef struct {
|
||||
char bind_ip[64];
|
||||
|
||||
int ssl_enable;
|
||||
int http_port;
|
||||
int https_port;
|
||||
|
||||
char cert_file[512];
|
||||
char key_file[512];
|
||||
|
||||
char smart_conf[512];
|
||||
char smart_perl_path[512];
|
||||
} nw_config_t;
|
||||
|
||||
static FILE *g_log_fp = NULL;
|
||||
static int g_log_level = LOG_LEVEL_DEFAULT;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Logging */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
static void log_open(void) {
|
||||
if (!g_log_fp) {
|
||||
g_log_fp = fopen(LOG_PATH_DEFAULT, "a");
|
||||
if (!g_log_fp) {
|
||||
g_log_fp = stderr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void log_msg(int level, const char *fmt, ...) {
|
||||
if (level > g_log_level) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_open();
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm tm_now;
|
||||
localtime_r(&now, &tm_now);
|
||||
|
||||
char tbuf[64];
|
||||
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) {
|
||||
lvl = "DEBUG";
|
||||
}
|
||||
|
||||
fprintf(g_log_fp, "[%s] [%s] ", tbuf, lvl);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(g_log_fp, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fputc('\n', g_log_fp);
|
||||
fflush(g_log_fp);
|
||||
}
|
||||
|
||||
static void die(const char *msg) {
|
||||
log_msg(LOG_LEVEL_ERROR, "%s: %s", msg, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void ssl_die(const char *msg) {
|
||||
log_msg(LOG_LEVEL_ERROR, "%s", msg);
|
||||
ERR_print_errors_fp(g_log_fp ? g_log_fp : stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Helpers */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
static void trim(char *s) {
|
||||
char *p = s;
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
if (p != s) {
|
||||
memmove(s, p, strlen(p) + 1);
|
||||
}
|
||||
|
||||
size_t len = strlen(s);
|
||||
while (len > 0 && isspace((unsigned char)s[len - 1])) {
|
||||
s[len - 1] = '\0';
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
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] == '"')) {
|
||||
memmove(s, s + 1, len - 2);
|
||||
s[len - 2] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
if (*p != '$') {
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
|
||||
size_t ki = 0;
|
||||
while (*p && (isalnum((unsigned char)*p) || *p == '_')) {
|
||||
if (ki + 1 < ksz) {
|
||||
key[ki++] = *p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
key[ki] = '\0';
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
if (*p != '=') {
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
|
||||
size_t vi = 0;
|
||||
while (*p && *p != ';' && *p != '\n' && *p != '\r') {
|
||||
if (vi + 1 < vsz) {
|
||||
val[vi++] = *p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
val[vi] = '\0';
|
||||
|
||||
trim(key);
|
||||
trim(val);
|
||||
strip_quotes(val);
|
||||
|
||||
return key[0] != '\0';
|
||||
}
|
||||
|
||||
static int file_exists_and_executable(const char *path) {
|
||||
return access(path, X_OK) == 0;
|
||||
}
|
||||
|
||||
static int set_nonblocking(int fd) {
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* smart.conf loading */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
static void init_defaults(nw_config_t *cfg, const char *smart_conf_path) {
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
|
||||
snprintf(cfg->bind_ip, sizeof(cfg->bind_ip), "%s", DEFAULT_BIND_IP);
|
||||
|
||||
cfg->ssl_enable = DEFAULT_SSL_ENABLE;
|
||||
cfg->http_port = DEFAULT_HTTP_PORT;
|
||||
cfg->https_port = DEFAULT_HTTPS_PORT;
|
||||
|
||||
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->smart_perl_path, sizeof(cfg->smart_perl_path), "%s", DEFAULT_SMART_PERL);
|
||||
snprintf(cfg->smart_conf, sizeof(cfg->smart_conf), "%s", smart_conf_path);
|
||||
}
|
||||
|
||||
static void load_smart_conf(nw_config_t *cfg) {
|
||||
FILE *fp = fopen(cfg->smart_conf, "r");
|
||||
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];
|
||||
|
||||
if (!parse_perl_assignment(line, key, sizeof(key), val, sizeof(val))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(key, "nw_bind_ip") == 0) {
|
||||
snprintf(cfg->bind_ip, sizeof(cfg->bind_ip), "%s", val);
|
||||
} else if (strcmp(key, "nw_log_level") == 0) {
|
||||
g_log_level = atoi(val);
|
||||
} else if (strcmp(key, "nw_ssl_enable") == 0) {
|
||||
cfg->ssl_enable = atoi(val);
|
||||
} else if (strcmp(key, "nw_http_port") == 0) {
|
||||
cfg->http_port = atoi(val);
|
||||
} else if (strcmp(key, "nw_https_port") == 0) {
|
||||
cfg->https_port = atoi(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) {
|
||||
snprintf(cfg->key_file, sizeof(cfg->key_file), "%s", val);
|
||||
} else if (strcmp(key, "smart_perl_path") == 0) {
|
||||
snprintf(cfg->smart_perl_path, sizeof(cfg->smart_perl_path), "%s", val);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (cfg->http_port < 0) {
|
||||
cfg->http_port = DEFAULT_HTTP_PORT;
|
||||
}
|
||||
if (cfg->https_port < 0) {
|
||||
cfg->https_port = DEFAULT_HTTPS_PORT;
|
||||
}
|
||||
|
||||
if (g_log_level < LOG_LEVEL_ERROR) {
|
||||
g_log_level = LOG_LEVEL_DEFAULT;
|
||||
}
|
||||
if (g_log_level > LOG_LEVEL_DEBUG) {
|
||||
g_log_level = LOG_LEVEL_DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Listener / socket helpers */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
static int create_listener(const char *bind_ip, int port) {
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
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);
|
||||
|
||||
if (strcmp(bind_ip, "0.0.0.0") == 0) {
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
} else {
|
||||
if (inet_pton(AF_INET, bind_ip, &addr.sin_addr) != 1) {
|
||||
close(fd);
|
||||
log_msg(LOG_LEVEL_ERROR, "Invalid bind IP: %s", bind_ip);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
close(fd);
|
||||
die("bind");
|
||||
}
|
||||
|
||||
if (listen(fd, NW_BACKLOG) < 0) {
|
||||
close(fd);
|
||||
die("listen");
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static ssize_t write_all_fd(int fd, const unsigned char *buf, size_t len) {
|
||||
size_t off = 0;
|
||||
|
||||
while (off < len) {
|
||||
ssize_t n = write(fd, buf + off, len - off);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
continue;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
off += (size_t)n;
|
||||
}
|
||||
|
||||
return (ssize_t)off;
|
||||
}
|
||||
|
||||
static ssize_t send_all_ssl(SSL *ssl, const unsigned char *buf, size_t len) {
|
||||
size_t off = 0;
|
||||
|
||||
while (off < len) {
|
||||
int n = SSL_write(ssl, buf + off, (int)(len - off));
|
||||
if (n <= 0) {
|
||||
int err = SSL_get_error(ssl, n);
|
||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
|
||||
continue;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
off += (size_t)n;
|
||||
}
|
||||
|
||||
return (ssize_t)off;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Perl launcher */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
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 };
|
||||
|
||||
if (pipe(inpipe) < 0) {
|
||||
die("pipe stdin");
|
||||
}
|
||||
|
||||
if (pipe(outpipe) < 0) {
|
||||
close(inpipe[0]);
|
||||
close(inpipe[1]);
|
||||
die("pipe stdout");
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
close(inpipe[0]);
|
||||
close(inpipe[1]);
|
||||
close(outpipe[0]);
|
||||
close(outpipe[1]);
|
||||
die("fork");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (dup2(outpipe[1], STDOUT_FILENO) < 0) {
|
||||
perror("dup2 stdout");
|
||||
_exit(127);
|
||||
}
|
||||
if (dup2(outpipe[1], STDERR_FILENO) < 0) {
|
||||
perror("dup2 stderr");
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
close(inpipe[0]);
|
||||
close(inpipe[1]);
|
||||
close(outpipe[0]);
|
||||
close(outpipe[1]);
|
||||
|
||||
execl(cfg->smart_perl_path, cfg->smart_perl_path, (char *)NULL);
|
||||
|
||||
perror("execl smart_perl_path");
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
close(inpipe[0]);
|
||||
close(outpipe[1]);
|
||||
|
||||
*child_stdin_fd = inpipe[1];
|
||||
*child_stdout_fd = outpipe[0];
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Shared child cleanup */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
static void cleanup_child_process(int *child_stdin_fd, int *child_stdout_fd, pid_t *child_pid) {
|
||||
if (*child_stdin_fd >= 0) {
|
||||
close(*child_stdin_fd);
|
||||
*child_stdin_fd = -1;
|
||||
}
|
||||
|
||||
if (*child_stdout_fd >= 0) {
|
||||
close(*child_stdout_fd);
|
||||
*child_stdout_fd = -1;
|
||||
}
|
||||
|
||||
if (*child_pid > 0) {
|
||||
int status = 0;
|
||||
pid_t w = waitpid(*child_pid, &status, WNOHANG);
|
||||
|
||||
if (w == 0) {
|
||||
kill(*child_pid, SIGTERM);
|
||||
waitpid(*child_pid, &status, 0);
|
||||
} else if (w < 0 && errno != ECHILD) {
|
||||
log_msg(LOG_LEVEL_ERROR, "waitpid failed for smart child: %s", strerror(errno));
|
||||
}
|
||||
|
||||
*child_pid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Plain HTTP handler */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
static void handle_connection_plain(int client_fd, const nw_config_t *cfg) {
|
||||
int child_stdin_fd = -1;
|
||||
int child_stdout_fd = -1;
|
||||
pid_t child_pid = -1;
|
||||
unsigned char buf[NW_BUF_SZ];
|
||||
|
||||
child_pid = spawn_smart_perl(cfg, &child_stdin_fd, &child_stdout_fd);
|
||||
|
||||
if (set_nonblocking(client_fd) < 0) {
|
||||
log_msg(LOG_LEVEL_ERROR, "Could not set client socket non-blocking");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (set_nonblocking(child_stdout_fd) < 0) {
|
||||
log_msg(LOG_LEVEL_ERROR, "Could not set child stdout non-blocking");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
struct pollfd pfds[2];
|
||||
pfds[0].fd = client_fd;
|
||||
pfds[0].events = POLLIN | POLLHUP | POLLERR;
|
||||
pfds[0].revents = 0;
|
||||
|
||||
pfds[1].fd = child_stdout_fd;
|
||||
pfds[1].events = POLLIN | POLLHUP | POLLERR;
|
||||
pfds[1].revents = 0;
|
||||
|
||||
int pr = poll(pfds, 2, -1);
|
||||
if (pr < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
log_msg(LOG_LEVEL_ERROR, "poll failed in plain handler: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Client -> Perl */
|
||||
if (pfds[0].revents & (POLLIN | POLLHUP | POLLERR)) {
|
||||
ssize_t n = read(client_fd, buf, sizeof(buf));
|
||||
if (n > 0) {
|
||||
if (write_all_fd(child_stdin_fd, buf, (size_t)n) < 0) {
|
||||
log_msg(LOG_LEVEL_ERROR, "Write to Perl stdin failed");
|
||||
break;
|
||||
}
|
||||
} else if (n == 0) {
|
||||
break;
|
||||
} else if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
log_msg(LOG_LEVEL_ERROR, "Read from HTTP client failed: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perl -> Client */
|
||||
if (pfds[1].revents & (POLLIN | POLLHUP | POLLERR)) {
|
||||
ssize_t rn = read(child_stdout_fd, buf, sizeof(buf));
|
||||
if (rn > 0) {
|
||||
if (write_all_fd(client_fd, buf, (size_t)rn) < 0) {
|
||||
log_msg(LOG_LEVEL_ERROR, "Write to HTTP client failed");
|
||||
break;
|
||||
}
|
||||
} else if (rn == 0) {
|
||||
break;
|
||||
} else if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
log_msg(LOG_LEVEL_ERROR, "Read from Perl stdout failed: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (child_pid > 0) {
|
||||
int status = 0;
|
||||
pid_t w = waitpid(child_pid, &status, WNOHANG);
|
||||
if (w == child_pid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
cleanup_child_process(&child_stdin_fd, &child_stdout_fd, &child_pid);
|
||||
close(client_fd);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* TLS handler */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
static void handle_connection_tls(SSL_CTX *ctx, int client_fd, const nw_config_t *cfg) {
|
||||
SSL *ssl = NULL;
|
||||
int child_stdin_fd = -1;
|
||||
int child_stdout_fd = -1;
|
||||
pid_t child_pid = -1;
|
||||
unsigned char buf[NW_BUF_SZ];
|
||||
|
||||
if (set_nonblocking(client_fd) < 0) {
|
||||
log_msg(LOG_LEVEL_ERROR, "Could not set client socket non-blocking");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ssl = SSL_new(ctx);
|
||||
if (!ssl) {
|
||||
log_msg(LOG_LEVEL_ERROR, "SSL_new failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
SSL_set_fd(ssl, client_fd);
|
||||
|
||||
if (SSL_accept(ssl) <= 0) {
|
||||
log_msg(LOG_LEVEL_ERROR, "SSL_accept failed");
|
||||
ERR_print_errors_fp(g_log_fp ? g_log_fp : stderr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
log_msg(LOG_LEVEL_INFO, "Accepted new TLS connection");
|
||||
|
||||
child_pid = spawn_smart_perl(cfg, &child_stdin_fd, &child_stdout_fd);
|
||||
|
||||
if (set_nonblocking(child_stdout_fd) < 0) {
|
||||
log_msg(LOG_LEVEL_ERROR, "Could not set child stdout non-blocking");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
struct pollfd pfds[2];
|
||||
pfds[0].fd = client_fd;
|
||||
pfds[0].events = POLLIN | POLLHUP | POLLERR;
|
||||
pfds[0].revents = 0;
|
||||
|
||||
pfds[1].fd = child_stdout_fd;
|
||||
pfds[1].events = POLLIN | POLLHUP | POLLERR;
|
||||
pfds[1].revents = 0;
|
||||
|
||||
int pr = poll(pfds, 2, -1);
|
||||
if (pr < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
log_msg(LOG_LEVEL_ERROR, "poll failed in TLS handler: %s", strerror(errno));
|
||||
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) {
|
||||
if (write_all_fd(child_stdin_fd, buf, (size_t)n) < 0) {
|
||||
log_msg(LOG_LEVEL_ERROR, "Write to Perl stdin failed");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int err = SSL_get_error(ssl, n);
|
||||
if (err == SSL_ERROR_ZERO_RETURN) {
|
||||
break;
|
||||
}
|
||||
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
|
||||
log_msg(LOG_LEVEL_ERROR, "SSL_read failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perl -> TLS client */
|
||||
if (pfds[1].revents & (POLLIN | POLLHUP | POLLERR)) {
|
||||
ssize_t rn = read(child_stdout_fd, buf, sizeof(buf));
|
||||
if (rn > 0) {
|
||||
if (send_all_ssl(ssl, buf, (size_t)rn) < 0) {
|
||||
log_msg(LOG_LEVEL_ERROR, "SSL_write failed");
|
||||
break;
|
||||
}
|
||||
} else if (rn == 0) {
|
||||
break;
|
||||
} else if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
log_msg(LOG_LEVEL_ERROR, "Read from Perl stdout failed: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (child_pid > 0) {
|
||||
int status = 0;
|
||||
pid_t w = waitpid(child_pid, &status, WNOHANG);
|
||||
if (w == child_pid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
cleanup_child_process(&child_stdin_fd, &child_stdout_fd, &child_pid);
|
||||
|
||||
if (ssl) {
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
}
|
||||
|
||||
close(client_fd);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* SIGCHLD handler */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
static void reap_children(int sig) {
|
||||
(void)sig;
|
||||
|
||||
for (;;) {
|
||||
pid_t pid = waitpid(-1, NULL, WNOHANG);
|
||||
if (pid <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Main */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *smart_conf_path = DEFAULT_SMART_CONF;
|
||||
int http_fd = -1;
|
||||
int https_fd = -1;
|
||||
SSL_CTX *ctx = NULL;
|
||||
|
||||
if (argc > 1) {
|
||||
smart_conf_path = argv[1];
|
||||
}
|
||||
|
||||
{
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = reap_children;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
||||
|
||||
if (sigaction(SIGCHLD, &sa, NULL) < 0) {
|
||||
die("sigaction(SIGCHLD)");
|
||||
}
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
log_open();
|
||||
log_msg(LOG_LEVEL_INFO, "nwwebui starting");
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 (https_fd >= 0) {
|
||||
pfds[nfds].fd = https_fd;
|
||||
pfds[nfds].events = POLLIN;
|
||||
pfds[nfds].revents = 0;
|
||||
nfds++;
|
||||
}
|
||||
|
||||
int pr = poll(pfds, nfds, -1);
|
||||
if (pr < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
log_msg(LOG_LEVEL_ERROR, "poll failed: %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nfds; i++) {
|
||||
if (!(pfds[i].revents & POLLIN)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct sockaddr_in peer;
|
||||
socklen_t peerlen = sizeof(peer);
|
||||
int 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_t 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (http_fd >= 0) {
|
||||
close(http_fd);
|
||||
}
|
||||
if (https_fd >= 0) {
|
||||
close(https_fd);
|
||||
}
|
||||
if (ctx) {
|
||||
SSL_CTX_free(ctx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
335
readconfig.pl
335
readconfig.pl
@@ -1,28 +1,12 @@
|
||||
#
|
||||
# SMArT
|
||||
#
|
||||
#
|
||||
# Configuration file code
|
||||
#
|
||||
#
|
||||
# Copyright 2001 Wilmer van der Gaast (lintux@lintux.cx)
|
||||
#
|
||||
#
|
||||
# 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 2 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, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
#
|
||||
# Updated for marker-aware config writing.
|
||||
|
||||
my( @info, @conf, $l );
|
||||
my( @info, @conf, @rawconf, $l );
|
||||
|
||||
$info[1] = 'Volume';
|
||||
$info[2] = 'Server name';
|
||||
@@ -53,6 +37,7 @@ $info[47] = 'Trustee files';
|
||||
$info[50] = 'Conversion tables';
|
||||
$info[60] = 'MAX_CONNECTIONS';
|
||||
$info[61] = 'MAX_NW_VOLS';
|
||||
$info[62] = 'Reserved';
|
||||
$info[63] = 'MAX_DIR_BASE_ENTRIES';
|
||||
$info[68] = 'USE_MMAP';
|
||||
$info[69] = 'HANDLE_ALL_SAP_TYPS';
|
||||
@@ -79,19 +64,25 @@ $info[400] = 'nwserv.stations file';
|
||||
$info[401] = 'Reply to nearest server requests';
|
||||
$info[402] = 'Reply to connect requests';
|
||||
|
||||
open( CONF, '<' . $mars_config );
|
||||
open( CONF, '<' . $mars_config ) or die "Could not open $mars_config: $!";
|
||||
@conf = ();
|
||||
@rawconf = ();
|
||||
|
||||
while( $l = <CONF> )
|
||||
{
|
||||
$l =~ s/[\r\n]//g;
|
||||
$l =~ s/[\t ]+/ /g;
|
||||
$l =~ s/#.*//;
|
||||
$l =~ s/^[\t ]*//;
|
||||
$l =~ s/[\t ]*$//;
|
||||
if( $l ne '' )
|
||||
{
|
||||
unshift( @conf, $l );
|
||||
}
|
||||
push( @rawconf, $l );
|
||||
|
||||
my $x = $l;
|
||||
$x =~ s/[\r\n]//g;
|
||||
$x =~ s/[\t ]+/ /g;
|
||||
$x =~ s/#.*//;
|
||||
$x =~ s/^[\t ]*//;
|
||||
$x =~ s/[\t ]*$//;
|
||||
|
||||
if( $x ne '' )
|
||||
{
|
||||
push( @conf, $x );
|
||||
}
|
||||
}
|
||||
close( CONF );
|
||||
|
||||
@@ -99,53 +90,279 @@ sortconfig();
|
||||
|
||||
sub sortconfig()
|
||||
{
|
||||
@conf = sort( { $a cmp $b } @conf );
|
||||
@conf = sort( { ( split( ' ', $a ) )[0] <=> ( split( ' ', $b ) )[0] } @conf );
|
||||
@conf = sort( { $a cmp $b } @conf );
|
||||
@conf = sort( { ( split( ' ', $a ) )[0] <=> ( split( ' ', $b ) )[0] } @conf );
|
||||
}
|
||||
|
||||
sub getconfigline( $ )
|
||||
{
|
||||
my( @c, $c );
|
||||
|
||||
@c = getconfig( @_ );
|
||||
$c = $c[0];
|
||||
$c =~ s/^[0-9]* //;
|
||||
return( $c );
|
||||
my( @c, $c );
|
||||
|
||||
@c = getconfig( @_ );
|
||||
$c = $c[0];
|
||||
$c =~ s/^[0-9]* //;
|
||||
return( $c );
|
||||
}
|
||||
|
||||
sub getconfig( $ )
|
||||
{
|
||||
my( @c );
|
||||
|
||||
@c = grep( /^$_[0] /i, @conf );
|
||||
return( @c );
|
||||
my( @c );
|
||||
|
||||
@c = grep( /^$_[0] /i, @conf );
|
||||
return( @c );
|
||||
}
|
||||
|
||||
sub addconfigline( $ )
|
||||
{
|
||||
unshift( @conf, $_[0] );
|
||||
unshift( @conf, $_[0] );
|
||||
}
|
||||
|
||||
sub delconfigline( $ )
|
||||
{
|
||||
@conf = grep( !/^$_[0] /i, grep( !/^$_[0]$/i, @conf ) );
|
||||
@conf = grep( !/^$_[0] /i, grep( !/^$_[0]$/i, @conf ) );
|
||||
}
|
||||
|
||||
sub normalize_line( $ )
|
||||
{
|
||||
my $x = $_[0];
|
||||
|
||||
$x =~ s/[\r\n]//g;
|
||||
$x =~ s/[\t ]+/ /g;
|
||||
$x =~ s/#.*//;
|
||||
$x =~ s/^[\t ]*//;
|
||||
$x =~ s/[\t ]*$//;
|
||||
|
||||
return( $x );
|
||||
}
|
||||
|
||||
sub section_of_line( $ )
|
||||
{
|
||||
my $x = normalize_line( $_[0] );
|
||||
|
||||
if( $x =~ /^([0-9]+)\b/ )
|
||||
{
|
||||
return( $1 );
|
||||
}
|
||||
|
||||
return( '' );
|
||||
}
|
||||
|
||||
sub grouped_section_key( $ )
|
||||
{
|
||||
my $sec = $_[0];
|
||||
|
||||
if( $sec >= 100 && $sec <= 106 ) { return '100-106'; }
|
||||
if( $sec >= 200 && $sec <= 202 ) { return '200-202'; }
|
||||
if( $sec >= 210 && $sec <= 211 ) { return '210-211'; }
|
||||
if( $sec >= 300 && $sec <= 302 ) { return '300-302'; }
|
||||
|
||||
return $sec;
|
||||
}
|
||||
|
||||
sub build_marker_map()
|
||||
{
|
||||
my( %map, $line, $sec, $key );
|
||||
|
||||
foreach $line ( @conf )
|
||||
{
|
||||
$sec = section_of_line( $line );
|
||||
next if $sec eq '';
|
||||
|
||||
$key = grouped_section_key( $sec );
|
||||
push( @{ $map{$key} }, $line );
|
||||
}
|
||||
|
||||
# Keep SYS as first entry in section 1
|
||||
if( defined( $map{'1'} ) )
|
||||
{
|
||||
my @sys = grep( /^1 SYS /, @{ $map{'1'} } );
|
||||
my @rest = grep( !/^1 SYS /, @{ $map{'1'} } );
|
||||
$map{'1'} = [ @sys, @rest ];
|
||||
}
|
||||
|
||||
return %map;
|
||||
}
|
||||
|
||||
sub writeconfig_compact()
|
||||
{
|
||||
my( $i, $l );
|
||||
|
||||
sortconfig();
|
||||
|
||||
open( CONF, '>' . $mars_config ) or die "Could not write $mars_config: $!";
|
||||
|
||||
$l = ( getconfig( '1 SYS' ) )[0];
|
||||
delconfigline( '1 SYS' );
|
||||
addconfigline( $l );
|
||||
|
||||
foreach $i ( @conf )
|
||||
{
|
||||
$l = $i;
|
||||
$l =~ s/ .*//;
|
||||
printf( CONF "%-50s # %s\n", $i, $info[$l] );
|
||||
}
|
||||
|
||||
close( CONF );
|
||||
}
|
||||
|
||||
sub writeconfig_markers()
|
||||
{
|
||||
my( %secmap, %emitted );
|
||||
my( $line, $active_key, $inside_active );
|
||||
|
||||
%secmap = build_marker_map();
|
||||
$inside_active = '';
|
||||
|
||||
open( CONF, '>' . $mars_config ) or die "Could not write $mars_config: $!";
|
||||
|
||||
foreach $line ( @rawconf )
|
||||
{
|
||||
if( $line =~ /^\s*#\s*>>>\s*SMARTHOOK\s+SECTION\s+([0-9]+(?:-[0-9]+)?)\s+ACTIVE\s+BEGIN/i )
|
||||
{
|
||||
$active_key = $1;
|
||||
$inside_active = $active_key;
|
||||
|
||||
print CONF $line;
|
||||
|
||||
if( defined( $secmap{$active_key} ) )
|
||||
{
|
||||
foreach my $entry ( @{ $secmap{$active_key} } )
|
||||
{
|
||||
print CONF $entry . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
$emitted{$active_key} = 1;
|
||||
next;
|
||||
}
|
||||
|
||||
if( $line =~ /^\s*#\s*<<<\s*SMARTHOOK\s+SECTION\s+([0-9]+(?:-[0-9]+)?)\s+ACTIVE\s+END/i )
|
||||
{
|
||||
$inside_active = '';
|
||||
print CONF $line;
|
||||
next;
|
||||
}
|
||||
|
||||
if( $inside_active ne '' )
|
||||
{
|
||||
# Skip old content inside ACTIVE blocks completely.
|
||||
next;
|
||||
}
|
||||
|
||||
print CONF $line;
|
||||
}
|
||||
|
||||
close( CONF );
|
||||
}
|
||||
|
||||
sub writeconfig_preserve_layout()
|
||||
{
|
||||
my( %secmap, %written, @sections, $sec, $line, $sysline );
|
||||
my( $heading_sec );
|
||||
|
||||
sortconfig();
|
||||
|
||||
$sysline = ( grep( /^1 SYS /, @conf ) )[0];
|
||||
if( defined( $sysline ) )
|
||||
{
|
||||
@conf = grep( $_ ne $sysline, @conf );
|
||||
unshift( @conf, $sysline );
|
||||
}
|
||||
|
||||
foreach $line ( @conf )
|
||||
{
|
||||
$sec = section_of_line( $line );
|
||||
if( $sec ne '' )
|
||||
{
|
||||
push( @{ $secmap{$sec} }, $line );
|
||||
}
|
||||
}
|
||||
|
||||
open( CONF, '>' . $mars_config ) or die "Could not write $mars_config: $!";
|
||||
|
||||
foreach $line ( @rawconf )
|
||||
{
|
||||
if( $line =~ /^\s*#.*Section\s+([0-9]+)\b/i )
|
||||
{
|
||||
$heading_sec = $1;
|
||||
|
||||
foreach $sec ( sort { $a <=> $b } keys( %secmap ) )
|
||||
{
|
||||
next if $written{$sec};
|
||||
next if $sec > $heading_sec;
|
||||
|
||||
if( defined( $secmap{$sec} ) )
|
||||
{
|
||||
foreach my $entry ( @{ $secmap{$sec} } )
|
||||
{
|
||||
print CONF $entry . "\n";
|
||||
}
|
||||
}
|
||||
$written{$sec} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$sec = section_of_line( $line );
|
||||
|
||||
if( $sec eq '' )
|
||||
{
|
||||
print CONF $line;
|
||||
next;
|
||||
}
|
||||
|
||||
if( ! $written{$sec} )
|
||||
{
|
||||
if( defined( $secmap{$sec} ) )
|
||||
{
|
||||
foreach my $entry ( @{ $secmap{$sec} } )
|
||||
{
|
||||
print CONF $entry . "\n";
|
||||
}
|
||||
}
|
||||
$written{$sec} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@sections = sort { $a <=> $b } keys( %secmap );
|
||||
foreach $sec ( @sections )
|
||||
{
|
||||
next if $written{$sec};
|
||||
|
||||
print CONF "\n";
|
||||
foreach my $entry ( @{ $secmap{$sec} } )
|
||||
{
|
||||
print CONF $entry . "\n";
|
||||
}
|
||||
$written{$sec} = 1;
|
||||
}
|
||||
|
||||
close( CONF );
|
||||
}
|
||||
|
||||
sub config_has_smart_markers()
|
||||
{
|
||||
foreach my $line ( @rawconf )
|
||||
{
|
||||
if( $line =~ /SMARTHOOK\s+SECTION/i )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub writeconfig()
|
||||
{
|
||||
my( $i, $l );
|
||||
|
||||
sortconfig();
|
||||
|
||||
open( CONF, '>' . $mars_config );
|
||||
$l = ( getconfig( '1 SYS' ) )[0];
|
||||
delconfigline( '1 SYS' );
|
||||
addconfigline( $l );
|
||||
foreach $i ( @conf )
|
||||
{
|
||||
$l = $i;
|
||||
$l =~ s/ .*//;
|
||||
printf( CONF "%-50s # %s\n", $i, $info[$l] );
|
||||
}
|
||||
close( CONF );
|
||||
if( defined( $smart_compact_nwservconf ) && $smart_compact_nwservconf )
|
||||
{
|
||||
writeconfig_compact();
|
||||
}
|
||||
elsif( config_has_smart_markers() )
|
||||
{
|
||||
writeconfig_markers();
|
||||
}
|
||||
else
|
||||
{
|
||||
writeconfig_preserve_layout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1167,7 +1167,7 @@ EOF
|
||||
}
|
||||
elsif( $c[1] eq 'smart' )
|
||||
{
|
||||
open( FILE, '<' . $base . '.nwclient' );
|
||||
open( FILE, '<' . $smart_nwclient_path ) or die "Could not open $smart_nwclient_path: $!";
|
||||
$x = <FILE>;
|
||||
close( FILE );
|
||||
|
||||
|
||||
@@ -25,16 +25,18 @@
|
||||
|
||||
$redirected = 0;
|
||||
|
||||
$server_id = 'Server: SMArT/Perl/0.99';
|
||||
$base = $0;
|
||||
$base =~ s/\/[^\/]*$//g;
|
||||
$base .= '/';
|
||||
$server_id = 'Server: SMArT/Perl/@MARS_NWE_VERSION@';
|
||||
|
||||
do( $base . 'smart.conf' );
|
||||
do( '@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf' )
|
||||
or die "Could not load @MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf: $@ $!";
|
||||
|
||||
close( STDERR );
|
||||
open( STDERR, '>>' . $base . 'error.log' );
|
||||
$ENV{HOME} = $base;
|
||||
open( STDERR, '>>' . $smart_log_path )
|
||||
or die "Could not open $smart_log_path: $!";
|
||||
|
||||
$ENV{HOME} = '@MARS_NWE_INSTALL_FULL_CONFDIR@';
|
||||
$smart_libexec_dir = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@';
|
||||
$smart_libexec_dir =~ s#/*$##;
|
||||
|
||||
$l = <STDIN>;
|
||||
$l =~ s/[\n\r]//g;
|
||||
@@ -75,7 +77,7 @@ else
|
||||
if( $l[0] ne 'root' )
|
||||
{ error( 401 ); }
|
||||
else
|
||||
{ if( $x = system( $base . 'check_login', @l ) )
|
||||
{ if( $x = system( $smart_check_login, @l ) )
|
||||
{ error( 401 ); } }
|
||||
}
|
||||
}
|
||||
@@ -104,14 +106,14 @@ foreach $p ( @p )
|
||||
|
||||
if( $c[0] eq 'apply' )
|
||||
{
|
||||
do( $base . 'readconfig.pl' );
|
||||
do( $base . 'apply.pl' );
|
||||
do( $smart_libexec_dir . '/readconfig.pl' );
|
||||
do( $smart_libexec_dir . '/apply.pl' );
|
||||
handle_request();
|
||||
exit;
|
||||
}
|
||||
elsif( $c[0] eq 'settings' )
|
||||
{
|
||||
do( $base . 'readconfig.pl' );
|
||||
do( $smart_libexec_dir . '/readconfig.pl' );
|
||||
}
|
||||
|
||||
drop_root();
|
||||
@@ -137,11 +139,11 @@ EOF
|
||||
}
|
||||
elsif( $c[0] eq 'static' )
|
||||
{
|
||||
do( $base . 'static.pl' );
|
||||
do( $smart_libexec_dir . '/static.pl' );
|
||||
}
|
||||
elsif( $c[0] eq 'settings' )
|
||||
{
|
||||
do( $base . 'settings.pl' );
|
||||
do( $smart_libexec_dir . '/settings.pl' );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -239,7 +241,7 @@ EOF
|
||||
|
||||
sub get_server
|
||||
{
|
||||
open( SFILE, '<' . $base . '.nwclient' );
|
||||
open( SFILE, '<' . $smart_nwclient_path );
|
||||
chomp( $line = <SFILE> );
|
||||
close( SFILE );
|
||||
|
||||
@@ -249,7 +251,7 @@ sub get_server
|
||||
|
||||
sub get_bindery_password
|
||||
{
|
||||
open( SFILE, '<' . $base . '.nwclient' );
|
||||
open( SFILE, '<' . $smart_nwclient_path );
|
||||
chomp( $line = <SFILE> );
|
||||
close( SFILE );
|
||||
|
||||
11
smart.conf
11
smart.conf
@@ -1,11 +0,0 @@
|
||||
$COLOR_BACK = "#F0F0FF";
|
||||
$COLOR_HEAD_BACK = "#C0C0FF";
|
||||
$COLOR_HEAD_FORE = "#000000";
|
||||
$COLOR_SUBH_BACK = "#D0D0FF";
|
||||
$COLOR_SUBH_FORE = "#000000";
|
||||
$COLOR_TEXT_BACK = "#E0E0FF";
|
||||
$COLOR_TEXT_FORE = "#000000";
|
||||
|
||||
$mars_config = '/usr/local/etc/nwserv.conf';
|
||||
|
||||
$nonroot_user = 'nobody';
|
||||
110
smart.conf.cmake
Normal file
110
smart.conf.cmake
Normal file
@@ -0,0 +1,110 @@
|
||||
# SMArT / nwwebui configuration file
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# UI colors
|
||||
# ------------------------------------------------------------
|
||||
|
||||
# Background color used for the main page body.
|
||||
$COLOR_BACK = "#F0F0FF";
|
||||
|
||||
# Background color used for section headers.
|
||||
$COLOR_HEAD_BACK = "#C0C0FF";
|
||||
|
||||
# Text color used for section headers.
|
||||
$COLOR_HEAD_FORE = "#000000";
|
||||
|
||||
# Background color used for subsection headers.
|
||||
$COLOR_SUBH_BACK = "#D0D0FF";
|
||||
|
||||
# Text color used for subsection headers.
|
||||
$COLOR_SUBH_FORE = "#000000";
|
||||
|
||||
# Background color used for normal content rows.
|
||||
$COLOR_TEXT_BACK = "#E0E0FF";
|
||||
|
||||
# Text color used for normal content rows.
|
||||
$COLOR_TEXT_FORE = "#000000";
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Main MARS NWE configuration
|
||||
# ------------------------------------------------------------
|
||||
|
||||
# Path to the main mars_nwe server configuration file.
|
||||
# This file is read and modified by the SMArT configuration pages.
|
||||
$mars_config = '@MARS_NWE_INSTALL_FULL_CONFDIR@/nwserv.conf';
|
||||
|
||||
# User name used when SMArT drops privileges for non-root operations.
|
||||
# Keep this set to an unprivileged local account.
|
||||
$nonroot_user = 'nobody';
|
||||
|
||||
# Write the mars_nwe configuration file in compact form.
|
||||
# 0 = preserve comments, blank lines and the original section layout
|
||||
# 1 = write a compact configuration file without the original long comments
|
||||
$smart_compact_nwservconf = 0;
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# SMArT internal file layout
|
||||
# ------------------------------------------------------------
|
||||
|
||||
# Absolute path to the SMArT configuration file itself.
|
||||
# Used when SMArT needs to append updated settings.
|
||||
$smart_conf_path = '@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf';
|
||||
|
||||
# File used to store bindery login information for SMArT helper tools.
|
||||
# This file should remain readable only by the service user or root.
|
||||
$smart_nwclient_path = '@MARS_NWE_INSTALL_FULL_CONFDIR@/.nwclient';
|
||||
|
||||
# Directory containing static HTML and image files served by SMArT.
|
||||
$smart_static_dir = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/static';
|
||||
|
||||
# Log file used by the Perl SMArT frontend.
|
||||
# Keep this separate from the nwwebui log file.
|
||||
$smart_log_path = '@MARS_NWE_LOG_DIR@/smart.log';
|
||||
|
||||
# Path to the PAM-based login helper used for root authentication.
|
||||
$smart_check_login = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/check_login';
|
||||
|
||||
# Optional explicit path to the main SMArT Perl program.
|
||||
# This is normally not required, because nwwebui already has a built-in default.
|
||||
# Uncomment and adjust only if a non-standard location must be used.
|
||||
# $smart_perl_path = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/smart';
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# nwwebui listener settings
|
||||
# ------------------------------------------------------------
|
||||
|
||||
# IP address used for the HTTP and HTTPS listeners.
|
||||
# Use 0.0.0.0 to listen on all IPv4 interfaces.
|
||||
# Use 127.0.0.1 for local-only testing.
|
||||
$nw_bind_ip = '0.0.0.0';
|
||||
|
||||
# Log level used by nwwebui.
|
||||
# 0 = errors only
|
||||
# 1 = informational messages
|
||||
# 2 = debug messages
|
||||
$nw_log_level = 1;
|
||||
|
||||
# Enable or disable TLS/SSL support.
|
||||
# 1 = enable HTTPS listener
|
||||
# 0 = disable HTTPS listener
|
||||
#
|
||||
# When disabled, nwwebui can still serve plain HTTP if nw_http_port > 0.
|
||||
$nw_ssl_enable = 0;
|
||||
|
||||
# Plain HTTP listener port.
|
||||
# Set to 0 to disable plain HTTP completely.
|
||||
# This is useful for local or isolated-network testing.
|
||||
$nw_http_port = 9080;
|
||||
|
||||
# HTTPS listener port.
|
||||
# Used only when $nw_ssl_enable is set to 1.
|
||||
# Set to 0 to disable HTTPS listening.
|
||||
$nw_https_port = 9443;
|
||||
|
||||
# TLS certificate file in PEM format.
|
||||
# Required only when HTTPS is enabled.
|
||||
$nw_cert_file = '@MARS_NWE_INSTALL_FULL_CONFDIR@/server.crt';
|
||||
|
||||
# TLS private key file in PEM format.
|
||||
# Required only when HTTPS is enabled.
|
||||
$nw_key_file = '@MARS_NWE_INSTALL_FULL_CONFDIR@/server.key';
|
||||
Reference in New Issue
Block a user