Witam. Natknalem sie na ta niespodzianke juz pare dni temu ale jakos nie mialem kiedy o tym napisac. Otoz z lmsd zniknela opcja blokujaca demonizowanie sie lmsd co w pratkyce uniemozliwia puszczanie go z innitaba co jest jednym z najbardziej sensownych (imho) sposobow odpalania najwazniejszych procesow. Postanowilem to sobie dorobic i jakaz byla moja niespodzianka gdy po chwili mialem dzialajacych kilka demonow. Przygladajac sie zrodlom natknolem sie na kolejny fork uruchamianacy nazwijmy to 'worker thread' co samo w sobie jest bardzo pozyteczne. Problem polegal na tym ze to potomek przejmowal role petli czekajcej na kolejne zdarzenie a proces macierzysty zaczynal odwalac czarna robote poczym sie konczyl. A init widzac ze proces zakonczyl dzialanie odpalal go ponownie. Musze przyznac ze najpierw myslalem ze to zwykla pomylka (jakie Alec ostatnio ci sie czesto zdarzaja ;-) ale po chwili zastanowienia doszlem do wniosku ze to wykonana specjalnie atrapa majaca ukryc problem z zombie'sami bedacymi nieuchronnym skutkiem odpalania niesynchronizowanych potomkow.
W zalaczeniu jest patch ktory: - doklada spowrotem opcje -f (run in foreground" albo jak kto woli "don't fork"), - poprawia blad z forkowaniem "worker thread'a" czyli sprawia ze caly program zachowuje sie jak na daemona przystalo (m.in nie skacze po pidach i nie zostawia zombich) - dodaje warunek zapobiegajacy bezsensownemu odpalaniu system() gdy zmienna command jest pusta (brak opcji -c "[..]") - zmienia logike funkcji crontab_match, dzieki czemu 'instancjie' z pustym polem crontab nie sa odpalane przy kazdej mozliwej okazji tylko podczas zadania reloadu. - no i na koniec potwierdzenie wykonania reloadu jest zapisywane do bazy tylko w tedy jesli reload byl zadany (updatowanie za kazdym razem jest mylace jesli demon odpala takie zadania jak ping i niepotrzebnie obciaza baze)
Grzegorz Stanislawski
Index: lmsd.c =================================================================== RCS file: /home/cvsroot/lms/daemon/lmsd.c,v retrieving revision 1.6 diff -u -r1.6 lmsd.c --- lmsd.c 25 Apr 2005 13:48:15 -0000 1.6 +++ lmsd.c 25 Apr 2005 19:53:52 -0000 @@ -30,10 +30,12 @@ #include <stdio.h> #include <string.h> #include <dlfcn.h> - +#include <sys/types.h> +#include <sys/wait.h> + #include "lmsd.h"
-int quit = 0, port = 0; +int quit = 0, port = 0, dontfork=0; char *db, *user, *passwd, *host, *dhost; unsigned char *command = NULL; unsigned char *iopt = NULL; @@ -41,6 +43,13 @@ static void parse_command_line(int argc, char **argv); static void free_module(MODULE *module); static int crontab_match(time_t tt, char *crontab); +struct sigaction act,orig; +//signal handler for SIGCHLD reaps zombie children +void sig_child(int signo) +{ + while(waitpid(-1, NULL, WNOHANG) > 0) + continue; +}
int main(int argc, char *argv[]) { @@ -95,8 +104,17 @@ g->config_getbool = &config_getbool; g->config_getdouble = &config_getdouble;
+ // catch SIGCHLD to catch zombies ;-) + act.sa_handler = sig_child; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction (SIGCHLD, &act, &orig) ; + // termination signals handling + signal(SIGINT, termination_handler); + signal(SIGTERM, termination_handler); + // daemonize - if ( !quit ) { + if ( !quit && !dontfork) { int fval = fork(); switch (fval) { @@ -114,10 +132,6 @@ } }
- // termination signals handling - signal(SIGINT, termination_handler); - signal(SIGTERM, termination_handler); - // main loop **************************************************** for (;;) { @@ -134,9 +148,13 @@ tt = cron_sync_sleep(); }
- // run shell command, i.e. secure connections tuneling - system(command); - + // run shell command, i.e. secure connections tuneling + if(command!=NULL) { +#ifdef DEBUG1 + syslog(LOG_INFO, "executing command %s.", command); +#endif + system(command); + } // try to connect to database if( !(g->conn = db_connect(db,user,passwd,host,port)) ) { @@ -243,16 +261,13 @@ // forking reload - we can do a job for longer than one minute if( i_no ) { - switch (fork()) - { - case -1: + int fval = fork(); + if (fval <0) { syslog(LOG_CRIT, "Fork error. Can't reload."); if ( quit ) termination_handler(1); - break; - case 0: // continue main loop - if( quit ) exit(0); // quiet parent exit - break; - default: + } else if (fval==0) { //child +// //restore old handler so we can wait() for childs executed by modules +// sigaction (SIGCHLD,&orig, NULL) ; #ifdef DEBUG1 syslog(LOG_INFO, "DEBUG: [lmsd] Reloading..."); #endif @@ -272,6 +287,7 @@ mod->file = strdup(instances[i].module); mod->instance = strdup(instances[i].name); mod->dlh = dlopen(mod->file, RTLD_NOW); + syslog(LOG_INFO, "module '%s': loaded", mod->file);
if( !mod->dlh ) { @@ -304,15 +320,19 @@ free_module(mod); } - // write reload timestamp - db_pexec(g->conn, "UPDATE daemonhosts SET lastreload=%NOW%, reload=0 WHERE name='?'", dhost); - db_disconnect(g->conn); - + // write reload timestamp only when reload was requested + if(reload) { + db_pexec(g->conn, "UPDATE daemonhosts SET lastreload=%NOW%, reload=0 WHERE name='?'", dhost); + db_disconnect(g->conn); + } // exit child (reload) thread - if( quit ) - termination_handler(0); // write info to syslog - else - exit(0); +#ifdef DEBUG1 + syslog(LOG_INFO, "Reload child exiting..."); +#endif + + exit(0); + }else { //parent + sleep(100); } for(i=0; i<i_no; i++) @@ -340,7 +360,7 @@ sscanf(REVISION, "$Id: lmsd.c,v %s", revision); - while ( (opt = getopt(argc, argv, "qvi:h:p:d:u:H:c:")) != -1 ) + while ( (opt = getopt(argc, argv, "qfvi:h:p:d:u:H:c:")) != -1 ) { switch (opt) { @@ -350,6 +370,9 @@ case 'q': quit = 1; break; + case 'f': + dontfork = 1; + break; case 'i': iopt = optarg; break; @@ -380,6 +403,7 @@ printf(" -H daemon_host\t\thost name where runs daemon (default: `hostname`)\n"); printf(" -c command\t\tshell command to run before database connecting (default: empty)\n"); printf(" -q \t\t\tdo a reload and quit\n"); + printf(" -f \t\t\tdo not fork to background\n"); printf(" -v \t\t\tprint version and copyright info\n"); printf(" -i "instance[ ...]" list of instances to reload\n"); exit(1); @@ -404,8 +428,8 @@ { if( cron_parse_time(&ct, crontab) != PARSE_OK ) return 0; - if( !cron_match_time(&ct, &tt) ) - return 0; + if( cron_match_time(&ct, &tt) ) + return 1; } - return 1; + return 0; }