r22418: Support running under launchd. We abstract the method of obtaining
authorJames Peach <jpeach@samba.org>
Fri, 20 Apr 2007 21:09:44 +0000 (21:09 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:19:33 +0000 (12:19 -0500)
sockets to listen on a little, because in the launchd case these
are provided for us. We also add an idle timeout so that a daemon
can exit after a period of inactivity.

source/Makefile.in
source/configure.in
source/include/smb_launchd.h [new file with mode: 0644]
source/lib/launchd.c [new file with mode: 0644]
source/nsswitch/winbindd.c
source/nsswitch/winbindd_nss.h
source/smbd/connection.c
source/smbd/server.c

index 702bd63cd58af8dcfce18a723c28eaeebf72e443..03ca7931d358c8df8d2222a269827b1aff41ffa8 100644 (file)
@@ -498,7 +498,7 @@ SMBD_OBJ_SRV = smbd/files.o smbd/chgpasswd.o smbd/connection.o \
               smbd/change_trust_pw.o smbd/fake_file.o \
               smbd/quotas.o smbd/ntquotas.o $(AFS_OBJ) smbd/msdfs.o \
               $(AFS_SETTOKEN_OBJ) smbd/aio.o smbd/statvfs.o \
-              smbd/dmapi.o $(MANGLE_OBJ) @VFS_STATIC@
+              smbd/dmapi.o lib/launchd.o $(MANGLE_OBJ) @VFS_STATIC@
 
 SMBD_OBJ_BASE = $(PARAM_OBJ) $(SMBD_OBJ_SRV) $(LIBSMB_OBJ) \
                $(RPC_SERVER_OBJ) $(RPC_PARSE_OBJ) $(SECRETS_OBJ) \
@@ -836,7 +836,7 @@ WINBINDD_OBJ = \
                $(PROFILE_OBJ) $(SLCACHE_OBJ) $(SMBLDAP_OBJ) \
                $(SECRETS_OBJ) $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(POPT_LIB_OBJ) \
                $(DCUTIL_OBJ) $(IDMAP_OBJ) $(NSS_INFO_OBJ) \
-               $(AFS_OBJ) $(AFS_SETTOKEN_OBJ) \
+               $(AFS_OBJ) $(AFS_SETTOKEN_OBJ) lib/launchd.o \
                $(LIBADS_SERVER_OBJ) $(SERVER_MUTEX_OBJ) $(LDB_OBJ)
 
 WBINFO_OBJ = nsswitch/wbinfo.o $(LIBSAMBA_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
index 1e9fca76f51b824955e9f735a0528059325e58c0..b467388947156244c171b025d13aceebcd83d1bf 100644 (file)
@@ -348,6 +348,35 @@ AC_ARG_WITH(selftest-prefix,
   esac
 ])
 
+AC_ARG_ENABLE(launchd,
+[  --enable-launchd        Support running under launchd (default=auto)])
+
+if test x"$enable_launchd" != x"no" ; then
+    AC_CACHE_CHECK([whether to include launchd support],
+       samba_cv_launchd_support,
+       [
+           AC_TRY_COMPILE(
+           [
+#include <launch.h>
+           ],
+           [
+               launchd_msg(NULL);
+               launchd_data_get_fd(NULL);
+           ],
+           samba_cv_launchd_support=yes,
+           samba_cv_launchd_support=no)
+       ])
+
+    if test x"$samba_cv_launchd_support" = x"yes" ; then
+       AC_DEFINE(WITH_LAUNCHD_SUPPORT, 1,
+                   [Whether launchd support should be enabled])
+    else
+       if test x"$enable_launchd" = x"yes" ; then
+           AC_ERROR(launchd support is not available)
+       fi
+    fi
+fi
+
 #################################################
 # set path of samba4's smbtorture
 smbtorture4_path=""
diff --git a/source/include/smb_launchd.h b/source/include/smb_launchd.h
new file mode 100644 (file)
index 0000000..2e758a4
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+   Unix SMB/CIFS implementation.
+   Launchd integration wrapper API
+
+   Copyright (C) James Peach 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 2 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, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+struct smb_launch_info
+{
+       int idle_timeout_secs;
+       int num_sockets;
+       int *socket_list;
+};
+
+/* Retrieve launchd configuration. Returns True if we are running under
+ * launchd, False otherwise. NOTE this does not guarantee to provide a list of
+ * sockets since this is a user configuration option.
+ */
+BOOL smb_launchd_checkin(struct smb_launch_info *linfo);
+
+/* Retrieve launchd configuration. The variadic arguments are a list of
+ * constant null-terminated strings. The strings are the names of the socket
+ * dictionaries to retrieve sockets from. The list of names is terminated by a
+ * NULL.
+ */
+BOOL smb_launchd_checkin_names(struct smb_launch_info *linfo, ...);
+
+/* Free any data or state associated with a successful launchd checkin. */
+void smb_launchd_checkout(struct smb_launch_info *linfo);
diff --git a/source/lib/launchd.c b/source/lib/launchd.c
new file mode 100644 (file)
index 0000000..1fd5a33
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+   Unix SMB/CIFS implementation.
+   Launchd integration wrapper API
+
+   Copyright (C) 2007 James Peach
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "smb_launchd.h"
+
+/* launchd source code and documentation is available here:
+ *     http://launchd.macosforge.org/
+ */
+
+#if defined(WITH_LAUNCHD_SUPPORT)
+
+#include <launch.h>
+#include <stdarg.h>
+
+typedef void (*launchd_iterator)(launch_data_t, const char*, void*);
+
+#define LAUNCHD_TRACE_LEVEL 10
+
+ void smb_launchd_checkout(struct smb_launch_info *linfo)
+{
+       talloc_free(linfo->socket_list);
+}
+
+static void pull_launch_sockets(launch_data_t key,
+                               const char *name,
+                               struct smb_launch_info *linfo)
+{
+       launch_data_type_t type;
+
+       type = launch_data_get_type(key);
+       DEBUG(LAUNCHD_TRACE_LEVEL,
+               ("Searching item name='%s' type=%d for sockets\n",
+                name ? name : "", (int)type));
+
+       switch (type) {
+       case LAUNCH_DATA_FD:
+               if (!linfo->socket_list) {
+                       /* We are counting the number of sockets. */
+                       linfo->num_sockets++;
+               } else {
+                       /* We are collecting the socket fds. */
+                       int fd = launch_data_get_fd(key);
+
+                       linfo->socket_list[linfo->num_sockets] = fd;
+                       linfo->num_sockets++;
+                       DEBUG(LAUNCHD_TRACE_LEVEL,
+                               ("Added fd=%d to launchd set\n", fd));
+               }
+               return;
+       case LAUNCH_DATA_ARRAY:
+       {
+               int i;
+               launch_data_t item;
+
+               for (i = 0; i < launch_data_array_get_count(key); ++i) {
+                       item = launch_data_array_get_index(key, i);
+                       pull_launch_sockets(item, name, linfo);
+               }
+               return;
+       }
+       case LAUNCH_DATA_DICTIONARY:
+               launch_data_dict_iterate(key,
+                       (launchd_iterator)pull_launch_sockets, linfo);
+               return;
+       default:
+               return;
+       }
+}
+
+ BOOL smb_launchd_checkin_names(struct smb_launch_info *linfo, ...)
+{
+       launch_data_t msg;
+       launch_data_t resp;
+       launch_data_t item;
+       BOOL is_launchd = False;
+
+       ZERO_STRUCTP(linfo);
+
+       msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
+       resp = launch_msg(msg);
+       if (resp == NULL) {
+               /* IPC to launchd failed. */
+               launch_data_free(msg);
+               return is_launchd;
+       }
+
+       if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
+               errno = launch_data_get_errno(resp);
+               goto done;
+       }
+
+       /* At this point, we know we are running under launchd. */
+       linfo->idle_timeout_secs = 600;
+       is_launchd = True;
+
+       if ((item = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_TIMEOUT))) {
+               linfo->idle_timeout_secs = launch_data_get_integer(item);
+       }
+
+       if ((item = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS))) {
+               int count = 0;
+               const char * sockname = NULL;
+               launch_data_t sockdata;
+               va_list args;
+
+               /* Figure out the maximum number of sockets. */
+               va_start(args, linfo);
+               while ((sockname = va_arg(args, const char *))) {
+                   ++count;
+               }
+               va_end(args);
+
+               DEBUG(LAUNCHD_TRACE_LEVEL, ("Found %d launchd sockets\n",
+                                       linfo->num_sockets));
+
+               if (launch_data_dict_get_count(item) < count) {
+                       DEBUG(0, ("%d launchd sockets requested, "
+                           "but only %d are available\n",
+                           count, launch_data_dict_get_count(item)));
+               }
+
+               linfo->socket_list = talloc_array(NULL, int, count);
+               if (linfo->socket_list == NULL) {
+                       goto done;
+               }
+
+               linfo->num_sockets = 0;
+               va_start(args, linfo);
+               while ((sockname = va_arg(args, const char *))) {
+                   sockdata = launch_data_dict_lookup(item, sockname);
+
+                   pull_launch_sockets(sockdata, sockname, linfo);
+                   DEBUG(LAUNCHD_TRACE_LEVEL,
+                           ("Added launchd socket \"%s\"\n", sockname));
+               }
+
+               SMB_ASSERT(count >= linfo->num_sockets);
+       }
+
+done:
+       launch_data_free(msg);
+       launch_data_free(resp);
+       return is_launchd;
+}
+
+ BOOL smb_launchd_checkin(struct smb_launch_info *linfo)
+{
+       launch_data_t msg;
+       launch_data_t resp;
+       launch_data_t item;
+       BOOL is_launchd = False;
+
+       ZERO_STRUCTP(linfo);
+
+       msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
+       resp = launch_msg(msg);
+       if (resp == NULL) {
+               /* IPC to launchd failed. */
+               launch_data_free(msg);
+               return is_launchd;
+       }
+
+       if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
+               errno = launch_data_get_errno(resp);
+               goto done;
+       }
+
+       /* At this point, we know we are running under launchd. */
+       linfo->idle_timeout_secs = 600;
+       is_launchd = True;
+
+       if ((item = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_TIMEOUT))) {
+               linfo->idle_timeout_secs = launch_data_get_integer(item);
+       }
+
+       if ((item = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS))) {
+               int count;
+
+               pull_launch_sockets(item, NULL, linfo);
+               DEBUG(LAUNCHD_TRACE_LEVEL, ("Found %d launchd sockets\n",
+                                       linfo->num_sockets));
+
+               count = linfo->num_sockets;
+               linfo->socket_list = talloc_array(NULL, int, count);
+               if (linfo->socket_list == NULL) {
+                       goto done;
+               }
+
+               linfo->num_sockets = 0;
+               pull_launch_sockets(item, NULL, linfo);
+
+               DEBUG(LAUNCHD_TRACE_LEVEL, ("Added %d launchd sockets\n",
+                                       linfo->num_sockets));
+
+               SMB_ASSERT(count == linfo->num_sockets);
+       }
+
+done:
+       launch_data_free(msg);
+       launch_data_free(resp);
+       return is_launchd;
+}
+
+#else /* defined(WITH_LAUNCHD_SUPPORT) */
+
+ BOOL smb_launchd_checkin(struct smb_launch_info * UNUSED(linfo))
+{
+       ZERO_STRUCTP(linfo);
+       return False;
+}
+
+ BOOL smb_launchd_checkin_names(struct smb_launch_info * UNUSED(linfo), ...)
+{
+       ZERO_STRUCTP(linfo);
+       return False;
+}
+
+ void smb_launchd_checkout(struct smb_launch_info * UNUSED(linfo))
+{
+}
+
+#endif /* defined(WITH_LAUNCHD_SUPPORT) */
+
index da0473583f5bd3183a0a236046ff7b274d836ea6..0dade535215281fb3b78369a53d8e1c951597a11 100644 (file)
@@ -7,6 +7,7 @@
    Copyright (C) Andrew Tridgell 2002
    Copyright (C) Jelmer Vernooij 2003
    Copyright (C) Volker Lendecke 2004
