s3:smbd: use signal events for SIGTERM, SIGHUP and SIGCHLD
[metze/samba/wip.git] / source3 / smbd / server.c
index 6241e96f77587972200224738a17b898862cfd5e..e6ea5f39865ec8bcd592b28688196a78183451f3 100644 (file)
 */
 
 #include "includes.h"
+#include "smbd/globals.h"
 
 static_decl_rpc;
 
-static int am_parent = 1;
-
-extern struct auth_context *negprot_global_auth_context;
-extern SIG_ATOMIC_T got_sig_term;
-extern SIG_ATOMIC_T reload_after_sighup;
-static SIG_ATOMIC_T got_sig_cld;
-
 #ifdef WITH_DFS
 extern int dcelogin_atmost_once;
 #endif /* WITH_DFS */
 
-/* really we should have a top level context structure that has the
-   client file descriptor as an element. That would require a major rewrite :(
-
-   the following 2 functions are an alternative - they make the file
-   descriptor private to smbd
- */
-static int server_fd = -1;
-
 int smbd_server_fd(void)
 {
        return server_fd;
@@ -59,61 +45,41 @@ int get_client_fd(void)
        return server_fd;
 }
 
-#ifdef CLUSTER_SUPPORT
-static int client_get_tcp_info(struct sockaddr_storage *server,
-                              struct sockaddr_storage *client)
-{
-       socklen_t length;
-       if (server_fd == -1) {
-               return -1;
-       }
-       length = sizeof(*server);
-       if (getsockname(server_fd, (struct sockaddr *)server, &length) != 0) {
-               return -1;
-       }
-       length = sizeof(*client);
-       if (getpeername(server_fd, (struct sockaddr *)client, &length) != 0) {
-               return -1;
-       }
-       return 0;
-}
-#endif
-
 struct event_context *smbd_event_context(void)
 {
-       static struct event_context *ctx;
-
-       if (!ctx && !(ctx = event_context_init(talloc_autofree_context()))) {
+       if (!smbd_event_ctx) {
+               smbd_event_ctx = event_context_init(talloc_autofree_context());
+       }
+       if (!smbd_event_ctx) {
                smb_panic("Could not init smbd event context");
        }
-       return ctx;
+       return smbd_event_ctx;
 }
 
 struct messaging_context *smbd_messaging_context(void)
 {
-       static struct messaging_context *ctx;
-
-       if (ctx == NULL) {
-               ctx = messaging_init(talloc_autofree_context(), server_id_self(),
-                                    smbd_event_context());
+       if (smbd_msg_ctx == NULL) {
+               smbd_msg_ctx = messaging_init(talloc_autofree_context(),
+                                             server_id_self(),
+                                             smbd_event_context());
        }
-       if (ctx == NULL) {
+       if (smbd_msg_ctx == NULL) {
                DEBUG(0, ("Could not init smbd messaging context.\n"));
        }
-       return ctx;
+       return smbd_msg_ctx;
 }
 
 struct memcache *smbd_memcache(void)
 {
-       static struct memcache *cache;
-
-       if (!cache
-           && !(cache = memcache_init(talloc_autofree_context(),
-                                      lp_max_stat_cache_size()*1024))) {
-
+       if (!smbd_memcache_ctx) {
+               smbd_memcache_ctx = memcache_init(talloc_autofree_context(),
+                                                 lp_max_stat_cache_size()*1024);
+       }
+       if (!smbd_memcache_ctx) {
                smb_panic("Could not init smbd memcache");
        }
-       return cache;
+
+       return smbd_memcache_ctx;
 }
 
 /*******************************************************************
@@ -147,35 +113,6 @@ static void smb_stat_cache_delete(struct messaging_context *msg,
        stat_cache_delete(name);
 }
 
-/****************************************************************************
- Terminate signal.
-****************************************************************************/
-
-static void sig_term(void)
-{
-       got_sig_term = 1;
-       sys_select_signal(SIGTERM);
-}
-
-/****************************************************************************
- Catch a sighup.
-****************************************************************************/
-
-static void sig_hup(int sig)
-{
-       reload_after_sighup = 1;
-       sys_select_signal(SIGHUP);
-}
-
-/****************************************************************************
- Catch a sigcld
-****************************************************************************/
-static void sig_cld(int sig)
-{
-       got_sig_cld = 1;
-       sys_select_signal(SIGCLD);
-}
-
 /****************************************************************************
   Send a SIGTERM to our process group.
 *****************************************************************************/
@@ -213,9 +150,6 @@ static bool open_sockets_inetd(void)
        
        /* close our standard file descriptors */
        close_low_fds(False); /* Don't close stderr */
-       
-       set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
-       set_socket_options(smbd_server_fd(), lp_socket_options());
 
        return True;
 }
@@ -269,9 +203,6 @@ struct child_pid {
        pid_t pid;
 };
 
-static struct child_pid *children;
-static int num_children;
-
 static void add_child_pid(pid_t pid)
 {
        struct child_pid *child;
@@ -338,6 +269,50 @@ static bool allowable_number_of_smbd_processes(void)
        return num_children < max_processes;
 }
 
+static void smbd_sig_chld_handler(struct tevent_context *ev,
+                                 struct tevent_signal *se,
+                                 int signum,
+                                 int count,
+                                 void *siginfo,
+                                 void *private_data)
+{
+       pid_t pid;
+       int status;
+
+       while ((pid = sys_waitpid(-1, &status, WNOHANG)) > 0) {
+               bool unclean_shutdown = False;
+
+               /* If the child terminated normally, assume
+                  it was an unclean shutdown unless the
+                  status is 0
+               */
+               if (WIFEXITED(status)) {
+                       unclean_shutdown = WEXITSTATUS(status);
+               }
+               /* If the child terminated due to a signal
+                  we always assume it was unclean.
+               */
+               if (WIFSIGNALED(status)) {
+                       unclean_shutdown = True;
+               }
+               remove_child_pid(pid, unclean_shutdown);
+       }
+}
+
+static void smbd_setup_sig_chld_handler(void)
+{
+       struct tevent_signal *se;
+
+       se = tevent_add_signal(smbd_event_context(),
+                              smbd_event_context(),
+                              SIGCHLD, 0,
+                              smbd_sig_chld_handler,
+                              NULL);
+       if (!se) {
+               exit_server("failed to setup SIGCHLD handler");
+       }
+}
+
 /****************************************************************************
  Open the socket communication.
 ****************************************************************************/
@@ -364,7 +339,7 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_
 #endif
 
        /* Stop zombies */
-       CatchSignal(SIGCLD, sig_cld);
+       smbd_setup_sig_chld_handler();
 
        FD_ZERO(&listen_set);
 
@@ -590,33 +565,8 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_
                fd_set r_fds, w_fds;
                int num;
 
-               /* Ensure we respond to PING and DEBUG messages from the main smbd. */
-               message_dispatch(smbd_messaging_context());
-
-               if (got_sig_cld) {
-                       pid_t pid;
-                       int status;
-
-                       got_sig_cld = False;
-
-                       while ((pid = sys_waitpid(-1, &status, WNOHANG)) > 0) {
-                               bool unclean_shutdown = False;
-                               
-                               /* If the child terminated normally, assume
-                                  it was an unclean shutdown unless the
-                                  status is 0 
-                               */
-                               if (WIFEXITED(status)) {
-                                       unclean_shutdown = WEXITSTATUS(status);
-                               }
-                               /* If the child terminated due to a signal
-                                  we always assume it was unclean.
-                               */
-                               if (WIFSIGNALED(status)) {
-                                       unclean_shutdown = True;
-                               }
-                               remove_child_pid(pid, unclean_shutdown);
-                       }
+               if (run_events(smbd_event_context(), 0, NULL, NULL)) {
+                       continue;
                }
 
                idle_timeout = timeval_zero();
@@ -640,22 +590,9 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_
                                 timeval_is_zero(&idle_timeout) ?
                                 NULL : &idle_timeout);
 
-               if (num == -1 && errno == EINTR) {
-                       if (got_sig_term) {
-                               exit_server_cleanly(NULL);
-                       }
-
-                       /* check for sighup processing */
-                       if (reload_after_sighup) {
-                               change_to_root_user();
-                               DEBUG(1,("Reloading services after SIGHUP\n"));
-                               reload_services(False);
-                               reload_after_sighup = 0;
-                       }
-
+               if (run_events(smbd_event_context(), num, &r_fds, &w_fds)) {
                        continue;
                }
-               
 
                /* If the idle timeout fired and we don't have any connected
                 * users, exit gracefully. We should be running under a process
@@ -670,10 +607,6 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_
                        --num;
                }
 
-               if (run_events(smbd_event_context(), num, &r_fds, &w_fds)) {
-                       continue;
-               }
-
                /* check if we need to reload services */
                check_reload(time(NULL));
 
@@ -706,17 +639,11 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_
                                continue;
                        }
 
-                       /* Ensure child is set to blocking mode */
-                       set_blocking(smbd_server_fd(),True);
-
-                       if (smbd_server_fd() != -1 && interactive)
+                       if (interactive)
                                return True;
 
                        if (allowable_number_of_smbd_processes() &&
-                           smbd_server_fd() != -1 &&
                            ((child = sys_fork())==0)) {
-                               char remaddr[INET6_ADDRSTRLEN];
-
                                /* Child code ... */
 
                                /* Stop zombies, the parent explicitly handles
@@ -735,17 +662,6 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_
                                close_low_fds(False);
                                am_parent = 0;
 
-                               set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
-                               set_socket_options(smbd_server_fd(),
-                                                  lp_socket_options());
-
-                               /* this is needed so that we get decent entries
-                                  in smbstatus for port 445 connects */
-                               set_remote_machine_name(get_peer_addr(smbd_server_fd(),
-                                                               remaddr,
-                                                               sizeof(remaddr)),
-                                                               false);
-
                                if (!reinit_after_fork(
                                            smbd_messaging_context(),
                                            smbd_event_context(),
@@ -754,6 +670,9 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_
                                        smb_panic("reinit_after_fork() failed");
                                }
 
+                               smbd_setup_sig_term_handler();
+                               smbd_setup_sig_hup_handler();
+
                                return True;
                        }
                        /* The parent doesn't need this socket */
@@ -891,12 +810,11 @@ static void exit_server_common(enum server_exit_reason how,
 static void exit_server_common(enum server_exit_reason how,
        const char *const reason)
 {
-       static int firsttime=1;
        bool had_open_conn;
 
-       if (!firsttime)
+       if (!exit_firsttime)
                exit(0);
-       firsttime = 0;
+       exit_firsttime = false;
 
        change_to_root_user();
 
@@ -980,34 +898,6 @@ void exit_server_fault(void)
        exit_server("critical server fault");
 }
 
-
-/****************************************************************************
-received when we should release a specific IP
-****************************************************************************/
-static void release_ip(const char *ip, void *priv)
-{
-       char addr[INET6_ADDRSTRLEN];
-
-       if (strcmp(client_socket_addr(get_client_fd(),addr,sizeof(addr)), ip) == 0) {
-               /* we can't afford to do a clean exit - that involves
-                  database writes, which would potentially mean we
-                  are still running after the failover has finished -
-                  we have to get rid of this process ID straight
-                  away */
-               DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n",
-                       ip));
-               /* note we must exit with non-zero status so the unclean handler gets
-                  called in the parent, so that the brl database is tickled */
-               _exit(1);
-       }
-}
-
-static void msg_release_ip(struct messaging_context *msg_ctx, void *private_data,
-                          uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
-{
-       release_ip((char *)data->data, NULL);
-}
-
 /****************************************************************************
  Initialise connect, service and file structs.
 ****************************************************************************/
@@ -1026,9 +916,6 @@ static bool init_structs(void )
 
        file_init();
 
-       /* for RPC pipes */
-       init_rpc_pipe_hnd();
-
        init_dptrs();
 
        if (!secrets_init())
@@ -1037,59 +924,6 @@ static bool init_structs(void )
        return True;
 }
 
-/*
- * Send keepalive packets to our client
- */
-static bool keepalive_fn(const struct timeval *now, void *private_data)
-{
-       if (!send_keepalive(smbd_server_fd())) {
-               DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
-               return False;
-       }
-       return True;
-}
-
-/*
- * Do the recurring check if we're idle
- */
-static bool deadtime_fn(const struct timeval *now, void *private_data)
-{
-       if ((conn_num_open() == 0)
-           || (conn_idle_all(now->tv_sec))) {
-               DEBUG( 2, ( "Closing idle connection\n" ) );
-               messaging_send(smbd_messaging_context(), procid_self(),
-                              MSG_SHUTDOWN, &data_blob_null);
-               return False;
-       }
-
-       return True;
-}
-
-/*
- * Do the recurring log file and smb.conf reload checks.
- */
-
-static bool housekeeping_fn(const struct timeval *now, void *private_data)
-{
-       change_to_root_user();
-
-       /* update printer queue caches if necessary */
-       update_monitored_printq_cache();
-
-       /* check if we need to reload services */
-       check_reload(time(NULL));
-
-       /* Change machine password if neccessary. */
-       attempt_machine_password_change();
-
-        /*
-        * Force a log file check.
-        */
-        force_check_log_size();
-        check_log_size();
-       return true;
-}
-
 /****************************************************************************
  main program.
 ****************************************************************************/
@@ -1103,13 +937,13 @@ extern void build_options(bool screen);
  int main(int argc,const char *argv[])
 {
        /* shall I run as a daemon */
-       static bool is_daemon = False;
-       static bool interactive = False;
-       static bool Fork = True;
-       static bool no_process_group = False;
-       static bool log_stdout = False;
-       static char *ports = NULL;
-       static char *profile_level = NULL;
+       bool is_daemon = false;
+       bool interactive = false;
+       bool Fork = true;
+       bool no_process_group = false;
+       bool log_stdout = false;
+       char *ports = NULL;
+       char *profile_level = NULL;
        int opt;
        poptContext pc;
        bool print_build_options = False;
@@ -1136,6 +970,8 @@ extern void build_options(bool screen);
        };
        TALLOC_CTX *frame = talloc_stackframe(); /* Setup tos. */
 
+       smbd_init_globals();
+
        TimeInit();
 
 #ifdef HAVE_SET_AUTH_PARAMETERS
@@ -1217,9 +1053,6 @@ extern void build_options(bool screen);
        fault_setup((void (*)(void *))exit_server_fault);
        dump_core_setup("smbd");
 
-       CatchSignal(SIGTERM , SIGNAL_CAST sig_term);
-       CatchSignal(SIGHUP,SIGNAL_CAST sig_hup);
-       
        /* we are never interested in SIGPIPE */
        BlockSignals(True,SIGPIPE);
 
@@ -1247,7 +1080,7 @@ extern void build_options(bool screen);
 
        reopen_logs();
 
-       DEBUG(0,("smbd version %s started.\n", SAMBA_VERSION_STRING));
+       DEBUG(0,("smbd version %s started.\n", samba_version_string()));
        DEBUGADD(0,("%s\n", COPYRIGHT_STARTUP_MESSAGE));
 
        DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
@@ -1329,6 +1162,9 @@ extern void build_options(bool screen);
                exit(1);
        }
 
+       smbd_setup_sig_term_handler();
+       smbd_setup_sig_hup_handler();
+
        /* Setup all the TDB's - including CLEAR_IF_FIRST tdb's. */
 
        if (smbd_memcache() == NULL) {
@@ -1393,111 +1229,10 @@ extern void build_options(bool screen);
        if (!open_sockets_smbd(is_daemon, interactive, ports))
                exit(1);
 
-       /*
-        * everything after this point is run after the fork()
-        */ 
-
-       static_init_rpc;
-
-       init_modules();
-
-       /* Possibly reload the services file. Only worth doing in
-        * daemon mode. In inetd mode, we know we only just loaded this.
-        */
-       if (is_daemon) {
-               reload_services(True);
-       }
-
-       if (!init_account_policy()) {
-               DEBUG(0,("Could not open account policy tdb.\n"));
-               exit(1);
-       }
-
-       if (*lp_rootdir()) {
-               if (chroot(lp_rootdir()) == 0)
-                       DEBUG(2,("Changed root to %s\n", lp_rootdir()));
-       }
-
-       /* Setup oplocks */
-       if (!init_oplocks(smbd_messaging_context()))
-               exit(1);
-
-       /* Setup aio signal handler. */
-       initialize_async_io_handler();
-
-       /* register our message handlers */
-       messaging_register(smbd_messaging_context(), NULL,
-                          MSG_SMB_FORCE_TDIS, msg_force_tdis);
-       messaging_register(smbd_messaging_context(), NULL,
-                          MSG_SMB_RELEASE_IP, msg_release_ip);
-       messaging_register(smbd_messaging_context(), NULL,
-                          MSG_SMB_CLOSE_FILE, msg_close_file);
-
-       if ((lp_keepalive() != 0)
-           && !(event_add_idle(smbd_event_context(), NULL,
-                               timeval_set(lp_keepalive(), 0),
-                               "keepalive", keepalive_fn,
-                               NULL))) {
-               DEBUG(0, ("Could not add keepalive event\n"));
-               exit(1);
-       }
-
-       if (!(event_add_idle(smbd_event_context(), NULL,
-                            timeval_set(IDLE_CLOSED_TIMEOUT, 0),
-                            "deadtime", deadtime_fn, NULL))) {
-               DEBUG(0, ("Could not add deadtime event\n"));
-               exit(1);
-       }
-
-       if (!(event_add_idle(smbd_event_context(), NULL,
-                            timeval_set(SMBD_SELECT_TIMEOUT, 0),
-                            "housekeeping", housekeeping_fn, NULL))) {
-               DEBUG(0, ("Could not add housekeeping event\n"));
-               exit(1);
-       }
-
-#ifdef CLUSTER_SUPPORT
-
-       if (lp_clustering()) {
-               /*
-                * We need to tell ctdb about our client's TCP
-                * connection, so that for failover ctdbd can send
-                * tickle acks, triggering a reconnection by the
-                * client.
-                */
-
-               struct sockaddr_storage srv, clnt;
-
-               if (client_get_tcp_info(&srv, &clnt) == 0) {
-
-                       NTSTATUS status;
-
-                       status = ctdbd_register_ips(
-                               messaging_ctdbd_connection(),
-                               (struct sockaddr *)&srv,
-                               (struct sockaddr *)&clnt,
-                               release_ip, NULL);
-
-                       if (!NT_STATUS_IS_OK(status)) {
-                               DEBUG(0, ("ctdbd_register_ips failed: %s\n",
-                                         nt_errstr(status)));
-                       }
-               } else
-               {
-                       DEBUG(0,("Unable to get tcp info for "
-                                "CTDB_CONTROL_TCP_CLIENT: %s\n",
-                                strerror(errno)));
-               }
-       }
-
-#endif
-
        TALLOC_FREE(frame);
 
        smbd_process();
 
-       namecache_shutdown();
-
        exit_server_cleanly(NULL);
        return(0);
 }