s3-smbd: Don't segfault if user specified ports out for range.
[samba.git] / source3 / smbd / server.c
index d3ce4b6f2d7b0162e10025df2cc6c281247f0e23..f34d9f6fdf6d13904873958f3977f999cee9d93b 100644 (file)
@@ -94,6 +94,7 @@ static void smb_conf_updated(struct messaging_context *msg,
 {
        DEBUG(10,("smb_conf_updated: Got message saying smb.conf was "
                  "updated. Reloading.\n"));
+       change_to_root_user();
        reload_services(False);
 }
 
@@ -180,19 +181,33 @@ static void msg_inject_fault(struct messaging_context *msg,
 }
 #endif /* DEVELOPER */
 
-struct child_pid {
-       struct child_pid *prev, *next;
-       pid_t pid;
-};
-
-static void add_child_pid(pid_t pid)
+/*
+ * Parent smbd process sets its own debug level first and then
+ * sends a message to all the smbd children to adjust their debug
+ * level to that of the parent.
+ */
+
+static void smbd_msg_debug(struct messaging_context *msg_ctx,
+                          void *private_data,
+                          uint32_t msg_type,
+                          struct server_id server_id,
+                          DATA_BLOB *data)
 {
        struct child_pid *child;
 
-       if (lp_max_smbd_processes() == 0) {
-               /* Don't bother with the child list if we don't care anyway */
-               return;
+       debug_message(msg_ctx, private_data, MSG_DEBUG, server_id, data);
+
+       for (child = children; child != NULL; child = child->next) {
+               messaging_send_buf(msg_ctx, pid_to_procid(child->pid),
+                                  MSG_DEBUG,
+                                  data->data,
+                                  strlen((char *) data->data) + 1);
        }
+}
+
+static void add_child_pid(pid_t pid)
+{
+       struct child_pid *child;
 
        child = SMB_MALLOC_P(struct child_pid);
        if (child == NULL) {
@@ -204,24 +219,53 @@ static void add_child_pid(pid_t pid)
        num_children += 1;
 }
 
+/*
+  at most every smbd:cleanuptime seconds (default 20), we scan the BRL
+  and locking database for entries to cleanup. As a side effect this
+  also cleans up dead entries in the connections database (due to the
+  traversal in message_send_all()
+
+  Using a timer for this prevents a flood of traversals when a large
+  number of clients disconnect at the same time (perhaps due to a
+  network outage).  
+*/
+
+static void cleanup_timeout_fn(struct event_context *event_ctx,
+                               struct timed_event *te,
+                               struct timeval now,
+                               void *private_data)
+{
+       struct timed_event **cleanup_te = (struct timed_event **)private_data;
+
+       DEBUG(1,("Cleaning up brl and lock database after unclean shutdown\n"));
+       message_send_all(smbd_messaging_context(), MSG_SMB_UNLOCK, NULL, 0, NULL);
+       messaging_send_buf(smbd_messaging_context(), procid_self(), 
+                               MSG_SMB_BRL_VALIDATE, NULL, 0);
+       /* mark the cleanup as having been done */
+       (*cleanup_te) = NULL;
+}
+
 static void remove_child_pid(pid_t pid, bool unclean_shutdown)
 {
        struct child_pid *child;
+       static struct timed_event *cleanup_te;
 
        if (unclean_shutdown) {
-               /* a child terminated uncleanly so tickle all processes to see 
-                  if they can grab any of the pending locks
-               */
-               DEBUG(3,(__location__ " Unclean shutdown of pid %u\n", (unsigned int)pid));
-               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;
+               /* a child terminated uncleanly so tickle all
+                  processes to see if they can grab any of the
+                  pending locks
+                */
+               DEBUG(3,(__location__ " Unclean shutdown of pid %u\n", 
+                       (unsigned int)pid));
+               if (!cleanup_te) {
+                       /* call the cleanup timer, but not too often */
+                       int cleanup_time = lp_parm_int(-1, "smbd", "cleanuptime", 20);
+                       cleanup_te = event_add_timed(smbd_event_context(), NULL,
+                                               timeval_current_ofs(cleanup_time, 0),
+                                               cleanup_timeout_fn, 
+                                               &cleanup_te);
+                       DEBUG(1,("Scheduled cleanup of brl and lock database after unclean shutdown\n"));
+               }
        }
 
        for (child = children; child != NULL; child = child->next) {
@@ -234,7 +278,8 @@ static void remove_child_pid(pid_t pid, bool unclean_shutdown)
                }
        }
 
-       DEBUG(0, ("Could not find child %d -- ignoring\n", (int)pid));
+       /* not all forked child processes are added to the children list */
+       DEBUG(1, ("Could not find child %d -- ignoring\n", (int)pid));
 }
 
 /****************************************************************************
@@ -497,6 +542,14 @@ static bool smbd_open_one_socket(struct smbd_parent_context *parent,
        return true;
 }
 
+static bool parent_housekeeping_fn(const struct timeval *now, void *private_data)
+{
+       DEBUG(5, ("houskeeping\n"));
+       /* check if we need to reload services */
+       check_reload(time(NULL));
+       return true;
+}
+
 /****************************************************************************
  Open the socket communication.
 ****************************************************************************/
@@ -507,6 +560,8 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
        int num_interfaces = iface_count();
        int i;
        char *ports;
+       char *tok;
+       const char *ptr;
        unsigned dns_port = 0;
 
 #ifdef HAVE_ATEXIT
@@ -528,6 +583,16 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
                ports = talloc_strdup(talloc_tos(), smb_ports);
        }
 