+   Copyright (C) James Peach 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
@@ -25,6 +26,7 @@
 
 #include "includes.h"
 #include "winbindd.h"
+#include "smb_launchd.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
@@ -32,6 +34,7 @@
 BOOL opt_nocache = False;
 
 extern BOOL override_logfile;
+static BOOL unlink_winbindd_socket = True;
 
 struct event_context *winbind_event_context(void)
 {
@@ -121,9 +124,11 @@ static void terminate(void)
        pstring path;
 
        /* Remove socket file */
-       pstr_sprintf(path, "%s/%s", 
-                WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
-       unlink(path);
+       if (unlink_winbindd_socket) {
+               pstr_sprintf(path, "%s/%s",
+                        WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
+               unlink(path);
+       }
 
        idmap_close();
        
@@ -714,28 +719,56 @@ static BOOL remove_idle_client(void)
        return False;
 }
 
+static BOOL winbindd_init_sockets(int *public_sock, int *priv_sock,
+                               int *idle_timeout_sec)
+{
+       struct smb_launch_info linfo;
+
+       if (smb_launchd_checkin_names(&linfo, "WinbindPublicPipe",
+                   "WinbindPrivilegedPipe", NULL)) {
+               if (linfo.num_sockets != 2) {
+                       DEBUG(0, ("invalid launchd configuration, "
+                               "expected 2 sockets but got %d\n",
+                               linfo.num_sockets));
+                       return False;
+               }
+
+               *public_sock = linfo.socket_list[0];
+               *priv_sock = linfo.socket_list[1];
+               *idle_timeout_sec = linfo.idle_timeout_secs;
+
+               unlink_winbindd_socket = False;
+
+               smb_launchd_checkout(&linfo);
+               return True;
+       } else {
+               *public_sock = open_winbindd_socket();
+               *priv_sock = open_winbindd_priv_socket();
+               *idle_timeout_sec = -1;
+
+               if (*public_sock == -1 || *priv_sock == -1) {
+                       DEBUG(0, ("failed to open winbindd pipes: %s\n",
+                           errno ? strerror(errno) : "unknown error"));
+                       return False;
+               }
+
+               return True;
+       }
+}
+
 /* Process incoming clients on listen_sock.  We use a tricky non-blocking,
    non-forking, non-threaded model which allows us to handle many
    simultaneous connections while remaining impervious to many denial of
    service attacks. */
 
-static void process_loop(void)
+static int process_loop(int listen_sock, int listen_priv_sock)
 {
        struct winbindd_cli_state *state;
        struct fd_event *ev;
        fd_set r_fds, w_fds;
-       int maxfd, listen_sock, listen_priv_sock, selret;
+       int maxfd, selret;
        struct timeval timeout, ev_timeout;
 
-       /* Open Sockets here to get stuff going ASAP */
-       listen_sock = open_winbindd_socket();
-       listen_priv_sock = open_winbindd_priv_socket();
-
-       if (listen_sock == -1 || listen_priv_sock == -1) {
-               perror("open_winbind_socket");
-               exit(1);
-       }
-
        /* We'll be doing this a lot */
 
        /* Handle messages */
@@ -903,6 +936,55 @@ static void process_loop(void)
                        winbind_child_died(pid);
                }
        }
+
+
+       return winbindd_num_clients();
+}
+
+static void winbindd_process_loop(enum smb_server_mode server_mode)
+{
+       int idle_timeout_sec;
+       struct timeval starttime;
+       int listen_public, listen_priv;
+
+       errno = 0;
+       if (!winbindd_init_sockets(&listen_public, &listen_priv,
+                                   &idle_timeout_sec)) {
+               terminate();
+       }
+
+       starttime = timeval_current();
+
+       if (listen_public == -1 || listen_priv == -1) {
+               DEBUG(0, ("failed to open winbindd pipes: %s\n",
+                           errno ? strerror(errno) : "unknown error"));
+               terminate();
+       }
+
+       for (;;) {
+               int clients = process_loop(listen_public, listen_priv);
+
+               /* Don't bother figuring out the idle time if we won't be
+                * timing out anyway.
+                */
+               if (idle_timeout_sec < 0) {
+                       continue;
+               }
+
+               if (clients == 0 && server_mode == SERVER_MODE_FOREGROUND) {
+                       struct timeval now;
+
+                       now = timeval_current();
+                       if (timeval_elapsed2(&starttime, &now) >
+                               (double)idle_timeout_sec) {
+                               DEBUG(0, ("idle for %d secs, exitting\n",
+                                           idle_timeout_sec));
+                               terminate();
+                       }
+               } else {
+                       starttime = timeval_current();
+               }
+       }
 }
 
 /* Main function */
