Просмотр исходного кода

cfg framework: fix the initialization of child processes

The number of child processes that keep updating their
local configuration needs to be known before any child
process is forked.
Before this the child processes increased the reference
counter of the callback function list items after forking.
If a child process was forked "too fast" then it freed the list
before the other processes had a chance to refer to the list
item. The result was that some child processes missed
the initial configuration changes. (Those changes that
had per-child process callback defined.)
Miklos Tirpak 16 лет назад
Родитель
Сommit
5322385b22
10 измененных файлов с 129 добавлено и 10 удалено
  1. 39 2
      cfg/cfg_struct.c
  2. 17 1
      cfg/cfg_struct.h
  3. 15 2
      doc/cfg.txt
  4. 26 0
      main.c
  5. 4 0
      modules_s/cpl-c/cpl.c
  6. 3 0
      modules_s/ctl/ctl.c
  7. 7 1
      modules_s/fifo/fifo.c
  8. 9 2
      modules_s/jabber/jabber.c
  9. 5 2
      modules_s/nathelper/natping.c
  10. 4 0
      modules_s/sms/sms.c

+ 39 - 2
cfg/cfg_struct.c

@@ -371,12 +371,49 @@ void cfg_destroy(void)
 	}
 	}
 }
 }
 
 
-/* per-child process init function */
+/* Register num number of child processes that will
+ * keep updating their local configuration.
+ * This function needs to be called from mod_init
+ * before any child process is forked.
+ */
+void cfg_register_child(int num)
+{
+	/* Increase the reference counter of the first list item
+	 * with the number of child processes.
+	 * If the counter was increased after forking then it
+	 * could happen that a child process is forked and updates
+	 * its local config very fast before the other processes have
+	 * a chance to refer to the list item. The result is that the
+	 * item is freed by the "fast" child process and the other
+	 * processes do not see the beginning of the list and miss
+	 * some config changes.
+	 */
+	atomic_add(&((*cfg_child_cb_first)->refcnt), num);
+}
+
+/* per-child process init function.
+ * It needs to be called from the forked process.
+ * cfg_register_child() must be called before this function!
+ */
 int cfg_child_init(void)
 int cfg_child_init(void)
 {
 {
 	/* set the callback list pointer to the beginning of the list */
 	/* set the callback list pointer to the beginning of the list */
 	cfg_child_cb = *cfg_child_cb_first;
 	cfg_child_cb = *cfg_child_cb_first;
-	atomic_inc(&cfg_child_cb->refcnt);
+
+	return 0;
+}
+
+/* Child process init function that can be called
+ * without cfg_register_child().
+ * Note that the child process may miss some configuration changes.
+ */
+int cfg_late_child_init(void)
+{
+	/* set the callback list pointer to the beginning of the list */
+	CFG_LOCK();
+	atomic_inc(&((*cfg_child_cb_first)->refcnt));
+	cfg_child_cb = *cfg_child_cb_first;
+	CFG_UNLOCK();
 
 
 	return 0;
 	return 0;
 }
 }

+ 17 - 1
cfg/cfg_struct.h

@@ -124,9 +124,25 @@ int sr_cfg_init(void);
 /* destroy the memory allocated for the cfg framework */
 /* destroy the memory allocated for the cfg framework */
 void cfg_destroy(void);
 void cfg_destroy(void);
 
 
-/* per-child process init function */
+/* Register num number of child processes that will
+ * keep updating their local configuration.
+ * This function needs to be called from mod_init
+ * before any child process is forked.
+ */
+void cfg_register_child(int num);
+
+/* per-child process init function.
+ * It needs to be called from the forked process.
+ * cfg_register_child() must be called before this function!
+ */
 int cfg_child_init(void);
 int cfg_child_init(void);
 
 