+       for (ptr = ports;
+            next_token_talloc(talloc_tos(),&ptr, &tok, " \t,");) {
+               unsigned port = atoi(tok);
+
+               if (port == 0 || port > 0xffff) {
+                       exit_server_cleanly("Invalid port in the config or on "
+                                           "the commandline specified!");
+               }
+       }
+
        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
@@ -539,8 +604,6 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
                for(i = 0; i < num_interfaces; i++) {
                        const struct sockaddr_storage *ifss =
                                        iface_n_sockaddr_storage(i);
-                       char *tok;
-                       const char *ptr;
 
                        if (ifss == NULL) {
                                DEBUG(0,("open_sockets_smbd: "
@@ -552,8 +615,12 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
                        for (ptr=ports;
                             next_token_talloc(talloc_tos(),&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;
                                }
 
                                if (!smbd_open_one_socket(parent, ifss, port)) {
@@ -565,8 +632,6 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
                /* Just bind to 0.0.0.0 - accept connections
                   from anywhere. */
 
-               char *tok;
-               const char *ptr;
                const char *sock_addr = lp_socket_address();
                char *sock_tok;
                const char *sock_ptr;
@@ -584,11 +649,7 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
                     next_token_talloc(talloc_tos(), &sock_ptr, &sock_tok, " \t,"); ) {
                        for (ptr=ports; next_token_talloc(talloc_tos(), &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.
@@ -624,6 +685,14 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
        claim_connection(NULL,"",
                         FLAG_MSG_GENERAL|FLAG_MSG_SMBD|FLAG_MSG_DBWRAP);
 
+       if (!(event_add_idle(smbd_event_context(), NULL,
+                            timeval_set(SMBD_HOUSEKEEPING_INTERVAL, 0),
+                            "parent_housekeeping", parent_housekeeping_fn,
+                            parent))) {
+               DEBUG(0, ("Could not add housekeeping event\n"));
+               exit(1);
+       }
+
         /* Listen to messages */
 
        messaging_register(smbd_messaging_context(), NULL,
@@ -636,6 +705,8 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
                           MSG_SMB_CONF_UPDATED, smb_conf_updated);
        messaging_register(smbd_messaging_context(), NULL,
                           MSG_SMB_STAT_CACHE_DELETE, smb_stat_cache_delete);
+       messaging_register(smbd_messaging_context(), NULL,
+                          MSG_DEBUG, smbd_msg_debug);
        brl_register_msgs(smbd_messaging_context());
 
 #ifdef CLUSTER_SUPPORT
@@ -688,9 +759,9 @@ static void smbd_parent_loop(struct smbd_parent_context *parent)
 /* NOTREACHED  return True; */
 }
 
-/****************************************************************************
- Reload printers
-**************************************************************************/
+/***************************************************************************
+ purge stale printers and reload from pre-populated pcap cache
+***************************************************************************/
 void reload_printers(void)
 {
        int snum;
@@ -698,9 +769,7 @@ void reload_printers(void)
        int pnum = lp_servicenumber(PRINTERS_NAME);
        const char *pname;
 
-       pcap_cache_reload();
-
-       /* remove stale printers */
+       DEBUG(10, ("reloading printer services from pcap cache\n"));
        for (snum = 0; snum < n_services; snum++) {
                /* avoid removing PRINTERS_NAME or non-autoloaded printers */
                if (snum == pnum || !(lp_snum_ok(snum) && lp_print_ok(snum) &&
@@ -736,6 +805,7 @@ bool reload_services(bool test)
                        set_dyn_CONFIGFILE(fname);
                        test = False;
                }
+               TALLOC_FREE(fname);
        }
 
        reopen_logs();
@@ -747,7 +817,7 @@ bool reload_services(bool test)
 
        ret = lp_load(get_dyn_CONFIGFILE(), False, False, True, True);
 
-       reload_printers();
+       pcap_cache_reload(&reload_printers);
 
        /* perhaps the config filename is now set */
        if (!test)
@@ -854,6 +924,10 @@ static void exit_server_common(enum server_exit_reason how,
        } else {    
                DEBUG(3,("Server exit (%s)\n",
                        (reason ? reason : "normal exit")));
+               if (am_parent) {
+                       pidfile_unlink();
+               }
+               gencache_stabilize();
        }
 
        /* if we had any open SMB connections when we exited then we
@@ -898,8 +972,6 @@ static bool init_structs(void )
 
        file_init();
 
-       init_dptrs();
-
        if (!secrets_init())
                return False;
 
@@ -1204,6 +1276,15 @@ extern void build_options(bool screen);
                return -1;
        }
 
+       /* Open the share_info.tdb here, so we don't have to open
+          after the fork on every single connection.  This is a small
+          performance improvment and reduces the total number of system
+          fds used. */
+       if (!share_info_db_init()) {
+               DEBUG(0,("ERROR: failed to load share info db.\n"));
+               exit(1);
+       }
+
        /* 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 
@@ -1249,9 +1330,12 @@ extern void build_options(bool screen);
                exit_server("open_sockets_smbd() failed");
 
        TALLOC_FREE(frame);
+       /* make sure we always have a valid stackframe */
+       frame = talloc_stackframe();
 
        smbd_parent_loop(parent);
 
        exit_server_cleanly(NULL);
+       TALLOC_FREE(frame);
        return(0);
 }