@@ -1114,9 +1196,7 @@ int main(int argc, char **argv, char **envp)
        smb_nscd_flush_group_cache();
 
        /* Loop waiting for requests */
-
-       while (1)
-               process_loop();
+       winbindd_process_loop(server_mode);
 
        return 0;
 }
index b6c262e466799a3cf4c247d3a4de2c08ab541eb9..96c4b1161c651a5b4392ec61ccfcd4e613fd37c2 100644 (file)
 #define _WINBINDD_NTDOM_H
 
 #define WINBINDD_SOCKET_NAME "pipe"            /* Name of PF_UNIX socket */
+
+/* Let the build environment override the public winbindd socket location. This
+ * is needed for launchd support -- jpeach.
+ */
+#ifndef WINBINDD_SOCKET_DIR
 #define WINBINDD_SOCKET_DIR  "/tmp/.winbindd"  /* Name of PF_UNIX dir */
+#endif
+
 #define WINBINDD_PRIV_SOCKET_SUBDIR "winbindd_privileged" /* name of subdirectory of lp_lockdir() to hold the 'privileged' pipe */
 #define WINBINDD_DOMAIN_ENV  "WINBINDD_DOMAIN" /* Environment variables */
 #define WINBINDD_DONT_ENV    "_NO_WINBINDD"
