summaryrefslogtreecommitdiffstats
path: root/cacheserver
diff options
context:
space:
mode:
Diffstat (limited to 'cacheserver')
-rw-r--r--cacheserver/Makefile6
-rw-r--r--cacheserver/utmpserver3.c326
2 files changed, 330 insertions, 2 deletions
diff --git a/cacheserver/Makefile b/cacheserver/Makefile
index 1681e468..b2678ef7 100644
--- a/cacheserver/Makefile
+++ b/cacheserver/Makefile
@@ -1,7 +1,7 @@
# $Id$
.include "../pttbbs.mk"
-PROGRAMS= utmpserver utmpsync utmpserver2
+PROGRAMS= utmpserver utmpsync utmpserver2 utmpserver3
UTILOBJ= ../util/util_stuff.o ../util/util_var.o ../util/util_file.o ../util/util_cache.o ../util/util_passwd.o ../util/util_record.o ../util/util_osdep.o ../util/util_args.o
all: ${PROGRAMS}
@@ -16,9 +16,11 @@ utmpserver: utmpserver.o $(UTILOBJ)
${CC} ${CFLAGS} ${LDFLAGS} -o $* $*.o $(UTILOBJ)
utmpserver2: utmpserver2.o friend.o $(UTILOBJ)
${CXX} ${CFLAGS} ${LDFLAGS} -o $* $*.o $(UTILOBJ) friend.o
+utmpserver3: utmpserver3.o friend.o $(UTILOBJ)
+ ${CXX} ${CFLAGS} ${LDFLAGS} -levent -o $* $*.o $(UTILOBJ) friend.o
utmpsync: utmpsync.o $(UTILOBJ)
${CC} ${CFLAGS} ${LDFLAGS} -o $* $*.o $(UTILOBJ)
clean:
- rm -f *~ ${PROGRAMS} friend.o utmpserver.o utmpserver2.o utmpsync.o
+ rm -f *~ ${PROGRAMS} friend.o utmpserver.o utmpserver2.o utmpserver3.o utmpsync.o
diff --git a/cacheserver/utmpserver3.c b/cacheserver/utmpserver3.c
new file mode 100644
index 00000000..1dccd016
--- /dev/null
+++ b/cacheserver/utmpserver3.c
@@ -0,0 +1,326 @@
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <event.h>
+
+#include "bbs.h"
+
+extern void utmplogin(int uid, int index, const int like[MAX_FRIEND], const int hate[MAX_REJECT]);
+extern int genfriendlist(int uid, int index, ocfs_t *fs, int maxfs);
+extern void utmplogoutall(void);
+#ifdef UTMPLOG
+FILE *logfp;
+#endif
+
+clock_t begin_clock;
+time_t begin_time;
+int count_flooding, count_login;
+
+#ifdef NOFLOODING
+/* 0 ok, 1 delay action, 2 reject */
+int action_frequently(int uid)
+{
+ int i;
+ time_t now = time(NULL);
+ time_t minute = now/60;
+ time_t hour = minute/60;
+
+ static time_t flood_base_minute;
+ static time_t flood_base_hour;
+ static struct {
+ unsigned short lastlogin; // truncated time_t
+ unsigned char minute_count;
+ unsigned char hour_count;
+ } flooding[MAX_USERS];
+
+ if(minute!=flood_base_minute) {
+ for(i=0; i<MAX_USERS; i++)
+ flooding[i].minute_count=0;
+ flood_base_minute=minute;
+ }
+ if(hour!=flood_base_hour) {
+ for(i=0; i<MAX_USERS; i++)
+ flooding[i].hour_count=0;
+ flood_base_hour=hour;
+ }
+
+ if(abs(flooding[uid].lastlogin-(unsigned short)now)<=3 ||
+ flooding[uid].minute_count>30 ||
+ flooding[uid].hour_count>60) {
+ count_flooding++;
+ return 2;
+ }
+
+ flooding[uid].minute_count++;
+ flooding[uid].hour_count++;
+ flooding[uid].lastlogin=now;
+
+ if(flooding[uid].minute_count>5 ||
+ flooding[uid].hour_count>20) {
+ count_flooding++;
+ return 1;
+ }
+ return 0;
+}
+#endif /* NOFLOODING */
+
+void syncutmp(int cfd) {
+ int i;
+ int like[MAX_FRIEND];
+ int hate[MAX_REJECT];
+
+#ifdef UTMPLOG
+ int x=-1;
+ if(logfp && ftell(logfp)> 500*(1<<20)) {
+ fclose(logfp);
+ logfp=NULL;
+ }
+ if(logfp) fwrite(&x, sizeof(x), 1, logfp);
+#endif
+
+ printf("logout all\n");
+ utmplogoutall();
+ fprintf(stderr,"sync begin\n");
+ for(i=0; i<USHM_SIZE; i++) {
+ int uid;
+ if( toread(cfd, &uid, sizeof(uid)) <= 0 ||
+ toread(cfd, like, sizeof(like)) <= 0 ||
+ toread(cfd, hate, sizeof(hate)) <= 0)
+ break;
+#ifdef UTMPLOG
+ if(logfp) {
+ fwrite(&uid, sizeof(uid), 1, logfp);
+ fwrite(like, sizeof(like), 1, logfp);
+ fwrite(hate, sizeof(hate), 1, logfp);
+ }
+#endif
+
+ if(uid != 0)
+ utmplogin(uid, i, like, hate);
+ }
+ if(i<USHM_SIZE) {
+#ifdef UTMPLOG
+ int x=-2;
+ if(logfp) fwrite(&x, sizeof(x), 1, logfp);
+#endif
+ }
+
+ fprintf(stderr,"sync end\n");
+}
+
+struct client_state {
+ struct event ev;
+ int state;
+ int next_block;
+ struct evbuffer *evb;
+};
+
+void processlogin(struct client_state *cs, int uid, int index)
+{
+ int like[MAX_FRIEND];
+ int hate[MAX_REJECT];
+ /* 因為 logout 的時候並不會通知 utmpserver , 可能會查到一些
+ 已經 logout 的帳號。所以不能只取 MAX_FRIEND 而要多取一些 */
+#define MAX_FS (2 * MAX_FRIEND)
+ int res;
+ int nfs;
+ ocfs_t fs[MAX_FS];
+
+ evbuffer_remove(cs->evb, like, sizeof(like));
+ evbuffer_remove(cs->evb, hate, sizeof(hate));
+#ifdef UTMPLOG
+ if(logfp) {
+ int x=-3;
+ fwrite(&x, sizeof(x), 1, logfp);
+ fwrite(&uid, sizeof(uid), 1, logfp);
+ fwrite(&index, sizeof(index), 1, logfp);
+ fwrite(like, sizeof(like), 1, logfp);
+ fwrite(hate, sizeof(hate), 1, logfp);
+ }
+#endif
+
+ utmplogin(uid, index, like, hate);
+ nfs=genfriendlist(uid, index, fs, MAX_FS);
+ res=0;
+#ifdef NOFLOODING
+ res=action_frequently(uid);
+#endif
+ evbuffer_drain(cs->evb, 2147483647);
+ evbuffer_add(cs->evb, &res, sizeof(res));
+ evbuffer_add(cs->evb, &nfs, sizeof(nfs));
+ evbuffer_add(cs->evb, fs, sizeof(ocfs_t) * nfs);
+}
+
+void showstat(void)
+{
+ clock_t now_clock=clock();
+ time_t now_time=time(0);
+
+ time_t used_time=now_time-begin_time;
+ clock_t used_clock=now_clock-begin_clock;
+
+ printf("%.24s : real %.0f cpu %.2f : %d login %d flood, %.2f login/sec, %.2f%% load.\n",
+ ctime(&now_time), (double)used_time, (double)used_clock/CLOCKS_PER_SEC,
+ count_login, count_flooding,
+ (double)count_login/used_time, (double)used_clock/CLOCKS_PER_SEC/used_time*100);
+
+ begin_time=now_time;
+ begin_clock=now_clock;
+ count_login=0;
+ count_flooding=0;
+}
+
+enum {
+ FSM_ENTER,
+ FSM_LOGIN,
+ FSM_PROCESSLOGIN,
+ FSM_SYNC,
+ FSM_WRITEBACK,
+ FSM_EXIT
+};
+
+static int firstsync=0;
+
+struct timeval tv = {5, 0};
+
+#define READ_BLOCK 1024
+
+void connection_client(int cfd, short event, void *arg)
+{
+ struct client_state *cs = arg;
+ int cmd;
+ int uid = 0, index = 0;
+
+ // ignore clients that timeout
+ if (event & EV_TIMEOUT)
+ cs->state = FSM_EXIT;
+
+ if (event & EV_READ) {
+ if (cs->state != FSM_ENTER) {
+ if (evbuffer_read(cs->evb, cfd, READ_BLOCK) < 0)
+ cs->state = FSM_EXIT;
+ }
+ else {
+ if (evbuffer_read(cs->evb, cfd, 4) < 0)
+ cs->state = FSM_EXIT;
+ }
+ }
+
+ while (EVBUFFER_LENGTH(cs->evb) >= cs->next_block || cs->state >= FSM_SYNC) {
+ switch (cs->state) {
+ case FSM_ENTER:
+ evbuffer_remove(cs->evb, &cmd, sizeof(cmd));
+ if (cmd == -1)
+ cs->state = FSM_SYNC;
+ else if (cmd == -2) {
+ fcntl(cfd, F_SETFL, fcntl(cfd, F_GETFL, 0) | O_NONBLOCK);
+ cs->next_block = 2 * sizeof(int);
+ cs->state = FSM_LOGIN;
+ }
+ else if (cmd >= 0)
+ cs->state = FSM_EXIT;
+ else {
+ printf("unknown cmd=%d\n",cmd);
+ cs->state = FSM_EXIT;
+ }
+ break;
+ case FSM_SYNC:
+ syncutmp(cfd);
+ cs->state = FSM_EXIT;
+ break;
+ case FSM_LOGIN:
+ if (firstsync) {
+ evbuffer_remove(cs->evb, &index, sizeof(index));
+ evbuffer_remove(cs->evb, &uid, sizeof(uid));
+ if (index < USHM_SIZE) {
+ cs->next_block = sizeof(int) * (MAX_FRIEND + MAX_REJECT);
+ cs->state = FSM_PROCESSLOGIN;
+ }
+ else {
+ fprintf(stderr, "bad index=%d\n", index);
+ cs->state = FSM_EXIT;
+ }
+ }
+ else
+ cs->state = FSM_EXIT;
+ break;
+ case FSM_PROCESSLOGIN:
+ count_login++;
+ processlogin(cs, uid, index);
+ if (count_login >= 4000 || (time(NULL) - begin_time) > 30*60)
+ showstat();
+ cs->state = FSM_WRITEBACK;
+ break;
+ case FSM_WRITEBACK:
+ if (event & EV_WRITE)
+ evbuffer_write(cs->evb, cfd);
+ if (EVBUFFER_LENGTH(cs->evb) == 0)
+ cs->state = FSM_EXIT;
+ break;
+ case FSM_EXIT:
+ close(cfd);
+ evbuffer_free(cs->evb);
+ free(cs);
+ return;
+ break;
+ }
+ }
+ event_add(&cs->ev, &tv);
+}
+
+void connection_accept(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;
+
+ struct client_state *cs = calloc(1, sizeof(struct client_state));
+ cs->state = FSM_ENTER;
+ cs->next_block = sizeof(int);
+ cs->evb = evbuffer_new();
+
+ event_set(&cs->ev, cfd, EV_READ | EV_WRITE, (void *) connection_client, cs);
+ event_add(&cs->ev, &tv);
+}
+
+int main(int argc, char *argv[])
+{
+ int ch, port = 5120, sfd;
+ char *iface_ip = NULL;
+
+#ifdef UTMPLOG
+ logfp = fopen("utmp.log","a");
+ if(logfp && ftell(logfp)> 500*(1<<20)) {
+ fclose(logfp);
+ logfp=NULL;
+ }
+#endif
+
+ Signal(SIGPIPE, SIG_IGN);
+ while( (ch = getopt(argc, argv, "p:i:h")) != -1 )
+ switch( ch ){
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 'i':
+ iface_ip = optarg;
+ break;
+ case 'h':
+ default:
+ fprintf(stderr, "usage: utmpserver [-i interface_ip] [-p port]\n");
+ return 1;
+ }
+
+ if( (sfd = tobind(iface_ip, port)) < 0 )
+ return 1;
+
+ event_init();
+ struct event ev;
+ event_set(&ev, sfd, EV_READ | EV_PERSIST, connection_accept, &ev);
+ event_add(&ev, NULL);
+ event_dispatch();
+ return 0;
+}