Browse code

core: The patch allows ser started in daemonize mode to return the proper error code.

If for example the listen address is not local or a DB connection uses an invalid password, then the command
"ser" would return 0 (OK) anyway (even if it fails to start).This occurs because all those checking
(socket, DB connections...) are performed *after* invoking daemonize() so the parent process (which
could be invoked by "/etc/init.d/ser start") returns 0 knowing nothing about those errors. This caused some
management tools like HeartBeat to work badly.

The patch is simple: the master process opens a pipe, and reads from one end. The forked main process,
after the correct initialization of components, will write some bytes to the writting end. In case of error,
no data is written to the pipe.

The master process waits for 10 seconds, or else it considers the main process blocked and exits with a different
error code( -2 ). In this case, it is left for the user to ensure that all ser children are killed correctly.

Patch initially from IƱaki Baz Castillo.

Marius Zbihlei authored on 21/04/2010 09:35:16
Showing 3 changed files
... ...
@@ -82,7 +82,7 @@
82 82
 		    (normally it shouldn't  be bigger  than 3) */
83 83
 
84 84
 /*! \brief daemon init, return 0 on success, -1 on error */
85
-int daemonize(char*  name)
85
+int daemonize(char*  name,  int daemon_status_fd_input)
86 86
 {
87 87
 	FILE *pid_stream;
88 88
 	pid_t pid;
... ...
@@ -113,8 +113,8 @@ int daemonize(char*  name)
113 113
 			LOG(L_CRIT, "Cannot fork:%s\n", strerror(errno));
114 114
 			goto error;
115 115
 		}else if (pid!=0){	
116
-			/*parent process => exit */
117
-			exit(0);
116
+			/*parent process => return 0 */
117
+			return 0;
118 118
 		}
119 119
 		/* become session leader to drop the ctrl. terminal */
120 120
 		if (setsid()<0){
... ...
@@ -211,9 +211,11 @@ int daemonize(char*  name)
211 211
 		/* continue, leave it open */
212 212
 	};
213 213
 	
214
-	/* close any open file descriptors */
214
+	/* close all but the daemon_status_fd_input as the main process
215
+	  must still write into it to tell the parent to exit with 0 */
215 216
 	closelog();
216 217
 	for (r=3;r<MAX_FD; r++){
218
+		if(r !=  daemon_status_fd_input)
217 219
 			close(r);
218 220
 	}
219 221
 	
... ...
@@ -30,7 +30,7 @@
30 30
 #ifndef _daemonize_h
31 31
 #define _daemonize_h
32 32
 
33
-int daemonize(char* name);
33
+int daemonize(char* name, int daemon_status_fd_input);
34 34
 int do_suid();
35 35
 int increase_open_fds(int target);
36 36
 int set_core_dump(int enable, int size);
... ...
@@ -73,6 +73,9 @@
73 73
  * 2008-08-08  sctp support (andrei)
74 74
  * 2008-08-19  -l support for mmultihomed addresses/addresses lists
75 75
  *                (e.g. -l (eth0, 1.2.3.4, foo.bar) ) (andrei)
76
+ *  2010-04-19 added daemon_status_fd pipe to communicate the parent process with
77
+               the main process in daemonize mode, so the parent process can return
78
+               the proper exit status code (ibc)
76 79
  */
77 80
 
78 81
 /*!
... ...
@@ -254,6 +257,10 @@ Options:\n\
254 254
 #endif
255 255
 ;
256 256
 
257
+/*! pipe to communicate the parent and main processes when daemonizing in order
258
+    to get the proper exit status code */
259
+int daemon_status_fd[2];
260
+
257 261
 /* print compile-time constants */
258 262
 void print_ct_constants()
259 263
 {
... ...
@@ -1565,7 +1572,14 @@ int main_loop()
1565 1565
 		}
1566 1566
 #endif
1567 1567
 		DBG("Expect maximum %d  open fds\n", get_max_open_fds());
1568
-
1568
+		/* in daemonize mode write into daemon_status_fd[1] so the parent process
1569
+	 	will exit with 0 */
1570
+		if (!dont_fork){
1571
+			if (write(daemon_status_fd[1], "go", 2)<0){
1572
+				LM_CRIT("error writing into daemon_status_fd[1]\n");
1573
+				goto error;
1574
+			}
1575
+		}
1569 1576
 		for(;;){
1570 1577
 			handle_sigs();
1571 1578
 			pause();
... ...
@@ -1634,6 +1648,13 @@ int main(int argc, char** argv)
1634 1634
 	int dont_fork_cnt;
1635 1635
 	struct name_lst* n_lst;
1636 1636
 
1637
+	/* variables to control the master process exit status */
1638
+	int fd_nbytes;
1639
+	char fd_readbuffer[5];
1640
+	struct timeval tval;
1641
+	fd_set fds;
1642
+	int res;
1643
+
1637 1644
 	/*init*/
1638 1645
 	time(&up_since);
1639 1646
 	creator_pid = getpid();
... ...
@@ -1827,6 +1848,7 @@ try_again:
1827 1827
 	debug_save = default_core_cfg.debug;
1828 1828
 	if ((yyparse()!=0)||(cfg_errors)){
1829 1829
 		fprintf(stderr, "ERROR: bad config file (%d errors)\n", cfg_errors);
1830
+
1830 1831
 		goto error;
1831 1832
 	}
1832 1833
 	if (cfg_warnings){
... ...
@@ -2170,7 +2192,41 @@ try_again:
2170 2170
 #endif /* USE_SCTP */
2171 2171
 	/* init_daemon? */
2172 2172
 	if (!dont_fork){
2173
-		if ( daemonize((log_name==0)?argv[0]:log_name) <0 ) goto error;
2173
+		if (pipe(daemon_status_fd)<0){
2174
+			LM_CRIT("could not create pipe(daemon_status_fd), exiting...\n");
2175
+			goto error;
2176
+		}
2177
+		if (daemonize((log_name==0)?argv[0]:log_name, daemon_status_fd[1]) < 0)
2178
+			goto error;
2179
+		/* parent process? then wait the main process to write into the pipe */
2180
+		if (getpid() == creator_pid) {
2181
+			/* close the output side of the pipe */
2182
+			close(daemon_status_fd[1]);
2183
+#define MASTER_MAX_SLEEP 10
2184
+try_select_again:	tval.tv_usec = 0;
2185
+			tval.tv_sec = MASTER_MAX_SLEEP;/* 10 seconds */
2186
+			FD_ZERO(&fds);
2187
+			FD_SET(daemon_status_fd[0], &fds);
2188
+			res = select(daemon_status_fd[0]+1, &fds, NULL, NULL, &tval);
2189
+			if(res == -1 && errno == EINTR && time(NULL)-up_since < 2*MASTER_MAX_SLEEP) 
2190
+				goto try_select_again;
2191
+
2192
+			switch(res){
2193
+				case -1: /* error on select*/ LOG(L_ERR, "Error in select in master process\n");exit(-1);
2194
+				case 0: /* timeout */ LOG(L_ERR, "timeout in select in master process\n");exit(-2);
2195
+				default:{
2196
+					fd_nbytes = read(daemon_status_fd[0], fd_readbuffer, 5);
2197
+					/* something read, ok, exit with 0 */
2198
+					if (fd_nbytes > 0)
2199
+						exit(0);
2200
+					/* nothing read, error */
2201
+					else{
2202
+						LOG(L_ERR, "Main process exited before writing to pipe\n");
2203
+						exit(-1);
2204
+					}
2205
+				}
2206
+			}
2207
+		}
2174 2208
 	}
2175 2209
 	if (install_sigs() != 0){
2176 2210
 		fprintf(stderr, "ERROR: could not install the signal handlers\n");