diff options
author | wens <wens@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2010-11-20 19:49:20 +0800 |
---|---|---|
committer | wens <wens@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2010-11-20 19:49:20 +0800 |
commit | 48758487a2d76d6b7e13bdabf2d7461c0fd5b816 (patch) | |
tree | 17c17d09715288fd7a4f76d679069a5e58594021 | |
parent | fb123595a5a4478e185d35944dfc47ea6082478b (diff) | |
download | pttbbs-48758487a2d76d6b7e13bdabf2d7461c0fd5b816.tar pttbbs-48758487a2d76d6b7e13bdabf2d7461c0fd5b816.tar.gz pttbbs-48758487a2d76d6b7e13bdabf2d7461c0fd5b816.tar.bz2 pttbbs-48758487a2d76d6b7e13bdabf2d7461c0fd5b816.tar.lz pttbbs-48758487a2d76d6b7e13bdabf2d7461c0fd5b816.tar.xz pttbbs-48758487a2d76d6b7e13bdabf2d7461c0fd5b816.tar.zst pttbbs-48758487a2d76d6b7e13bdabf2d7461c0fd5b816.zip |
New memcache protocol interface to bcache (readonly)
git-svn-id: http://opensvn.csie.org/pttbbs/trunk@5269 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
-rw-r--r-- | pttbbs/daemon/boardd/Makefile | 26 | ||||
-rw-r--r-- | pttbbs/daemon/boardd/boardd.c | 332 |
2 files changed, 358 insertions, 0 deletions
diff --git a/pttbbs/daemon/boardd/Makefile b/pttbbs/daemon/boardd/Makefile new file mode 100644 index 00000000..8ad7b2ed --- /dev/null +++ b/pttbbs/daemon/boardd/Makefile @@ -0,0 +1,26 @@ +# $Id$ +SRCROOT= ../.. +.include "$(SRCROOT)/pttbbs.mk" + +PROGRAMS= boardd +UTILDIR= $(SRCROOT)/util +UTILOBJ= $(UTILDIR)/util_var.o + +LDLIBS+=$(SRCROOT)/common/bbs/libcmbbs.a \ + $(SRCROOT)/common/sys/libcmsys.a \ + $(SRCROOT)/common/osdep/libosdep.a \ + -levent + +all: ${PROGRAMS} + +.SUFFIXES: .c .cpp .o +.c.o: + $(CC) $(CFLAGS) -c $*.c +.cpp.o: + $(CXX) $(CXXFLAGS) -c $*.cpp + +boardd: boardd.o $(UTILOBJ) + ${CC} ${CFLAGS} ${LDFLAGS} -o $* $> $(LDLIBS) + +clean: + rm -f *~ ${PROGRAMS} boardd.o diff --git a/pttbbs/daemon/boardd/boardd.c b/pttbbs/daemon/boardd/boardd.c new file mode 100644 index 00000000..1c26abfa --- /dev/null +++ b/pttbbs/daemon/boardd/boardd.c @@ -0,0 +1,332 @@ +// $Id$ + +// Copyright (c) 2010, 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> +#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 "cmsys.h" +#include "cmbbs.h" +#include "config.h" +#include "var.h" + +static struct event ev_listen; +static int clients = 0; + +static const char whitespace[] = " \t\r\n"; + +#define TIMEOUT 600 +#define MAX_CLIENTS 1000 +#define BUF_SIZE 8192 + +typedef struct { + struct bufferevent * bufev; + int fd; +} conn_ctx; + +typedef struct { + char *cmd; + void (*func)(conn_ctx *ctx, const char * arg); +} CMD; + +static char databuf[BUF_SIZE]; + +void cb_endconn(struct bufferevent *bufev, short what, void *arg); + +// helper function + +void answer_key(const char *key, int keylen, struct evbuffer *buf) +{ + char *data, *p; + int bid; + boardheader_t *bptr; + + if (isdigit(*key)) { + if ((bid = atoi(key)) == 0 || bid > MAX_BOARD) + return; + + if ((p = memchr(key, '.', keylen)) == NULL) + return; + + p++; + bptr = getbcache(bid); + + if (!bptr->brdname[0] || bptr->brdattr & (BRD_HIDE | BRD_TOP) || + (bptr->level & ~(PERM_BASIC|PERM_CHAT|PERM_PAGE|PERM_POST|PERM_LOGINOK) && + !(bptr->brdattr & BRD_POSTMASK))) + return; + + if (strncmp(p, "isboard", 7) == 0) + data = (bptr->brdattr & BRD_GROUPBOARD) ? "0" : "1"; + else if (strncmp(p, "hidden", 6) == 0) + data = (bptr->brdattr & (BRD_HIDE | BRD_TOP) || + (bptr->level & ~(PERM_BASIC|PERM_CHAT|PERM_PAGE|PERM_POST|PERM_LOGINOK) && + !(bptr->brdattr & BRD_POSTMASK))) ? "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) { + 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); + } + + *p = '\0'; + } else + return; + } else if (strncmp(key, "tobid.", 6) == 0) { + bid = getbnum(key + 6); + bptr = getbcache(bid); + + if (!bptr->brdname[0] || bptr->brdattr & (BRD_HIDE | BRD_TOP) || + (bptr->level && !(bptr->brdattr & BRD_POSTMASK) && + (bptr->level & ~(PERM_BASIC|PERM_CHAT|PERM_PAGE|PERM_POST|PERM_LOGINOK)))) + return; + + snprintf(databuf, sizeof(databuf), "%d", bid); + data = databuf; + } 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) +{ + const char *key; + size_t keylen; + static struct evbuffer *buf; + + key = arg + strspn(arg, whitespace); + + if (*key == '\0') { + bufferevent_write(ctx->bufev, "ERROR\r\n", 7); + 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); + + bufferevent_write_buffer(ctx->bufev, buf); + + key += keylen; + key += strspn(key, whitespace); + } + + bufferevent_write(ctx->bufev, "END\r\n", 5); +} + +void +cmd_version(conn_ctx *ctx, const char *arg) +{ + const char msg[] = "VERSION 0.0.1\r\n"; + bufferevent_write(ctx->bufev, msg, strlen(msg)); +} + +void +cmd_unknown(conn_ctx *ctx, const char *arg) +{ + const char msg[] = "SERVER_ERROR Not implemented\r\n"; + bufferevent_write(ctx->bufev, msg, strlen(msg)); +} + +void +cmd_quit(conn_ctx *ctx, const char * arg) +{ + cb_endconn(ctx->bufev, 0, ctx); +} + +static const CMD cmdlist[] = { + {"get", cmd_get}, + {"quit", cmd_quit}, + {"version", cmd_version}, + {NULL, cmd_unknown} +}; + +// Callbacks + +void +cb_client(struct bufferevent *bufev, void *arg) +{ + conn_ctx *ctx = arg; + char *p, *cmd, *line = NULL; + int i; + + if ((line = evbuffer_readline(EVBUFFER_INPUT(bufev))) == NULL) + return; + + cmd = line + strspn(line, whitespace); + p = cmd + strcspn(cmd, whitespace); + *p++ = '\0'; + p += strspn(p, whitespace); + + for (i = 0; cmdlist[i].cmd; i++) + if (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); + + if (clients == MAX_CLIENTS) + event_add(&ev_listen, NULL); + clients--; +} + +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); +} + +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); + + clients++; + + if (clients > MAX_CLIENTS) + event_del(&ev_listen); +} + +int main(int argc, char *argv[]) +{ + int ch, sfd = 0, inetd = 0, daemon = 1; + char *iface_ip = "127.0.0.1:5150"; + + 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 = tobind(iface_ip)) < 0) + return 1; + + setuid(BBSUID); + setgid(BBSGID); + + srandom(getpid() + time(NULL)); + attach_SHM(); + + if (daemon) + daemonize(BBSHOME "/run/boardd.pid", NULL); + + event_init(); + 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_add(&ev_listen, NULL); + } else + setup_client(0); + event_dispatch(); + + return 0; +} |