浏览代码

core: daemon status/pipe fixes & interface changes

- moved most of the daemon status stuff to daemonize.[ch].
- nicer interface (e.g. daemon_status_send(code))
- send/read only 1 byte which will be used as exit code
- send an error status on error (fixes
  "Main process exited before writing to pipe" error message)
- disabled the timeout.  Not needed (now a status is sent always
  and even an unlikely process crash before sending it is detected via
  the read() failure) and very hard to find a good value (some
  setups start very slow).
- close the pipe "send" fd in processes not needing it
- attempt to send back status only if dont_daemonize is not set
  (not only if dont_fork==0, it is possible to have forking
  enabled, but daemonize disabled: ser -DD)
- BSDed daemonize.[ch] and pt.[ch]
Andrei Pelinescu-Onciul 15 年之前
父节点
当前提交
83e91df144
共有 5 个文件被更改,包括 252 次插入140 次删除
  1. 181 25
      daemonize.c
  2. 20 16
      daemonize.h
  3. 21 53
      main.c
  4. 16 27
      pt.c
  5. 14 19
      pt.h

+ 181 - 25
daemonize.c

@@ -3,21 +3,17 @@
  *
  * Copyright (C) 2001-2003 FhG Fokus
  *
- * This file is part of SIP-router, a free SIP server.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * SIP-router is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * SIP-router is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 /*
  * 
@@ -81,16 +77,165 @@
 #define MAX_FD 32 /* maximum number of inherited open file descriptors,
 		    (normally it shouldn't  be bigger  than 3) */
 
