/* Unix SMB/CIFS implementation. Main SMB server routines Copyright (C) Andrew Tridgell 1992-1998 Copyright (C) Martin Pool 2002 Copyright (C) Jelmer Vernooij 2002 Copyright (C) James J Myers 2003 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" /* called on a fatal error that should cause this server to terminate */ void exit_server(struct server_context *smb, const char *reason) { smb->model_ops->terminate_connection(smb, reason); } /* add a socket address to the list of events, one event per port */ static void add_socket(struct event_context *events, struct model_ops *model_ops, struct in_addr *ifip) { char *ports = lp_smb_ports(); char *ptr, *tok; const char *delim = ", "; for (tok=strtok_r(ports, delim, &ptr); tok; tok=strtok_r(NULL, delim, &ptr)) { unsigned port = atoi(tok); struct fd_event fde; if (port == 0) continue; fde.fd = open_socket_in(SOCK_STREAM, port, 0, ifip->s_addr, True); if (fde.fd == -1) { DEBUG(0,("Failed to open socket on %s:%u - %s\n", inet_ntoa(*ifip), port, strerror(errno))); continue; } /* ready to listen */ set_socket_options(fde.fd, "SO_KEEPALIVE"); set_socket_options(fde.fd, lp_socket_options()); if (listen(fde.fd, 10) == -1) { DEBUG(0,("Failed to listen on %s:%d - %s\n", inet_ntoa(*ifip), port, strerror(errno))); close(fde.fd); continue; } /* we are only interested in read events on the listen socket */ fde.flags = EVENT_FD_READ; fde.private = model_ops; fde.handler = model_ops->accept_connection; event_add_fd(events, &fde); } } /**************************************************************************** Open the socket communication. ****************************************************************************/ static void open_sockets_smbd(struct event_context *events, struct model_ops *model_ops) { if (lp_interfaces() && lp_bind_interfaces_only()) { int num_interfaces = iface_count(); int i; /* 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. */ for(i = 0; i < num_interfaces; i++) { struct in_addr *ifip = iface_n_ip(i); if (ifip == NULL) { DEBUG(0,("open_sockets_smbd: interface %d has NULL IP address !\n", i)); continue; } add_socket(events, model_ops, ifip); } } else { TALLOC_CTX *mem_ctx = talloc_init("open_sockets_smbd"); struct in_addr *ifip = interpret_addr2(mem_ctx, lp_socket_address()); /* Just bind to lp_socket_address() (usually 0.0.0.0) */ if (!mem_ctx) { smb_panic("No memory"); } add_socket(events, model_ops, ifip); talloc_destroy(mem_ctx); } } /**************************************************************************** Reload the services file. **************************************************************************/ BOOL reload_services(struct server_context *smb, 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); test = False; } } reopen_logs(); if (test && !lp_file_list_changed()) return(True); if (smb) { lp_killunused(smb, conn_snum_used); } ret = lp_load(dyn_CONFIGFILE, False, False, True); load_printers(); /* perhaps the config filename is now set */ if (!test) reload_services(smb, True); reopen_logs(); load_interfaces(); mangle_reset_cache(); reset_stat_cache(); /* this forces service parameters to be flushed */ set_current_service(NULL,True); return(ret); } /**************************************************************************** Initialise connect, service and file structs. ****************************************************************************/ static BOOL init_structs(void) { init_names(); file_init(); init_rpc_pipe_hnd(); secrets_init(); /* we want to re-seed early to prevent time delays causing client problems at a later date. (tridge) */ generate_random_buffer(NULL, 0, False); return True; } /* setup the events for the chosen process model */ static void setup_process_model(struct event_context *events, const char *model) { struct model_ops *ops; process_model_init(); ops = process_model_byname(model); if (!ops) { DEBUG(0,("Unknown process model '%s'\n", model)); exit(-1); } ops->model_startup(); /* now setup the listening sockets, adding event handlers to the events structure */ open_sockets_smbd(events, ops); } /**************************************************************************** main program. ****************************************************************************/ int main(int argc,const char *argv[]) { BOOL is_daemon = False; BOOL interactive = False; BOOL Fork = True; BOOL log_stdout = False; int opt; poptContext pc; struct event_context *events; const char *model = "standard"; struct poptOption long_options[] = { POPT_AUTOHELP {"daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon (default)" }, {"interactive", 'i', POPT_ARG_VAL, &interactive, True, "Run interactive (not a daemon)"}, {"foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools & etc)" }, {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" }, {"build-options", 'b', POPT_ARG_NONE, NULL, 'b', "Print build options" }, {"port", 'p', POPT_ARG_STRING, NULL, 0, "Listen on the specified ports"}, {"model", 'M', POPT_ARG_STRING, &model, 0, "select process model"}, POPT_COMMON_SAMBA { NULL } }; pc = poptGetContext("smbd", argc, argv, long_options, 0); while((opt = poptGetNextOpt(pc)) != -1) { switch (opt) { case 'b': /* Display output to screen as well as debug */ build_options(True); exit(0); break; case 'p': lp_set_cmdline("smb ports", poptGetOptArg(pc)); break; } } poptFreeContext(pc); events = event_context_init(); load_case_tables(); if (interactive) { Fork = False; log_stdout = True; } 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); fault_setup((void (*)(void *))exit_server); /* we are never interested in SIGPIPE */ BlockSignals(True,SIGPIPE); #if defined(SIGFPE) /* we are never interested in SIGFPE */ BlockSignals(True,SIGFPE); #endif #if defined(SIGUSR2) /* We are no longer interested in USR2 */ BlockSignals(True,SIGUSR2); #endif /* POSIX demands that signals are inherited. If the invoking process has * these signals masked, we will have problems, as we won't recieve them. */ BlockSignals(False, SIGHUP); BlockSignals(False, SIGUSR1); BlockSignals(False, SIGTERM); /* we want total control over the permissions on created files, so set our umask to 0 */ umask(0); reopen_logs(); DEBUG(0,("smbd version %s started.\n", SAMBA_VERSION)); DEBUGADD(0,("Copyright Andrew Tridgell and the Samba Team 1992-2003\n")); /* Output the build options to the debug log */ build_options(False); if (sizeof(uint16) < 2 || sizeof(uint32) < 4) { DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n")); exit(1); } DEBUG(0,("Using %s process model\n", model)); if (!reload_services(NULL, False)) return(-1); init_structs(); 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 (is_daemon && !interactive) { DEBUG(3,("Becoming a daemon.\n")); become_daemon(Fork); } if (!directory_exist(lp_lockdir(), NULL)) { mkdir(lp_lockdir(), 0755); } if (is_daemon) { pidfile_create("smbd"); } register_msg_pool_usage(); register_dmalloc_msgs(); setup_process_model(events, model); /* wait for events */ return event_loop_wait(events); }