#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
#include <poll.h>
#include <semaphore.h>
#include "../util/pool.h"
#include "../public/nsapi.h"
#include "../util/plist.h"
#include "../util/date.h"
#include <cx/string.h>
#include "webserver.h"
#include "log.h"
#include "httprequest.h"
#include "httplistener.h"
#include "srvctrl.h"
#include "configmanager.h"
#define LOG_THREAD_STACK_SIZE 32768
#define LOG_THREAD_MAX_POLL_FAILS 10
#define LOG_THREAD_READ_BUF 2048
static int std_out[
2];
static int std_err[
2];
static WSBool is_daemon;
void test() {
time_t t = time(
NULL);
pool_handle_t *pool = pool_create();
cxmutstr date = date_format_http(t, pool);
printf(
"%s\n", date.ptr);
}
WSBool main_is_daemon(
void) {
return is_daemon;
}
void sig_usr1_reload(
int sig) {
log_ereport(
LOG_INFORM,
"sig reload");
(
void)webserver_reconfig();
signal(
SIGUSR1, sig_usr1_reload);
}
void sig_term(
int sig) {
webserver_shutdown();
}
static void set_pipe_nonblocking(
int fd) {
int flags =
0;
flags = fcntl(fd,
F_GETFL,
0);
fcntl(fd,
F_SETFL, flags & ~
O_NONBLOCK);
}
static char log_pipe_readbuf[
LOG_THREAD_READ_BUF];
static char log_pipe_stdout_buf[
LOG_THREAD_READ_BUF];
static char log_pipe_stderr_buf[
LOG_THREAD_READ_BUF];
static size_t log_pipe_stdout_tmp_pos =
0;
static size_t log_pipe_stderr_tmp_pos =
0;
static int daemon_start_pipe[
2];
static int parent_log_pipe[
2];
static LogDup startup_log;
static void finish_daemon_startup(
int status) {
if(status !=
0) {
log_ereport(
LOG_FAILURE,
"daemon startup failed");
if(status >
127) {
status =
127;
}
}
char s = (
char)status;
write(daemon_start_pipe[
1], &s,
1);
close(daemon_start_pipe[
1]);
log_remove_logdup(&startup_log);
close(parent_log_pipe[
0]);
close(parent_log_pipe[
1]);
}
static int log_pipe(
const char *name,
int fd,
char *buf,
size_t *pos) {
ssize_t r = read(fd, log_pipe_readbuf,
2);
if(r <=
0) {
return r ==
0 ?
0 :
1;
}
char *tmp = buf;
int tmplen = *pos;
int s =
0;
for(
int i=
0;i<r;i++) {
if(log_pipe_readbuf[i] ==
'\n') {
if(tmplen + i-s >
0) {
log_message(name,
"%.*s%.*s", tmplen, tmp, i-s, log_pipe_readbuf + s);
}
tmplen =
0;
*pos =
0;
s = i+
1;
}
}
int remaining = r - s;
if(tmplen + remaining >=
LOG_THREAD_READ_BUF) {
log_message(name,
"%.*s%.*s", tmplen, tmp, remaining, log_pipe_readbuf + s);
*pos =
0;
}
else if(remaining >
0) {
memcpy(buf + *pos, log_pipe_readbuf + s, remaining);
*pos += remaining;
}
return 0;
}
void* log_pipe_thread(
void *data) {
set_pipe_nonblocking(std_out[
0]);
set_pipe_nonblocking(std_err[
0]);
struct pollfd fds[
2];
fds[
0].fd = std_out[
0];
fds[
0].events =
POLLIN;
fds[
1].fd = std_err[
0];
fds[
1].events =
POLLIN;
int poll_fails =
0;
for(;;) {
if(poll(fds,
1,
1000000) <
0) {
if(errno ==
EINTR) {
continue;
}
log_ereport(
LOG_FAILURE,
"log thread poll failed: %s", strerror(errno));
if(poll_fails++ >
LOG_THREAD_MAX_POLL_FAILS) {
break;
}
}
if(fds[
0].revents &
POLLIN) {
if(log_pipe(
"stdout", fds[
0].fd, log_pipe_stdout_buf, &log_pipe_stdout_tmp_pos)) {
log_ereport(
LOG_WARN,
"log_pipe stdout failed");
break;
}
}
if(fds[
1].revents &
POLLIN) {
if(log_pipe(
"stderr", fds[
0].fd, log_pipe_stderr_buf, &log_pipe_stderr_tmp_pos)) {
log_ereport(
LOG_WARN,
"log_pipe stderr failed");
break;
}
}
}
log_ereport(
LOG_INFORM,
"log thread end");
return NULL;
}
static int main_daemon_startup_wait(
void) {
char buf[
2048];
ssize_t r;
while((r = read(parent_log_pipe[
0], buf,
2048)) >
0) {
ssize_t pos =
0;
while(r >
0) {
ssize_t w = write(
STDOUT_FILENO, buf+pos, r-pos);
pos += w;
r -= w;
break;
}
}
char ret;
if(read(daemon_start_pipe[
0], &ret,
1) !=
1) {
return 255;
}
return ret;
}
static void startup_log_write(
void *cookie,
char *msg,
size_t length) {
write(parent_log_pipe[
1], msg, length);
}
int main(
int argc,
char **argv) {
is_daemon =
1;
for(
int i=
0;i<argc;i++) {
char *p = argv[i];
if(p[
0] ==
'-' && p[
1] ==
'c') {
is_daemon =
0;
break;
}
}
if(init_logging()) {
fprintf(stderr,
"OOM\n");
return 1;
}
if(is_daemon) {
if(pipe(daemon_start_pipe)) {
perror(
"pipe");
return EXIT_FAILURE;
}
if(pipe(parent_log_pipe)) {
perror(
"pipe");
return EXIT_FAILURE;
}
log_ereport(
LOG_INFORM,
"start daemon");
startup_log.write = startup_log_write;
log_add_logdup(&startup_log);
pid_t pid = fork();
if(pid <
0) {
perror(
"fork");
return EXIT_FAILURE;
}
else if(pid >
0) {
close(daemon_start_pipe[
1]);
close(parent_log_pipe[
1]);
return main_daemon_startup_wait();
}
if(setsid() <
0) {
fprintf(stderr,
"setsid failed\n");
return EXIT_FAILURE;
}
close(daemon_start_pipe[
0]);
if(pipe(std_out) !=
0) {
perror(
"pipe");
return EXIT_FAILURE;
}
if(pipe(std_err) !=
0) {
perror(
"pipe");
return EXIT_FAILURE;
}
for(
int i=
0;i<
3;i++) {
close(i);
}
dup2(std_out[
1],
1);
dup2(std_err[
1],
2);
close(std_out[
1]);
close(std_err[
1]);
pthread_attr_t tattr;
pthread_attr_init(&tattr);
pthread_attr_setstacksize(&tattr,
LOG_THREAD_STACK_SIZE);
pthread_t tid;
pthread_create(&tid, &tattr, log_pipe_thread,
NULL);
}
pool_init(
NULL,
NULL,
NULL);
signal(
SIGUSR1, sig_usr1_reload);
signal(
SIGTERM, sig_term);
signal(
SIGINT, sig_term);
struct sigaction act;
ZERO(&act,
sizeof(
struct sigaction));
act.sa_handler =
SIG_IGN;
sigaction(
SIGPIPE, &act,
NULL);
log_ereport(
LOG_INFORM,
"startup");
int status;
status = webserver_init();
if(status !=
0) {
log_ereport(
LOG_FAILURE,
"cannot initialize server.");
return EXIT_FAILURE;
}
status = webserver_run();
if(is_daemon) {
finish_daemon_startup(status);
}
if(status !=
0) {
log_ereport(
LOG_FAILURE,
"cannot run server.");
return EXIT_FAILURE;
}
if(srvctrl_wait()) {
return EXIT_FAILURE;
}
webserver_end();
log_ereport(
LOG_INFORM,
"webserver: exit");
return EXIT_SUCCESS;
}