Merge branch 'v3-2-test' of ssh://git.samba.org/data/git/samba into ctdb-merge
[nivanova/samba-autobuild/.git] / source3 / smbd / server.c
index c09a0d7b98cd94f3be0f22e61b30b6ed11fbfaa0..7116027adfe841502abae99f4d1906d724e789af 100644 (file)
@@ -1,21 +1,22 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    Main SMB server routines
    Copyright (C) Andrew Tridgell               1992-1998
    Copyright (C) Martin Pool                   2002
    Copyright (C) Jelmer Vernooij               2002-2003
-   Copyright (C) James Peach                   2007
-   
+   Copyright (C) Volker Lendecke               1993-2007
+   Copyright (C) Jeremy Allison                        1993-2007
+
    This program 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 3 of the License, or
    (at your option) any later version.
-   
+
    This program 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, see <http://www.gnu.org/licenses/>.
 */
@@ -26,14 +27,7 @@ static_decl_rpc;
 
 static int am_parent = 1;
 
-/* the last message the was processed */
-int last_message = -1;
-
-/* a useful macro to debug the last message processed */
-#define LAST_MESSAGE() smb_fn_name(last_message)
-
 extern struct auth_context *negprot_global_auth_context;
-extern pstring user_socket_options;
 extern SIG_ATOMIC_T got_sig_term;
 extern SIG_ATOMIC_T reload_after_sighup;
 static SIG_ATOMIC_T got_sig_cld;
@@ -58,7 +52,11 @@ int smbd_server_fd(void)
 static void smbd_set_server_fd(int fd)
 {
        server_fd = fd;
-       client_setfd(fd);
+}
+
+int get_client_fd(void)
+{
+       return server_fd;
 }
 
 struct event_context *smbd_event_context(void)
@@ -82,6 +80,19 @@ struct messaging_context *smbd_messaging_context(void)
        return ctx;
 }
 
+struct memcache *smbd_memcache(void)
+{
+       static struct memcache *cache;
+
+       if (!cache
+           && !(cache = memcache_init(NULL,
+                                      lp_max_stat_cache_size()*1024))) {
+
+               smb_panic("Could not init smbd memcache");
+       }
+       return cache;
+}
+
 /*******************************************************************
  What to do when smb.conf is updated.
  ********************************************************************/
@@ -170,7 +181,7 @@ static void msg_sam_sync(struct messaging_context *msg,
  Open the socket communication - inetd.
 ****************************************************************************/
 
-static BOOL open_sockets_inetd(void)
+static bool open_sockets_inetd(void)
 {
        /* Started from inetd. fd 0 is the socket. */
        /* We will abort gracefully when the client or remote system 
@@ -181,7 +192,7 @@ static BOOL open_sockets_inetd(void)
        close_low_fds(False); /* Don't close stderr */
        
        set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
-       set_socket_options(smbd_server_fd(), user_socket_options);
+       set_socket_options(smbd_server_fd(), lp_socket_options());
 
        return True;
 }
@@ -257,10 +268,20 @@ static void add_child_pid(pid_t pid)
        num_children += 1;
 }
 
