core: daemon status/pipe fixes & interface changes
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Thu, 19 Aug 2010 18:03:54 +0000 (20:03 +0200)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Thu, 19 Aug 2010 18:42:54 +0000 (20:42 +0200)
- 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]

daemonize.c
daemonize.h
main.c
pt.c
pt.h

index d39cbb3..7411652 100644 (file)
@@ -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.
  */
 /*
  * 
 #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
 }
-
-
index af71a1b..622a710 100644 (file)
@@ -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: */
diff --git a/main.c b/main.c
index c6df65c..753b63b 100644 (file)
--- a/main.c
+++ b/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;
-
 }
diff --git a/pt.c b/pt.c
index a288723..994f998 100644 (file)
--- a/pt.c
+++ b/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:
- *    info@iptel.org
- *
- * 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:
  *  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));
diff --git a/pt.h b/pt.h
index 525c078..2d16aa1 100644 (file)
--- a/pt.h
+++ b/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:
- *    info@iptel.org
- *
- * 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:
  *  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