+/* Child process init function that can be called
+ * without cfg_register_child().
+ * Note that the child process may miss some configuration changes.
+ */
+int cfg_late_child_init(void);
+
 /* per-child process destroy function
 /* per-child process destroy function
  * Should be called only when the child process exits,
  * Should be called only when the child process exits,
  * but SER continues running.
  * but SER continues running.

+ 15 - 2
doc/cfg.txt

@@ -334,6 +334,14 @@ update its own local configuration the following way:
 
 
 #include "../../cfg/cfg_struct.h"
 #include "../../cfg/cfg_struct.h"
 
 
+int mod_init(void)
+{
+	/* register the number of children
+	 * that will keep updating their local
+	 * configuration */
+	cfg_register_child(1);
+}
+
 void loop_forever(void)
 void loop_forever(void)
 {
 {
 	while(1) {
 	while(1) {
@@ -373,8 +381,13 @@ int new_process(void)
 	if (pid == 0) {
 	if (pid == 0) {
 		/* This is the child process */
 		/* This is the child process */
 
 
-		/* initialize the config framework */
-		if (cfg_child_init()) return -1;
+		/* initialize the config framework
+		 * There is no chance to register the
+		 * number of forked processes from mod_init,
+		 * hence the late version of ...child_init()
+		 * needs to be called.
+		 */
+		if (cfg_late_child_init()) return -1;
 
 
 		loop_forever(); /* the function may return */
 		loop_forever(); /* the function may return */
 
 

+ 26 - 0
main.c

@@ -1170,6 +1170,16 @@ int main_loop()
 			LOG(L_WARN, "WARNING: using only the first listen address"
 			LOG(L_WARN, "WARNING: using only the first listen address"
 						" (no fork)\n");
 						" (no fork)\n");
 		}
 		}
+
+		/* Register the children that will keep updating their
+		 * local configuration */
+		cfg_register_child(
+				1   /* main = udp listener */
+				+ 1 /* timer */
+#ifdef USE_SLOW_TIMER
+				+ 1 /* slow timer */
+#endif
+			);
 		if (do_suid()==-1) goto error; /* try to drop privileges */
 		if (do_suid()==-1) goto error; /* try to drop privileges */
 		/* process_no now initialized to zero -- increase from now on
 		/* process_no now initialized to zero -- increase from now on
 		   as new processes are forked (while skipping 0 reserved for main
 		   as new processes are forked (while skipping 0 reserved for main
@@ -1252,6 +1262,16 @@ int main_loop()
 		return udp_rcv_loop();
 		return udp_rcv_loop();
 	}else{ /* fork: */
 	}else{ /* fork: */
 
 
+		/* Register the children that will keep updating their
+		 * local configuration. (udp/tcp/sctp listeneres
+		 * will be added later.) */
+		cfg_register_child(
+				1   /* timer */
+#ifdef USE_SLOW_TIMER
+				+ 1 /* slow timer */
+#endif
+			);
+
 		for(si=udp_listen;si;si=si->next){
 		for(si=udp_listen;si;si=si->next){
 			/* create the listening socket (for each address)*/
 			/* create the listening socket (for each address)*/
 			/* udp */
 			/* udp */
@@ -1265,6 +1285,8 @@ int main_loop()
 					(si->address.af==AF_INET6))
 					(si->address.af==AF_INET6))
 				sendipv6=si;
 				sendipv6=si;
 	#endif
 	#endif
+			/* children_no per each socket */
+			cfg_register_child(children_no);
 		}
 		}
 #ifdef USE_SCTP
 #ifdef USE_SCTP
 		if (!sctp_disable){
 		if (!sctp_disable){
@@ -1281,6 +1303,8 @@ int main_loop()
 						(si->address.af==AF_INET6))
 						(si->address.af==AF_INET6))
 					sendipv6_sctp=si;
 					sendipv6_sctp=si;
 		#endif
 		#endif
+				/* sctp_children_no per each socket */
+				cfg_register_child(sctp_children_no);
 			}
 			}
 		}
 		}
 #endif /* USE_SCTP */
 #endif /* USE_SCTP */
@@ -1301,6 +1325,8 @@ int main_loop()
 					sendipv6_tcp=si;
 					sendipv6_tcp=si;
 		#endif
 		#endif
 			}
 			}
+			/* the number of sockets does not matter */
+			cfg_register_child(tcp_children_no + 1 /* tcp main */);
 		}
 		}
 #ifdef USE_TLS
 #ifdef USE_TLS
 		if (!tls_disable && tls_has_init_si()){
 		if (!tls_disable && tls_has_init_si()){

+ 4 - 0
modules_s/cpl-c/cpl.c

@@ -375,6 +375,10 @@ static int cpl_init(void)
 		strlower( &cpl_env.realm_prefix );
 		strlower( &cpl_env.realm_prefix );
 	}
 	}
 
 