-/*! \brief daemon init, return 0 on success, -1 on error */
-int daemonize(char*  name,  int daemon_status_fd_input)
+/** temporary pipe FDs for sending exit status back to the ancestor process.
+ * This pipe is used to send the desired exit status to the initial process,
+ * that waits for it in the foreground. This way late errors preventing
+ * startup (e.g. during modules child inits or TCP late init) can still be
+ * reported back.
+ */
+static int daemon_status_fd[2];
+
+
+
+/** init daemon status reporting.
+ * Must be called before any other daemon_status function has a chance to
+ * run.
+ */
+void daemon_status_init()
+{
+	daemon_status_fd[0] = -1;
+	daemon_status_fd[1] = -1;
+}
+
+
+
+/** pre-daemonize init for daemon status reporting.
+ * Must be called before forking.
+ * Typically the parent process will call daemon_status_wait() while
+ * one of the children will call daemon_status_send() at some point.
+ *
+ * @return 0 on success, -1 on error (and sets errno).
+ */
+int daemon_status_pre_daemonize()
+{
+	int ret;
+	
+retry:
+	ret = pipe(daemon_status_fd);
+	if (ret < 0 && errno == EINTR)
+		goto retry;
+	return ret;
+}
+
+
+
+/** wait for an exit status to be send by daemon_status_send().
+ * @param status - filled with the sent status (a char).
+ * @return  0 on success, -1 on error (e.g. process died before sending
+ *          status, not intialized a.s.o.).
+ * Side-effects: it will close the write side of the pipe
+ *  (must not be used from the same process as the daemon_status_send()).
+ * Note: if init is not complete (only init, but no pre-daemonize)
+ * it will return success always and status 0.
+ */
+int daemon_status_wait(char* status)
+{
+	int ret;
+	
+	/* close the output side of the pipe */
+	if (daemon_status_fd[1] != -1) {
+		close(daemon_status_fd[1]);
+		daemon_status_fd[1] = -1;
+	}
+	if (daemon_status_fd[0] == -1) {
+		*status = 0;
+		return -1;
+	}
+retry:
+	ret = read(daemon_status_fd[0], status, 1);
+	if (ret < 0 && errno == EINTR)
+		goto retry;
+	return (ret ==1 ) ? 0 : -1;
+}
+
+
+
+/** send 'status' to a waiting process running daemon_status_wait().
+ * @param status - status byte
+ * @return 0 on success, -1 on error.
+ * Note: if init is not complete (only init, but no pre-daemonize)
+ * it will return success always.
+ */
+int daemon_status_send(char status)
+{
+	int ret;
+
+	if (daemon_status_fd[1] == -1)
+		return 0;
+retry:
+	ret = write(daemon_status_fd[1], &status, 1);
+	if (ret < 0 && errno == EINTR)
+		goto retry;
+	return (ret ==1 ) ? 0 : -1;
+}
+
+
+
+/** cleanup functions for new processes.
+ * Should be called after fork(), for each new process that _does_ _not_
+ * use  daemon_status_send() or daemon_status_wait().
+ */
+void daemon_status_on_fork_cleanup()
+{
+	if (daemon_status_fd[0] != -1) {
+		close(daemon_status_fd[0]);
+		daemon_status_fd[0] = -1;
+	}
+	if (daemon_status_fd[1] != -1) {
+		close(daemon_status_fd[1]);
+		daemon_status_fd[1] = -1;
+	}
+}
+
+
+
+/** cleanup functions for processes that don't intead to wait.
+ * Should be called after fork(), for each new process that doesn't
+ * use daemon_status_wait().
+ */
+void daemon_status_no_wait()
+{
+	if (daemon_status_fd[0] != -1) {
+		close(daemon_status_fd[0]);
+		daemon_status_fd[0] = -1;
+	}
+}
+
+
+
+/** daemon init.
+ *@param name - daemon name used for logging (used when opening syslog).
+ *@param status_wait  - if 1 the original process will wait until it gets
+ *                  an exit code send using daemon_status_send().
+ *@return 0 in the child process (in case of daemonize mode),
+ *        -1 on error.
+ * The original process that called daemonize() will be terminated if
+ * dont_daemonize == 0. The exit code depends on status_wait. If status_wait
+ * is non-zero, the original process will wait for a status code, that
+ * must be sent with daemon_status_send() (daemon_status_send() must be
+ * called or the original process will remain waiting until all the children
+ * close()). If status_wait is 0, the original process will exit immediately
+ * with exit(0).
+ * Global variables/config params used:
+ * dont_daemonize
+ * chroot_dir
+ * working_dir
+ * pid_file - if set the pid will be written here (ascii).
+ * pgid_file - if set, the pgid will be written here (ascii).
+ * log_stderr - if not set syslog will be opened (openlog(name,...))
+ * 
+ *
+ * Side-effects:
+ *  sets own_pgid after becoming session leader (own process group).
+*/
+int daemonize(char*  name,  int status_wait)
 {
 	FILE *pid_stream;
 	pid_t pid;
 	int r, p;
-
+	char pipe_status;
 
 	p=-1;
-
 	/* flush std file descriptors to avoid flushes after fork
 	 *  (same message appearing multiple times)
 	 *  and switch to unbuffered
@@ -108,14 +253,27 @@ int daemonize(char*  name,  int daemon_status_fd_input)
 	}
 
 	if (!dont_daemonize) {
+		if (status_wait) {
+			if (daemon_status_pre_daemonize() < 0)
+				goto error;
+		}
 		/* fork to become!= group leader*/
 		if ((pid=fork())<0){
 			LOG(L_CRIT, "Cannot fork:%s\n", strerror(errno));
 			goto error;
-		}else if (pid!=0){	
-			/*parent process => return 0 */
-			return 0;
+		}else if (pid!=0){
+			if (status_wait) {
+				if (daemon_status_wait(&pipe_status) == 0)
+					exit((int)pipe_status);
+				else{
+					LOG(L_ERR, "Main process exited before writing to pipe\n");
+					exit(-1);
+				}
+			}
+			exit(0);
 		}
+		if (status_wait)
+			daemon_status_no_wait(); /* clean unused read fd */
 		/* become session leader to drop the ctrl. terminal */
 		if (setsid()<0){
 			LOG(L_WARN, "setsid failed: %s\n",strerror(errno));
@@ -211,11 +369,11 @@ int daemonize(char*  name,  int daemon_status_fd_input)
 		/* continue, leave it open */
 	};
 	
-	/* close all but the daemon_status_fd_input as the main process
+	/* close all but the daemon_status_fd output as the main process
 	  must still write into it to tell the parent to exit with 0 */
 	closelog();
 	for (r=3;r<MAX_FD; r++){
-		if(r !=  daemon_status_fd_input)
+		if(r !=  daemon_status_fd[1])
 			close(r);
 	}
 	
@@ -440,5 +598,3 @@ int set_rt_prio(int prio, int policy)
 	return -1;
 #endif
 }
