/* $Id$ */
#include "bbs.h"
#include <sys/wait.h>
#include <string.h>
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; k<ui->friendtotal && k<MAX_FRIEND &&
(int)(ui->friend_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
now = time(NULL);
#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
cmputmpgo(const void * i, const void * j)
{
userinfo_t *a=&SHM->uinfo[*(int*)i],*b=&SHM->uinfo[*(int*)j];
int played_a=(a->go_win+a->go_lose+a->go_tie)!=0;
int played_b=(b->go_win+b->go_lose+b->go_tie)!=0;
int type;
if ((type = played_b - played_a))
return type;
if (played_a == 0)
return 0;
if ((type = b->go_win - a->go_win))
return type;
if ((type = a->go_lose - b->go_lose))
return type;
return a->go_tie - b->go_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][7],
SHM->sorted[ns][0], sizeof(int) * count);
memcpy(SHM->sorted[ns][8],
SHM->sorted[ns][0], sizeof(int) * count);
qsort(SHM->sorted[ns][7], count, sizeof(int), cmputmpuid);
qsort(SHM->sorted[ns][8], 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);
memcpy(SHM->sorted[ns][6],
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);
qsort(SHM->sorted[ns][6], count, sizeof(int), cmputmpgo);
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","now", "nWelcomes", "shutdown", 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;
}
#if 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);
}
}
#endif
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 now, t;
#ifdef OUTTA_TIMER
now = SHM->GV2.e.now;
#else
now = time(NULL);
#endif
t = 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;
}
void lockbcache(void)
{
int i;
for( i = 0 ; i < 10 && SHM->Bbusystate ; ++i ){
printf("SHM->Bbusystate is currently locked (value: %d). "
"please wait... ", SHM->Bbusystate);
sleep(1);
}
if( i == 10 )
puts("steal bcache lock\n");
SHM->Bbusystate = 1;
}
void unlockbcache(void)
{
SHM->Bbusystate = 0;
}
int fixbcache(int argc, char **argv)
{
int n, fd, bid, changed = 0;
boardheader_t bh;
if( (fd = open(fn_board, O_RDONLY)) < 0 ){
perror("open .BRD");
return 1;
}
for( bid = 0 ;
(bid < MAX_BOARD && read(fd, &bh, sizeof(bh)) == sizeof(bh)) ;
++bid ){
if( strcmp(bh.brdname, bcache[bid].brdname) != 0 ){
char fn[PATHLEN];
printf("bid: %d, brdname not match(.BRD: %s, bcache: %s). "
"fix it!\n",
bid + 1, bh.brdname, bcache[bid].brdname);
changed = 1;
lockbcache();
bcache[bid] = bh;
unlockbcache();
sprintf(fn, "boards/%c/%s/.DIR.bottom",
bh.brdname[0],
bh.brdname);
n = get_num_records(fn, sizeof(fileheader_t));
if( n > 5 )
n = 5;
SHM->n_bottom[bid] = n;
}
}
close(fd);
if( changed ){
puts("re-sort bcache");
sort_bcache();
}
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;i<size;i++)
if(a[i]!=0) return 0;
return 1;
}
#define TESTZERO(x,i) do { if(!iszero((x), sizeof(x))) printf("%s is dirty(i=%d)\n",#x,i); } while(0);
int testgap(int argc, char *argv[])
{
int i;
TESTZERO(SHM->gap_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; i<USHM_SIZE; i++) {
TESTZERO(SHM->uinfo[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; i<STAT_NUM; i++) {
const char *desc= i*sizeof(char*)<sizeof(stat_desc)?stat_desc[i]:"?";
printf("%s:\t%d\n", desc, SHM->statistic[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"},
{fixbcache, "fixbcache", "fix 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;
}