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 257
 #endif
255 258
 ;
256 259
 
260
+/*! pipe to communicate the parent and main processes when daemonizing in order
261
+    to get the proper exit status code */
262
+int daemon_status_fd[2];
263
+
257 264
 /* print compile-time constants */
258 265
 void print_ct_constants()
259 266
 {
... ...
@@ -1565,7 +1572,14 @@ int main_loop()
1565 1572
 		}
1566 1573
 #endif
1567 1574
 		DBG("Expect maximum %d  open fds\n", get_max_open_fds());
1568
-
1575
+		/* in daemonize mode write into daemon_status_fd[1] so the parent process
1576
+	 	will exit with 0 */
1577
+		if (!dont_fork){
1578
+			if (write(daemon_status_fd[1], "go", 2)<0){
1579
+				LM_CRIT("error writing into daemon_status_fd[1]\n");
1580
+				goto error;
1581
+			}
1582
+		}
1569 1583
 		for(;;){
1570 1584
 			handle_sigs();
1571 1585
 			pause();
... ...
@@ -1634,6 +1648,13 @@ int main(int argc, char** argv)
1634 1648
 	int dont_fork_cnt;
1635 1649
 	struct name_lst* n_lst;
1636 1650
 
1651
+	/* variables to control the master process exit status */
1652
+	int fd_nbytes;
1653
+	char fd_readbuffer[5];
1654
+	struct timeval tval;
1655
+	fd_set fds;
1656
+	int res;
1657
+
1637 1658
 	/*init*/
1638 1659
 	time(&up_since);
1639 1660
 	creator_pid = getpid();
... ...
@@ -1827,6 +1848,7 @@ try_again:
1827 1848
 	debug_save = default_core_cfg.debug;
1828 1849
 	if ((yyparse()!=0)||(cfg_errors)){
1829 1850
 		fprintf(stderr, "ERROR: bad config file (%d errors)\n", cfg_errors);
1851
+
1830 1852
 		goto error;
1831 1853
 	}
1832 1854
 	if (cfg_warnings){
... ...
@@ -2170,7 +2192,41 @@ try_again:
2170 2192
 #endif /* USE_SCTP */
2171 2193
 	/* init_daemon? */
2172 2194
 	if (!dont_fork){
2173
-		if ( daemonize((log_name==0)?argv[0]:log_name) <0 ) goto error;
2195
+		if (pipe(daemon_status_fd)<0){
2196
+			LM_CRIT("could not create pipe(daemon_status_fd), exiting...\n");
2197
+			goto error;
2198
+		}
2199
+		if (daemonize((log_name==0)?argv[0]:log_name, daemon_status_fd[1]) < 0)
2200
+			goto error;
2201
+		/* parent process? then wait the main process to write into the pipe */
2202
+		if (getpid() == creator_pid) {
2203
+			/* close the output side of the pipe */
2204
+			close(daemon_status_fd[1]);
2205
+#define MASTER_MAX_SLEEP 10
2206
+try_select_again:	tval.tv_usec = 0;
2207
+			tval.tv_sec = MASTER_MAX_SLEEP;/* 10 seconds */
2208
+			FD_ZERO(&fds);
2209
+			FD_SET(daemon_status_fd[0], &fds);
2210
+			res = select(daemon_status_fd[0]+1, &fds, NULL, NULL, &tval);
2211
+			if(res == -1 && errno == EINTR && time(NULL)-up_since < 2*MASTER_MAX_SLEEP) 
2212
+				goto try_select_again;
2213
+
2214
+			switch(res){
2215
+				case -1: /* error on select*/ LOG(L_ERR, "Error in select in master process\n");exit(-1);
2216
+				case 0: /* timeout */ LOG(L_ERR, "timeout in select in master process\n");exit(-2);
2217
+				default:{
2218
+					fd_nbytes = read(daemon_status_fd[0], fd_readbuffer, 5);
2219
+					/* something read, ok, exit with 0 */
2220
+					if (fd_nbytes > 0)
2221
+						exit(0);
2222
+					/* nothing read, error */
2223
+					else{
2224
+						LOG(L_ERR, "Main process exited before writing to pipe\n");
2225
+						exit(-1);
2226
+					}
2227
+				}
2228
+			}
2229
+		}
2174 2230
 	}
2175 2231
 	if (install_sigs() != 0){
2176 2232
 		fprintf(stderr, "ERROR: could not install the signal handlers\n");