diff options
-rw-r--r-- | pttbbs/daemon/angelbeats/Makefile | 2 | ||||
-rw-r--r-- | pttbbs/daemon/angelbeats/abc.c | 93 | ||||
-rw-r--r-- | pttbbs/daemon/angelbeats/angelbeats.c | 189 | ||||
-rw-r--r-- | pttbbs/include/daemons.h | 34 |
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 { |