+	/* Register a child process that will keep updating
+	 * its local configuration */
+	cfg_register_child(1);
+
 	return 0;
 	return 0;
 error:
 error:
 	return -1;
 	return -1;

+ 3 - 0
modules_s/ctl/ctl.c

@@ -36,6 +36,7 @@
 #include "../../ut.h"
 #include "../../ut.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../pt.h"
 #include "../../pt.h"
+#include "../../cfg/cfg_struct.h"
 #include "ctrl_socks.h"
 #include "ctrl_socks.h"
 #include "io_listener.h"
 #include "io_listener.h"
 
 
@@ -270,6 +271,8 @@ static int mod_init(void)
 		/* we will fork */
 		/* we will fork */
 		register_procs(1); /* we will be creating an extra process */
 		register_procs(1); /* we will be creating an extra process */
 		register_fds(fd_no);
 		register_fds(fd_no);
+		/* The child process will keep updating its local configuration */
+		cfg_register_child(1);
 	}
 	}
 #ifdef USE_FIFO
 #ifdef USE_FIFO
 	fifo_rpc_init();
 	fifo_rpc_init();

+ 7 - 1
modules_s/fifo/fifo.c

@@ -33,6 +33,7 @@
 #include "../../ut.h"
 #include "../../ut.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../pt.h"
 #include "../../pt.h"
+#include "../../cfg/cfg_struct.h"
 #include "fifo_server.h"
 #include "fifo_server.h"
 #include "fifo.h"
 #include "fifo.h"
 
 
@@ -82,7 +83,12 @@ static int mod_init(void)
 	     /* Signal to the core that we will be creating one
 	     /* Signal to the core that we will be creating one
 	      * additional process
 	      * additional process
 	      */
 	      */
-	if (fifo) register_procs(1);
+	if (fifo) {
+		register_procs(1);
+		/* The child process will keep updating its
+		 * local configuration */
+		cfg_register_child(1);
+	}
 	return 0;
 	return 0;
 }
 }
 
 

+ 9 - 2
modules_s/jabber/jabber.c

@@ -280,6 +280,10 @@ static int mod_init(void)
 		return -1;
 		return -1;
 	}
 	}
 
 
+	/* register nrw + 1 number of children that will keep
+	 * updating their local configuration */
+	cfg_register_child(nrw + 1);
+
 	DBG("XJAB:mod_init: initialized ...\n");
 	DBG("XJAB:mod_init: initialized ...\n");
 	return 0;
 	return 0;
 }
 }
@@ -850,8 +854,11 @@ void xjab_check_workers(int mpid)
 			}
 			}
 
 
 
 
-			/* initialize the config framework */
-			if (cfg_child_init()) return;
+			/* initialize the config framework
+			 * The child process was not registered under
+			 * the framework during mod_init, therefore the
+			 * late version needs to be called. (Miklos) */
+			if (cfg_late_child_init()) return;
 
 
 			ctx = db_ctx("jabber");
 			ctx = db_ctx("jabber");
 			if (ctx == NULL) goto dberror;
 			if (ctx == NULL) goto dberror;

+ 5 - 2
modules_s/nathelper/natping.c

@@ -109,10 +109,13 @@ natpinger_init(void)
 		 * Use timer only in single process. For forked SER,
 		 * Use timer only in single process. For forked SER,
 		 * use separate process (see natpinger_child_init())
 		 * use separate process (see natpinger_child_init())
 		 */
 		 */
-		if (dont_fork)
+		if (dont_fork) {
 			register_timer(natping, NULL, natping_interval);
 			register_timer(natping, NULL, natping_interval);
-		else
+		} else {
 			register_procs(1); /* register the separate natpinger process */
 			register_procs(1); /* register the separate natpinger process */
+			/* The process will keep updating its configuration */
+			cfg_register_child(1);
+		}
 
 
 		if (natping_method == NULL) {
 		if (natping_method == NULL) {
 			if (natping_crlf == 0)
 			if (natping_crlf == 0)

+ 4 - 0
modules_s/sms/sms.c

@@ -627,6 +627,10 @@ int global_init()
 		goto error;
 		goto error;
 	}
 	}
 	*queued_msgs = 0;
 	*queued_msgs = 0;
+	
+	/* register nr_of_modems number of child processes that will
+	 * update their local configuration */
+	cfg_register_child(nr_of_modems);
 
 
 	return 1;
 	return 1;
 error:
 error: