/*
*
* 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 <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#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;
}
}
syntax highlighted by Code2HTML, v. 0.9.1