mfork.c

Go to the documentation of this file.
00001 
00011 /* $Id: mfork.c,v 1.18 2005/11/10 20:46:10 seymour Exp $ */
00012 /* $UTK_Copyright: $ */
00013 
00014 #include "mfork.h"
00015 
00016 static void mfork_child_loop(mfork_data_t *);
00017 static pid_t mfork_internal(mfork_func_t, int, void **, 
00018   mfork_func_t, mfork_func_t, mfork_func_t, int, int);
00019 
00020 static icl_list_t *mfork_child_list = NULL;
00021 static Sigfunc *mfork_sigchld_callback = NULL;
00022 
00048 pid_t
00049 mfork(mfork_func_t func, int freq_sec, void **args, 
00050   mfork_func_t pre_func, mfork_func_t post_func, mfork_func_t exit_func,
00051   int num_restarts)
00052 {
00053   return mfork_internal(func, freq_sec, args, pre_func, post_func,
00054      exit_func, num_restarts, 0);
00055 }
00056 
00083 static pid_t
00084 mfork_internal(mfork_func_t func, int freq_sec, void **args, 
00085   mfork_func_t pre_func, mfork_func_t post_func, mfork_func_t exit_func,
00086   int num_restarts, int fork_delay)
00087 {
00088   mfork_data_t *mfork_data;
00089   pid_t pid;
00090 
00091   if(!func) return -1;
00092 
00093   if(!mfork_child_list) {
00094     mfork_child_list = icl_list_new();
00095     if(!mfork_child_list) {
00096       ERRPRINTF("Unable to create list for child pids\n");
00097       return -1; 
00098     }
00099   }
00100 
00101   if(gs_signal(SIGCHLD, mfork_sigchld_handler) == SIG_ERR) {
00102     ERRPRINTF("Could not set up SIGCHLD handler\n");
00103     return -1; 
00104   }
00105 
00106   mfork_data = (mfork_data_t *)malloc(sizeof(mfork_data_t));
00107   if(!mfork_data) {
00108     ERRPRINTF("Error creating new child entry\n");
00109     return -1;
00110   }
00111 
00112   mfork_data->update_freq = freq_sec;
00113   mfork_data->func = func;
00114   mfork_data->pre_func = pre_func;
00115   mfork_data->post_func = post_func;
00116   mfork_data->exit_func = exit_func;
00117   mfork_data->args = args;
00118   mfork_data->num_restarts = num_restarts;
00119 
00120   if(mfork_data->pre_func)
00121     mfork_data->pre_func(mfork_data->args);
00122 
00123   sleep(fork_delay);
00124 
00125   pid = fork();
00126 
00127   switch(pid) {
00128     case -1:
00129       ERRPRINTF("fork() failed\n");
00130       return pid;
00131     case 0:
00132       mfork_data->pid = getpid();
00133 
00134       if(mfork_data->update_freq < 0)
00135         mfork_data->func(mfork_data->args);
00136       else
00137         mfork_child_loop(mfork_data);
00138 
00139       _exit(0);
00140     default:
00141       mfork_data->pid = pid;
00142 
00143       if(icl_list_append(mfork_child_list, mfork_data) == NULL) {
00144         ERRPRINTF("Warning: failed to insert child into list.\n");
00145         ERRPRINTF("         we will let it run and hope for the best,\n");
00146         ERRPRINTF("         but it won't be restartable.\n");
00147       }
00148 
00149       if(mfork_data->post_func)
00150         mfork_data->post_func(mfork_data->args);
00151   }
00152 
00153   return pid;
00154 }
00155 
00162 int
00163 mfork_finalize()
00164 {
00165   icl_list_destroy(mfork_child_list, free);
00166   mfork_child_list = NULL;
00167 
00168   return 0;
00169 }
00170 
00179 static void
00180 mfork_child_loop(mfork_data_t *mfork_data)
00181 {
00182   int sleep_count, parent_chk_freq;
00183 
00184   sleep_count = 0;
00185   parent_chk_freq = MIN(2, mfork_data->update_freq);
00186 
00187   while(1) {
00188     sleep(parent_chk_freq);
00189 
00190     sleep_count += parent_chk_freq;
00191 
00192     if(!mfork_check_parent()) {
00193       ERRPRINTF("Parent died, so I am exiting\n");
00194       if(mfork_data->exit_func)
00195         mfork_data->exit_func(mfork_data->args);
00196       _exit(0);
00197     }
00198 
00199     if(sleep_count >= mfork_data->update_freq) {
00200       mfork_data->func(mfork_data->args);
00201       sleep_count = 0;
00202     }
00203   }
00204 
00205   return;
00206 }
00207 
00217 int
00218 mfork_set_secondary_sigchld_handler(Sigfunc *func)
00219 {
00220   mfork_sigchld_callback = func;
00221   return 0;
00222 }
00223 
00232 void
00233 mfork_sigchld_handler(int sig)
00234 {
00235   icl_list_t *tmp_child;
00236   int child_found = 0;
00237   pid_t child_pid;
00238 
00239   if(!mfork_child_list) {
00240     ERRPRINTF("Error: NULL child list in SIGCHLD handler\n");
00241     return;
00242   }
00243 
00244   DBGPRINTF("Got sigchld, sig = %d\n", sig);
00245 
00246   for(tmp_child = icl_list_first(mfork_child_list); tmp_child; 
00247     tmp_child = icl_list_next(mfork_child_list, tmp_child)) 
00248   {
00249     mfork_data_t *tmp_data = (mfork_data_t *)tmp_child->data;
00250 
00251     if(!tmp_data) continue;
00252 
00253     child_pid = waitpid(tmp_data->pid, NULL, WNOHANG | WUNTRACED);
00254 
00255     if(child_pid == tmp_data->pid) {
00256       LOGPRINTF("Child %d died, restarting now...\n", tmp_data->pid);
00257 
00258       child_found = 1;
00259 
00260       if(icl_list_delete(mfork_child_list, tmp_child, NULL) < 0)
00261         ERRPRINTF("Warning: failed to remove child entry from list\n");
00262 
00263       if(tmp_data->num_restarts-- > 0)
00264       {
00265         if(mfork_internal(tmp_data->func, tmp_data->update_freq,
00266            tmp_data->args, tmp_data->pre_func, tmp_data->post_func, 
00267            tmp_data->exit_func, tmp_data->num_restarts, 1) < 0) 
00268         {
00269           ERRPRINTF("error restarting child process.\n");
00270           break;
00271         }
00272       }
00273 
00274       free(tmp_data);
00275       break;
00276     }
00277   }
00278 
00279   if(!child_found) {
00280     if(mfork_sigchld_callback)
00281       mfork_sigchld_callback(sig);
00282     else
00283       while((child_pid = waitpid(-1, NULL, WNOHANG)) > 0)
00284         DBGPRINTF("child %d terminated\n", (int)child_pid);
00285   }
00286 
00287   return;
00288 }
00289 
00298 int
00299 mfork_check_parent()
00300 {
00301   if(getppid() == 1)
00302     return FALSE;
00303     
00304   return TRUE;
00305 }