summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pttbbs/daemon/angelbeats/Makefile2
-rw-r--r--pttbbs/daemon/angelbeats/abc.c93
-rw-r--r--pttbbs/daemon/angelbeats/angelbeats.c189
-rw-r--r--pttbbs/include/daemons.h34
4 files changed, 302 insertions, 16 deletions
diff --git a/pttbbs/daemon/angelbeats/Makefile b/pttbbs/daemon/angelbeats/Makefile
index 0fb8fcb2..8667abab 100644
--- a/pttbbs/daemon/angelbeats/Makefile
+++ b/pttbbs/daemon/angelbeats/Makefile
@@ -23,7 +23,7 @@ angelbeats: angelbeats.o
${CC} ${CFLAGS} ${LDFLAGS} -levent -o $@ $> $(UTILOBJ) $(LDLIBS)
abc: abc.o
- ${CC} ${CFLAGS} ${LDFLAGS} -levent -o $@ $> $(LDLIBS)
+ ${CC} ${CFLAGS} ${LDFLAGS} -levent -o $@ $> $(UTILOBJ) $(LDLIBS)
install: $(PROGS)
install -d $(BBSHOME)/bin/
diff --git a/pttbbs/daemon/angelbeats/abc.c b/pttbbs/daemon/angelbeats/abc.c
index 175f57b5..29ada916 100644
--- a/pttbbs/daemon/angelbeats/abc.c
+++ b/pttbbs/daemon/angelbeats/abc.c
@@ -1,5 +1,96 @@
+// Angel Beats! Test Client
+//
+// Copyright (C) 2010, Hung-Te Lin <piaip@csie.ntu.edu.tw>
+// All rights reserved
#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bbs.h"
+#include "daemons.h"
+
+// standalone client to test angelbeats
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ angel_beats_data req = {0};
+ angel_beats_report rpt = {0};
+ attach_SHM();
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s operation [uid]\n", argv[0]);
+ return 0;
+ }
+
+ if ( (fd = toconnect(ANGELBEATS_ADDR)) < 0 ) {
+ perror("toconnect");
+ return 1;
+ }
+
+ if (strcmp(argv[1], "reload") == 0)
+ {
+ req.operation = ANGELBEATS_REQ_RELOAD;
+ }
+ else if (strcmp(argv[1], "suggest") == 0)
+ {
+ req.operation = ANGELBEATS_REQ_SUGGEST;
+ if (argc > 2)
+ req.master_uid = searchuser(argv[2], NULL);
+ }
+ else if (strcmp(argv[1], "unlink") == 0)
+ {
+ if (argc != 3) {
+ printf("need target id.\n");
+ return -1;
+ }
+ req.operation = ANGELBEATS_REQ_REMOVE_LINK;
+ req.master_uid = 0; // anyone, just unlink
+ req.angel_uid = searchuser(argv[2], NULL);
+ if (!req.angel_uid) {
+ printf("invalid user id: %s\n", argv[2]);
+ return -1;
+ }
+ }
+ if (strcmp(argv[1], "report") == 0)
+ {
+ req.operation = ANGELBEATS_REQ_REPORT;
+ }
+ else
+ return 0;
+
+ req.cb = sizeof(req);
+ if (towrite(fd, &req, sizeof(req)) != sizeof(req)) {
+ perror("towrite");
+ return 1;
+ }
+ if (req.operation == ANGELBEATS_REQ_REPORT) {
+ if (toread(fd, &rpt, sizeof(rpt)) != sizeof(rpt)) {
+ perror("toread");
+ return 1;
+ }
+ assert(rpt.cb == sizeof(rpt));
+ printf("total_angels=%d\n"
+ "total_online_angels=%d\n"
+ "total_active_angels=%d\n"
+ "low_average_masters=%d\n"
+ "high_average_masters=%d\n"
+ "my_active_masters=%d\n",
+ rpt.total_angels,
+ rpt.total_online_angels,
+ rpt.total_active_angels,
+ rpt.low_average_masters,
+ rpt.high_average_masters,
+ rpt.my_active_masters);
+ } else {
+ if (toread(fd, &req, sizeof(req)) != sizeof(req)) {
+ perror("toread");
+ return 1;
+ }
+ printf("result: angel_uid=%d\n", req.angel_uid);
+ }
-int main() {
return 0;
}
+
+
diff --git a/pttbbs/daemon/angelbeats/angelbeats.c b/pttbbs/daemon/angelbeats/angelbeats.c
index c01f9c6c..ac8351aa 100644
--- a/pttbbs/daemon/angelbeats/angelbeats.c
+++ b/pttbbs/daemon/angelbeats/angelbeats.c
@@ -1,4 +1,4 @@
-// Angel Beats Daemon
+// Angel Beats! Daemon
// Make angel distribution more balanced
//
// Create: Hung-Te Lin <piaip@csie.org>
@@ -9,7 +9,9 @@
#include <stdio.h>
#include <stdlib.h>
+#include <signal.h>
#include <time.h>
+#include <event.h>
#include "bbs.h"
#include "daemons.h"
@@ -18,6 +20,11 @@
static int debug = 1;
static int verbose = 1;
+// same as expire length
+#ifndef ANGELBEATS_INACTIVE_TIME
+#define ANGELBEATS_INACTIVE_TIME ( 120 * DAY_SECONDS )
+#endif
+
//////////////////////////////////////////////////////////////////////////////
// AngelInfo list operation
@@ -126,21 +133,32 @@ suggest_online_angel() {
for (i = 0; i < g_angel_list_size; i++) {
AngelInfo *kanade = g_angel_list+i;
+ int is_good_uid = 0;
// XXX or search_ulist_pid ?
astat = search_ulist_userid(kanade->userid);
-
+ // XXX TODO search back-forward?
+
+ // we have to take care of multi-login sessions,
+ // so it's better to reject if any of the sessions wants to reject.
for (; astat &&
strcasecmp(astat->userid, kanade->userid) == 0; astat++) {
- // ignore invalid entries
- if (!(astat->userlevel & PERM_ANGEL))
- continue; // XXX maybe we can quick abort?
- if (astat->angelpause || astat->mode == DEBUGSLEEPING)
+ // ignore all dead processes
+ if (astat->mode == DEBUGSLEEPING)
continue;
- // a good candidate
- candidates[num_candidates++] = astat->uid;
- break;
+ // if any sessions is not safe, ignore it.
+ if (!(astat->userlevel & PERM_ANGEL) ||
+ astat->angelpause) {
+ is_good_uid = 0;
+ break;
+ }
+ if (!is_good_uid)
+ is_good_uid = astat->uid;
}
+
+ // a good candidate?
+ if (is_good_uid)
+ candidates[num_candidates++] = is_good_uid;
// terminate when too many candidates
if (num_candidates >= ANGEL_SUGGEST_RANGE)
break;
@@ -161,8 +179,8 @@ suggest_online_angel() {
}
int
-inc_angel_master(const char *userid) {
- AngelInfo *kanade = angel_list_find_by_userid(userid);
+inc_angel_master(int uid) {
+ AngelInfo *kanade = angel_list_find_by_uid(uid);
if (!kanade)
return 0;
kanade->masters++;
@@ -172,13 +190,13 @@ inc_angel_master(const char *userid) {
}
int
-dec_angel_master(const char *userid) {
- AngelInfo *kanade = angel_list_find_by_userid(userid);
+dec_angel_master(int uid) {
+ AngelInfo *kanade = angel_list_find_by_uid(uid);
if (!kanade)
return 0;
if (kanade->masters == 0) {
fprintf(stderr, "warning: trying to decrease angel master "
- "which was already zero: %s\n", userid);
+ "which was already zero: %d\n", uid);
return 0;
}
kanade->masters--;
@@ -212,6 +230,13 @@ init_angel_list_callback(void *ctx, int uidx, userec_t *u) {
// add reference if I have an angel.
if (!u->myangel[0])
return 0;
+
+ // skip inactive users. however, this makes the counter
+ // incorrect when those kind of use goes online.
+ // anyway that should not be a big change...
+ if (time4(0) > u->lastlogin + ANGELBEATS_INACTIVE_TIME )
+ return 0;
+
kanade = angel_list_find_by_userid(u->myangel);
if (!kanade) {
// valid angel?
@@ -238,10 +263,116 @@ init_angel_list() {
return 0;
}
+int
+create_angel_report(angel_beats_report *prpt) {
+ int i;
+ AngelInfo *kanade = g_angel_list;
+ userinfo_t *astat = NULL;
+
+ prpt->total_angels = g_angel_list_size;
+ for (i = 0; i < g_angel_list_size; i++, kanade++) {
+ // online?
+ int is_pause = 0, is_online = 0;
+ for (astat = search_ulistn(kanade->uid, 1);
+ astat && astat->uid == kanade->uid;
+ astat++) {
+ if (astat->mode == DEBUGSLEEPING)
+ continue;
+ if (!(astat->userlevel & PERM_ANGEL)) // what now?
+ break;
+ if (astat->angelpause)
+ is_pause = 1;
+ is_online = 1;
+ }
+ prpt->total_online_angels += is_online;
+ if (is_online && !is_pause)
+ prpt->total_active_angels++;
+ }
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// network libevent
+struct timeval tv = {5, 0};
+static struct event ev_listen, ev_sighup;
+
+//////////////////////////////////////////////////////////////////////////////
+// main
+
+static void
+sighup_cb(int signal, short event, void *arg) {
+ init_angel_list();
+}
+
+static void
+client_cb(int fd, short event, void *arg) {
+ int len;
+ angel_beats_data data ={0};
+
+ // ignore clients that timeout or sending invalid request
+ if (event & EV_TIMEOUT)
+ goto end;
+ if ( (len = read(fd, &data, sizeof(data))) != sizeof(data) )
+ goto end;
+ if (data.cb != sizeof(data))
+ goto end;
+
+ if (debug) printf("got request: %d\n", data.operation);
+ switch(data.operation) {
+ case ANGELBEATS_REQ_INVALID:
+ break;
+ case ANGELBEATS_REQ_RELOAD:
+ init_angel_list();
+ break;
+ case ANGELBEATS_REQ_SUGGEST_AND_LINK:
+ data.angel_uid = suggest_online_angel();
+ if (data.angel_uid > 0) {
+ inc_angel_master(data.angel_uid);
+ }
+ break;
+ case ANGELBEATS_REQ_REMOVE_LINK:
+ dec_angel_master(data.angel_uid);
+ break;
+ case ANGELBEATS_REQ_REPORT:
+ {
+ printf("ANGELBEATS_REQ_REPORT\n");
+ angel_beats_report rpt = {0};
+ rpt.cb = sizeof(rpt);
+ create_angel_report(&rpt);
+ // write different kind of data!
+ write(fd, &rpt, sizeof(rpt));
+ goto end;
+ }
+ break;
+ }
+ write(fd, &data, sizeof(data));
+
+end:
+ // cleanup
+ close(fd);
+ free(arg);
+}
+
+static void
+listen_cb(int fd, short event, void *arg) {
+ int cfd;
+
+ if ((cfd = accept(fd, NULL, NULL)) < 0 )
+ return;
+
+ if (debug) printf("accept new connection!\n");
+ struct event *ev = malloc(sizeof(struct event));
+
+ event_set(ev, cfd, EV_READ, client_cb, ev);
+ event_add(ev, &tv);
+}
+
int
main(int argc, char *argv[]) {
int i;
AngelInfo *kanade;
+ int ch, sfd, go_daemon = 0;
+ const char *iface_ip = ANGELBEATS_ADDR;
// things to do:
// 1. if cmd='reload' or first run, read passwd and init_angel_list
@@ -249,6 +380,22 @@ main(int argc, char *argv[]) {
// 3. if cmd='inc_master', decrease master counter of target angel
// 4. if cmd='dec_master', increase master counter of target angel
// 5. if cmd='report', give verbose reports
+
+ while ( (ch = getopt(argc, argv, "i:Dh")) != -1 ) {
+ switch( ch ) {
+ case 'i':
+ iface_ip = optarg;
+ break;
+
+ case 'D':
+ go_daemon = !go_daemon;
+
+ case 'h':
+ default:
+ fprintf(stderr, "usage: %s [-D] [-i [interface_ip]:port]\n", argv[0]);
+ return 1;
+ }
+ }
srand(time(NULL));
Signal(SIGPIPE, SIG_IGN);
@@ -267,6 +414,20 @@ main(int argc, char *argv[]) {
kanade->masters);
}
printf("suggested angel=%d\n", suggest_online_angel());
+
+ if (go_daemon)
+ daemonize(BBSHOME "/run/angelbeats.pid", NULL);
+
+ if ( (sfd = tobind(iface_ip)) < 0 )
+ return 1;
+
+ event_init();
+ event_set(&ev_listen, sfd, EV_READ | EV_PERSIST, listen_cb, &ev_listen);
+ event_add(&ev_listen, NULL);
+ signal_set(&ev_sighup, SIGHUP, sighup_cb, &ev_sighup);
+ signal_add(&ev_sighup, NULL);
+ event_dispatch();
+
return 0;
}
diff --git a/pttbbs/include/daemons.h b/pttbbs/include/daemons.h
index 46b30db1..40260f64 100644
--- a/pttbbs/include/daemons.h
+++ b/pttbbs/include/daemons.h
@@ -53,6 +53,40 @@ typedef struct login_data
} login_data;
///////////////////////////////////////////////////////////////////////
+// Angel Beats! Daemon
+
+#ifndef ANGELBEATS_ADDR
+#define ANGELBEATS_ADDR ":5132"
+#endif
+
+enum ANGELBEATS_OPERATIONS {
+ ANGELBEATS_REQ_INVALID = 0,
+ ANGELBEATS_REQ_REPORT,
+ ANGELBEATS_REQ_RELOAD,
+ ANGELBEATS_REQ_SUGGEST,
+ ANGELBEATS_REQ_SUGGEST_AND_LINK,
+ ANGELBEATS_REQ_REMOVE_LINK,
+};
+
+typedef struct {
+ short cb; // size of current structure
+ short operation;
+
+ int angel_uid;
+ int master_uid;
+} angel_beats_data ;
+
+typedef struct {
+ short cb;
+ short total_angels;
+ short total_online_angels;
+ short total_active_angels;
+ short low_average_masters;
+ short high_average_masters;
+ short my_active_masters;
+} angel_beats_report ;
+
+///////////////////////////////////////////////////////////////////////
// online friend relation daemon
//
typedef struct {