summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pttbbs/daemon/boardd/LICENSE26
-rw-r--r--pttbbs/daemon/boardd/Makefile13
-rw-r--r--pttbbs/daemon/boardd/boardd.c370
-rw-r--r--pttbbs/daemon/boardd/server.c194
-rw-r--r--pttbbs/daemon/boardd/server.h19
5 files changed, 383 insertions, 239 deletions
diff --git a/pttbbs/daemon/boardd/LICENSE b/pttbbs/daemon/boardd/LICENSE
new file mode 100644
index 00000000..50b70e96
--- /dev/null
+++ b/pttbbs/daemon/boardd/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2010-2011, Chen-Yu Tsai <wens@csie.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The names of the author and contributors may be used to endorse or
+ promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/pttbbs/daemon/boardd/Makefile b/pttbbs/daemon/boardd/Makefile
index 1256059d..4cd5ac3d 100644
--- a/pttbbs/daemon/boardd/Makefile
+++ b/pttbbs/daemon/boardd/Makefile
@@ -4,16 +4,23 @@ SRCROOT= ../..
.include "$(SRCROOT)/pttbbs.mk"
PROG= boardd
+SRCS= boardd.c server.c
-SRCS= boardd.c
-
+CLEANFILES+= *~
UTILDIR= $(SRCROOT)/util
UTILOBJ= $(UTILDIR)/util_var.o
+LIBEVENT_CFLAGS!= pkg-config --cflags libevent
+LIBEVENT_LIBS_L!= pkg-config --libs-only-L libevent
+LIBEVENT_LIBS_l!= pkg-config --libs-only-l libevent
+
+CFLAGS+= $(LIBEVENT_CFLAGS)
+LDFLAGS+= $(LIBEVENT_LIBS_L)
+
LDADD+= $(UTILOBJ) \
$(SRCROOT)/common/bbs/libcmbbs.a \
$(SRCROOT)/common/sys/libcmsys.a \
$(SRCROOT)/common/osdep/libosdep.a \
- -levent
+ $(LIBEVENT_LIBS_l)
.include <bsd.prog.mk>
diff --git a/pttbbs/daemon/boardd/boardd.c b/pttbbs/daemon/boardd/boardd.c
index b6fb5cc7..e6e7966f 100644
--- a/pttbbs/daemon/boardd/boardd.c
+++ b/pttbbs/daemon/boardd/boardd.c
@@ -1,343 +1,241 @@
// $Id$
+// Memcached protocol based board data export daemon
-// Copyright (c) 2010, Chen-Yu Tsai <wens@csie.org>
+// Copyright (c) 2010-2011, Chen-Yu Tsai <wens@csie.org>
// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-//
-// 1. Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-// 3. The names of the author and contributors may be used to endorse or
-// promote products derived from this software without specific prior
-// written permission.
-//
-// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <stdio.h>
+
+// TODO:
+// 1. [done] add hotboard support
+// 2. [done] rewrite with libevent 2.0
+// 3. [done] split out independent server code
+// 4. [done] add article list support
+// 5. add article content support
+// 6. encode output in UTF-8 (with UAO support)
+// 7. encode article list in JSON for better structure
+
#include <stdlib.h>
#include <string.h>
-#include <strings.h>
#include <ctype.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <event.h>
+#include <event2/buffer.h>
+#include <event2/bufferevent.h>
+#include <event2/util.h>
-#include "cmsys.h"
-#include "cmbbs.h"
-#include "config.h"
-#include "var.h"
+#include <cmbbs.h>
+#include <var.h>
+#include <perm.h>
-static struct event ev_listen;
+#include "server.h"
-static const char whitespace[] = " \t\r\n";
+#define DEFAULT_ARTICLE_LIST 20
-#define TIMEOUT 600
-#define MAX_CLIENTS 1000
-#define BUF_SIZE 8192
+// helper function
-typedef struct {
- struct bufferevent * bufev;
- int fd;
-} conn_ctx;
+#define BOARD_HIDDEN(bptr) (bptr->brdattr & (BRD_HIDE | BRD_TOP) || \
+ (bptr->level & ~(PERM_BASIC|PERM_CHAT|PERM_PAGE|PERM_POST|PERM_LOGINOK) && \
+ !(bptr->brdattr & BRD_POSTMASK)))
-typedef struct {
- char *cmd;
- void (*func)(conn_ctx *ctx, const char * arg);
-} CMD;
+static void
+article_list(struct evbuffer *buf, boardheader_t *bptr, int offset, int length)
+{
+ int total, fd = -1;
+ char path[PATH_MAX];
+ fileheader_t fhdr;
-static char databuf[BUF_SIZE];
+ setbfile(path, bptr->brdname, FN_DIR);
+ total = get_num_records(path, sizeof(fileheader_t));
-void cb_endconn(struct bufferevent *bufev, short what, void *arg);
+ if (total <= 0)
+ return;
-// helper function
+ while (offset < 0)
+ offset += total;
-#define BOARD_HIDDEN(bptr) (bptr->brdattr & (BRD_HIDE | BRD_TOP) || \
- (bptr->level & ~(PERM_BASIC|PERM_CHAT|PERM_PAGE|PERM_POST|PERM_LOGINOK) && \
- !(bptr->brdattr & BRD_POSTMASK)))
-void answer_key(const char *key, int keylen, struct evbuffer *buf)
+ while (length-- > 0) {
+ if (get_records_keep(path, &fhdr, sizeof(fhdr), ++offset, 1, &fd) <= 0)
+ break;
+
+ evbuffer_add_printf(buf, "%d,%s,%s,%s,%s\n",
+ offset, fhdr.filename, fhdr.date, fhdr.owner, fhdr.title);
+ }
+
+ if (fd >= 0)
+ close(fd);
+}
+
+static void
+answer_key(struct evbuffer *buf, const char *key)
{
- char *data, *p;
int bid;
boardheader_t *bptr;
if (isdigit(*key)) {
+ char *p;
+
if ((bid = atoi(key)) == 0 || bid > MAX_BOARD)
return;
- if ((p = memchr(key, '.', keylen)) == NULL)
+ if ((p = strchr(key, '.')) == NULL)
return;
- p++;
bptr = getbcache(bid);
if (!bptr->brdname[0] || BOARD_HIDDEN(bptr))
return;
- if (strncmp(p, "isboard", 7) == 0)
- data = (bptr->brdattr & BRD_GROUPBOARD) ? "0" : "1";
- else if (strncmp(p, "hidden", 6) == 0)
- data = BOARD_HIDDEN(bptr) ? "1" : "0";
- else if (strncmp(p, "brdname", 7) == 0)
- data = bptr->brdname;
- else if (strncmp(p, "over18", 6) == 0)
- data = (bptr->brdattr & BRD_OVER18) ? "1" : "0";
- else if (strncmp(p, "title", 5) == 0)
- data = bptr->title;
- else if (strncmp(p, "BM", 2) == 0)
- data = bptr->BM;
- else if (strncmp(p, "parent", 6) == 0) {
- snprintf(databuf, sizeof(databuf), "%d", bptr->parent);
- data = databuf;
- } else if (strncmp(p, "children", 8) == 0) {
+ key = p + 1;
+
+ if (strcmp(key, "isboard") == 0)
+ evbuffer_add_printf(buf, "%d", (bptr->brdattr & BRD_GROUPBOARD) ? 0 : 1);
+ else if (strcmp(key, "over18") == 0)
+ evbuffer_add_printf(buf, "%d", (bptr->brdattr & BRD_OVER18) ? 0 : 1);
+ else if (strcmp(key, "hidden") == 0)
+ evbuffer_add_printf(buf, "%d", BOARD_HIDDEN(bptr) ? 0 : 1);
+ else if (strcmp(key, "brdname") == 0)
+ evbuffer_add(buf, bptr->brdname, strlen(bptr->brdname));
+ else if (strcmp(key, "title") == 0)
+ evbuffer_add(buf, bptr->title + 7, strlen(bptr->title) - 7);
+ else if (strcmp(key, "class") == 0)
+ evbuffer_add(buf, bptr->title, 4);
+ else if (strcmp(key, "BM") == 0)
+ evbuffer_add(buf, bptr->BM, strlen(bptr->BM));
+ else if (strcmp(key, "parent") == 0)
+ evbuffer_add_printf(buf, "%d", bptr->parent);
+ else if (strcmp(key, "count") == 0) {
+ char path[PATH_MAX];
+ setbfile(path, bptr->brdname, FN_DIR);
+ evbuffer_add_printf(buf, "%d", get_num_records(path, sizeof(fileheader_t)));
+ } else if (strcmp(key, "children") == 0) {
if (!(bptr->brdattr & BRD_GROUPBOARD))
return;
- data = p = databuf;
for (bid = bptr->firstchild[1]; bid > 0; bid = bptr->next[1]) {
bptr = getbcache(bid);
- p += snprintf(p, sizeof(databuf) - (databuf - p), "%d,", bid);
+ evbuffer_add_printf(buf, "%d,", bid);
}
+ } else if (strncmp(key, "articles.", 9) == 0) {
+ int offset, length;
+
+ key += 9;
+
+ if (!isdigit(*key) && *key != '-')
+ return;
+
+ offset = atoi(key);
+ p = strchr(key, '.');
- *--p = '\0';
+ if (!p || (length = atoi(p+1)) == 0)
+ length = DEFAULT_ARTICLE_LIST;
+
+ return article_list(buf, bptr, offset, length);
} else
return;
} else if (strncmp(key, "tobid.", 6) == 0) {
- char *bname = strndup(key + 6, keylen - 6);
- bid = getbnum(bname);
- free(bname);
+ bid = getbnum(key + 6);
bptr = getbcache(bid);
if (!bptr->brdname[0] || BOARD_HIDDEN(bptr))
return;
- snprintf(databuf, sizeof(databuf), "%d", bid);
- data = databuf;
+ evbuffer_add_printf(buf, "%d", bid);
#if HOTBOARDCACHE
} else if (strncmp(key, "hotboards", 9) == 0) {
- data = p = databuf;
for (bid = 0; bid < SHM->nHOTs; bid++) {
bptr = getbcache(SHM->HBcache[bid] + 1);
if (BOARD_HIDDEN(bptr))
continue;
- p += snprintf(p, sizeof(databuf) - (databuf - p), "%d,", SHM->HBcache[bid] + 1);
+ evbuffer_add_printf(buf, "%d,", SHM->HBcache[bid] + 1);
}
-
- *--p = '\0';
#endif
- } else
- return;
-
- evbuffer_add_printf(buf, "VALUE %.*s 0 %ld\r\n%s\r\n", keylen, key, strlen(data), data);
+ }
}
// Command functions
void
-cmd_get(conn_ctx *ctx, const char *arg)
+cmd_get(struct bufferevent *bev, void *ctx, int argc, char **argv)
{
- const char *key;
- size_t keylen;
- static struct evbuffer *buf;
+ struct evbuffer *output = bufferevent_get_output(bev),
+ *buf = evbuffer_new();
- key = arg + strspn(arg, whitespace);
-
- if (*key == '\0') {
- bufferevent_write(ctx->bufev, "ERROR\r\n", 7);
+ if (*argv++ == NULL) {
+ evbuffer_add_reference(output, "ERROR\r\n", 7, NULL, NULL);
return;
}
- if (buf == NULL)
- if ((buf = evbuffer_new()) == NULL) {
- const char msg[] = "SERVER_ERROR Can't allocate buffer\r\n";
- bufferevent_write(ctx->bufev, msg, strlen(msg));
- return;
- }
-
- while (*key) {
- keylen = strcspn(key, whitespace);
-
- answer_key(key, keylen, buf);
+ do {
+ answer_key(buf, *argv);
+ if (evbuffer_get_length(buf) == 0)
+ continue;
+ evbuffer_add_printf(output, "VALUE %s 0 %ld\r\n", *argv, evbuffer_get_length(buf));
+ evbuffer_add_buffer(output, buf);
+ evbuffer_add_printf(output, "\r\n");
+ } while (*++argv);
- bufferevent_write_buffer(ctx->bufev, buf);
-
- key += keylen;
- key += strspn(key, whitespace);
- }
-
- bufferevent_write(ctx->bufev, "END\r\n", 5);
+ evbuffer_add_reference(output, "END\r\n", 5, NULL, NULL);
}
void
-cmd_version(conn_ctx *ctx, const char *arg)
+cmd_version(struct bufferevent *bev, void *ctx, int argc, char **argv)
{
const char msg[] = "VERSION 0.0.1\r\n";
- bufferevent_write(ctx->bufev, msg, strlen(msg));
+ evbuffer_add_reference(bufferevent_get_output(bev), msg, strlen(msg), NULL, NULL);
}
void
-cmd_unknown(conn_ctx *ctx, const char *arg)
+cmd_unknown(struct bufferevent *bev, void *ctx, int argc, char **argv)
{
const char msg[] = "SERVER_ERROR Not implemented\r\n";
- bufferevent_write(ctx->bufev, msg, strlen(msg));
+ evbuffer_add_reference(bufferevent_get_output(bev), msg, strlen(msg), NULL, NULL);
}
void
-cmd_quit(conn_ctx *ctx, const char * arg)
+cmd_quit(struct bufferevent *bev, void *ctx, int argc, char **argv)
{
- cb_endconn(ctx->bufev, 0, ctx);
+ bufferevent_free(bev);
}
-static const CMD cmdlist[] = {
+static const struct {
+ const char *cmd;
+ void (*func)(struct bufferevent *bev, void *ctx, int argc, char **argv);
+} cmdlist[] = {
{"get", cmd_get},
{"quit", cmd_quit},
{"version", cmd_version},
{NULL, cmd_unknown}
};
-// Callbacks
-
void
-cb_client(struct bufferevent *bufev, void *arg)
+client_read_cb(struct bufferevent *bev, void *ctx)
{
- conn_ctx *ctx = arg;
- char *p, *cmd, *line = NULL;
- int i;
+ int argc, i;
+ char **argv;
+ size_t len;
+ struct evbuffer *input = bufferevent_get_input(bev);
+ char *line = evbuffer_readln(input, &len, EVBUFFER_EOL_CRLF);
- if ((line = evbuffer_readline(EVBUFFER_INPUT(bufev))) == NULL)
+ if (!line)
return;
- fprintf(stderr, "Client cmd [%d] %s\n", ctx->fd, line);
-
- cmd = line + strspn(line, whitespace);
- p = cmd + strcspn(cmd, whitespace);
- *p++ = '\0';
- p += strspn(p, whitespace);
+ argc = split_args(line, &argv);
for (i = 0; cmdlist[i].cmd; i++)
- if (strcasecmp(line, cmdlist[i].cmd) == 0)
+ if (evutil_ascii_strcasecmp(line, cmdlist[i].cmd) == 0)
break;
- (cmdlist[i].func)(ctx, p);
-
- free(line);
-}
-
-void
-cb_endconn(struct bufferevent *bufev, short what, void *arg)
-{
- conn_ctx *ctx = arg;
- close(ctx->fd);
- bufferevent_free(bufev);
- free(ctx);
+ (cmdlist[i].func)(bev, ctx, argc, argv);
- fprintf(stderr, "Client disconnect: %d\n", ctx->fd);
-}
-
-void
-setup_client(int fd)
-{
- fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
-
- conn_ctx *ctx = calloc(1, sizeof(conn_ctx));
-
- if (ctx == NULL) {
- close(fd);
- return;
- }
-
- if ((ctx->bufev = bufferevent_new(fd, cb_client, NULL, cb_endconn, ctx)) == NULL) {
- free(ctx);
- close(fd);
- return;
- }
-
- ctx->fd = fd;
- bufferevent_settimeout(ctx->bufev, TIMEOUT, TIMEOUT);
- bufferevent_enable(ctx->bufev, EV_READ | EV_WRITE);
+ free(argv);
+ free(line);
}
void
-cb_listen(int fd, short event, void *arg)
-{
- struct sockaddr_in clientaddr;
- socklen_t len = sizeof(clientaddr);
- int cfd;
-
- if ((cfd = accept(fd, (struct sockaddr *)&clientaddr, &len)) < 0 )
- return;
-
- setup_client(cfd);
-
- fprintf(stderr, "Client connect: %d\n", cfd);
-}
-
-int main(int argc, char *argv[])
+setup_program()
{
- int ch, sfd = 0, inetd = 0, daemon = 1;
- char *iface_ip = "127.0.0.1:5150";
- struct event_base *evb;
-
- Signal(SIGPIPE, SIG_IGN);
- while ((ch = getopt(argc, argv, "Dil:h")) != -1)
- switch (ch) {
- case 'D':
- daemon = 0;
- break;
- case 'i':
- inetd = 1;
- break;
- case 'l':
- iface_ip = optarg;
- break;
- case 'h':
- default:
- fprintf(stderr, "usage: boardd [-D] [-i] [-l [interface_ip]:port]\n");
- return 1;
- }
-
- if (!inetd)
- if ((sfd = tobindex(iface_ip, 100, NULL, 1)) < 0)
- return 1;
-
setuid(BBSUID);
setgid(BBSGID);
+ chdir(BBSHOME);
- srandom(getpid() + time(NULL));
attach_SHM();
-
- if (daemon)
- daemonize(BBSHOME "/run/boardd.pid", NULL);
-
- evb = event_init();
- event_base_priority_init(evb, 4);
-
- if (!inetd) {
- fcntl(sfd, F_SETFL, fcntl(sfd, F_GETFL, 0) | O_NONBLOCK);
- event_set(&ev_listen, sfd, EV_READ | EV_PERSIST, cb_listen, &ev_listen);
- event_priority_set(&ev_listen, 3);
- event_add(&ev_listen, NULL);
- } else
- setup_client(0);
- event_dispatch();
-
- return 0;
}
+
diff --git a/pttbbs/daemon/boardd/server.c b/pttbbs/daemon/boardd/server.c
new file mode 100644
index 00000000..2f9bbac1
--- /dev/null
+++ b/pttbbs/daemon/boardd/server.c
@@ -0,0 +1,194 @@
+// $Id$
+// Barebone TCP socket server daemon based on libevent 2.0
+
+// Copyright (c) 2011, Chen-Yu Tsai <wens@csie.org>
+// All rights reserved.
+
+// This is a simple TCP/IP server daemon based on libevent 2.0.
+// This program does not depend on anything other than libevent 2.0.
+// Without additional code linked in, this program alone will behave as an
+// echo server.
+//
+// You can supply your client_read_cb(), client_event_cb(), setup_client(), or
+// setup_program() functions to modify the behavior of the server.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <event2/event.h>
+#include <event2/buffer.h>
+#include <event2/bufferevent.h>
+#include <event2/util.h>
+#include <event2/listener.h>
+
+#include "server.h"
+
+static const struct timeval timeout = {600, 0};
+
+int
+split_args(char *line, char ***argp)
+{
+ int argc = 0;
+ char *p, **argv;
+
+ if ((argv = calloc(MAX_ARGS + 1, sizeof(char *))) == NULL)
+ return -1;
+
+ while ((p = strsep(&line, " \t\r\n")) != NULL) {
+ argv[argc++] = p;
+
+ if (argc == MAX_ARGS)
+ break;
+ }
+
+ argv = realloc(argv, (argc + 1) * sizeof(char *));
+ *argp = argv;
+
+ return argc;
+}
+
+void __attribute__((weak))
+client_read_cb(struct bufferevent *bev, void *ctx)
+{
+ bufferevent_write_buffer(bev, bufferevent_get_input(bev));
+}
+
+void __attribute__((weak))
+client_event_cb(struct bufferevent *bev, short events, void *ctx)
+{
+ if (events & BEV_EVENT_ERROR)
+ perror("Error from bufferevent");
+ if (events & (BEV_EVENT_EOF | BEV_EVENT_TIMEOUT | BEV_EVENT_ERROR)) {
+ bufferevent_free(bev);
+ }
+}
+
+void __attribute__((weak))
+setup_client(struct event_base *base, evutil_socket_t fd,
+ struct sockaddr *address, int socklen)
+{
+ struct bufferevent *bev = bufferevent_socket_new(base, fd,
+ BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
+ bufferevent_setcb(bev, client_read_cb, NULL, client_event_cb, NULL);
+ bufferevent_set_timeouts(bev, &timeout, &timeout);
+ bufferevent_enable(bev, EV_READ|EV_WRITE);
+}
+
+static void
+accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd,
+ struct sockaddr *address, int socklen, void *ctx)
+{
+ struct event_base *base = evconnlistener_get_base(listener);
+ return setup_client(base, fd, address, socklen);
+}
+
+int __attribute__((weak)) daemon(int nochdir, int noclose);
+void __attribute__((weak)) setup_program();
+
+int main(int argc, char *argv[])
+{
+ int ch, inetd = 0, run_as_daemon = 1;
+ const char *iface_ip = "127.0.0.1:5150";
+ struct event_base *base;
+ struct evconnlistener *listener;
+
+ while ((ch = getopt(argc, argv, "Dil:h")) != -1)
+ switch (ch) {
+ case 'D':
+ run_as_daemon = 0;
+ break;
+ case 'i':
+ inetd = 1;
+ break;
+ case 'l':
+ iface_ip = optarg;
+ break;
+ case 'h':
+ default:
+ fprintf(stderr, "usage: " " [-D] [-i] [-l interface_ip:port]\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (run_as_daemon && !inetd)
+ if (daemon(1, 1) < 0) {
+ perror("daemon");
+ exit(EXIT_FAILURE);
+ }
+
+ base = event_base_new();
+ assert(base);
+
+ if (!inetd) {
+ struct sockaddr sa;
+ int len = sizeof(sa);
+
+ if (evutil_parse_sockaddr_port(iface_ip, &sa, &len) < 0)
+ exit(EXIT_FAILURE);
+
+ listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
+ LEV_OPT_CLOSE_ON_FREE | LEV_OPT_CLOSE_ON_EXEC | LEV_OPT_REUSEABLE,
+ 100, &sa, len);
+
+ if (!listener)
+ exit(EXIT_FAILURE);
+ } else {
+ struct sockaddr sa;
+ socklen_t len = sizeof(sa);
+ getpeername(0, &sa, &len);
+ setup_client(base, 0, &sa, len);
+ }
+
+ if (setup_program)
+ setup_program();
+
+ signal(SIGPIPE, SIG_IGN);
+
+ event_base_dispatch(base);
+
+ return 0;
+}
+
+#ifdef __linux__
+
+int
+daemon(int nochdir, int noclose)
+{
+ int fd;
+
+ switch (fork()) {
+ case -1:
+ return -1;
+ case 0:
+ break;
+ default:
+ _exit(0);
+ }
+
+ if (setsid() == -1)
+ return -1;
+
+ if (!nochdir)
+ chdir("/");
+
+ if (!noclose && (fd = open("/dev/null", O_RDWR)) >= 0) {
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+
+ if (fd > 2)
+ close(fd);
+ }
+
+ return 0;
+}
+
+#endif // __linux__
diff --git a/pttbbs/daemon/boardd/server.h b/pttbbs/daemon/boardd/server.h
new file mode 100644
index 00000000..9cba93b7
--- /dev/null
+++ b/pttbbs/daemon/boardd/server.h
@@ -0,0 +1,19 @@
+// $Id$
+
+// Copyright (c) 2011, Chen-Yu Tsai <wens@csie.org>
+// All rights reserved.
+
+#include <event2/listener.h>
+#include <event2/bufferevent.h>
+
+#ifndef MAX_ARGS
+#define MAX_ARGS 100
+#endif
+
+extern void client_read_cb(struct bufferevent *bev, void *ctx);
+extern void client_event_cb(struct bufferevent *bev, short events, void *ctx);
+extern void setup_client(struct event_base *base, evutil_socket_t fd,
+ struct sockaddr *address, int socklen);
+extern void setup_program();
+
+extern int split_args(char *line, char ***argp);