-
-

+ 20 - 16
daemonize.h

@@ -3,28 +3,25 @@
  *
  * Copyright (C) 2001-2003 FhG Fokus
  *
- * This file is part of SIP-router, a free SIP server.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * SIP-router is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * SIP-router is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 /*
  * 
  * History:
  * --------
  *  2004-02-20  created by andrei
- *  2007-06-07 added mem_lock_pages() (andrei)
+ *  2007-06-07  added mem_lock_pages() (andrei)
+ *  2010-08-19  send status via pipe code derived from 9167c1 (ibc) (andrei)
  */
 
 #ifndef _daemonize_h
@@ -37,5 +34,12 @@ int set_core_dump(int enable, int size);
 int mem_lock_pages();
 int set_rt_prio(int prio, int policy);
 
+void daemon_status_init();
+void daemon_status_on_fork_cleanup();
+int daemon_status_send(char status);
+void daemon_status_no_wait();
+void daemon_status_on_fork_cleanup();
+
+#endif /*_daemonize_h */
 
-#endif
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 21 - 53
main.c

@@ -76,6 +76,7 @@
  * 2010-04-19  added daemon_status_fd pipe to communicate the parent process
  *              with the main process in daemonize mode, so the parent process
  *              can return the proper exit status code (ibc)
+ * 2010-08-19  moved the daemon status stuff to daemonize.c (andrei)
  */
 
 /** main file (init, daemonize, startup) 
@@ -259,9 +260,6 @@ Options:\n\
 #endif
 ;
 
-/*! pipe to communicate the parent and main processes when daemonizing in order
-    to get the proper exit status code */
-int daemon_status_fd[2];
 
 /* print compile-time constants */
 void print_ct_constants()
@@ -1666,11 +1664,11 @@ int main_loop()
 		}
 #endif
 		DBG("Expect maximum %d  open fds\n", get_max_open_fds());