-static void remove_child_pid(pid_t pid)
+static void remove_child_pid(pid_t pid, bool unclean_shutdown)
 {
        struct child_pid *child;
 
+       if (unclean_shutdown) {
+               /* a child terminated uncleanly so tickle all processes to see 
+                  if they can grab any of the pending locks
+               */
+               messaging_send_buf(smbd_messaging_context(), procid_self(), 
+                                  MSG_SMB_BRL_VALIDATE, NULL, 0);
+               message_send_all(smbd_messaging_context(), 
+                                MSG_SMB_UNLOCK, NULL, 0, NULL);
+       }
+
        if (lp_max_smbd_processes() == 0) {
                /* Don't bother with the child list if we don't care anyway */
                return;
@@ -283,7 +304,7 @@ static void remove_child_pid(pid_t pid)
  Have we reached the process limit ?
 ****************************************************************************/
 
-static BOOL allowable_number_of_smbd_processes(void)
+static bool allowable_number_of_smbd_processes(void)
 {
        int max_processes = lp_max_smbd_processes();
 
@@ -293,31 +314,24 @@ static BOOL allowable_number_of_smbd_processes(void)
        return num_children < max_processes;
 }
 
-/****************************************************************************
- Are we idle enough that we could safely exit?
-****************************************************************************/
-
-static BOOL smbd_is_idle(void)
-{
-       /* Currently we define "idle" as having no client connections. */
-       return count_all_current_connections() == 0;
-}
-
 /****************************************************************************
  Open the socket communication.
 ****************************************************************************/
 
-static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_ports)
+static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ports)
 {
+       int num_interfaces = iface_count();
        int num_sockets = 0;
        int fd_listenset[FD_SETSIZE];
        fd_set listen_set;
        int s;
        int maxfd = 0;
        int i;
-       struct timeval idle_timeout = timeval_zero();
+       char *ports;
+       struct dns_reg_state * dns_reg = NULL;
+       unsigned dns_port = 0;
 
-       if (server_mode == SERVER_MODE_INETD) {
+       if (!is_daemon) {
                return open_sockets_inetd();
        }
 
@@ -336,19 +350,194 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
 
        FD_ZERO(&listen_set);
 
-       /* At this point, it doesn't matter what daemon mode we are in, we
-        * need some sockets to listen on.
-        */
-       num_sockets = smbd_sockinit(smb_ports, fd_listenset, &idle_timeout);
-       if (num_sockets == 0) {
-               return False;
+       /* use a reasonable default set of ports - listing on 445 and 139 */
+       if (!smb_ports) {
+               ports = lp_smb_ports();
+               if (!ports || !*ports) {
+                       ports = smb_xstrdup(SMB_PORTS);
+               } else {
+                       ports = smb_xstrdup(ports);
+               }
+       } else {
+               ports = smb_xstrdup(smb_ports);
+       }
+
+       if (lp_interfaces() && lp_bind_interfaces_only()) {
+               /* We have been given an interfaces line, and been
+                  told to only bind to those interfaces. Create a
+                  socket per interface and bind to only these.
+               */
+
+               /* Now open a listen socket for each of the
+                  interfaces. */
+               for(i = 0; i < num_interfaces; i++) {
+                       TALLOC_CTX *frame = NULL;
+                       const struct sockaddr_storage *ifss =
+                                       iface_n_sockaddr_storage(i);
+                       char *tok;
+                       const char *ptr;
+
+                       if (ifss == NULL) {
+                               DEBUG(0,("open_sockets_smbd: "
+                                       "interface %d has NULL IP address !\n",
+                                       i));
+                               continue;
+                       }
+
+                       frame = talloc_stackframe();
+                       for (ptr=ports;
+                                       next_token_talloc(frame,&ptr, &tok, " \t,");) {
+                               unsigned port = atoi(tok);
+                               if (port == 0 || port > 0xffff) {
+                                       continue;
+                               }
+
+                               /* Keep the first port for mDNS service
+                                * registration.
+                                */
+                               if (dns_port == 0) {
+                                       dns_port = port;
+                               }
+
+                               s = fd_listenset[num_sockets] =
+                                       open_socket_in(SOCK_STREAM,
+                                                       port,
+                                                       num_sockets == 0 ? 0 : 2,
+                                                       ifss,
+                                                       true);
+                               if(s == -1) {
+                                       continue;
+                               }
+
+                               /* ready to listen */
+                               set_socket_options(s,"SO_KEEPALIVE");
+                               set_socket_options(s,lp_socket_options());
+
+                               /* Set server socket to
+                                * non-blocking for the accept. */
+                               set_blocking(s,False);
+
+                               if (listen(s, SMBD_LISTEN_BACKLOG) == -1) {
+                                       DEBUG(0,("open_sockets_smbd: listen: "
+                                               "%s\n", strerror(errno)));
+                                       close(s);
+                                       TALLOC_FREE(frame);
+                                       return False;
+                               }
+                               FD_SET(s,&listen_set);
+                               maxfd = MAX( maxfd, s);
+
+                               num_sockets++;
+                               if (num_sockets >= FD_SETSIZE) {
+                                       DEBUG(0,("open_sockets_smbd: Too "
+                                               "many sockets to bind to\n"));
+                                       TALLOC_FREE(frame);
+                                       return False;
+                               }
+                       }
+                       TALLOC_FREE(frame);
+               }
+       } else {
+               /* Just bind to 0.0.0.0 - accept connections
+                  from anywhere. */
+
+               TALLOC_CTX *frame = talloc_stackframe();
+               char *tok;
+               const char *ptr;
+               const char *sock_addr = lp_socket_address();
+               char *sock_tok;
+               const char *sock_ptr;
+
+               if (strequal(sock_addr, "0.0.0.0") ||
+                               strequal(sock_addr, "::")) {
+#if HAVE_IPV6
+                       sock_addr = "::,0.0.0.0";
+#else
+                       sock_addr = "0.0.0.0";
+#endif
+               }
+
+               for (sock_ptr=sock_addr;
+                               next_token_talloc(frame, &sock_ptr, &sock_tok, " \t,"); ) {
+                       for (ptr=ports; next_token_talloc(frame, &ptr, &tok, " \t,"); ) {
+                               struct sockaddr_storage ss;
+
+                               unsigned port = atoi(tok);
+                               if (port == 0 || port > 0xffff) {
+                                       continue;
+                               }
+
+                               /* Keep the first port for mDNS service
+                                * registration.
+                                */
+                               if (dns_port == 0) {
+                                       dns_port = port;
+                               }
+
+                               /* open an incoming socket */
+                               if (!interpret_string_addr(&ss, sock_tok,
+                                               AI_NUMERICHOST|AI_PASSIVE)) {
+                                       continue;
+                               }
+
+                               s = open_socket_in(SOCK_STREAM,
+                                               port,
+                                               num_sockets == 0 ? 0 : 2,
+                                               &ss,
+                                               true);
+                               if (s == -1) {
+                                       continue;
+                               }
+
+                               /* ready to listen */
+                               set_socket_options(s,"SO_KEEPALIVE");
+                               set_socket_options(s,lp_socket_options());
+
+                               /* Set server socket to non-blocking
+                                * for the accept. */
+                               set_blocking(s,False);
+
+                               if (listen(s, SMBD_LISTEN_BACKLOG) == -1) {
+                                       DEBUG(0,("open_sockets_smbd: "
+                                               "listen: %s\n",
+                                                strerror(errno)));
+                                       close(s);
+                                       TALLOC_FREE(frame);
+                                       return False;
+                               }
+
+                               fd_listenset[num_sockets] = s;
+                               FD_SET(s,&listen_set);
+                               maxfd = MAX( maxfd, s);
+
+                               num_sockets++;
+
+                               if (num_sockets >= FD_SETSIZE) {
+                                       DEBUG(0,("open_sockets_smbd: Too "
+                                               "many sockets to bind to\n"));
+                                       TALLOC_FREE(frame);
+                                       return False;
+                               }
+                       }
+               }
+               TALLOC_FREE(frame);
        }
 
-       for (i = 0; i < num_sockets; ++i) {
-               FD_SET(fd_listenset[i], &listen_set);
-               maxfd = MAX(maxfd, fd_listenset[i]);
+       SAFE_FREE(ports);
+
+       if (num_sockets == 0) {
+               DEBUG(0,("open_sockets_smbd: No "
+                       "sockets available to bind to.\n"));
+               return false;
        }
 
+       /* Setup the main smbd so that we can get messages. Note that
+          do this after starting listening. This is needed as when in
+          clustered mode, ctdb won't allow us to start doing database
+          operations until it has gone thru a full startup, which
+          includes checking to see that smbd is listening. */
+       claim_connection(NULL,"",FLAG_MSG_GENERAL|FLAG_MSG_SMBD);
+
         /* Listen to messages */
 
        messaging_register(smbd_messaging_context(), NULL,
@@ -358,7 +547,7 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
        messaging_register(smbd_messaging_context(), NULL,
                           MSG_SMB_FILE_RENAME, msg_file_was_renamed);
        messaging_register(smbd_messaging_context(), NULL,
-                          MSG_SMB_CONF_UPDATED, smb_conf_updated); 
+                          MSG_SMB_CONF_UPDATED, smb_conf_updated);
        messaging_register(smbd_messaging_context(), NULL,
                           MSG_SMB_STAT_CACHE_DELETE, smb_stat_cache_delete);
        brl_register_msgs(smbd_messaging_context());
@@ -372,49 +561,59 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
           for each incoming connection */
        DEBUG(2,("waiting for a connection\n"));
        while (1) {
-               struct timeval now;
+               struct timeval now, idle_timeout;
                fd_set r_fds, w_fds;
                int num;
-               
-               /* Free up temporary memory from the main smbd. */
-               lp_TALLOC_FREE();
 
                /* 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, NULL, WNOHANG)) > 0) {
-                               remove_child_pid(pid);
+                       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);
                        }
                }
 
-               memcpy((char *)&r_fds, (char *)&listen_set, 
+               idle_timeout = timeval_zero();
+
+               memcpy((char *)&r_fds, (char *)&listen_set,
                       sizeof(listen_set));
                FD_ZERO(&w_fds);
                GetTimeOfDay(&now);
 
+               /* Kick off our mDNS registration. */
+               if (dns_port != 0) {
+                       dns_register_smbd(&dns_reg, dns_port, &maxfd,
+                                       &r_fds, &idle_timeout);
+               }
+
                event_add_to_select_args(smbd_event_context(), &now,
                                         &r_fds, &w_fds, &idle_timeout,
                                         &maxfd);
 
-               if (timeval_is_zero(&idle_timeout)) {
-                       num = sys_select(maxfd + 1, &r_fds, &w_fds,
-                                       NULL, NULL);
-               } else {
-                       num = sys_select(maxfd + 1, &r_fds, &w_fds,
-                                       NULL, &idle_timeout);
-
-                       /* If the idle timeout fired and we are idle, exit
-                        * gracefully. We expect to be running under a process
-                        * controller that will restart us if necessry.
-                        */
-                       if (num == 0 && smbd_is_idle()) {
-                               exit_server_cleanly("idle timeout");
-                       }
-               }
+               num = sys_select(maxfd+1,&r_fds,&w_fds,NULL,
+                                timeval_is_zero(&idle_timeout) ?
+                                NULL : &idle_timeout);
 
                if (num == -1 && errno == EINTR) {
                        if (got_sig_term) {
@@ -431,6 +630,20 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
 
                        continue;
                }
+               
+
+               /* If the idle timeout fired and we don't have any connected
+                * users, exit gracefully. We should be running under a process
+                * controller that will restart us if necessry.
+                */
+               if (num == 0 && count_all_current_connections() == 0) {
+                       exit_server_cleanly("idle timeout");
+               }
+
+               /* process pending nDNS responses */
+               if (dns_register_smbd_reply(dns_reg, &r_fds, &idle_timeout)) {
+                       --num;
+               }
 
                if (run_events(smbd_event_context(), num, &r_fds, &w_fds)) {
                        continue;
@@ -458,10 +671,10 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
                        }
 
                        smbd_set_server_fd(accept(s,&addr,&in_addrlen));
-                       
+
                        if (smbd_server_fd() == -1 && errno == EINTR)
                                continue;
-                       
+
                        if (smbd_server_fd() == -1) {
                                DEBUG(0,("open_sockets_smbd: accept: %s\n",
                                         strerror(errno)));
@@ -471,40 +684,43 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
                        /* Ensure child is set to blocking mode */
                        set_blocking(smbd_server_fd(),True);
 
-                       /* In interactive mode, return with a connected socket.
-                        * Foreground and daemon modes should fork worker
-                        * processes.
-                        */
-                       if (server_mode == SERVER_MODE_INTERACTIVE) {
+                       if (smbd_server_fd() != -1 && 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
                                 * them, counting worker smbds. */
                                CatchChild();
-                               
+
                                /* close the listening socket(s) */
                                for(i = 0; i < num_sockets; i++)
                                        close(fd_listenset[i]);
-                               
+
+                               /* close our mDNS daemon handle */
+                               dns_register_close(&dns_reg);
+
                                /* close our standard file
                                   descriptors */
                                close_low_fds(False);
                                am_parent = 0;
-                               
+
                                set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
-                               set_socket_options(smbd_server_fd(),user_socket_options);
-                               
+                               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()),
-                                                       False);
-                               
+                               set_remote_machine_name(get_peer_addr(smbd_server_fd(),
+                                                               remaddr,
+                                                               sizeof(remaddr)),
+                                                               false);
+
                                /* Reset the state of the random
                                 * number generation system, so
                                 * children do not get the same random
@@ -518,10 +734,10 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
                                        smb_panic("tdb_reopen_all failed");
                                }
 
-                               return True; 
+                               return True;
                        }
                        /* The parent doesn't need this socket */
-                       close(smbd_server_fd()); 
+                       close(smbd_server_fd());
 
                        /* Sun May 6 18:56:14 2001 ackley@cs.unm.edu:
                                Clear the closed fd info out of server_fd --
@@ -552,7 +768,7 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
                         * (ca. 100kb).
                         * */
                        force_check_log_size();
+
                } /* end for num */
        } /* end while 1 */
 
@@ -596,16 +812,15 @@ void reload_printers(void)
  Reload the services file.
 **************************************************************************/
 
-BOOL reload_services(BOOL test)
+bool reload_services(bool test)
 {
-       BOOL ret;
-       
+       bool ret;
+
        if (lp_loaded()) {
-               pstring fname;
-               pstrcpy(fname,lp_configfile());
+               char *fname = lp_configfile();
                if (file_exist(fname, NULL) &&
-                   !strcsequal(fname, dyn_CONFIGFILE)) {
-                       pstrcpy(dyn_CONFIGFILE, fname);
+                   !strcsequal(fname, get_dyn_CONFIGFILE())) {
+                       set_dyn_CONFIGFILE(fname);
                        test = False;
                }
        }
@@ -617,7 +832,7 @@ BOOL reload_services(BOOL test)
 
        lp_killunused(conn_snum_used);
 
-       ret = lp_load(dyn_CONFIGFILE, False, False, True, True);
+       ret = lp_load(get_dyn_CONFIGFILE(), False, False, True, True);
 
        reload_printers();
 
@@ -629,9 +844,9 @@ BOOL reload_services(BOOL test)
 
        load_interfaces();
 
-       if (smbd_server_fd() != -1) {      
+       if (smbd_server_fd() != -1) {
                set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
-               set_socket_options(smbd_server_fd(), user_socket_options);
+               set_socket_options(smbd_server_fd(), lp_socket_options());
        }
 
        mangle_reset_cache();
@@ -689,11 +904,8 @@ static void exit_server_common(enum server_exit_reason how,
        locking_end();
        printing_end();
 
-       server_encryption_shutdown();
-
        if (how != SERVER_EXIT_NORMAL) {
                int oldlevel = DEBUGLEVEL;
-               char *last_inbuf = get_InBuffer();
 
                DEBUGLEVEL = 10;
 
@@ -703,10 +915,6 @@ static void exit_server_common(enum server_exit_reason how,
                DEBUGSEP(0);
 
                log_stack_trace();
-               if (last_inbuf) {
-                       DEBUG(0,("Last message was %s\n", LAST_MESSAGE()));
-                       show_msg(last_inbuf);
-               }
 
                DEBUGLEVEL = oldlevel;
                dump_core();
@@ -734,11 +942,34 @@ void exit_server_fault(void)
        exit_server("critical server fault");
 }
 
+
+/****************************************************************************
+received when we should release a specific IP
+****************************************************************************/
+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)
+{
+       const char *ip = (const char *)data->data;
+       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));
+               _exit(0);
+       }
+}
+
+
 /****************************************************************************
  Initialise connect, service and file structs.
 ****************************************************************************/
 
-static BOOL init_structs(void )
+static bool init_structs(void )
 {
        /*
         * Set the machine NETBIOS name if not already
@@ -765,7 +996,7 @@ static BOOL init_structs(void )
 /*
  * Send keepalive packets to our client
  */
-static BOOL keepalive_fn(const struct timeval *now, void *private_data)
+static bool keepalive_fn(const struct timeval *now, void *private_data)
 {
        if (!send_keepalive(smbd_server_fd())) {
                DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
@@ -777,7 +1008,7 @@ static BOOL keepalive_fn(const struct timeval *now, void *private_data)
 /*
  * Do the recurring check if we're idle
  */
-static BOOL deadtime_fn(const struct timeval *now, void *private_data)
+static bool deadtime_fn(const struct timeval *now, void *private_data)
 {
        if ((conn_num_open() == 0)
            || (conn_idle_all(now->tv_sec))) {
@@ -799,31 +1030,35 @@ static BOOL deadtime_fn(const struct timeval *now, void *private_data)
    mkproto.h.  Mixing $(builddir) and $(srcdir) source files in the current
    prototype generation system is too complicated. */
 
-extern void build_options(BOOL screen);
+extern void build_options(bool screen);
 
  int main(int argc,const char *argv[])
 {
        /* shall I run as a daemon */
-       BOOL no_process_group = False;
-       BOOL log_stdout = False;
-       const char *ports = NULL;
-       const char *profile_level = NULL;
+       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;
        int opt;
        poptContext pc;
-
-       enum smb_server_mode server_mode = SERVER_MODE_DAEMON;
-
+       bool print_build_options = False;
+        enum {
+               OPT_DAEMON = 1000,
+               OPT_INTERACTIVE,
+               OPT_FORK,
+               OPT_NO_PROCESS_GROUP,
+               OPT_LOG_STDOUT
+       };
        struct poptOption long_options[] = {
        POPT_AUTOHELP
-       {"daemon", 'D', POPT_ARG_VAL, &server_mode, SERVER_MODE_DAEMON,
-               "Become a daemon (default)" },
-       {"interactive", 'i', POPT_ARG_VAL, &server_mode, SERVER_MODE_INTERACTIVE,
-               "Run interactive (not a daemon)"},
-       {"foreground", 'F', POPT_ARG_VAL, &server_mode, SERVER_MODE_FOREGROUND,
-               "Run daemon in foreground (for daemontools, etc.)" },
-       {"no-process-group", '\0', POPT_ARG_VAL, &no_process_group, True,
-               "Don't create a new process group" },
-       {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+       {"daemon", 'D', POPT_ARG_NONE, NULL, OPT_DAEMON, "Become a daemon (default)" },
+       {"interactive", 'i', POPT_ARG_NONE, NULL, OPT_INTERACTIVE, "Run interactive (not a daemon)"},
+       {"foreground", 'F', POPT_ARG_NONE, NULL, OPT_FORK, "Run daemon in foreground (for daemontools, etc.)" },
+       {"no-process-group", '\0', POPT_ARG_NONE, NULL, OPT_NO_PROCESS_GROUP, "Don't create a new process group" },
+       {"log-stdout", 'S', POPT_ARG_NONE, NULL, OPT_LOG_STDOUT, "Log to stdout" },
        {"build-options", 'b', POPT_ARG_NONE, NULL, 'b', "Print build options" },
        {"port", 'p', POPT_ARG_STRING, &ports, 0, "Listen on the specified ports"},
        {"profiling-level", 'P', POPT_ARG_STRING, &profile_level, 0, "Set profiling level","PROFILE_LEVEL"},
@@ -831,8 +1066,7 @@ extern void build_options(BOOL screen);
        POPT_COMMON_DYNCONFIG
        POPT_TABLEEND
        };
-
-       load_case_tables();
+       TALLOC_CTX *frame = talloc_stackframe(); /* Setup tos. */
 
        TimeInit();
 
@@ -841,18 +1075,49 @@ extern void build_options(BOOL screen);
 #endif
 
        pc = poptGetContext("smbd", argc, argv, long_options, 0);
-       
        while((opt = poptGetNextOpt(pc)) != -1) {
                switch (opt)  {
+               case OPT_DAEMON:
+                       is_daemon = true;
+                       break;
+               case OPT_INTERACTIVE:
+                       interactive = true;
+                       break;
+               case OPT_FORK:
+                       Fork = false;
+                       break;
+               case OPT_NO_PROCESS_GROUP:
+                       no_process_group = true;
+                       break;
+               case OPT_LOG_STDOUT:
+                       log_stdout = true;
+                       break;
                case 'b':
-                       build_options(True); /* Display output to screen as well as debug */ 
-                       exit(0);
+                       print_build_options = True;
                        break;
+               default:
+                       d_fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                                 poptBadOption(pc, 0), poptStrerror(opt));
+                       poptPrintUsage(pc, stderr, 0);
+                       exit(1);
                }
        }
-
        poptFreeContext(pc);
 
+       if (interactive) {
+               Fork = False;
+               log_stdout = True;
+       }
+
+       setup_logging(argv[0],log_stdout);
+
+       if (print_build_options) {
+               build_options(True); /* Display output to screen as well as debug */
+               exit(0);
+       }
+
+       load_case_tables();
+
 #ifdef HAVE_SETLUID
        /* needed for SecureWare on SCO */
        setluid(0);
@@ -862,20 +1127,15 @@ extern void build_options(BOOL screen);
 
        set_remote_machine_name("smbd", False);
 
-       if (server_mode == SERVER_MODE_INTERACTIVE) {
-               log_stdout = True;
-               if (DEBUGLEVEL >= 9) {
-                       talloc_enable_leak_report();
-               }
+       if (interactive && (DEBUGLEVEL >= 9)) {
+               talloc_enable_leak_report();
        }
 
-       if (log_stdout && server_mode == SERVER_MODE_DAEMON) {
+       if (log_stdout && Fork) {
                DEBUG(0,("ERROR: Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"));
                exit(1);
        }
 
-       setup_logging(argv[0],log_stdout);
-
        /* we want to re-seed early to prevent time delays causing
            client problems at a later date. (tridge) */
        generate_random_buffer(NULL, 0);
@@ -919,8 +1179,8 @@ extern void build_options(BOOL screen);
 
        reopen_logs();
 
-       DEBUG(0,( "smbd version %s started.\n", SAMBA_VERSION_STRING));
-       DEBUGADD( 0, ( "%s\n", COPYRIGHT_STARTUP_MESSAGE ) );
+       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",
                 (int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid()));
@@ -959,19 +1219,21 @@ extern void build_options(BOOL screen);
 
        DEBUG(3,( "loaded services\n"));
 
-       if (is_a_socket(0)) {
-               if (server_mode == SERVER_MODE_DAEMON) {
-                       DEBUG(0,("standard input is a socket, "
-                                   "assuming -F option\n"));
-               }
-               server_mode = SERVER_MODE_INETD;
+       if (!is_daemon && !is_a_socket(0)) {
+               if (!interactive)
+                       DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+
+               /*
+                * Setting is_daemon here prevents us from eventually calling
+                * the open_sockets_inetd()
+                */
+
+               is_daemon = True;
        }
 
-       if (server_mode == SERVER_MODE_DAEMON) {
+       if (is_daemon && !interactive) {
                DEBUG( 3, ( "Becoming a daemon.\n" ) );
-               become_daemon(True, no_process_group);
-       } else if (server_mode == SERVER_MODE_FOREGROUND) {
-               become_daemon(False, no_process_group);
+               become_daemon(Fork, no_process_group);
        }
 
 #if HAVE_SETPGID
@@ -979,24 +1241,27 @@ extern void build_options(BOOL screen);
         * If we're interactive we want to set our own process group for
         * signal management.
         */
-       if (server_mode == SERVER_MODE_INTERACTIVE && !no_process_group) {
+       if (interactive && !no_process_group)
                setpgid( (pid_t)0, (pid_t)0);
-       }
 #endif
 
        if (!directory_exist(lp_lockdir(), NULL))
                mkdir(lp_lockdir(), 0755);
 
-       if (server_mode != SERVER_MODE_INETD &&
-           server_mode != SERVER_MODE_INTERACTIVE) {
+       if (is_daemon)
                pidfile_create("smbd");
-       }
 
        /* Setup all the TDB's - including CLEAR_IF_FIRST tdb's. */
 
        if (smbd_messaging_context() == NULL)
                exit(1);
 
+       if (smbd_memcache() == NULL) {
+               exit(1);
+       }
+
+       memcache_set_global(smbd_memcache());
+
        /* Initialise the password backed before the global_sam_sid
           to ensure that we fetch from ldap before we make a domain sid up */
 
@@ -1019,7 +1284,7 @@ extern void build_options(BOOL screen);
        if (!connections_init(True))
                exit(1);
 
-       if (!locking_init(0))
+       if (!locking_init())
                exit(1);
 
        namecache_enable();
@@ -1040,29 +1305,16 @@ extern void build_options(BOOL screen);
                return -1;
        }
 
-       /* Setup the main smbd so that we can get messages. */
-       /* don't worry about general printing messages here */
-
-       claim_connection(NULL,"",FLAG_MSG_GENERAL|FLAG_MSG_SMBD);
-
        /* only start the background queue daemon if we are 
           running as a daemon -- bad things will happen if
           smbd is launched via inetd and we fork a copy of 
           ourselves here */
-       if (server_mode != SERVER_MODE_INETD &&
-           server_mode != SERVER_MODE_INTERACTIVE) {
-               start_background_queue(); 
-       }
 
-       /* Always attempt to initialize DMAPI. We will only use it later if
-        * lp_dmapi_support is set on the share, but we need a single global
-        * session to work with.
-        */
-       dmapi_init_session();
+       if ( is_daemon && !interactive )
+               start_background_queue(); 
 
-       if (!open_sockets_smbd(server_mode, ports)) {
+       if (!open_sockets_smbd(is_daemon, interactive, ports))
                exit(1);
-       }
 
        /*
         * everything after this point is run after the fork()
@@ -1075,8 +1327,7 @@ extern void build_options(BOOL screen);
        /* Possibly reload the services file. Only worth doing in
         * daemon mode. In inetd mode, we know we only just loaded this.
         */
-       if (server_mode != SERVER_MODE_INETD &&
-           server_mode != SERVER_MODE_INTERACTIVE) {
+       if (is_daemon) {
                reload_services(True);
        }
 
@@ -1093,7 +1344,7 @@ extern void build_options(BOOL screen);
        /* Setup oplocks */
        if (!init_oplocks(smbd_messaging_context()))
                exit(1);
-       
+
        /* Setup aio signal handler. */
        initialize_async_io_handler();
 
@@ -1107,6 +1358,8 @@ extern void build_options(BOOL screen);
        /* 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);
 
        if ((lp_keepalive() != 0)
            && !(event_add_idle(smbd_event_context(), NULL,
@@ -1124,6 +1377,8 @@ extern void build_options(BOOL screen);
                exit(1);
        }
 
+       TALLOC_FREE(frame);
+
        smbd_process();
 
        namecache_shutdown();