index 5c31a5460ba92c8b88c826dfe5f74e6b8def4dea..e609b90a507fc7296de5b0a96a79df6b59b4ff03 100644 (file)
@@ -96,13 +96,15 @@ static int count_fn( TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *u
        struct connections_data crec;
        struct count_stat *cs = (struct count_stat *)udp;
  
-       if (dbuf.dsize != sizeof(crec))
+       if (dbuf.dsize != sizeof(crec)) {
                return 0;
+       }
 
        memcpy(&crec, dbuf.dptr, sizeof(crec));
  
-       if (crec.cnum == -1)
+       if (crec.cnum == -1) {
                return 0;
+       }
 
        /* If the pid was not found delete the entry from connections.tdb */
 
@@ -113,9 +115,19 @@ static int count_fn( TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *u
                        DEBUG(0,("count_fn: tdb_delete failed with error %s\n", tdb_errorstr(tdb) ));
                return 0;
        }
-
-       if (strequal(crec.servicename, cs->name))
+       if (cs->name) {
+               /* We are counting all the connections to a given share. */
+               if (strequal(crec.servicename, cs->name)) {
+                       cs->curr_connections++;
+               }
+       } else {
+               /* We are counting all the connections. Static registrations
+                * like the lpq backgroud process and the smbd daemon process
+                * have a cnum of -1, so won't be counted here.
+                */
                cs->curr_connections++;
+       }
 
        return 0;
 }