-		/* in daemonize mode write into daemon_status_fd[1] so the parent process
-	 	will exit with 0 */
-		if (!dont_fork){
-			if (write(daemon_status_fd[1], "go", 2)<0){
-				LM_CRIT("error writing into daemon_status_fd[1]\n");
+		/* in daemonize mode send the exit code back to the parent process */
+		if (!dont_daemonize) {
+			if (daemon_status_send(0) < 0) {
+				ERR("error sending daemon status: %s [%d]\n",
+						strerror(errno), errno);
 				goto error;
 			}
 		}
@@ -1741,13 +1739,6 @@ int main(int argc, char** argv)
 	int debug_save, debug_flag;
 	int dont_fork_cnt;
 	struct name_lst* n_lst;
-
-	/* variables to control the master process exit status */
-	int fd_nbytes;
-	char fd_readbuffer[5];
-	struct timeval tval;
-	fd_set fds;
-	int res;
 	char *p;
 
 	/*init*/
@@ -1758,6 +1749,7 @@ int main(int argc, char** argv)
 	debug_flag=0;
 	dont_fork_cnt=0;
 
+	daemon_status_init();
 	/*init pkg mallocs (before parsing cfg or cmd line !)*/
 	if (init_pkg_mallocs()==-1)
 		goto error;
@@ -2322,43 +2314,8 @@ try_again:
 	}
 #endif /* USE_SCTP */
 	/* init_daemon? */
-	if (!dont_fork){
-		if (pipe(daemon_status_fd)<0){
-			LM_CRIT("could not create pipe(daemon_status_fd), exiting...\n");
-			goto error;
-		}
-		if (daemonize((log_name==0)?argv[0]:log_name, daemon_status_fd[1]) < 0)
-			goto error;
-		/* parent process? then wait the main process to write into the pipe */
-		if (getpid() == creator_pid) {
-			/* close the output side of the pipe */
-			close(daemon_status_fd[1]);
-#define MASTER_MAX_SLEEP 10
-try_select_again:	tval.tv_usec = 0;
-			tval.tv_sec = MASTER_MAX_SLEEP;/* 10 seconds */
-			FD_ZERO(&fds);
-			FD_SET(daemon_status_fd[0], &fds);
-			res = select(daemon_status_fd[0]+1, &fds, NULL, NULL, &tval);
-			if(res == -1 && errno == EINTR && time(NULL)-up_since < 2*MASTER_MAX_SLEEP) 
-				goto try_select_again;
-
-			switch(res){
-				case -1: /* error on select*/ LOG(L_ERR, "Error in select in master process\n");exit(-1);
-				case 0: /* timeout */ LOG(L_ERR, "timeout in select in master process\n");exit(-2);
-				default:{
-					fd_nbytes = read(daemon_status_fd[0], fd_readbuffer, 5);
-					/* something read, ok, exit with 0 */
-					if (fd_nbytes > 0)
-						exit(0);
-					/* nothing read, error */
-					else{
-						LOG(L_ERR, "Main process exited before writing to pipe\n");
-						exit(-1);
-					}
-				}
-			}
-		}
-	}
+	if( !dont_fork && daemonize((log_name==0)?argv[0]:log_name, 1) < 0)
+		goto error;
 	if (install_sigs() != 0){
 		fprintf(stderr, "ERROR: could not install the signal handlers\n");
 		goto error;
@@ -2439,14 +2396,25 @@ try_select_again:	tval.tv_usec = 0;
 #endif
 
 	ret=main_loop();
+	if (ret < 0)
+		goto error;
 	/*kill everything*/
 	if (is_main) shutdown_children(SIGTERM, 0);
+	if (!dont_daemonize) {
+		if (daemon_status_send(0) < 0)
+			ERR("error sending exit status: %s [%d]\n",
+					strerror(errno), errno);
+	}
 	/* else terminate process */
 	return ret;
 
 error:
 	/*kill everything*/
 	if (is_main) shutdown_children(SIGTERM, 0);
+	if (!dont_daemonize) {
+		if (daemon_status_send((char)-1) < 0)
+			ERR("error sending exit status: %s [%d]\n",
+					strerror(errno), errno);
+	}
 	return -1;
-
 }

+ 16 - 27
pt.c

@@ -3,30 +3,19 @@
  *
  * Process Table
  *
- *
- *
  * Copyright (C) 2001-2003 FhG Fokus
  *
- * This file is part of ser, a free SIP server.
- *
- * ser is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- *    [email protected]
- *
- * ser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 /*
  * History:
@@ -36,13 +25,11 @@
  *  2006-10-25	sanity check before allowing forking w/ tcp support (is_main
  *               & tcp not started yet); set is_main=0 in childs (andrei)
  *  2007-07-04	added register_fds() and get_max_open_fds(() (andrei)
+ *  2010-08-19	use daemon_status_on_fork_cleanup() (andrei)
  */
-
-/*!
- * \file
- * \brief SIP-router core :: 
- * \ingroup core
- * Module: \ref core
+/** internal fork functions and process table.
+ * @file: pt.c
+ * @ingroup core
  */
 
 
@@ -314,6 +301,7 @@ int fork_process(int child_id, char *desc, int make_sock)
 		/* child */
 		is_main=0; /* a forked process cannot be the "main" one */
 		process_no=child_process_no;
+		daemon_status_on_fork_cleanup();
 		/* close tcp unix sockets if this is not tcp main */
 #ifdef USE_TCP
 		close_extra_socks(child_id, process_no);
@@ -465,6 +453,7 @@ int fork_tcp_process(int child_id, char *desc, int r, int *reader_fd_1)
 				tcp_children[i].unix_sock=-1;
 			}
 		}
+		daemon_status_on_fork_cleanup();
 		srand(new_seed1);
 		fastrand_seed(rand());
 		srandom(new_seed2+time(0));

+ 14 - 19
pt.h

@@ -7,26 +7,17 @@
  *
  * Copyright (C) 2001-2003 FhG Fokus
  *
- * This file is part of ser, a free SIP server.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * ser is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- *    [email protected]
- *
- * ser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 /*
  * History:
@@ -35,6 +26,10 @@
  *  2006-06-14	added process table in shared mem (dragos)
  *  2007-07-04	added register_fds() and get_max_open_fds(() (andrei)
  */
+/** internal fork functions and process table.
+ * @file: pt.h
+ * @ingroup core
+ */
 
 
 #ifndef _PT_H