summaryrefslogtreecommitdiffstats
path: root/daemon/cached
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/cached')
-rw-r--r--daemon/cached/Makefile30
-rw-r--r--daemon/cached/README1
-rw-r--r--daemon/cached/authserver.c236
-rw-r--r--daemon/cached/friend.cpp350
-rw-r--r--daemon/cached/utmpserver.c222
-rw-r--r--daemon/cached/utmpserver2.c290
-rw-r--r--daemon/cached/utmpserver3.c341
-rw-r--r--daemon/cached/utmpsync.c27
8 files changed, 1497 insertions, 0 deletions
diff --git a/daemon/cached/Makefile b/daemon/cached/Makefile
new file mode 100644
index 00000000..bdff3f08
--- /dev/null
+++ b/daemon/cached/Makefile
@@ -0,0 +1,30 @@
+# $Id$
+SRCROOT= ..
+.include "$(SRCROOT)/pttbbs.mk"
+
+PROGRAMS= utmpserver utmpsync utmpserver2 utmpserver3 authserver
+UTILDIR= $(SRCROOT)/util
+UTILOBJ= $(UTILDIR)/util_stuff.o $(UTILDIR)/util_var.o $(UTILDIR)/util_file.o $(UTILDIR)/util_cache.o $(UTILDIR)/util_passwd.o $(UTILDIR)/util_record.o $(UTILDIR)/util_osdep.o $(UTILDIR)/util_args.o
+
+all: ${PROGRAMS}
+
+.SUFFIXES: .c .cpp .o
+.c.o:
+ $(CCACHE) $(CC) $(CFLAGS) -c $*.c
+.cpp.o:
+ $(CCACHE) $(CXX) $(CFLAGS) -c $*.cpp
+
+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)
+
+authserver: authserver.o $(UTILOBJ)
+ ${CC} ${CFLAGS} ${LDFLAGS} -lcrypt -levent -o $* $>
+
+clean:
+ rm -f *~ ${PROGRAMS} friend.o utmpserver.o utmpserver2.o utmpserver3.o utmpsync.o authserver.o
diff --git a/daemon/cached/README b/daemon/cached/README
new file mode 100644
index 00000000..3bfeddbc
--- /dev/null
+++ b/daemon/cached/README
@@ -0,0 +1 @@
+這是一個測試的東西. 除非確定你知道這個程式在幹什麼, 否則請不理會這個目錄 :P
diff --git a/daemon/cached/authserver.c b/daemon/cached/authserver.c
new file mode 100644
index 00000000..5fbba03a
--- /dev/null
+++ b/daemon/cached/authserver.c
@@ -0,0 +1,236 @@
+/* $Id$ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <event.h>
+
+#include "bbs.h"
+
+struct timeval tv = {5, 0};
+struct event ev;
+int clients = 0;
+
+#define READ_BLOCK 256
+#define MAX_CLIENTS 10
+
+#define AUTH_PASSWDS BBSHOME "/.AUTH_PASSWDS"
+#define ENTRY_SIZE 64
+
+struct client_state {
+ struct event ev;
+ int state;
+ int uid;
+ int len;
+ int response;
+ struct evbuffer *evb;
+};
+
+enum {
+ FSM_ENTER,
+ FSM_AUTH,
+ FSM_SETPASSWD,
+ FSM_RESPOND,
+ FSM_EXIT
+};
+
+/**
+ * 瑼X亙蝣
+ * @return 1 - 撖蝣潮航炊, 0 - 撖蝣潭迤蝣
+ */
+int check_passwd(int uid, char *passwd)
+{
+ char buf[ENTRY_SIZE];
+ int i, result = 1;
+
+ if ((i = open(AUTH_PASSWDS, O_WRONLY)) < 0)
+ return result;
+
+ if (lseek(i, uid * ENTRY_SIZE, SEEK_SET) < 0)
+ goto end;
+
+ if (read(i, buf, ENTRY_SIZE) < ENTRY_SIZE)
+ goto end;
+
+ if (!strcmp(buf, crypt(passwd, buf)))
+ result = 0;
+end:
+ memset(buf, 0, ENTRY_SIZE);
+ close(i);
+ return result;
+}
+
+/**
+ * 閮剖啣蝣
+ * DES crypt 撠勗末
+ */
+void set_passwd(int uid, char *passwd)
+{
+ char buf[ENTRY_SIZE];
+ char saltc[3], c;
+ int i;
+
+ i = 9 * random();
+ saltc[0] = i & 077;
+ saltc[1] = (i >> 6) & 077;
+
+ for (i = 0; i < 2; i++) {
+ c = saltc[i] + '.';
+ if (c > '9')
+ c += 7;
+ if (c > 'Z')
+ c += 6;
+ saltc[i] = c;
+ }
+ saltc[2] = '\0';
+
+ memset(buf, 0, sizeof(buf));
+ strlcpy(buf, crypt(passwd, saltc), sizeof(buf));
+
+ if ((i = open(AUTH_PASSWDS, O_WRONLY)) < 0)
+ return;
+
+ if (lseek(i, uid * ENTRY_SIZE, SEEK_SET) < 0)
+ goto close;
+ write(i, buf, ENTRY_SIZE);
+close:
+ close(i);
+}
+
+void connection_client(int cfd, short event, void *arg)
+{
+ struct client_state *cs = arg;
+ int cmd;
+ static char buf[128];
+
+ // ignore clients that timeout
+ if (event & EV_TIMEOUT)
+ cs->state = FSM_EXIT;
+
+ if (event & EV_READ) {
+ if (evbuffer_read(cs->evb, cfd, READ_BLOCK) < 0)
+ cs->state = FSM_EXIT;
+ }
+
+ while (1) {
+ switch (cs->state) {
+ case FSM_ENTER:
+ if (EVBUFFER_LENGTH(cs->evb) < sizeof(int))
+ goto break_out;
+ evbuffer_remove(cs->evb, &cmd, sizeof(cmd));
+ cs->state = FSM_AUTH;
+
+ case FSM_AUTH:
+ if (EVBUFFER_LENGTH(cs->evb) < sizeof(int) * 2)
+ goto break_out;
+
+ evbuffer_remove(cs->evb, &cs->uid, sizeof(cs->uid));
+ evbuffer_remove(cs->evb, &cs->len, sizeof(cs->len));
+ if (EVBUFFER_LENGTH(cs->evb) < cs->len)
+ goto break_out;
+
+ memset(buf, 0, sizeof(buf));
+ evbuffer_remove(cs->evb, buf, cs->len);
+ cs->response = check_passwd(cs->uid, buf);
+ memset(buf, 0, sizeof(buf));
+
+ if (cmd == 2)
+ cs->state = FSM_SETPASSWD;
+ else {
+ cs->state = FSM_RESPOND;
+ goto break_out;
+ }
+
+ case FSM_SETPASSWD:
+ if (EVBUFFER_LENGTH(cs->evb) < sizeof(int))
+ goto break_out;
+
+ evbuffer_remove(cs->evb, &cs->len, sizeof(cs->len));
+ if (EVBUFFER_LENGTH(cs->evb) < cs->len)
+ goto break_out;
+
+ memset(buf, 0, sizeof(buf));
+ evbuffer_remove(cs->evb, buf, cs->len);
+ set_passwd(cs->uid, buf);
+ memset(buf, 0, sizeof(buf));
+
+ cs->state = FSM_RESPOND;
+ goto break_out;
+
+ case FSM_RESPOND:
+ write(cfd, &cs->response, sizeof(cs->response));
+ cs->state = FSM_EXIT;
+
+ case FSM_EXIT:
+ if (clients == MAX_CLIENTS)
+ event_add(&ev, NULL);
+ close(cfd);
+ evbuffer_free(cs->evb);
+ free(cs);
+ clients--;
+ return;
+ }
+ }
+
+break_out:
+ if (cs->state == FSM_RESPOND)
+ event_set(&cs->ev, cfd, EV_WRITE, (void *) connection_client, cs);
+ 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;
+
+ fcntl(cfd, F_SETFL, fcntl(cfd, F_GETFL, 0) | O_NONBLOCK);
+
+ struct client_state *cs = calloc(1, sizeof(struct client_state));
+ cs->state = 0;
+ cs->evb = evbuffer_new();
+
+ event_set(&cs->ev, cfd, EV_READ, (void *) connection_client, cs);
+ event_add(&cs->ev, &tv);
+ clients++;
+
+ if (clients > MAX_CLIENTS)
+ event_del(&ev);
+}
+
+int main(int argc, char *argv[])
+{
+ int ch, port = 5121, sfd;
+ char *iface_ip = NULL;
+
+ 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: authserver [-i interface_ip] [-p port]\n");
+ return 1;
+ }
+
+ if( (sfd = tobind(iface_ip, port)) < 0 )
+ return 1;
+
+ srandom(getpid() + time(NULL));
+ event_init();
+ event_set(&ev, sfd, EV_READ | EV_PERSIST, connection_accept, &ev);
+ event_add(&ev, NULL);
+ event_dispatch();
+ return 0;
+}
diff --git a/daemon/cached/friend.cpp b/daemon/cached/friend.cpp
new file mode 100644
index 00000000..e62a7198
--- /dev/null
+++ b/daemon/cached/friend.cpp
@@ -0,0 +1,350 @@
+#define NDEBUG
+#include <algorithm>
+#include <cassert>
+
+// for some constant and type
+#include "bbs.h"
+
+/* for each login of user,
+ * input: my index, friend[MAX_FRIEND] of uid, reject[MAX_REJECT] of uid,
+ * for all my relation,
+ * output: his index, his uid, relation to me, relation to him
+ */
+/* 支 user utmp 銋憭, 券函 ref index 賣舫, 蝣箔 insert & delete O(1) */
+/* 嗆鈭 refer resource recycle */
+
+typedef int Uid;
+typedef int Idx;
+
+
+struct Relation {
+ Uid him;
+ short him_offset;
+
+ Relation(Uid _him, short _him_offset=-1) :him(_him),him_offset(_him_offset) {}
+};
+
+template<class T>
+struct freelist {
+ static const int KEEP = 64;
+ static T* list[8][KEEP]; // 2^0~2^7
+ static int tail[8];
+
+#define IS_2xxN(a) (a && (a&(a-1))==0)
+ static T* alloc(int n) {
+ assert(n>0);
+ if(n<256 && IS_2xxN(n) && sizeof(T)*n<65536) {
+ int t=n;
+ int slot;
+ for(slot=0; t>1; t/=2)
+ slot++;
+ assert(0<=slot && slot<8);
+ if(tail[slot]) {
+ return list[slot][--tail[slot]];
+ }
+ }
+ return (T*)malloc(sizeof(T)*n);
+ }
+ static void free(T* p, int n) {
+ assert(n>0);
+ if(n<256 && IS_2xxN(n) && sizeof(T)*n<65536) {
+ int t=n;
+ int slot;
+ for(slot=0; t>1; t/=2)
+ slot++;
+ assert(0<=slot && slot<8);
+ if(tail[slot]<KEEP) {
+ list[slot][tail[slot]++]=p;
+ return;
+ }
+ }
+ ::free(p);
+ }
+};
+
+template<class T> T* freelist<T>::list[8][KEEP];
+template<class T> int freelist<T>::tail[8]={0};
+
+template<class T,int MIN_ROOM = 8, class S = int>
+struct myvector {
+ // 憭扯港唾 STL vector, 雿 STL capacity 芣憓銝蝮桀
+ // (敺靘潛, 隞 online friend 靘隤, capacity 銝蝮桀嗅祕瘝隞暻澆蔣)
+ // 甇文, pointer 64bit 璈其閬 8bytes, basic overhead 8*3 bytes,
+ // 雿鞈瘝暻澆之, 寧 S(int or short) 摮 size & capacity 瘥頛
+ T *base;
+ S room, n;
+
+ myvector() :base(0),room(0),n(0) {}
+ ~myvector() {
+ clear();
+ }
+ S append(T data) {
+ if(room<n+1)
+ resizefor(n+1);
+ base[n++]=data;
+ return n-1;
+ }
+ void pop_back() {
+ assert(n>0);
+ n--;
+ resizefor(n);
+ }
+ void clear() {
+ n=0;
+ resizefor(n);
+ }
+ /*
+ T& operator[](int idx) {
+ return base[idx];
+ }
+ */
+
+ void resizefor(S size) {
+ assert(size>=n);
+ if(size==0) {
+ if(base) freelist<T>::free(base, room);
+ base=0;
+ room=0;
+ } else {
+ S origroom=room;
+ if(room==0)
+ room=MIN_ROOM;
+ while(room<size) room=S(room*2);
+ if(size<MIN_ROOM) size=MIN_ROOM;
+ while(room/2>size) room=S(room/2);
+ if(room!=origroom || base==0) {
+ //base=(T*)realloc(base, sizeof(T)*room);
+ T* tmp=freelist<T>::alloc(room);
+ assert(tmp);
+ if(n>0)
+ memcpy(tmp, base, sizeof(T)*n);
+ if(base!=0)
+ freelist<T>::free(base, origroom);
+ base=tmp;
+ }
+ assert(base);
+ }
+ }
+};
+
+template<class R,class B>
+struct RelationList: public myvector<Relation, 8, short> {
+ RelationList() :myvector<Relation, 8, short>() {}
+ void add(Uid me, Uid him) {
+ RelationList<B,R>& bl=R::backlist(him);
+ short me_offset=append(Relation(him));
+ short him_offset=bl.append(Relation(me,me_offset));
+
+ setbackoffset(me_offset,him_offset);
+ assert(bl.base[him_offset].him==me);
+ assert(bl.base[him_offset].him_offset==me_offset);
+ }
+ void deleteall(Uid me) {
+ for(int i=0; i<n; i++) {
+ RelationList<B,R>& bl=R::backlist(base[i].him);
+ assert(bl.base[base[i].him_offset].him==me);
+ assert(bl.base[base[i].him_offset].him_offset==i);
+ bl.delete_half(base[i].him_offset);
+ //try_recycle(base[i].him); // dirty
+ }
+ clear();
+ }
+ private:
+ void setbackoffset(short which,short offset) {
+ assert(0<=which && which<n);
+ base[which].him_offset=offset;
+ }
+ void delete_half(short offset) {
+ assert(0<=offset && offset<n);
+ if(offset<n-1) {
+ base[offset]=base[n-1];
+ R::backlist(base[offset].him).setbackoffset(base[offset].him_offset,offset);
+ }
+ pop_back();
+ }
+ friend class RelationList<B,R>;
+};
+
+struct Like;
+struct Likeby;
+struct Hate;
+struct Hateby;
+struct Like: public Relation {
+ Like(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {}
+ static RelationList<Likeby,Like>& backlist(Uid him);
+};
+struct Likeby: public Relation {
+ Likeby(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {}
+ static RelationList<Like,Likeby>& backlist(Uid him);
+};
+struct Hate: public Relation {
+ Hate(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {}
+ static RelationList<Hateby,Hate>& backlist(Uid him);
+};
+struct Hateby: public Relation {
+ Hateby(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {}
+ static RelationList<Hate,Hateby>& backlist(Uid him);
+};
+
+
+struct Utmp {
+ Utmp() {
+ for(int i=0; i<USHM_SIZE; i++)
+ utmp[i]=-1;
+ }
+ /*
+ Uid& operator[](int idx) {
+ return utmp[idx];
+ }
+ */
+ public:
+ Uid utmp[USHM_SIZE];
+};
+static Utmp utmp;
+
+struct BBSUser {
+ Uid me;
+ int online;
+ /* assume utmplist is short, so just use plain vector and linear search */
+ myvector<int,2> utmplist;
+
+ RelationList<Like,Likeby> like;
+ RelationList<Hate,Hateby> hate;
+ RelationList<Likeby,Like> likeby;
+ RelationList<Hateby,Hate> hateby;
+
+ BBSUser() :me(-1),online(0),utmplist(),like(),hate(),likeby(),hateby() {}
+ BBSUser(Uid uid) :me(uid),online(0),utmplist(),like(),hate(),likeby(),hateby() {}
+
+ void login(int utmpidx, const Uid likehim[MAX_FRIEND], const Uid hatehim[MAX_REJECT]) {
+ if(online>0) {
+ /* multiple login 閰, 隞交敺銝甈∠ like/hate 箸 */
+ like.deleteall(me);
+ hate.deleteall(me);
+ }
+ utmp.utmp[utmpidx]=me;
+ utmplist.append(utmpidx);
+ online++;
+ assert(online==utmplist.n);
+ for(int i=0; i<MAX_FRIEND && likehim[i]; i++)
+ like.add(me, likehim[i]);
+ for(int i=0; i<MAX_REJECT && hatehim[i]; i++)
+ hate.add(me, hatehim[i]);
+ }
+
+ void logout(int utmpidx) {
+ assert(utmp.utmp[utmpidx]==me);
+ assert(online==utmplist.n);
+ for(int i=0; i<utmplist.n; i++)
+ if(utmplist.base[i]==utmpidx) {
+ utmplist.base[i]=utmplist.base[utmplist.n-1];
+ utmplist.pop_back();
+ break;
+ }
+ utmp.utmp[utmpidx]=-1;
+ online--;
+ assert(online==utmplist.n);
+ if(online==0) {
+ like.deleteall(me);
+ hate.deleteall(me);
+ }
+ }
+ bool isfree() const {
+ return online==0 && like.n==0 && hate.n==0 && likeby.n==0 && hateby.n==0;
+ }
+};
+
+struct UserList {
+ BBSUser users[MAX_USERS];
+
+ UserList() {
+ for(int i=0; i<MAX_USERS; i++)
+ users[i].me=i;
+ }
+ void login(Uid uid, Idx idx, const Uid likehim[MAX_FRIEND], const Uid hatehim[MAX_REJECT]) {
+ assert(uid<MAX_USERS);
+ assert(idx<USHM_SIZE);
+ /* 望潔嗅 logout event, 甇 logout 芰潛 utmp override */
+ if(utmp.utmp[idx]!=-1) users[utmp.utmp[idx]].logout(idx);
+ users[uid].login(idx, likehim, hatehim);
+ }
+};
+
+struct UserList userlist;
+RelationList<Likeby,Like>& Like::backlist(Uid him) { return userlist.users[him].likeby; }
+RelationList<Like,Likeby>& Likeby::backlist(Uid him) { return userlist.users[him].like; }
+RelationList<Hateby,Hate>& Hate::backlist(Uid him) { return userlist.users[him].hateby; }
+RelationList<Hate,Hateby>& Hateby::backlist(Uid him) { return userlist.users[him].hate; }
+
+struct Result {
+ Uid who;
+ int bits;
+ Result(Uid _who, int _bits) :who(_who),bits(_bits) {}
+ bool operator<(const Result& b) const {
+ return who<b.who;
+ }
+};
+
+int reverse_friend_stat(int stat)
+{
+ int stat1 = 0;
+ if (stat & IFH) stat1 |= HFM;
+ if (stat & IRH) stat1 |= HRM;
+ if (stat & HFM) stat1 |= IFH;
+ if (stat & HRM) stat1 |= IRH;
+ return stat1;
+}
+
+extern "C" void utmplogin(int uid, int index, const int like[MAX_FRIEND], const int hate[MAX_REJECT])
+{
+ /* login */
+ userlist.login(uid, index, like, hate);
+}
+
+extern "C" int genfriendlist(int uid, int index, ocfs_t *fs, int maxfs)
+{
+ /* collect data */
+ BBSUser& u=userlist.users[uid];
+ myvector<Result,64,short> work;
+ for(int i=0; i<u.like.n; i++) work.append(Result(u.like.base[i].him, IFH));
+ for(int i=0; i<u.hate.n; i++) work.append(Result(u.hate.base[i].him, IRH));
+ for(int i=0; i<u.likeby.n; i++) work.append(Result(u.likeby.base[i].him, HFM));
+ for(int i=0; i<u.hateby.n; i++) work.append(Result(u.hateby.base[i].him, HRM));
+
+ /* sort */
+ std::sort(work.base, work.base+work.n);
+ /* merge */
+ if(work.n>0) {
+ int newn=1;
+ for(int i=1; i<work.n; i++)
+ if(work.base[i].who==work.base[newn-1].who) {
+ work.base[newn-1].bits|=work.base[i].bits;
+ } else {
+ work.base[newn++]=work.base[i];
+ }
+ work.n=newn;
+ }
+ /* fill */
+ int nfs=0;
+ for(int i=0; i<work.n && nfs<maxfs; i++) {
+ BBSUser& h=userlist.users[work.base[i].who];
+ for(int j=0; j<h.utmplist.n && nfs<maxfs; j++) {
+ int rstat=reverse_friend_stat(work.base[i].bits);
+ if(h.utmplist.base[j]==index) continue;
+ fs[nfs].index=h.utmplist.base[j];
+ fs[nfs].uid=h.me;
+ fs[nfs].friendstat=(work.base[i].bits<<24)|h.utmplist.base[j];
+ fs[nfs].rfriendstat=(rstat<<24)|index;
+ nfs++;
+ }
+ }
+ return nfs;
+}
+
+extern "C" void utmplogoutall(void)
+{
+ for(int i=0; i<USHM_SIZE; i++)
+ if(utmp.utmp[i]!=-1)
+ userlist.users[utmp.utmp[i]].logout(i);
+}
+
diff --git a/daemon/cached/utmpserver.c b/daemon/cached/utmpserver.c
new file mode 100644
index 00000000..85adf8c3
--- /dev/null
+++ b/daemon/cached/utmpserver.c
@@ -0,0 +1,222 @@
+/* $Id$ */
+#include "bbs.h"
+#include <err.h>
+
+struct {
+ int uid;
+ int nFriends, nRejects;
+ int friend[MAX_FRIEND];
+ int reject[MAX_REJECT];
+} utmp[USHM_SIZE];
+
+#ifdef NOFLOODING
+#define MAXWAIT 1024
+#define FLUSHTIME (3600*6)
+
+struct {
+ time_t lasttime;
+ int count;
+} flooding[MAX_USERS];
+
+int nWaits, lastflushtime;
+struct {
+ int uid;
+ int fd;
+ int index;
+} waitqueue[MAXWAIT];
+#endif /* NOFLOODING */
+
+inline int countarray(int *s, int max)
+{
+ int i;
+ for( i = 0 ; i < max && s[i] ; ++i )
+ ;
+ return i;
+}
+
+int
+reverse_friend_stat(int stat)
+{
+ int stat1 = 0;
+ if (stat & IFH)
+ stat1 |= HFM;
+ if (stat & IRH)
+ stat1 |= HRM;
+ if (stat & HFM)
+ stat1 |= IFH;
+ if (stat & HRM)
+ stat1 |= IRH;
+ if (stat & IBH)
+ stat1 |= IBH;
+ return stat1;
+}
+
+int set_friend_bit(int me, int ui)
+{
+ int hit = 0;
+ /* 判斷對方是否為我的朋友 ? */
+ if( intbsearch(utmp[ui].uid, utmp[me].friend, utmp[me].nFriends) )
+ hit = IFH;
+
+ /* 判斷我是否為對方的朋友 ? */
+ if( intbsearch(utmp[me].uid, utmp[ui].friend, utmp[ui].nFriends) )
+ hit |= HFM;
+
+ /* 判斷對方是否為我的仇人 ? */
+ if( intbsearch(utmp[ui].uid, utmp[me].reject, utmp[me].nRejects) )
+ hit |= IRH;
+
+ /* 判斷我是否為對方的仇人 ? */
+ if( intbsearch(utmp[me].uid, utmp[ui].reject, utmp[ui].nRejects) )
+ hit |= HRM;
+
+ return hit;
+}
+
+void initdata(int index)
+{
+ utmp[index].nFriends = countarray(utmp[index].friend, MAX_FRIEND);
+ utmp[index].nRejects = countarray(utmp[index].reject, MAX_REJECT);
+ if( utmp[index].nFriends > 0 )
+ qsort(utmp[index].friend, utmp[index].nFriends,
+ sizeof(int), qsort_intcompar);
+ if( utmp[index].nRejects > 0 )
+ qsort(utmp[index].reject, utmp[index].nRejects,
+ sizeof(int), qsort_intcompar);
+}
+
+inline void syncutmp(int cfd)
+{
+ int nSynced = 0, i;
+ for( i = 0 ; i < USHM_SIZE ; ++i, ++nSynced )
+ if( toread(cfd, &utmp[i].uid, sizeof(utmp[i].uid)) > 0 &&
+ toread(cfd, utmp[i].friend, sizeof(utmp[i].friend)) > 0 &&
+ toread(cfd, utmp[i].reject, sizeof(utmp[i].reject)) > 0 ){
+ if( utmp[i].uid )
+ initdata(i);
+ }
+ else
+ for( ; i < USHM_SIZE ; ++i )
+ utmp[i].uid = 0;
+ close(cfd);
+ fprintf(stderr, "%d users synced\n", nSynced);
+}
+
+void processlogin(int cfd, int uid, int index)
+{
+ if( toread(cfd, utmp[index].friend, sizeof(utmp[index].friend)) > 0 &&
+ toread(cfd, utmp[index].reject, sizeof(utmp[index].reject)) > 0 ){
+ /* 因為 logout 的時候並不會通知 utmpserver , 可能會查到一些
+ 已經 logout 的帳號。所以不能只取 MAX_FRIEND 而要多取一些 */
+#define MAX_FS (2 * MAX_FRIEND)
+ int iu, nFrs, stat, rstat;
+ ocfs_t fs[MAX_FS];
+
+ utmp[index].uid = uid;
+ initdata(index);
+
+ for( nFrs = iu = 0 ; iu < USHM_SIZE && nFrs < MAX_FS ; ++iu )
+ if( iu != index && utmp[iu].uid ){
+ if( (stat = set_friend_bit(index, iu)) ){
+ rstat = reverse_friend_stat(stat);
+ fs[nFrs].index = iu;
+ fs[nFrs].uid = utmp[iu].uid;
+ fs[nFrs].friendstat = (stat << 24) + iu;
+ fs[nFrs].rfriendstat = (rstat << 24) + index;
+ ++nFrs;
+ }
+ }
+
+ towrite(cfd, &fs, sizeof(ocfs_t) * nFrs);
+ }
+ close(cfd);
+}
+
+#ifdef NOFLOODING
+void flushwaitqueue(void)
+{
+ int i;
+ for( i = 0 ; i < nWaits ; ++i )
+ processlogin(waitqueue[i].fd, waitqueue[i].uid, waitqueue[i].index);
+ lastflushtime = time(NULL);
+ nWaits = 0;
+ memset(flooding, 0, sizeof(flooding));
+}
+#endif
+
+/* XXX 具有被 DoS 的可能, 請用 firewall 之類擋起來 */
+int main(int argc, char **argv)
+{
+ struct sockaddr_in clientaddr;
+ int ch, port = 5120, sfd, cfd, len, index, uid;
+ char *iface_ip = NULL;
+
+ 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;
+
+#ifdef NOFLOODING
+ lastflushtime = time(NULL);
+#endif
+ while( 1 ){
+#ifdef NOFLOODING
+ if( lastflushtime < (time(NULL) - 1800) )
+ flushwaitqueue();
+#endif
+
+ len = sizeof(clientaddr);
+ if( (cfd = accept(sfd, (struct sockaddr *)&clientaddr, &len)) < 0 ){
+ if( errno != EINTR )
+ sleep(1);
+ continue;
+ }
+ toread(cfd, &index, sizeof(index));
+ if( index == -1 ){
+ syncutmp(cfd);
+ continue;
+ }
+
+ if( toread(cfd, &uid, sizeof(uid)) > 0 ){
+ if( !(0 < uid || uid > MAX_USERS) ){ /* for safety */
+ close(cfd);
+ continue;
+ }
+
+#ifdef NOFLOODING
+ if( (time(NULL) - flooding[uid].lasttime) < 20 )
+ ++flooding[uid].count;
+ if( flooding[uid].count > 10 ){
+ if( nWaits == MAXWAIT )
+ flushwaitqueue();
+ waitqueue[nWaits].uid = uid;
+ waitqueue[nWaits].index = index;
+ waitqueue[nWaits].fd = cfd;
+ ++nWaits;
+
+ continue;
+ }
+ flooding[uid].lasttime = time(NULL);
+#endif
+
+ /* cfd will be closed in processlogin() */
+ processlogin(cfd, uid, index);
+ } else {
+ close(cfd);
+ }
+ }
+ return 0;
+}
diff --git a/daemon/cached/utmpserver2.c b/daemon/cached/utmpserver2.c
new file mode 100644
index 00000000..12c3a299
--- /dev/null
+++ b/daemon/cached/utmpserver2.c
@@ -0,0 +1,290 @@
+/* $Id$ */
+#include <stdio.h>
+#include <sys/time.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 FAKEDATA
+FILE *fp;
+#endif
+#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;
+#ifdef FAKEDATA
+ fread(&uid, sizeof(uid), 1, fp);
+ if(uid==-2)
+ break;
+ fread(like, sizeof(like), 1, fp);
+ fread(hate, sizeof(hate), 1, fp);
+#else
+ if( toread(cfd, &uid, sizeof(uid)) <= 0 ||
+ toread(cfd, like, sizeof(like)) <= 0 ||
+ toread(cfd, hate, sizeof(hate)) <= 0)
+ break;
+#endif
+#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");
+}
+
+void processlogin(int cfd, 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];
+
+#ifdef FAKEDATA
+ fread(like, sizeof(like), 1, fp);
+ fread(hate, sizeof(hate), 1, fp);
+#else
+ if(toread(cfd, like, sizeof(like)) <= 0 ||
+ toread(cfd, hate, sizeof(hate)) <= 0)
+ return;
+#endif
+#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);
+#ifndef FAKEDATA
+ res=0;
+#ifdef NOFLOODING
+ res=action_frequently(uid);
+#endif
+ towrite(cfd, &res, sizeof(res));
+ towrite(cfd, &nfs, sizeof(nfs));
+ towrite(cfd, fs, sizeof(ocfs_t) * nfs);
+#endif
+}
+
+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;
+}
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in clientaddr;
+ int ch, port = 5120, sfd, cfd, len;
+ char *iface_ip = NULL;
+ int cmd;
+ int uid,index;
+ int fail;
+ int firstsync=0;
+
+#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;
+ }
+
+#ifdef FAKEDATA
+ fp=fopen("utmp.data","rb");
+#else
+ if( (sfd = tobind(iface_ip, port)) < 0 )
+ return 1;
+#endif
+ while(1) {
+#ifdef FAKEDATA
+ if(fread(&cmd, sizeof(cmd), 1, fp)==0) break;
+#else
+ len = sizeof(clientaddr);
+ if( (cfd = accept(sfd, (struct sockaddr *)&clientaddr, &len)) < 0 ){
+ if( errno != EINTR )
+ sleep(1);
+ continue;
+ }
+ toread(cfd, &cmd, sizeof(cmd));
+#endif
+
+ if(cmd==-1) {
+ syncutmp(cfd);
+#ifndef FAKEDATA
+ close(cfd);
+#endif
+ firstsync=1;
+ continue;
+ }
+ if(!firstsync) {
+ // don't accept client before first sync, to prevent incorrect friend data
+ close(cfd);
+ continue;
+ }
+
+ fail=0;
+#ifdef FAKEDATA
+ fread(&uid, sizeof(uid), 1, fp);
+ fread(&index, sizeof(index), 1, fp);
+#else
+ if(cmd==-2) {
+ if(toread(cfd, &index, sizeof(index)) <= 0)
+ fail=1;
+ if(toread(cfd, &uid, sizeof(uid)) <= 0)
+ fail=1;
+ } else if(cmd>=0) {
+ // old client
+ fail=1;
+ } else {
+ printf("unknown cmd=%d\n",cmd);
+ }
+#endif
+ if(index>=USHM_SIZE) {
+ fprintf(stderr, "bad index=%d\n",index);
+ fail=1;
+ }
+
+ if(fail) {
+#ifndef FAKEDATA
+ close(cfd);
+#endif
+ continue;
+ }
+
+ count_login++;
+ processlogin(cfd, uid, index);
+ if(count_login>=4000 || time(NULL)-begin_time>30*60)
+ showstat();
+#ifndef FAKEDATA
+ close(cfd);
+#endif
+ }
+#ifdef FAKEDATA
+ fclose(fp);
+#endif
+ return 0;
+}
diff --git a/daemon/cached/utmpserver3.c b/daemon/cached/utmpserver3.c
new file mode 100644
index 00000000..0e1eef02
--- /dev/null
+++ b/daemon/cached/utmpserver3.c
@@ -0,0 +1,341 @@
+/* $Id$ */
+#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;
+ 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_SYNC,
+ FSM_LOGIN,
+ FSM_WRITEBACK,
+ FSM_EXIT
+};
+
+static int firstsync=0;
+
+struct timeval tv = {5, 0};
+struct event ev;
+int clients = 0;
+
+#define READ_BLOCK 1024
+#define MAX_CLIENTS 10
+
+void connection_client(int cfd, short event, void *arg)
+{
+ struct client_state *cs = arg;
+ int cmd, break_out = 0;
+ 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 (!break_out) {
+ switch (cs->state) {
+ case FSM_ENTER:
+ if (EVBUFFER_LENGTH(cs->evb) < sizeof(int)) {
+ break_out = 1;
+ break;
+ }
+ 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->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);
+ firstsync = 1;
+ cs->state = FSM_EXIT;
+ break;
+ case FSM_LOGIN:
+ if (firstsync) {
+ if (EVBUFFER_LENGTH(cs->evb) < (2 + MAX_FRIEND + MAX_REJECT) * sizeof(int)) {
+ break_out = 1;
+ break;
+ }
+ evbuffer_remove(cs->evb, &index, sizeof(index));
+ evbuffer_remove(cs->evb, &uid, sizeof(uid));
+ if (index >= USHM_SIZE) {
+ fprintf(stderr, "bad index=%d\n", index);
+ cs->state = FSM_EXIT;
+ break;
+ }
+ count_login++;
+ processlogin(cs, uid, index);
+ if (count_login >= 4000 || (time(NULL) - begin_time) > 30*60)
+ showstat();
+ cs->state = FSM_WRITEBACK;
+ break_out = 1;
+ }
+ else
+ cs->state = FSM_EXIT;
+ break;
+ case FSM_WRITEBACK:
+ if (event & EV_WRITE)
+ if (evbuffer_write(cs->evb, cfd) <= 0 && EVBUFFER_LENGTH(cs->evb) > 0)
+ break_out = 1;
+ if (EVBUFFER_LENGTH(cs->evb) == 0)
+ cs->state = FSM_EXIT;
+ break;
+ case FSM_EXIT:
+ if (clients == MAX_CLIENTS)
+ event_add(&ev, NULL);
+ close(cfd);
+ evbuffer_free(cs->evb);
+ free(cs);
+ clients--;
+ return;
+ break;
+ }
+ }
+ if (cs->state == FSM_WRITEBACK)
+ event_set(&cs->ev, cfd, EV_WRITE, (void *) connection_client, cs);
+ 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->evb = evbuffer_new();
+
+ event_set(&cs->ev, cfd, EV_READ, (void *) connection_client, cs);
+ event_add(&cs->ev, &tv);
+ clients++;
+
+ if (clients >= MAX_CLIENTS) {
+ event_del(&ev);
+ }
+}
+
+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();
+ event_set(&ev, sfd, EV_READ | EV_PERSIST, connection_accept, &ev);
+ event_add(&ev, NULL);
+ event_dispatch();
+ return 0;
+}
diff --git a/daemon/cached/utmpsync.c b/daemon/cached/utmpsync.c
new file mode 100644
index 00000000..69d1c623
--- /dev/null
+++ b/daemon/cached/utmpsync.c
@@ -0,0 +1,27 @@
+/* $Id$ */
+#include "bbs.h"
+#include <err.h>
+
+extern SHM_t *SHM;
+
+int main(int argc, char **argv)
+{
+ int sfd, index, i;
+ attach_SHM();
+ if( (sfd = toconnect(OUTTACACHEHOST, OUTTACACHEPORT)) < 0 ) {
+ printf("connect fail\n");
+ return 1;
+ }
+
+ index = -1;
+ towrite(sfd, &index, sizeof(index));
+ for( i = 0 ; i < USHM_SIZE ; ++i )
+ if( towrite(sfd, &SHM->uinfo[i].uid, sizeof(SHM->uinfo[i].uid)) < 0 ||
+ towrite(sfd, SHM->uinfo[i].myfriend,
+ sizeof(SHM->uinfo[i].myfriend)) < 0 ||
+ towrite(sfd, SHM->uinfo[i].reject,
+ sizeof(SHM->uinfo[i].reject)) < 0 ){
+ fprintf(stderr, "sync error %d\n", i);
+ }
+ return 0;
+}