@@ -139,14 +151,30 @@ int count_current_connections( const char *sharename, BOOL clear  )
         */
 
        if (tdb_traverse(tdb, count_fn, &cs) == -1) {
-               DEBUG(0,("claim_connection: traverse of connections.tdb failed with error %s.\n",
+               DEBUG(0,("count_current_connections: traverse of connections.tdb failed with error %s\n",
                        tdb_errorstr(tdb) ));
-               return False;
+               DEBUGADD(0, ("count_current_connections: connection count of %d might not be accurate",
+                           cs.curr_connections));
        }
-       
+
+       /* If the traverse failed part-way through, we at least return
+        * as many connections as we had already counted. If it failed
+        * right at the start, we will return 0, which is about all we
+        * can do anywway.
+        */
+
        return cs.curr_connections;
 }
 
+/****************************************************************************
+ Count the number of connections open across all shares.
+****************************************************************************/
+
+int count_all_current_connections(void)
+{
+       return count_current_connections(NULL, True /* clear stale entries */);
+}
+
 /****************************************************************************
  Claim an entry in the connections database.
 ****************************************************************************/
index 255f9d0c6574adaa305b664b9b9de553c723fd36..78f9779cfc35619bcdeec2bbe6d5e5d5cebdbda5 100644 (file)
@@ -4,6 +4,7 @@
    Copyright (C) Andrew Tridgell               1992-1998
    Copyright (C) Martin Pool                   2002
    Copyright (C) Jelmer Vernooij               2002-2003
+   Copyright (C) James Peach                   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
@@ -21,6 +22,7 @@
 */
 
 #include "includes.h"
+#include "smb_launchd.h"
 
 static_decl_rpc;
 
@@ -296,39 +298,13 @@ static BOOL allowable_number_of_smbd_processes(void)
        return num_children < max_processes;
 }
 
