From 44f5211c170c0ddc2be23717f1ad08f5fc0b329a Mon Sep 17 00:00:00 2001 From: James Peach Date: Fri, 20 Apr 2007 21:09:44 +0000 Subject: r22418: Support running under launchd. We abstract the method of obtaining 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. (This used to be commit fc8589a3371d396197fae508e563f814899c2beb) --- source3/smbd/connection.c | 42 +++++++++-- source3/smbd/server.c | 175 +++++++++++++++++++++++++++++++++------------- 2 files changed, 162 insertions(+), 55 deletions(-) (limited to 'source3/smbd') diff --git a/source3/smbd/connection.c b/source3/smbd/connection.c index 5c31a5460b..e609b90a50 100644 --- a/source3/smbd/connection.c +++ b/source3/smbd/connection.c @@ -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. ****************************************************************************/ diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 255f9d0c65..78f9779cfc 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -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)); -- cgit