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;
}