-/****************************************************************************
- Open the socket communication.
-****************************************************************************/
-
-static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_ports)
+static int init_sockets_smbd(const char *smb_ports,
+                               int fd_listenset[FD_SETSIZE])
 {
        int num_interfaces = iface_count();
+       char * ports;
        int num_sockets = 0;
-       int fd_listenset[FD_SETSIZE];
-       fd_set listen_set;
-       int s;
-       int maxfd = 0;
-       int i;
-       char *ports;
-
-       if (server_mode == SERVER_MODE_INETD) {
-               return open_sockets_inetd();
-       }
-
-#ifdef HAVE_ATEXIT
-       {
-               static int atexit_set;
-               if(atexit_set == 0) {
-                       atexit_set=1;
-                       atexit(killkids);
-               }
-       }
-#endif
-
-       /* Stop zombies */
-       CatchSignal(SIGCLD, sig_cld);
-                               
-       FD_ZERO(&listen_set);
+       int i, s;
 
        /* use a reasonable default set of ports - listing on 445 and 139 */
        if (!smb_ports) {
@@ -356,7 +332,7 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
                        const char *ptr;
 
                        if(ifip == NULL) {
-                               DEBUG(0,("open_sockets_smbd: interface %d has NULL IP address !\n", i));
+                               DEBUG(0,("init_sockets_smbd: interface %d has NULL IP address !\n", i));
                                continue;
                        }
 
@@ -367,7 +343,7 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
                                }
                                s = fd_listenset[num_sockets] = open_socket_in(SOCK_STREAM, port, 0, ifip->s_addr, True);
                                if(s == -1)
-                                       return False;
+                                       return 0;
 
                                /* ready to listen */
                                set_socket_options(s,"SO_KEEPALIVE"); 
@@ -379,15 +355,13 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
                                if (listen(s, SMBD_LISTEN_BACKLOG) == -1) {
                                        DEBUG(0,("listen: %s\n",strerror(errno)));
                                        close(s);
-                                       return False;
+                                       return 0;
                                }
-                               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"));
-                                       return False;
+                                       DEBUG(0,("init_sockets_smbd: Too many sockets to bind to\n"));
+                                       return 0;
                                }
                        }
                }
@@ -407,7 +381,7 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
                        s = open_socket_in(SOCK_STREAM, port, 0,
                                           interpret_addr(lp_socket_address()),True);
                        if (s == -1)
-                               return(False);
+                               return 0;
                
                        /* ready to listen */
                        set_socket_options(s,"SO_KEEPALIVE"); 
@@ -417,26 +391,122 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
                        set_blocking(s,False); 
  
                        if (listen(s, SMBD_LISTEN_BACKLOG) == -1) {
-                               DEBUG(0,("open_sockets_smbd: listen: %s\n",
+                               DEBUG(0,("init_sockets_smbd: listen: %s\n",
                                         strerror(errno)));
                                close(s);
-                               return False;
+                               return 0;
                        }
 
                        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"));
-                               return False;
+                               DEBUG(0,("init_sockets_smbd: Too many sockets to bind to\n"));
+                               return 0;
                        }
                }
        } 
 
        SAFE_FREE(ports);
