/* * * pool.c Management of process pool * * * TODO: * * general: statistics bookkeeping * * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "debug.h" #include "serverchild.h" #include "pool.h" #include #include #include #include #include #include #include #include #include #include #include #define P_SIZE 100000 static Scoreboard_t *scoreboard; static int shmid; static int sb_lockfd; extern int GeneralStopRequested; extern ChildInfo_t childinfo; /* * * * Scoreboard * * */ State_t state_new(void); State_t state_new(void) { State_t s; s.pid = -1; s.ctime = time(0); s.status = STATE_NOOP; s.count = 0; s.client = "none"; return s; } int set_lock(int type); int set_lock(int type) { int result, serr; struct flock lock; lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLOCK */ lock.l_start = 0; lock.l_whence = 0; lock.l_len = 1; result = fcntl(sb_lockfd, F_SETLK, &lock); if (result == -1) { serr=errno; trace(TRACE_DEBUG, "%s,%s: error: %s", __FILE__, __func__, strerror(serr)); /* TODO: this needs fixing */ switch (serr) { case EDEADLK: sleep(2); set_lock(type); break; } errno=serr; } return result; } void scoreboard_new(serverConfig_t * conf) { int serr; if ((shmid = shmget(IPC_PRIVATE, P_SIZE, 0644 | IPC_CREAT)) == -1) { serr = errno; trace(TRACE_FATAL, "%s,%s: shmget failed [%s]",__FILE__,__func__, strerror(serr)); } scoreboard = shmat(shmid, (void *) 0, 0); serr=errno; if (scoreboard == (Scoreboard_t *) (-1)) { trace(TRACE_FATAL, "%s,%s: scoreboard init failed [%s]",__FILE__,__func__,strerror(serr)); scoreboard_delete(); } scoreboard_lock_new(); scoreboard->conf = conf; scoreboard_setup(); scoreboard_conf_check(); } void scoreboard_lock_new(void) { if ( (sb_lockfd = open(scoreboard_lock_getfilename(),O_RDWR|O_CREAT|O_TRUNC,0600)) < 0) { trace(TRACE_FATAL, "%s,%s, opening lockfile [%s] failed", __FILE__, __func__, scoreboard_lock_getfilename()); } } void scoreboard_setup(void) { int i; scoreboard_wrlck(); for (i = 0; i < HARD_MAX_CHILDREN; i++) { scoreboard->child[i] = state_new(); } scoreboard_unlck(); } char * scoreboard_lock_getfilename(void) { static char *filename = NULL; int maxlen = strlen(SCOREBOARD_LOCK_FILE) + 16; if (filename == NULL) { filename = dm_malloc(sizeof(char *) * maxlen); snprintf(filename, maxlen, "%s_%d.LCK", SCOREBOARD_LOCK_FILE, getpid()); } return filename; } void scoreboard_conf_check(void) { /* some sanity checks on boundaries */ scoreboard_wrlck(); if (scoreboard->conf->maxChildren > HARD_MAX_CHILDREN) { trace(TRACE_WARNING, "%s,%s: MAXCHILDREN too large. Decreasing to [%d]", __FILE__,__func__,HARD_MAX_CHILDREN); scoreboard->conf->maxChildren = HARD_MAX_CHILDREN; } if (scoreboard->conf->maxChildren < scoreboard->conf->startChildren) { trace(TRACE_WARNING, "%s,%s: MAXCHILDREN too small. Increasing to NCHILDREN [%d]", __FILE__,__func__,scoreboard->conf->startChildren); scoreboard->conf->maxChildren = scoreboard->conf->startChildren; } if (scoreboard->conf->maxSpareChildren > scoreboard->conf->maxChildren) { trace(TRACE_WARNING, "%s,%s: MAXSPARECHILDREN too large. Decreasing to MAXCHILDREN [%d]", __FILE__,__func__,scoreboard->conf->maxChildren); scoreboard->conf->maxSpareChildren = scoreboard->conf->maxChildren; } if (scoreboard->conf->maxSpareChildren < scoreboard->conf->minSpareChildren) { trace(TRACE_WARNING, "%s,%s: MAXSPARECHILDREN too small. Increasing to MINSPARECHILDREN [%d]", __FILE__,__func__,scoreboard->conf->minSpareChildren); scoreboard->conf->maxSpareChildren = scoreboard->conf->minSpareChildren; } scoreboard_unlck(); } void scoreboard_release(pid_t pid) { int key; key = getKey(pid); if (key == -1) return; scoreboard_wrlck(); scoreboard->child[key] = state_new(); scoreboard_unlck(); } void scoreboard_delete() { if (shmdt(scoreboard) == -1) trace(TRACE_FATAL, "scoreboard_delete(): detach shared mem failed"); if (shmctl(shmid, IPC_RMID, NULL) == -1) trace(TRACE_FATAL, "scoreboard_delete(): delete shared mem segment failed"); if (unlink(scoreboard_lock_getfilename()) == -1) trace(TRACE_ERROR, "scoreboard_delete(): error deleting scoreboard lock " "file %s", scoreboard_lock_getfilename()); return; } int count_spare_children() { int i, count; count = 0; scoreboard_rdlck(); for (i = 0; i < scoreboard->conf->maxChildren; i++) { if (scoreboard->child[i].pid > 0 && scoreboard->child[i].status == STATE_IDLE) count++; } scoreboard_unlck(); return count; } int count_children() { int i, count; count = 0; scoreboard_rdlck(); for (i = 0; i < scoreboard->conf->maxChildren; i++) { if (scoreboard->child[i].pid > 0) count++; } scoreboard_unlck(); return count; } pid_t get_idle_spare() { int i; pid_t idlepid = (pid_t) -1; /* get the last-in-first-out idle process */ scoreboard_rdlck(); for (i = scoreboard->conf->maxChildren - 1; i >= 0; i--) { if ((scoreboard->child[i].pid > 0) && (scoreboard->child[i].status == STATE_IDLE)) { idlepid = scoreboard->child[i].pid; break; } } scoreboard_unlck(); return idlepid; } int getKey(pid_t pid) { int i; scoreboard_rdlck(); for (i = 0; i < scoreboard->conf->maxChildren; i++) { if (scoreboard->child[i].pid == pid) { scoreboard_unlck(); return i; } } scoreboard_unlck(); trace(TRACE_ERROR, "%s,%s: pid NOT found on scoreboard [%d]", __FILE__, __func__, pid); return -1; } /* * * * Child * * */ int child_register() { int i; trace(TRACE_MESSAGE, "%s,%s: register child [%d]", __FILE__, __func__, getpid()); scoreboard_wrlck(); for (i = 0; i < scoreboard->conf->maxChildren; i++) { if (scoreboard->child[i].pid == -1) break; if (scoreboard->child[i].pid == getpid()) { scoreboard_unlck(); trace(TRACE_FATAL, "%s,%s: child already registered.", __FILE__, __func__); return -1; } } if (i == scoreboard->conf->maxChildren) { scoreboard_unlck(); trace(TRACE_WARNING, "%s,%s: no empty slot found", __FILE__, __func__); return -1; } scoreboard->child[i].pid = getpid(); scoreboard->child[i].status = STATE_IDLE; scoreboard_unlck(); trace(TRACE_MESSAGE, "%s,%s: initializing child_state [%d] using slot [%d]", __FILE__, __func__, getpid(), i); return 0; } static void child_set_status(int status) { int key; pid_t pid; pid = getpid(); key = getKey(pid); if (key == -1) return; scoreboard_wrlck(); scoreboard->child[key].status = status; scoreboard_unlck(); } void child_reg_connected() { child_set_status(STATE_CONNECTED); } void child_reg_disconnected() { child_set_status(STATE_IDLE); } void child_unregister() { child_set_status(STATE_WAIT); } /* * * * Server * * */ void manage_start_children() { /* * * startup the first batch of forked processes * */ int i; for (i = 0; i < scoreboard->conf->startChildren; i++) { if (CreateChild(&childinfo) == -1) { manage_stop_children(); trace(TRACE_FATAL, "%s,%s: could not create children. Fatal.", __FILE__, __func__); exit(0); } } } void manage_restart_children() { /* restart active children */ int i; pid_t chpid; for (i=0; i< scoreboard->conf->maxChildren; i++) { chpid=scoreboard->child[i].pid; if (chpid == -1) continue; if (waitpid(chpid, NULL, WNOHANG|WUNTRACED) == chpid) { scoreboard_release(chpid); trace(TRACE_MESSAGE,"%s,%s: child [%d] exited. Restarting...", __FILE__, __func__, chpid); if (CreateChild(&childinfo)== -1) { trace(TRACE_ERROR,"%s,%s: createchild failed. Bailout...", __FILE__, __func__); GeneralStopRequested=1; manage_stop_children(); exit(1); } } } sleep(1); } void manage_stop_children() { /* * * cleanup all remaining forked processes * */ int stillSomeAlive = 1; int i, cnt = 0; pid_t chpid; trace(TRACE_MESSAGE, "%s,%s: General stop requested. Killing children.. ", __FILE__,__func__); while (stillSomeAlive && cnt < 10) { stillSomeAlive = 0; cnt++; for (i = 0; i < scoreboard->conf->maxChildren; i++) { chpid = scoreboard->child[i].pid; if (chpid <= 0) continue; if (waitpid(chpid, NULL, WNOHANG | WUNTRACED) == chpid) { scoreboard_release(chpid); } else { stillSomeAlive = 1; if (cnt==1) /* no use killing the dead */ kill(chpid, SIGTERM); usleep(1000); } } sleep(cnt); } if (stillSomeAlive) { trace(TRACE_INFO, "%s,%s: not all children terminated at SIGTERM, killing hard now", __FILE__,__func__); for (i = 0; i < scoreboard->conf->maxChildren; i++) { chpid = scoreboard->child[i].pid; if (chpid > 0) kill(chpid, SIGKILL);; scoreboard_release(chpid); } } } void manage_spare_children() { /* * * manage spare children while running * */ int somethingchanged; pid_t chpid; chpid = getpid(); somethingchanged = 0; if (GeneralStopRequested) return; /* scale up */ while ((count_children() < scoreboard->conf->startChildren) || (count_spare_children() < scoreboard->conf->minSpareChildren)) { if (count_children() >= scoreboard->conf->maxChildren) break; somethingchanged = 1; trace(TRACE_INFO, "%s,%s: creating spare child", __FILE__,__func__); if ((chpid = CreateChild(&childinfo)) < 0) { trace(TRACE_ERROR, "%s,%s: unable to start new child", __FILE__,__func__); break; } } /* scale down */ while ((count_children() > scoreboard->conf->startChildren) && (count_spare_children() > scoreboard->conf->maxSpareChildren)) { somethingchanged = 1; if ((chpid = get_idle_spare()) > 0) { trace(TRACE_INFO, "%s,%s: killing overcomplete spare [%d]", __FILE__,__func__,chpid); kill(chpid, SIGTERM); if (waitpid(chpid, NULL, 0) == chpid) { trace(TRACE_INFO, "%s,%s: spare child [%u] has exited", __FILE__,__func__,chpid); } scoreboard_release(chpid); } else { trace(TRACE_ERROR, "%s,%s: unable to get pid for idle spare", __FILE__,__func__); break; } } /* scoreboard */ if (somethingchanged > 0) { trace(TRACE_MESSAGE, "%s,%s: children [%d/%d], spares [%d (%d - %d)]", __FILE__,__func__, count_children(), scoreboard->conf->maxChildren, count_spare_children(), scoreboard->conf->minSpareChildren, scoreboard->conf->maxSpareChildren); } if (!count_children()) { trace(TRACE_WARNING, "%s,%s: no children left ?. Aborting.", __FILE__,__func__); GeneralStopRequested = 1; } }