#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "log.h"
#include "webserver.h"
#include "../util/systhr.h"
#include <cx/utils.h>
#include <cx/buffer.h>
#include <cx/printf.h>
#include "srvctrl.h"
#define SRVCTRL_THREAD_STACKSIZE 262144
static int srvctrl;
static cxmutstr srvctrl_path;
static WSBool srv_shutdown;
int srvctrl_init(ServerConfiguration *cfg) {
srvctrl_path = cx_asprintf(
"%s/private/srvctrl.sock", cfg->tmp.ptr);
struct sockaddr_un addr;
if(srvctrl_path.length >
sizeof(addr.sun_path)-
1) {
log_ereport(
LOG_CATASTROPHE,
"path ''%s'' too long for unix domain socket",
srvctrl_path.ptr);
return -
1;
}
if(unlink(srvctrl_path.ptr)) {
if(errno !=
ENOENT) {
log_ereport(
LOG_CATASTROPHE,
"cannot unlink old srvctrl socket ''%s'': %s",
srvctrl_path.ptr,
strerror(errno));
return -
1;
}
}
ZERO(&addr,
sizeof(addr));
addr.sun_family =
AF_UNIX;
memcpy(addr.sun_path, srvctrl_path.ptr, srvctrl_path.length);
srvctrl = socket(
AF_UNIX,
SOCK_STREAM,
0);
if(srvctrl == -
1) {
log_ereport(
LOG_CATASTROPHE,
"Cannot create server control socket: %s",
strerror(errno));
return -
1;
}
if(bind(srvctrl, (
struct sockaddr*)&addr,
sizeof(addr))) {
log_ereport(
LOG_CATASTROPHE,
"srvctrl socket bind failed: %s",
strerror(errno));
return -
1;
}
return 0;
}
int srvctrl_wait() {
listen(srvctrl,
8);
for(;;) {
int fd = accept(srvctrl,
NULL,
0);
if(fd <
0) {
if(srv_shutdown)
break;
log_ereport(
LOG_FAILURE,
"srvctrl: accept failed: %s",
strerror(errno));
break;
}
SrvCtrlClient *client = srvctrl_create_client(fd);
SYS_THREAD t = systhread_start(
SYSTHREAD_DEFAULT_PRIORITY,
SRVCTRL_THREAD_STACKSIZE,
(thrstartfunc)srvctrl_thread,
client);
systhread_detach(t);
}
close(srvctrl);
unlink(srvctrl_path.ptr);
free(srvctrl_path.ptr);
return 0;
}
void srvctrl_shutdown() {
srv_shutdown =
TRUE;
shutdown(srvctrl,
SHUT_RDWR);
}
SrvCtrlClient* srvctrl_create_client(
int fd) {
SrvCtrlClient *client = malloc(
sizeof(SrvCtrlClient));
ZERO(client,
sizeof(SrvCtrlClient));
client->fd = fd;
return client;
}
void* srvctrl_thread(SrvCtrlClient *client) {
LogDup log;
log.write = (log_writefunc)srvctrl_log;
log.cookie = client;
log_add_logdup(&log);
char buf[
64];
CxBuffer line;
cxBufferInit(&line,
NULL,
32, cxDefaultAllocator,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
ssize_t r;
WSBool br =
FALSE;
while((r = read(client->fd, buf,
64)) >
0) {
for(
int i=
0;i<r;i++) {
char c = buf[i];
if(c ==
'\n') {
cxmutstr ln = cx_mutstrn(line.space, line.pos);
if(srvctrl_handle_cmd(client, ln)) {
br =
TRUE;
break;
}
}
else {
cxBufferPut(&line, c);
}
}
if(br) {
break;
}
}
log_remove_logdup(&log);
cxBufferDestroy(&line);
close(client->fd);
free(client);
return NULL;
}
int srvctrl_handle_cmd(SrvCtrlClient *client, cxmutstr cmd) {
if(!cx_strcmp(cx_strcast(cmd), cx_str(
"reconfig"))) {
log_ereport(
LOG_INFORM,
"reconfigure server");
if(!webserver_reconfig()) {
log_ereport(
LOG_INFORM,
"reconfig: success");
}
}
else if(!cx_strcmp(cx_strcast(cmd), cx_str(
"shutdown"))) {
webserver_shutdown();
}
else if(!cx_strcmp(cx_strcast(cmd), cx_str(
"stat"))) {
}
else if(!cx_strcmp(cx_strcast(cmd), cx_str(
"log"))) {
return 0;
}
else {
log_ereport(
LOG_FAILURE,
"unknown srvctrl command: %.*s",
(
int)cmd.length,
cmd.ptr);
}
return 1;
}
void srvctrl_log(SrvCtrlClient *client,
char *msg,
size_t len) {
char msgheader[
4];
msgheader[
0] =
SRV_MSG_LOG;
uint16_t msglen = len;
memcpy(&msgheader[
1], &msglen,
2);
write(client->fd, msgheader,
3);
size_t pos =
0;
ssize_t w =
0;
while(pos < len) {
w = write(client->fd, msg + pos, len - pos);
if(w <=
0) {
break;
}
pos += w;
}
}