00001
00011
00012
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 }