Functions | Variables

mfork.c File Reference

#include "mfork.h"
Include dependency graph for mfork.c:

Go to the source code of this file.

Functions

static void mfork_child_loop (mfork_data_t *)
static pid_t mfork_internal (mfork_func_t, int, void **, mfork_func_t, mfork_func_t, mfork_func_t, int, int)
pid_t mfork (mfork_func_t func, int freq_sec, void **args, mfork_func_t pre_func, mfork_func_t post_func, mfork_func_t exit_func, int num_restarts)
int mfork_finalize ()
int mfork_set_secondary_sigchld_handler (Sigfunc *func)
void mfork_sigchld_handler (int sig)
int mfork_check_parent ()

Variables

static icl_list_t * mfork_child_list = NULL
static Sigfunc * mfork_sigchld_callback = NULL

Detailed Description

mfork is a "monitored fork" routine. This allows you to fork a child process and have it automatically restarted if it dies and if the parent dies, the child notices and terminates itself. This also allows you to specify that the function should be called at some periodic interval after the fork.

Definition in file mfork.c.


Function Documentation

pid_t mfork ( mfork_func_t  func,
int  freq_sec,
void **  args,
mfork_func_t  pre_func,
mfork_func_t  post_func,
mfork_func_t  exit_func,
int  num_restarts 
)

Forks a monitored child process.

Parameters:
func -- the function to be called after the child is forked.
freq_sec -- the frequency (in seconds) that the function 'func' should be called. If 'freq_sec' is less than 0, the function is only called once.
args -- array of pointers to the arguments to be passed to function 'func'.
pre_func -- function to be called before forking (and upon subsequent re-forks). This is optional and should be NULL if not desired.
post_func -- function to be called after forking (and after subsequent re-forks). This is optional and should be NULL if not desired.
exit_func -- function called by the child when it realizes that the parent has died. This is called just before the child terminates. This is optional and should be NULL if not desired.
num_restarts -- the number of times to allow the child to die. this helps to avoid infinitely restarting a child process that just keeps dying.
Returns:
pid of the child process. returns -1 on error.

Definition at line 49 of file mfork.c.

