-/*
+/*
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) 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/>.
*/
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;
static void smbd_set_server_fd(int fd)
{
server_fd = fd;
- client_setfd(fd);
+}
+
+int get_client_fd(void)
+{
+ return server_fd;
+}
+
+int client_get_tcp_info(struct sockaddr_in *server, struct sockaddr_in *client)
+{
+ socklen_t length;
+ if (server_fd == -1) {
+ return -1;
+ }
+ length = sizeof(*server);
+ if (getsockname(server_fd, (struct sockaddr *)server, &length) != 0) {
+ return -1;
+ }
+ length = sizeof(*client);
+ if (getpeername(server_fd, (struct sockaddr *)client, &length) != 0) {
+ return -1;
+ }
+ return 0;
}
struct event_context *smbd_event_context(void)
{
static struct messaging_context *ctx;
- if (!ctx && !(ctx = messaging_init(NULL, server_id_self(),
- smbd_event_context()))) {
- smb_panic("Could not init smbd messaging context");
+ if (ctx == NULL) {
+ ctx = messaging_init(NULL, server_id_self(),
+ smbd_event_context());
+ }
+ if (ctx == NULL) {
+ DEBUG(0, ("Could not init smbd messaging context.\n"));
}
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.
********************************************************************/
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;
}
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
+ */
+ DEBUG(3,(__location__ " Unclean shutdown of pid %u\n", 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;
int maxfd = 0;
int i;
char *ports;
+ struct dns_reg_state * dns_reg = NULL;
+ unsigned dns_port = 0;
if (!is_daemon) {
return open_sockets_inetd();
/* 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);
- fstring tok;
+ char *tok;
const char *ptr;
if (ifss == NULL) {
continue;
}
- for (ptr=ports; next_token(&ptr, tok, " \t,",
- sizeof(tok)); ) {
+ 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, 0,
- ifss, True);
+ open_socket_in(SOCK_STREAM,
+ port,
+ num_sockets == 0 ? 0 : 2,
+ ifss,
+ true);
if(s == -1) {
- return false;
+ continue;
}
/* ready to listen */
set_socket_options(s,"SO_KEEPALIVE");
- set_socket_options(s,user_socket_options);
+ set_socket_options(s,lp_socket_options());
/* Set server socket to
* non-blocking for the accept. */
DEBUG(0,("open_sockets_smbd: listen: "
"%s\n", strerror(errno)));
close(s);
+ TALLOC_FREE(frame);
return False;
}
FD_SET(s,&listen_set);
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. */
- fstring tok;
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *tok;
const char *ptr;
const char *sock_addr = lp_socket_address();
- fstring sock_tok;
+ char *sock_tok;
const char *sock_ptr;
- if (strequal(sock_addr, "0.0.0.0") ||
+ if (sock_addr[0] == '\0' ||
+ strequal(sock_addr, "0.0.0.0") ||
strequal(sock_addr, "::")) {
#if HAVE_IPV6
sock_addr = "::,0.0.0.0";
#endif
}
- for (sock_ptr=sock_addr; next_token(&sock_ptr, sock_tok, " \t,",
- sizeof(sock_tok)); ) {
- for (ptr=ports; next_token(&ptr, tok, " \t,",
- sizeof(tok)); ) {
+ 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)) {
- return false;
+ continue;
}
- s = open_socket_in(SOCK_STREAM, port, 0,
- &ss, true);
+ s = open_socket_in(SOCK_STREAM,
+ port,
+ num_sockets == 0 ? 0 : 2,
+ &ss,
+ true);
if (s == -1) {
- return false;
+ continue;
}
/* ready to listen */
set_socket_options(s,"SO_KEEPALIVE");
- set_socket_options(s,user_socket_options);
+ set_socket_options(s,lp_socket_options());
/* Set server socket to non-blocking
* for the accept. */
"listen: %s\n",
strerror(errno)));
close(s);
+ TALLOC_FREE(frame);
return False;
}
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);
}
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);
+ claim_connection(NULL,"",
+ FLAG_MSG_GENERAL|FLAG_MSG_SMBD|FLAG_MSG_DBWRAP);
/* Listen to messages */
MSG_SMB_STAT_CACHE_DELETE, smb_stat_cache_delete);
brl_register_msgs(smbd_messaging_context());
+#ifdef CLUSTER_SUPPORT
+ if (lp_clustering()) {
+ ctdbd_register_reconfigure(messaging_ctdbd_connection());
+ }
+#endif
+
#ifdef DEVELOPER
messaging_register(smbd_messaging_context(), NULL,
MSG_SMB_INJECT_FAULT, msg_inject_fault);
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);
}
}
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);
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;
continue;
if (smbd_server_fd() == -1) {
- DEBUG(0,("open_sockets_smbd: accept: %s\n",
+ DEBUG(2,("open_sockets_smbd: accept: %s\n",
strerror(errno)));
continue;
}
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
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);
-
- /* Reset the state of the random
- * number generation system, so
- * children do not get the same random
- * numbers as each other */
-
- set_need_random_reseed();
- /* tdb needs special fork handling - remove
- * CLEAR_IF_FIRST flags */
- if (tdb_reopen_all(1) == -1) {
- DEBUG(0,("tdb_reopen_all failed.\n"));
- smb_panic("tdb_reopen_all failed");
+ set_remote_machine_name(get_peer_addr(smbd_server_fd(),
+ remaddr,
+ sizeof(remaddr)),
+ false);
+
+ if (!reinit_after_fork(
+ smbd_messaging_context(), true)) {
+ DEBUG(0,("reinit_after_fork() failed\n"));
+ smb_panic("reinit_after_fork() failed");
}
return True;
bool reload_services(bool test)
{
bool ret;
-
+
if (lp_loaded()) {
- pstring fname;
- pstrcpy(fname,lp_configfile());
- if (file_exist(fname, NULL) &&
- !strcsequal(fname, dyn_CONFIGFILE)) {
- pstrcpy(dyn_CONFIGFILE, fname);
+ char *fname = lp_configfile();
+ if (file_exist(fname) &&
+ !strcsequal(fname, get_dyn_CONFIGFILE())) {
+ set_dyn_CONFIGFILE(fname);
test = False;
}
}
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();
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();
enum server_exit_reason { SERVER_EXIT_NORMAL, SERVER_EXIT_ABNORMAL };
static void exit_server_common(enum server_exit_reason how,
- const char *const reason) NORETURN_ATTRIBUTE;
+ const char *const reason) _NORETURN_;
static void exit_server_common(enum server_exit_reason how,
const char *const reason)
{
static int firsttime=1;
+ bool had_open_conn;
if (!firsttime)
exit(0);
(negprot_global_auth_context->free)(&negprot_global_auth_context);
}
- conn_close_all();
+ had_open_conn = conn_close_all();
invalidate_all_vuids();
}
#endif
+#ifdef USE_DMAPI
+ /* Destroy Samba DMAPI session only if we are master smbd process */
+ if (am_parent) {
+ if (!dmapi_destroy_session()) {
+ DEBUG(0,("Unable to close Samba DMAPI session\n"));
+ }
+ }
+#endif
+
locking_end();
printing_end();
(reason ? reason : "normal exit")));
}
- exit(0);
+ /* if we had any open SMB connections when we exited then we
+ need to tell the parent smbd so that it can trigger a retry
+ of any locks we may have been holding or open files we were
+ blocking */
+ if (had_open_conn) {
+ exit(1);
+ } else {
+ exit(0);
+ }
}
void exit_server(const char *const explanation)
exit_server("critical server fault");
}
+
+/****************************************************************************
+received when we should release a specific IP
+****************************************************************************/
+static void release_ip(const char *ip, void *priv)
+{
+ char addr[INET6_ADDRSTRLEN];
+
+ if (strcmp(client_socket_addr(get_client_fd(),addr,sizeof(addr)), ip) == 0) {
+ /* we can't afford to do a clean exit - that involves
+ database writes, which would potentially mean we
+ are still running after the failover has finished -
+ we have to get rid of this process ID straight
+ away */
+ DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n",
+ ip));
+ /* note we must exit with non-zero status so the unclean handler gets
+ called in the parent, so that the brl database is tickled */
+ _exit(1);
+ }
+}
+
+static void msg_release_ip(struct messaging_context *msg_ctx, void *private_data,
+ uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
+{
+ release_ip((char *)data->data, NULL);
+}
+
/****************************************************************************
Initialise connect, service and file structs.
****************************************************************************/
init_dptrs();
- secrets_init();
+ if (!secrets_init())
+ return False;
return True;
}
return True;
}
+/*
+ * Do the recurring log file and smb.conf reload checks.
+ */
+
+static bool housekeeping_fn(const struct timeval *now, void *private_data)
+{
+ change_to_root_user();
+
+ /* update printer queue caches if necessary */
+ update_monitored_printq_cache();
+
+ /* check if we need to reload services */
+ check_reload(time(NULL));
+
+ /* Change machine password if neccessary. */
+ attempt_machine_password_change();
+
+ /*
+ * Force a log file check.
+ */
+ force_check_log_size();
+ check_log_size();
+ return true;
+}
/****************************************************************************
main program.
POPT_COMMON_DYNCONFIG
POPT_TABLEEND
};
-
- load_case_tables();
+ TALLOC_CTX *frame = talloc_stackframe(); /* Setup tos. */
TimeInit();
}
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);
set_remote_machine_name("smbd", False);
- if (interactive) {
- Fork = False;
- log_stdout = True;
- }
-
if (interactive && (DEBUGLEVEL >= 9)) {
talloc_enable_leak_report();
}
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);
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()));
exit(1);
}
- /*
- * Do this before reload_services.
- */
+ if (!lp_load_initial_only(get_dyn_CONFIGFILE())) {
+ DEBUG(0, ("error opening config file\n"));
+ exit(1);
+ }
+
+ if (smbd_messaging_context() == NULL)
+ exit(1);
if (!reload_services(False))
return(-1);
setpgid( (pid_t)0, (pid_t)0);
#endif
- if (!directory_exist(lp_lockdir(), NULL))
+ if (!directory_exist(lp_lockdir()))
mkdir(lp_lockdir(), 0755);
if (is_daemon)
pidfile_create("smbd");
+ if (!reinit_after_fork(smbd_messaging_context(), false)) {
+ DEBUG(0,("reinit_after_fork() failed\n"));
+ exit(1);
+ }
+
/* Setup all the TDB's - including CLEAR_IF_FIRST tdb's. */
- if (smbd_messaging_context() == NULL)
+ 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 */
if (!connections_init(True))
exit(1);
- if (!locking_init(0))
+ if (!locking_init())
exit(1);
namecache_enable();
- if (!init_registry())
+ if (!W_ERROR_IS_OK(registry_init_full()))
exit(1);
#if 0
smbd is launched via inetd and we fork a copy of
ourselves here */
- if ( is_daemon && !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
+ && lp_parm_bool(-1, "smbd", "backgroundqueue", true)) {
+ start_background_queue();
+ }
if (!open_sockets_smbd(is_daemon, interactive, ports))
exit(1);
}
if (*lp_rootdir()) {
- if (sys_chroot(lp_rootdir()) == 0)
+ if (chroot(lp_rootdir()) == 0)
DEBUG(2,("Changed root to %s\n", lp_rootdir()));
}
/* Setup oplocks */
if (!init_oplocks(smbd_messaging_context()))
exit(1);
-
+
/* Setup aio signal handler. */
initialize_async_io_handler();
- /*
- * For clustering, we need to re-init our ctdbd connection after the
- * fork
- */
- if (!NT_STATUS_IS_OK(messaging_reinit(smbd_messaging_context())))
- exit(1);
-
/* register our message handlers */
messaging_register(smbd_messaging_context(), NULL,
MSG_SMB_FORCE_TDIS, msg_force_tdis);
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_SMB_RELEASE_IP, msg_release_ip);
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_SMB_CLOSE_FILE, msg_close_file);
if ((lp_keepalive() != 0)
&& !(event_add_idle(smbd_event_context(), NULL,
exit(1);
}
+ if (!(event_add_idle(smbd_event_context(), NULL,
+ timeval_set(SMBD_SELECT_TIMEOUT, 0),
+ "housekeeping", housekeeping_fn, NULL))) {
+ DEBUG(0, ("Could not add housekeeping event\n"));
+ exit(1);
+ }
+
+#ifdef CLUSTER_SUPPORT
+
+ if (lp_clustering()) {
+ /*
+ * We need to tell ctdb about our client's TCP
+ * connection, so that for failover ctdbd can send
+ * tickle acks, triggering a reconnection by the
+ * client.
+ */
+
+ struct sockaddr_in srv, clnt;
+
+ if (client_get_tcp_info(&srv, &clnt) == 0) {
+
+ NTSTATUS status;
+
+ status = ctdbd_register_ips(
+ messaging_ctdbd_connection(),
+ &srv, &clnt, release_ip, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("ctdbd_register_ips failed: %s\n",
+ nt_errstr(status)));
+ }
+ } else
+ {
+ DEBUG(0,("Unable to get tcp info for "
+ "CTDB_CONTROL_TCP_CLIENT: %s\n",
+ strerror(errno)));
+ }
+ }
+
+#endif
+
+ TALLOC_FREE(frame);
+
smbd_process();
namecache_shutdown();