/* $Id$ */ #include "bbs.h" #include #include extern SHM_t *SHM; /* utmpfix ----------------------------------------------------------------- */ /* TODO merge with mbbsd/talk.c logout_friend_online() */ int logout_friend_online(userinfo_t *utmp) { int my_friend_idx, thefriend; int k; int offset=(int) (utmp - &SHM->uinfo[0]); userinfo_t *ui; for(; utmp->friendtotal>0; utmp->friendtotal--) { if( !(0 <= utmp->friendtotal && utmp->friendtotal < MAX_FRIEND) ) return 1; my_friend_idx=utmp->friendtotal-1; thefriend = (utmp->friend_online[my_friend_idx] & 0xFFFFFF); utmp->friend_online[my_friend_idx]=0; if( !(0 <= thefriend && thefriend < USHM_SIZE) ) { printf("\tonline friend error(%d)\n", thefriend); continue; } ui = &SHM->uinfo[thefriend]; if(ui->pid==0 || ui==utmp) continue; if(ui->friendtotal > MAX_FRIEND || ui->friendtotal<0) { printf("\tfriend(%d) has too many/less(%d) friends\n", thefriend, ui->friendtotal); continue; } for(k=0; kfriendtotal && kfriend_online[k] & 0xFFFFFF) !=offset; k++); if( k < ui->friendtotal && k < MAX_FRIEND ){ ui->friendtotal--; ui->friend_online[k]=ui->friend_online[ui->friendtotal]; ui->friend_online[ui->friendtotal]=0; } } return 0; } void purge_utmp(userinfo_t *uentp) { logout_friend_online(uentp); //memset(uentp, 0, sizeof(userinfo_t)); } typedef struct { int index; int idle; } IDLE_t; int sfIDLE(const void *a, const void *b) { return ((IDLE_t *)b)->idle - ((IDLE_t *)a)->idle; } int utmpfix(int argc, char **argv) { int i, fast = 0, nownum = SHM->UTMPnumber; int which, nactive = 0, dofork = 1, daemonsleep = 0; time_t now; char *clean, buf[1024]; IDLE_t idle[USHM_SIZE]; char changeflag = 0; time_t idletimeout = IDLE_TIMEOUT; int lowerbound = 100, upperbound = 0; char ch; int killtop = 0; struct { pid_t pid; int where; } killlist[USHM_SIZE]; while( (ch = getopt(argc, argv, "nt:l:FD:u:")) != -1 ) switch( ch ){ case 'n': fast = 1; break; case 't': idletimeout = atoi(optarg); break; case 'l': lowerbound = atoi(optarg); break; case 'F': dofork = 0; break; case 'D': daemonsleep = atoi(optarg); break; case 'u': upperbound = atoi(optarg); break; default: printf("usage:\tshmctl\tutmpfix [-n] [-t timeout] [-F] [-D sleep]\n"); return 1; } if( daemonsleep ) switch( fork() ){ case -1: perror("fork()"); return 0; case 0: break; default: return 0; } if( daemonsleep || dofork ){ int times = 1000, status; pid_t pid; while( daemonsleep ? 1 : times-- ) switch( pid = fork() ){ case -1: sleep(1); break; case 0: #ifndef VALGRIND setproctitle("utmpfix"); #endif goto DoUtmpfix; default: #ifndef VALGRIND setproctitle(daemonsleep ? "utmpfixd(wait for %d)" : "utmpfix(wait for %d)", (int)pid); #endif waitpid(pid, &status, 0); if( WIFEXITED(status) && !daemonsleep ) return 0; if( !WIFEXITED(status) ){ /* last utmpfix fails, so SHM->UTMPbusystate is holded */ SHM->UTMPbusystate = 0; } } return 0; // never reach } DoUtmpfix: killtop=0; changeflag=0; for( i = 0 ; i < 5 ; ++i ) if( !SHM->UTMPbusystate ) break; else{ puts("utmpshm is busy...."); sleep(1); } SHM->UTMPbusystate = 1; printf("starting scaning... %s \n", (fast ? "(fast mode)" : "")); nownum = SHM->UTMPnumber; #ifdef OUTTA_TIMER now = SHM->GV2.e.now; #else time(&now); #endif for( i = 0, nactive = 0 ; i < USHM_SIZE ; ++i ) if( SHM->uinfo[i].pid ){ idle[nactive].index = i; idle[nactive].idle = now - SHM->uinfo[i].lastact; ++nactive; } if( !fast ) qsort(idle, nactive, sizeof(IDLE_t), sfIDLE); #define addkilllist(a) \ do { \ pid_t pid=SHM->uinfo[(a)].pid; \ if(pid > 0) { \ killlist[killtop].where = (a); \ killlist[killtop++].pid = pid; \ } \ } while( 0 ) for( i = 0 ; i < nactive ; ++i ){ which = idle[i].index; clean = NULL; if( !isalpha(SHM->uinfo[which].userid[0]) ){ clean = "userid error"; addkilllist(which); } else if( memchr(SHM->uinfo[which].userid, '\0', IDLEN + 1) == NULL ){ clean = "userid without z"; addkilllist(which); } else if( SHM->uinfo[which].friendtotal > MAX_FRIEND || SHM->uinfo[which].friendtotal<0 ){ clean = "too many/less friend"; addkilllist(which); } else if( searchuser(SHM->uinfo[which].userid, NULL) == 0 ){ clean = "user not exist"; addkilllist(which); } else if( kill(SHM->uinfo[which].pid, 0) < 0 ){ /* 此條件應放最後; 其他欄位沒問題但 process 不存在才 purge_utmp */ clean = "process error"; purge_utmp(&SHM->uinfo[which]); } #ifdef DOTIMEOUT else if( (strcasecmp(SHM->uinfo[which].userid, STR_GUEST)==0 && idle[i].idle > 60*15) || (!fast && nownum > lowerbound && idle[i].idle > idletimeout ) ) { sprintf(buf, "timeout(%s", ctime4(&SHM->uinfo[which].lastact)); buf[strlen(buf) - 1] = 0; strcat(buf, ")"); clean = buf; addkilllist(which); purge_utmp(&SHM->uinfo[which]); printf("%s\n", buf); --nownum; continue; } #endif if( clean ){ printf("clean %06d(%s), userid: %s\n", i, clean, SHM->uinfo[which].userid); memset(&SHM->uinfo[which], 0, sizeof(userinfo_t)); --nownum; changeflag = 1; } } for( i = 0 ; i < killtop ; ++i ){ printf("sending SIGHUP to %d\n", (int)killlist[i].pid); kill(killlist[i].pid, SIGHUP); } sleep(3); for( i = 0 ; i < killtop ; ++i ) // FIXME 前面已經 memset 把 SHM->uinfo[which] 清掉了, 此處檢查 pid 無用 if( SHM->uinfo[killlist[i].where].pid == killlist[i].pid && kill(killlist[i].pid, 0) == 0 ){ // still alive printf("sending SIGKILL to %d\n", (int)killlist[i].pid); kill(killlist[i].pid, SIGKILL); purge_utmp(&SHM->uinfo[killlist[i].where]); } SHM->UTMPbusystate = 0; if( changeflag ) SHM->UTMPneedsort = 1; if( daemonsleep ){ do{ sleep(daemonsleep); } while( upperbound && SHM->UTMPnumber < upperbound ); goto DoUtmpfix; /* XXX: goto */ } return 0; } /* end of utmpfix ---------------------------------------------------------- */ /* utmpsortd --------------------------------------------------------------- */ static int cmputmpuserid(const void * i, const void * j) { return strncasecmp(SHM->uinfo[*(int*)i].userid, SHM->uinfo[*(int*)j].userid, IDLEN); } static int cmputmpmode(const void * i, const void * j) { return SHM->uinfo[*(int*)i].mode - SHM->uinfo[*(int*)j].mode; } static int cmputmpidle(const void * i, const void * j) { return SHM->uinfo[*(int*)i].lastact - SHM->uinfo[*(int*)j].lastact; } static int cmputmpfrom(const void * i, const void * j) { return strncmp(SHM->uinfo[*(int*)i].from, SHM->uinfo[*(int*)j].from, sizeof(SHM->uinfo[0].from)); } static int cmputmpfive(const void * i, const void * j) { userinfo_t *a=&SHM->uinfo[*(int*)i],*b=&SHM->uinfo[*(int*)j]; int played_a=(a->five_win+a->five_lose+a->five_tie)!=0; int played_b=(b->five_win+b->five_lose+b->five_tie)!=0; int type; if ((type = played_b - played_a)) return type; if (played_a == 0) return 0; if ((type = b->five_win - a->five_win)) return type; if ((type = a->five_lose - b->five_lose)) return type; return a->five_tie - b->five_tie; } static int cmputmpchc(const void * i, const void * j) { userinfo_t *a=&SHM->uinfo[*(int*)i],*b=&SHM->uinfo[*(int*)j]; int total_a=a->chc_win+a->chc_lose+a->chc_tie; int total_b=b->chc_win+b->chc_lose+b->chc_tie; int played_a=(total_a!=0); int played_b=(total_b!=0); int type; // NOTE: 目前 "別找我下棋" 不影響排序 /* 1. "找我下棋" 排最前面 */ if ((a->withme&WITHME_CHESS)!=(b->withme&WITHME_CHESS)) return (a->withme&WITHME_CHESS)?-1:1; #ifdef CHC_SORTBY_RATING /* 2. 下超過十盤棋用等級分排序 */ if ((total_a>=10)!=(total_b>=10)) return (total_a>=10)?-1:1; if (total_a>=10 && total_b>=10) { if (a->chess_elo_rating!=b->chess_elo_rating) return b->chess_elo_rating-a->chess_elo_rating; } #endif /* 3. 有下過棋的在沒下過的前面 */ if ((type = played_b - played_a)) return type; if (played_a == 0) return 0; /* 4. 剩下(下不超過十盤或等級分相同, 或不用等級分排序)的人以勝負和排 */ if ((type = b->chc_win - a->chc_win)) return type; if ((type = a->chc_lose - b->chc_lose)) return type; return a->chc_tie - b->chc_tie; } static int cmputmppid(const void * i, const void * j) { return SHM->uinfo[*(int*)i].pid - SHM->uinfo[*(int*)j].pid; } static int cmputmpuid(const void * i, const void * j) { return SHM->uinfo[*(int*)i].uid - SHM->uinfo[*(int*)j].uid; } inline void utmpsort(int sortall) { userinfo_t *uentp; int count, i, ns; short nusers[MAX_BOARD]; SHM->UTMPbusystate = 1; #ifdef OUTTA_TIMER SHM->UTMPuptime = SHM->GV2.e.now; #else SHM->UTMPuptime = time(NULL); #endif ns = (SHM->currsorted ? 0 : 1); for (uentp = &SHM->uinfo[0], count = i = 0; i < USHM_SIZE; ++i, uentp = &SHM->uinfo[i]) { if (uentp->pid) { if (uentp->sex < 0 || uentp->sex > 7) purge_utmp(uentp); else SHM->sorted[ns][0][count++] = i; } } SHM->UTMPnumber = count; qsort(SHM->sorted[ns][0], count, sizeof(int), cmputmpuserid); memcpy(SHM->sorted[ns][6], SHM->sorted[ns][0], sizeof(int) * count); memcpy(SHM->sorted[ns][7], SHM->sorted[ns][0], sizeof(int) * count); qsort(SHM->sorted[ns][6], count, sizeof(int), cmputmpuid); qsort(SHM->sorted[ns][7], count, sizeof(int), cmputmppid); if( sortall ){ memcpy(SHM->sorted[ns][1], SHM->sorted[ns][0], sizeof(int) * count); memcpy(SHM->sorted[ns][2], SHM->sorted[ns][0], sizeof(int) * count); memcpy(SHM->sorted[ns][3], SHM->sorted[ns][0], sizeof(int) * count); memcpy(SHM->sorted[ns][4], SHM->sorted[ns][0], sizeof(int) * count); memcpy(SHM->sorted[ns][5], SHM->sorted[ns][0], sizeof(int) * count); qsort(SHM->sorted[ns][1], count, sizeof(int), cmputmpmode); qsort(SHM->sorted[ns][2], count, sizeof(int), cmputmpidle); qsort(SHM->sorted[ns][3], count, sizeof(int), cmputmpfrom); qsort(SHM->sorted[ns][4], count, sizeof(int), cmputmpfive); qsort(SHM->sorted[ns][5], count, sizeof(int), cmputmpchc); memset(nusers, 0, sizeof(nusers)); for (i = 0; i < count; ++i) { uentp = &SHM->uinfo[SHM->sorted[ns][0][i]]; if (uentp && uentp->pid && 0 < uentp->brc_id && uentp->brc_id < MAX_BOARD) ++nusers[uentp->brc_id - 1]; } { #if HOTBOARDCACHE int k, r, last = 0, top = 0; int HBcache[HOTBOARDCACHE]; for (i = 0; i < HOTBOARDCACHE; i++) HBcache[i]=-1; #endif for (i = 0; i < SHM->Bnumber; i++) if (SHM->bcache[i].brdname[0] != 0){ SHM->bcache[i].nuser = nusers[i]; #if HOTBOARDCACHE if( nusers[i] > 8 && (top < HOTBOARDCACHE || nusers[i] > last) && IS_BOARD(&SHM->bcache[i]) && #ifdef USE_COOLDOWN !(SHM->bcache[i].brdattr & BRD_COOLDOWN) && #endif IS_OPENBRD(&SHM->bcache[i]) ){ for( k = top - 1 ; k >= 0 ; --k ) if(HBcache[k]>=0 && nusers[i] < SHM->bcache[HBcache[k]].nuser ) break; if( top < HOTBOARDCACHE ) ++top; for( r = top - 1 ; r > (k + 1) ; --r ) HBcache[r] = HBcache[r - 1]; HBcache[k + 1] = i; last = nusers[HBcache[top - 1]]; } #endif } #if HOTBOARDCACHE memcpy(SHM->HBcache, HBcache, sizeof(HBcache)); SHM->nHOTs = top; #endif } } SHM->currsorted = ns; SHM->UTMPbusystate = 0; } int utmpsortd(int argc, char **argv) { pid_t pid; int interval; // sleep interval in microsecond(1/10**6) int sortall, counter = 0; if( fork() > 0 ){ puts("sortutmpd daemonized..."); return 0; } if( argc < 2 || (interval = atoi(argv[1])) < 500000 ) interval = 1000000; // default to 1 sec sortall = ((argc < 3) ? 1 : atoi(argv[2])); #ifndef VALGRIND setproctitle("shmctl utmpsortd"); #endif while( 1 ){ if( (pid = fork()) != 0 ){ int s; waitpid(pid, &s, 0); } else{ while( 1 ){ int i; for( i = 0 ; SHM->UTMPbusystate && i < 5 ; ++i ) usleep(300000); if( SHM->UTMPneedsort ){ if( ++counter == sortall ){ utmpsort(1); counter = 0; } else utmpsort(0); } usleep(interval); } } } } /* end of utmpsortd -------------------------------------------------------- */ char *CTIMEx(char *buf, time4_t t) { strcpy(buf, ctime4(&t)); buf[strlen(buf) - 1] = 0; return buf; } int utmpstatus(int argc, char **argv) { time_t now; char upbuf[64], nowbuf[64]; #ifdef OUTTA_TIMER now = SHM->GV2.e.now; #else now = time(NULL); #endif CTIMEx(upbuf, SHM->UTMPuptime); CTIMEx(nowbuf, now); printf("now: %s\n", nowbuf); printf("currsorted: %d\n", SHM->currsorted); printf("uptime: %s\n", upbuf); printf("number: %d\n", SHM->UTMPnumber); printf("busystate: %d\n", SHM->UTMPbusystate); return 0; } int utmpreset(int argc, char **argv) { SHM->UTMPbusystate=0; utmpstatus(0, NULL); return 0; } #define TIMES 10 int utmpwatch(int argc, char **argv) { int i; while( 1 ){ for( i = 0 ; i < TIMES ; ++i ){ usleep(300); if( !SHM->UTMPbusystate ) break; puts("busying..."); } if( i == TIMES ){ puts("reset!"); SHM->UTMPbusystate = 0; } } return 0; } int utmpnum(int argc, char **argv) { printf("%d.0\n", SHM->UTMPnumber); return 0; } char *GV2str[] = {"dymaxactive", "toomanyusers", "noonlineuser", NULL}; int showglobal(int argc, char **argv) { int i; for( i = 0 ; GV2str[i] != NULL ; ++i ) printf("GV2.%s = %d\n", GV2str[i], SHM->GV2.v[i]); return 0; } int setglobal(int argc, char **argv) { int where, value; if( argc != 3 ){ puts("usage: shmctl setglobal (GV2) newvalue"); return 1; } value = atoi(argv[2]); for( where = 0 ; GV2str[where] != NULL ; ++where ) if( strcmp(GV2str[where], argv[1]) == 0 ){ printf("GV2.%s = %d -> ", GV2str[where], SHM->GV2.v[where]); printf("%d\n", SHM->GV2.v[where] = value); return 0; } printf("SHM global variable %s not found\n", argv[1]); return 1; } int listpid(int argc, char **argv) { int i; for( i = 0 ; i < USHM_SIZE ; ++i ) if( SHM->uinfo[i].pid > 0 ) printf("%d\n", SHM->uinfo[i].pid); return 0; } int listbrd(int argc, char **argv) { int i = 0; if (argc == 2) i = atoi(argv[1]); if(i > 0 && i < MAX_BOARD) { int di = i; /* print details */ boardheader_t b = bcache[di-1]; printf("brdname(bid):\t%s\n", b.brdname); printf("title:\t%s\n", b.title); printf("BM:\t%s\n", b.BM); printf("brdattr:\t%08x ", b.brdattr); #define SHOWBRDATTR(x) if(b.brdattr & x) printf(#x " "); SHOWBRDATTR(BRD_NOZAP); SHOWBRDATTR(BRD_NOCOUNT); SHOWBRDATTR(BRD_NOTRAN); SHOWBRDATTR(BRD_GROUPBOARD); SHOWBRDATTR(BRD_HIDE); SHOWBRDATTR(BRD_POSTMASK); SHOWBRDATTR(BRD_ANONYMOUS); SHOWBRDATTR(BRD_DEFAULTANONYMOUS); SHOWBRDATTR(BRD_BAD); SHOWBRDATTR(BRD_VOTEBOARD); SHOWBRDATTR(BRD_WARNEL); SHOWBRDATTR(BRD_TOP); SHOWBRDATTR(BRD_NORECOMMEND); SHOWBRDATTR(BRD_BLOG); SHOWBRDATTR(BRD_BMCOUNT); SHOWBRDATTR(BRD_SYMBOLIC); SHOWBRDATTR(BRD_NOBOO); SHOWBRDATTR(BRD_LOCALSAVE); SHOWBRDATTR(BRD_RESTRICTEDPOST); SHOWBRDATTR(BRD_GUESTPOST); SHOWBRDATTR(BRD_COOLDOWN); SHOWBRDATTR(BRD_CPLOG); SHOWBRDATTR(BRD_NOFASTRECMD); printf("\n"); printf("post_limit_posts:\t%d\n", b.post_limit_posts); printf("post_limit_logins:\t%d\n", b.post_limit_logins); printf("post_limit_regtime:\t%d\n", b.post_limit_regtime); printf("level:\t%d\n", b.level); printf("gid:\t%d\n", b.gid); printf("parent:\t%d\n", b.parent); printf("childcount:\t%d\n", b.childcount); printf("nuser:\t%d\n", b.nuser); printf("next[0]:\t%d\n", b.next[0]); printf("next[1]:\t%d\n", b.next[1]); printf("firstchild[0]:\t%d\n", b.firstchild[0]); printf("firstchild[1]:\t%d\n", b.firstchild[1]); printf("---- children: ---- \n"); for (i = 0; i < MAX_BOARD; i++) { if(bcache[i].gid == di && bcache[i].brdname[0]) printf("%4d %-13s%-25.25s%s\n", i+1, bcache[i].brdname, bcache[i].BM, bcache[i].title); } } else for( i = 0 ; i < MAX_BOARD ; ++i ) { if(bcache[i].brdname[0]) printf("%03d %-13s%-25.25s%s\n", i+1, bcache[i].brdname, bcache[i].BM, bcache[i].title); } return 0; } static void update_brd(int i) { if(substitute_record(BBSHOME "/" FN_BOARD, &bcache[i],sizeof(boardheader_t),i+1) < 0) { printf("\n! CANNOT WRITE: " BBSHOME "/" FN_BOARD "\n"); exit(0); } } int fixbrd(int argc, char **argv) { int i = 0; for( i = 0 ; i < MAX_BOARD ; ++i ) { if(!bcache[i].brdname[0]) continue; /* do whatever you wanna fix here. */ #if 0 /* upgrade from old NOFASTRECMD (default pause) to new format * (BM config) */ if(bcache[i].brdattr & BRD_NOFASTRECMD) { printf("board with no fastrecmd: #%d [%s]\n", i+1, bcache[i].brdname); bcache[i].fastrecommend_pause = 90; update_brd(i); } #endif #if 0 /* fix parent, hope so */ if(bcache[i].parent > MAX_BOARD) { printf("parent: #%d [%s] *%d\n", i+1, bcache[i].brdname, bcache[i].parent); bcache[i].parent = 0; update_brd(i); } #endif #if 0 /* alert wrong gid */ if(bcache[i].gid < 1) { printf("gid: #%d [%s] *%d\n", i+1, bcache[i].brdname, bcache[i].gid); } #endif } return 0; } #ifdef OUTTA_TIMER int timed(int argc, char **argv) { pid_t pid; if( (pid = fork()) < 0 ) perror("fork()"); if( pid != 0 ) return 0; #ifndef VALGRIND setproctitle("shmctl timed"); #endif while( 1 ){ SHM->GV2.e.now = time(NULL); sleep(1); } } #endif #if 0 void buildclass(int bid, int level) { boardheader_t *bptr; if( level == 20 ){ /* for safty */ printf("is there something wrong? class level: %d\n", level); return; } bptr = &bcache[bid]; if (bptr->firstchild[0] == NULL || bptr->childcount <= 0) load_uidofgid(bid + 1, 1); /* 因為這邊 bid從 0開始, 所以再 +1 回來 */ if (bptr->firstchild[1] == NULL || bptr->childcount <= 0) load_uidofgid(bid + 1, 1); /* 因為這邊 bid從 0開始, 所以再 +1 回來 */ for (bptr = bptr->firstchild[0]; bptr != NULL ; bptr = bptr->next[0]) { if( bptr->brdattr & BRD_GROUPBOARD ) buildclass(bptr - bcache, level + 1); } } #endif int bBMC(int argc, char **argv) { int i; for( i = 0 ; i < MAX_BOARD ; ++i ) if( bcache[i].brdname[0] ) buildBMcache(i + 1); /* XXXbid */ return 0; } #ifdef NOKILLWATERBALL int nkwbd(int argc, char **argv) { int ch, sleeptime = 5, timeout = 5; while( (ch = getopt(argc, argv, "s:t:h")) != -1 ) switch( ch ){ case 's': if( (sleeptime = atoi(optarg)) <= 0 ){ fprintf(stderr, "sleeptime <= 0? set to 5"); sleeptime = 5; } break; case 't': if( (timeout = atoi(optarg)) <= 0 ){ fprintf(stderr, "timeout <= 0? set to 5"); timeout = 20; } break; default: fprintf(stderr, "usage: shmctl nkwbd [-s sleeptime] [-t timeout]\n"); return 0; } #ifndef VALGRIND setproctitle("shmctl nkwbd(sleep%d,timeout%d)", sleeptime, timeout); #endif switch( fork() ){ case -1: perror("fork()"); return 0; break; case 0: /* child */ while( 1 ){ int i; time_t t = SHM->GV2.e.now - timeout; for( i = 0 ; i < USHM_SIZE ; ++i ) if( SHM->uinfo[i].pid && SHM->uinfo[i].wbtime && SHM->uinfo[i].wbtime < t ){ kill(SHM->uinfo[i].pid, SIGUSR2); } sleep(sleeptime); } break; default: /* parent */ fprintf(stderr, "nkwbd\n"); return 0; } return 0; } #endif int SHMinit(int argc, char **argv) { int ch; int no_uhash_loader = 0; while( (ch = getopt(argc, argv, "n")) != -1 ) switch( ch ){ case 'n': no_uhash_loader = 1; break; default: printf("usage:\tshmctl\tSHMinit\n"); return 0; } puts("loading uhash..."); system("bin/uhash_loader"); attach_SHM(); #ifdef OUTTA_TIMER puts("timed..."); timed(1, argv); #endif puts("loading bcache..."); reload_bcache(); puts("building BMcache..."); bBMC(1, argv); #if 0 puts("building class..."); buildclass(0, 0); #endif if( !no_uhash_loader ){ puts("utmpsortd..."); utmpsortd(1, argv); } #ifdef NOKILLWATERBALL puts("nkwbd..."); nkwbd(1, argv); #endif return 0; } int hotboard(int argc, char **argv) { #define isvisiableboard(bptr) \ ((bptr)->brdname[0] && \ !((bptr)->brdattr & BRD_GROUPBOARD) && \ !(((bptr)->brdattr & (BRD_HIDE | BRD_TOP)) || \ ((bptr)->level && !((bptr)->brdattr & BRD_POSTMASK) && \ ((bptr)->level & \ ~(PERM_BASIC|PERM_CHAT|PERM_PAGE|PERM_POST|PERM_LOGINOK))))) int ch, topn = 20, i, nbrds, j, k, nusers; struct bs { int nusers; boardheader_t *b; } *brd; while( (ch = getopt(argc, argv, "t:h")) != -1 ) switch( ch ){ case 't': topn = atoi(optarg); if( topn <= 0 ){ goto hotboardusage; return 1; } break; case 'h': default: hotboardusage: fprintf(stderr, "usage: shmctl hotboard [-t topn]\n"); return 1; } brd = (struct bs *)malloc(sizeof(struct bs) * topn); brd[0].b = &SHM->bcache[0]; brd[0].nusers = brd[0].b->brdname[0] ? brd[0].b->nuser : 0; nbrds = 1; for( i = 1 ; i < MAX_BOARD ; ++i ) if( (isvisiableboard(&SHM->bcache[i])) && (nbrds != topn || SHM->bcache[i].nuser > brd[nbrds - 1].nusers) ){ nusers = SHM->bcache[i].nuser; // no race ? for( k = nbrds - 2 ; k >= 0 ; --k ) if( brd[k].nusers > nusers ) break; if( (k + 1) < nbrds && (k + 2) < topn ) for( j = nbrds - 1 ; j >= k + 1 ; --j ) brd[j] = brd[j - 1]; brd[k + 1].nusers = nusers; brd[k + 1].b = &SHM->bcache[i]; if( nbrds < topn ) ++nbrds; } for( i = 0 ; i < nbrds ; ++i ) printf("%05d|%-12s|%s\n", brd[i].nusers, brd[i].b->brdname, brd[i].b->title); return 0; } int usermode(int argc, char **argv) { int i, modes[MAX_MODES]; memset(modes, 0, sizeof(modes)); for( i = 0 ; i < USHM_SIZE ; ++i ) if( SHM->uinfo[i].userid[0] ) ++modes[ (int)SHM->uinfo[i].mode ]; for( i = 0 ; i < MAX_MODES ; ++i ) printf("%03d|%05d|%s\n", i, modes[i], ModeTypeTable[i]); return 0; } int torb(int argc, char **argv) { reload_bcache(); puts("bcache reloaded"); return 0; } int rlfcache(int argc, char **argv) { reload_fcache(); puts("fcache reloaded"); return 0; } int iszero(void *addr, int size) { char *a=(char*)addr; int i; for(i=0;igap_1,0); TESTZERO(SHM->gap_2,0); TESTZERO(SHM->gap_3,0); TESTZERO(SHM->gap_4,0); TESTZERO(SHM->gap_5,0); TESTZERO(SHM->gap_6,0); TESTZERO(SHM->gap_7,0); TESTZERO(SHM->gap_8,0); TESTZERO(SHM->gap_9,0); TESTZERO(SHM->gap_10,0); TESTZERO(SHM->gap_11,0); TESTZERO(SHM->gap_12,0); TESTZERO(SHM->gap_13,0); TESTZERO(SHM->gap_14,0); TESTZERO(SHM->gap_15,0); TESTZERO(SHM->gap_16,0); TESTZERO(SHM->gap_17,0); TESTZERO(SHM->gap_18,0); TESTZERO(SHM->gap_19,0); for(i=0; iuinfo[i].gap_1,i); TESTZERO(SHM->uinfo[i].gap_2,i); TESTZERO(SHM->uinfo[i].gap_3,i); TESTZERO(SHM->uinfo[i].gap_4,i); } return 0; } int showstat(int argc, char *argv[]) { int i; int flag_clear=0; char *stat_desc[]={ "STAT_LOGIN", "STAT_SHELLLOGIN", "STAT_VEDIT", "STAT_TALKREQUEST", "STAT_WRITEREQUEST", "STAT_MORE", "STAT_SYSWRITESOCKET", "STAT_SYSSELECT", "STAT_SYSREADSOCKET", "STAT_DOSEND", "STAT_SEARCHUSER", "STAT_THREAD", "STAT_SELECTREAD", "STAT_QUERY", "STAT_DOTALK", "STAT_FRIENDDESC", "STAT_FRIENDDESC_FILE", "STAT_PICKMYFRIEND", "STAT_PICKBFRIEND", "STAT_GAMBLE", "STAT_DOPOST", "STAT_READPOST", "STAT_RECOMMEND", "STAT_TODAYLOGIN_MIN", "STAT_TODAYLOGIN_MAX", "STAT_SIGINT", "STAT_SIGQUIT", "STAT_SIGILL", "STAT_SIGABRT", "STAT_SIGFPE", "STAT_SIGBUS", "STAT_SIGSEGV", "STAT_READPOST_12HR", "STAT_READPOST_1DAY", "STAT_READPOST_3DAY", "STAT_READPOST_7DAY", "STAT_READPOST_OLD", "STAT_SIGXCPU", }; if(argv[1] && strcmp(argv[1],"-c")==0) flag_clear=1; for(i=0; istatistic[i]); } if(flag_clear) memset(SHM->statistic, 0, sizeof(SHM->statistic)); return 0; } int dummy(int argc, char *argv[]) { return 0; } struct { int (*func)(int, char **); char *cmd, *descript; } cmd[] = { {dummy, "\b\b\b\bStart daemon:", ""}, {utmpsortd, "utmpsortd", "utmp sorting daemon"}, #ifdef OUTTA_TIMER {timed, "timed", "time daemon for OUTTA_TIMER"}, #endif #ifdef NOKILLWATERBALL {nkwbd, "nkwbd", "NOKillWaterBall daemon"}, #endif {dummy, "\b\b\b\bBuild cache/fix tool:", ""}, {torb, "reloadbcache", "reload bcache"}, {rlfcache, "reloadfcache", "reload fcache"}, {bBMC, "bBMC", "build BM cache"}, {utmpfix, "utmpfix", "clear dead userlist entry & kick idle user"}, {utmpreset, "utmpreset", "SHM->busystate=0"}, {utmpwatch, "utmpwatch", "to see if busystate is always 1 then fix it"}, {dummy, "\b\b\b\bShow info:", ""}, {utmpnum, "utmpnum", "print SHM->number for snmpd"}, {utmpstatus, "utmpstatus", "list utmpstatus"}, {listpid, "listpid", "list all pids of mbbsd"}, {listbrd, "listbrd", "list board info in SHM"}, {fixbrd, "fixbrd", "fix board info in SHM"}, {hotboard, "hotboard", "list boards of most bfriends"}, {usermode, "usermode", "list #users in the same mode"}, {showstat, "showstat", "show statistics"}, {testgap, "testgap", "test SHM->gap zeroness"}, {dummy, "\b\b\b\bMisc:", ""}, {showglobal, "showglobal", "show GLOBALVAR[]"}, {setglobal, "setglobal", "set GLOBALVAR[]"}, {SHMinit, "SHMinit", "initialize SHM (including uhash_loader)"}, {NULL, NULL, NULL} }; extern char ** environ; int main(int argc, char **argv) { int i = 0; chdir(BBSHOME); initsetproctitle(argc, argv, environ); if( argc >= 2 ){ if( strcmp(argv[1], "init") == 0 ){ /* in this case, do NOT run attach_SHM here. because uhash_loader is not run yet. */ return SHMinit(argc - 1, &argv[1]); } attach_SHM(); /* shmctl doesn't need resolve_boards() first */ //resolve_boards(); resolve_garbage(); resolve_fcache(); for( i = 0 ; cmd[i].func != NULL ; ++i ) if( strcmp(cmd[i].cmd, argv[1]) == 0 ){ return cmd[i].func(argc - 1, &argv[1]); } } if( argc == 1 || cmd[i].func == NULL ){ /* usage */ printf("usage: shmctl [command] [options]\n"); printf("commands:\n"); for( i = 0 ; cmd[i].func != NULL ; ++i ) printf("\t%-15s%s\n", cmd[i].cmd, cmd[i].descript); } return 0; }