+       return num_sockets;
+}
+
+static int init_sockets_launchd(const struct smb_launch_info *linfo,
+                               const char * smb_ports,
+                               int fd_listenset[FD_SETSIZE])
+{
+       int num_sockets;
+       int i;
+
+       /* The launchd service configuration does not have to provide sockets,
+        * even though it's basically useless without it.
+        */
+       if (!linfo->num_sockets) {
+               return init_sockets_smbd(smb_ports, fd_listenset);
+       }
+
+       /* Make sure we don't get more sockets than we can handle. */
+       num_sockets = MIN(FD_SETSIZE, linfo->num_sockets);
+       memcpy(fd_listenset, linfo->socket_list, num_sockets * sizeof(int));
+
+       /* Get the sockets ready. This could be hoisted into
+        * open_sockets_smbd(), but the order of socket operations might
+        * matter for some platforms, so this approach seems less risky.
+        *      --jpeach
+        */
+       for (i = 0; i < num_sockets; ++i) {
+               set_socket_options(fd_listenset[i], "SO_KEEPALIVE");
+               set_socket_options(fd_listenset[i], user_socket_options);
+
+               /* Set server socket to non-blocking for the accept. */
+               set_blocking(fd_listenset[i], False);
+       }
+
+       return num_sockets;
+}
+
+/****************************************************************************
+ Open the socket communication.
+****************************************************************************/
+
+static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_ports)
+{
+       int num_sockets = 0;
+       int fd_listenset[FD_SETSIZE];
+       fd_set listen_set;
+       int s;
+       int maxfd = 0;
+       int i;
+       struct timeval idle_timeout = {0, 0};
+       struct smb_launch_info linfo;
+
+       if (server_mode == SERVER_MODE_INETD) {
+               return open_sockets_inetd();
+       }
+
+#ifdef HAVE_ATEXIT
+       {
+               static int atexit_set;
+               if(atexit_set == 0) {
+                       atexit_set=1;
+                       atexit(killkids);
+               }
+       }
+#endif
+
+       /* Stop zombies */
+       CatchSignal(SIGCLD, sig_cld);
+
+       FD_ZERO(&listen_set);
+
+       /* At this point, it doesn't matter what daemon mode we are in, we
+        * need some sockets to listen on. If we are in FOREGROUND mode,
+        * the launchd checkin might succeed. If we are in DAEMON or
+        * INTERACTIVE modes, it will fail and we will open the sockets
+        * ourselves.
+        */
+       if (smb_launchd_checkin(&linfo)) {
+               /* We are running under launchd and launchd has
+                * opened some sockets for us.
+                */
+               num_sockets = init_sockets_launchd(&linfo,
+                                           smb_ports,
+                                           fd_listenset);
+               idle_timeout.tv_sec = linfo.idle_timeout_secs;
+               smb_launchd_checkout(&linfo);
+       } else {
+               num_sockets = init_sockets_smbd(smb_ports,
+                                           fd_listenset);
+       }
+
+       if (num_sockets == 0) {
+               return False;
+       }
+
+       for (i = 0; i < num_sockets; ++i) {
+               FD_SET(fd_listenset[i], &listen_set);
+               maxfd = MAX(maxfd, fd_listenset[i]);
+       }
 
         /* Listen to messages */
 
@@ -476,8 +546,9 @@ static BOOL open_sockets_smbd(enum smb_server_mode server_mode, const char *smb_
 
                memcpy((char *)&lfds, (char *)&listen_set, 
                       sizeof(listen_set));
-               
-               num = sys_select(maxfd+1,&lfds,NULL,NULL,NULL);
+
+               num = sys_select(maxfd+1,&lfds,NULL,NULL,
+                       idle_timeout.tv_sec ? &idle_timeout : NULL);
                
                if (num == -1 && errno == EINTR) {
                        if (got_sig_term) {
@@ -494,7 +565,15 @@ 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");
+               }
+
                /* check if we need to reload services */
                check_reload(time(NULL));