{
  return mfork_internal(func, freq_sec, args, pre_func, post_func,
     exit_func, num_restarts, 0);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mfork_check_parent (  ) 

Checks whether the parent is still alive. This is used for children that are not periodically called since we cannot check the parent for the user in that situation.

Returns:
TRUE if the parent is still alive, FALSE otherwise.

Definition at line 299 of file mfork.c.

{
  if(getppid() == 1)
    return FALSE;
    
  return TRUE;
}

Here is the caller graph for this function:

static void mfork_child_loop ( mfork_data_t *  mfork_data  )  [static]

This loops forever (or at least until the parent dies) and calls the user's function every 'mfork_data->update_freq' seconds.

Parameters:
mfork_data -- pointer to the mfork data structure for this child

Definition at line 180 of file mfork.c.

{
  int sleep_count, parent_chk_freq;

  sleep_count = 0;
  parent_chk_freq = MIN(2, mfork_data->update_freq);

  while(1) {
    sleep(parent_chk_freq);

    sleep_count += parent_chk_freq;

    if(!mfork_check_parent()) {
      ERRPRINTF("Parent died, so I am exiting\n");
      if(mfork_data->exit_func)
        mfork_data->exit_func(mfork_data->args);
      _exit(0);
    }

    if(sleep_count >= mfork_data->update_freq) {
      mfork_data->func(mfork_data->args);
      sleep_count = 0;
    }
  }

  return;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mfork_finalize (  ) 

Free resources used by the mfork manager.

Returns:
0 on success, -1 on failure.

Definition at line 163 of file mfork.c.

{
  icl_list_destroy(mfork_child_list, free);
  mfork_child_list = NULL;

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static pid_t mfork_internal ( mfork_func_t  func,
int  freq_sec,
void **  args,
mfork_func_t  pre_func,
mfork_func_t  post_func,
mfork_func_t  exit_func,
int  num_restarts,
int  fork_delay 
) [static]

The real implementation.. for internal use.

Parameters:
func -- the function to be called after the child is forked.
freq_sec -- the frequency (in seconds) that the function 'func' should be called. If 'freq_sec' is less than 0, the function is only called once.
args -- array of pointers to the arguments to be passed to function 'func'.
pre_func -- function to be called before forking (and upon subsequent re-forks). This is optional and should be NULL if not desired.
post_func -- function to be called after forking (and after subsequent re-forks). This is optional and should be NULL if not desired.
exit_func -- function called by the child when it realizes that the parent has died. This is called just before the child terminates. This is optional and should be NULL if not desired.
num_restarts -- the number of times to allow the child to die. this helps to avoid infinitely restarting a child process that just keeps dying.
fork_delay -- the number of seconds to sleep before forking.
Returns:
pid of the child process. returns -1 on error.

Definition at line 84 of file mfork.c.

{
  mfork_data_t *mfork_data;
  pid_t pid;

  if(!func) return -1;

  if(!mfork_child_list) {
    mfork_child_list = icl_list_new();
    if(!mfork_child_list) {
      ERRPRINTF("Unable to create list for child pids\n");
      return -1; 
    }
  }

  if(gs_signal(SIGCHLD, mfork_sigchld_handler) == SIG_ERR) {
    ERRPRINTF("Could not set up SIGCHLD handler\n");
    return -1; 
  }

  mfork_data = (mfork_data_t *)malloc(sizeof(mfork_data_t));
  if(!mfork_data) {
    ERRPRINTF("Error creating new child entry\n");
    return -1;
  }

  mfork_data->update_freq = freq_sec;
  mfork_data->func = func;
  mfork_data->pre_func = pre_func;
  mfork_data->post_func = post_func;
  mfork_data->exit_func = exit_func;
  mfork_data->args = args;
  mfork_data->num_restarts = num_restarts;

  if(mfork_data->pre_func)
    mfork_data->pre_func(mfork_data->args);

  sleep(fork_delay);

  pid = fork();

  switch(pid) {
    case -1:
      ERRPRINTF("fork() failed\n");
      return pid;
    case 0:
      mfork_data->pid = getpid();

      if(mfork_data->update_freq < 0)
        mfork_data->func(mfork_data->args);
      else
        mfork_child_loop(mfork_data);

      _exit(0);
    default:
      mfork_data->pid = pid;

      if(icl_list_append(mfork_child_list, mfork_data) == NULL) {
        ERRPRINTF("Warning: failed to insert child into list.\n");
        ERRPRINTF("         we will let it run and hope for the best,\n");
        ERRPRINTF("         but it won't be restartable.\n");
      }

      if(mfork_data->post_func)
        mfork_data->post_func(mfork_data->args);
  }

  return pid;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mfork_set_secondary_sigchld_handler ( Sigfunc *  func  ) 

Sets a callback for handling SIGCHLD signals that do not correspond to an mfork-controlled child.

Parameters:
func - the function to be called
Returns:
0 on sucess, -1 on failure

Definition at line 218 of file mfork.c.

{
  mfork_sigchld_callback = func;
  return 0;
}

void mfork_sigchld_handler ( int  sig  ) 

Handles SIGCHLD signals and restarts the child process if it was an mforked child. If it wasn't mforked, we just waitpid() to avoid zombies.

Parameters:
sig -- the signal that was received

Definition at line 233 of file mfork.c.

{
  icl_list_t *tmp_child;
  int child_found = 0;
  pid_t child_pid;

  if(!mfork_child_list) {
    ERRPRINTF("Error: NULL child list in SIGCHLD handler\n");
    return;
  }

  DBGPRINTF("Got sigchld, sig = %d\n", sig);

  for(tmp_child = icl_list_first(mfork_child_list); tmp_child; 
    tmp_child = icl_list_next(mfork_child_list, tmp_child)) 
  {
    mfork_data_t *tmp_data = (mfork_data_t *)tmp_child->data;

    if(!tmp_data) continue;

    child_pid = waitpid(tmp_data->pid, NULL, WNOHANG | WUNTRACED);

    if(child_pid == tmp_data->pid) {
      LOGPRINTF("Child %d died, restarting now...\n", tmp_data->pid);

      child_found = 1;

      if(icl_list_delete(mfork_child_list, tmp_child, NULL) < 0)
        ERRPRINTF("Warning: failed to remove child entry from list\n");

      if(tmp_data->num_restarts-- > 0)
      {
        if(mfork_internal(tmp_data->func, tmp_data->update_freq,
           tmp_data->args, tmp_data->pre_func, tmp_data->post_func, 
           tmp_data->exit_func, tmp_data->num_restarts, 1) < 0) 
        {
          ERRPRINTF("error restarting child process.\n");
          break;
        }
      }

      free(tmp_data);
      break;
    }
  }

  if(!child_found) {
    if(mfork_sigchld_callback)
      mfork_sigchld_callback(sig);
    else
      while((child_pid = waitpid(-1, NULL, WNOHANG)) > 0)
        DBGPRINTF("child %d terminated\n", (int)child_pid);
  }

  return;
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

icl_list_t* mfork_child_list = NULL [static]

Definition at line 20 of file mfork.c.

Sigfunc* mfork_sigchld_callback = NULL [static]

Definition at line 21 of file mfork.c.