summaryrefslogtreecommitdiff
path: root/source4/wrepld/server.c
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2003-08-13 01:53:07 +0000
committerAndrew Tridgell <tridge@samba.org>2003-08-13 01:53:07 +0000
commitef2e26c91b80556af033d3335e55f5dfa6fff31d (patch)
treefaa21bfd7e7b5247250b47c7891dc1a5ebee6be9 /source4/wrepld/server.c
downloadsamba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.gz
samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.bz2
samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.zip
first public release of samba4 code
(This used to be commit b0510b5428b3461aeb9bbe3cc95f62fc73e2b97f)
Diffstat (limited to 'source4/wrepld/server.c')
-rw-r--r--source4/wrepld/server.c737
1 files changed, 737 insertions, 0 deletions
diff --git a/source4/wrepld/server.c b/source4/wrepld/server.c
new file mode 100644
index 0000000000..afeec3402b
--- /dev/null
+++ b/source4/wrepld/server.c
@@ -0,0 +1,737 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB server routines
+ Copyright (C) Jean François Micouleau 1998-2002.
+
+ 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 "wins_repl.h"
+
+extern WINS_OWNER *global_wins_table;
+extern int partner_count;
+
+extern fd_set *listen_set;
+extern int listen_number;
+extern int *sock_array;
+
+extern TALLOC_CTX *mem_ctx;
+
+int wins_port = 42;
+
+/****************************************************************************
+ when exiting, take the whole family
+****************************************************************************/
+static void *dflt_sig(void)
+{
+ exit_server("caught signal");
+ return NULL;
+}
+
+/****************************************************************************
+ reload the services file
+ **************************************************************************/
+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);
+ test = False;
+ }
+ }
+
+ reopen_logs();
+
+ if (test && !lp_file_list_changed())
+ return(True);
+
+ ret = lp_load(dyn_CONFIGFILE,False,False,True);
+
+
+ /* perhaps the config filename is now set */
+ if (!test)
+ reload_services(True);
+
+ reopen_logs();
+
+ load_interfaces();
+
+ return(ret);
+}
+
+/****************************************************************************
+ Catch a sighup.
+****************************************************************************/
+
+VOLATILE sig_atomic_t reload_after_sighup = False;
+
+static void sig_hup(int sig)
+{
+ BlockSignals(True,SIGHUP);
+ DEBUG(0,("Got SIGHUP\n"));
+
+ sys_select_signal();
+ reload_after_sighup = True;
+ BlockSignals(False,SIGHUP);
+}
+
+#if DUMP_CORE
+/*******************************************************************
+prepare to dump a core file - carefully!
+********************************************************************/
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+ pstrcpy(dname,lp_logfile());
+ if ((p=strrchr_m(dname,'/'))) *p=0;
+ pstrcat(dname,"/corefiles");
+ mkdir(dname,0700);
+ sys_chown(dname,getuid(),getgid());
+ chmod(dname,0700);
+ if (chdir(dname)) return(False);
+ umask(~(0700));
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit(RLIMIT_CORE, &rlp);
+ rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
+ setrlimit(RLIMIT_CORE, &rlp);
+ getrlimit(RLIMIT_CORE, &rlp);
+ DEBUG(3,("Core limits now %d %d\n",
+ (int)rlp.rlim_cur,(int)rlp.rlim_max));
+ }
+#endif
+#endif
+
+
+ DEBUG(0,("Dumping core in %s\n",dname));
+ abort();
+ return(True);
+}
+#endif
+
+/****************************************************************************
+exit the server
+****************************************************************************/
+void exit_server(const char *reason)
+{
+ static int firsttime=1;
+
+ if (!firsttime)
+ exit(0);
+ firsttime = 0;
+
+ DEBUG(2,("Closing connections\n"));
+
+ if (!reason) {
+ int oldlevel = DEBUGLEVEL;
+ DEBUGLEVEL = 10;
+ DEBUGLEVEL = oldlevel;
+ DEBUG(0,("===============================================================\n"));
+#if DUMP_CORE
+ if (dump_core()) return;
+#endif
+ }
+
+ DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+ exit(0);
+}
+
+/****************************************************************************
+ Usage of the program.
+****************************************************************************/
+
+static void usage(char *pname)
+{
+
+ d_printf("Usage: %s [-DFSaioPh?V] [-d debuglevel] [-l log basename] [-p port]\n", pname);
+ d_printf(" [-O socket options] [-s services file]\n");
+ d_printf("\t-D Become a daemon (default)\n");
+ d_printf("\t-F Run daemon in foreground (for daemontools, etc)\n");
+ d_printf("\t-S Log to stdout\n");
+ d_printf("\t-a Append to log file (default)\n");
+ d_printf("\t-i Run interactive (not a daemon)\n" );
+ d_printf("\t-o Overwrite log file, don't append\n");
+ d_printf("\t-h Print usage\n");
+ d_printf("\t-? Print usage\n");
+ d_printf("\t-V Print version\n");
+ d_printf("\t-d debuglevel Set the debuglevel\n");
+ d_printf("\t-l log basename. Basename for log/debug files\n");
+ d_printf("\t-p port Listen on the specified port\n");
+ d_printf("\t-O socket options Socket options\n");
+ d_printf("\t-s services file. Filename of services file\n");
+ d_printf("\n");
+}
+
+/****************************************************************************
+ Create an fd_set containing all the sockets in the subnet structures,
+ plus the broadcast sockets.
+***************************************************************************/
+
+static BOOL create_listen_fdset(void)
+{
+ int i;
+ int num_interfaces = iface_count();
+ int s;
+
+ listen_set = (fd_set *)malloc(sizeof(fd_set));
+ if(listen_set == NULL) {
+ DEBUG(0,("create_listen_fdset: malloc fail !\n"));
+ return True;
+ }
+
+#ifdef HAVE_ATEXIT
+ {
+ static int atexit_set;
+ if(atexit_set == 0) {
+ atexit_set=1;
+ }
+ }
+#endif
+
+ FD_ZERO(listen_set);
+
+ 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
+ socket per interface and bind to only these.
+ */
+
+ if(num_interfaces > FD_SETSIZE) {
+ DEBUG(0,("create_listen_fdset: Too many interfaces specified to bind to. Number was %d max can be %d\n", num_interfaces, FD_SETSIZE));
+ return False;
+ }
+
+ /* Now open a listen socket for each of the interfaces. */
+ for(i = 0; i < num_interfaces; i++) {
+ struct in_addr *ifip = iface_n_ip(i);
+
+ if(ifip == NULL) {
+ DEBUG(0,("create_listen_fdset: interface %d has NULL IP address !\n", i));
+ continue;
+ }
+ s = open_socket_in(SOCK_STREAM, wins_port, 0, ifip->s_addr, True);
+ if(s == -1)
+ return False;
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,lp_socket_options());
+
+ if (listen(s, 5) == -1) {
+ DEBUG(5,("listen: %s\n",strerror(errno)));
+ close(s);
+ return False;
+ }
+ add_fd_to_sock_array(s);
+ FD_SET(s, listen_set);
+ }
+ } else {
+ /* Just bind to 0.0.0.0 - accept connections from anywhere. */
+ num_interfaces = 1;
+
+ /* open an incoming socket */
+ s = open_socket_in(SOCK_STREAM, wins_port, 0, interpret_addr(lp_socket_address()),True);
+ if (s == -1)
+ return(False);
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s, lp_socket_options());
+
+ if (listen(s, 5) == -1) {
+ DEBUG(0,("create_listen_fdset: listen: %s\n", strerror(errno)));
+ close(s);
+ return False;
+ }
+
+ add_fd_to_sock_array(s);
+ FD_SET(s, listen_set);
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ read a packet from a socket and parse it, returning a packet ready
+ to be used or put on the queue. This assumes a UDP socket
+ ******************************************************************/
+static struct wins_packet_struct *read_wins_packet(int fd, int timeout)
+{
+ struct wins_packet_struct *p;
+ GENERIC_PACKET *q;
+ struct BUFFER inbuf;
+ ssize_t len=0;
+ size_t total=0;
+ ssize_t ret;
+ BOOL ok = False;
+
+ inbuf.buffer=NULL;
+ inbuf.length=0;
+ inbuf.offset=0;
+
+ if(!grow_buffer(&inbuf, 4))
+ return NULL;
+
+ ok = (read(fd, inbuf.buffer,4) == 4);
+ if (!ok)
+ return NULL;
+ len = smb_len(inbuf.buffer);
+
+ if (len<=0)
+ return NULL;
+
+ if(!grow_buffer(&inbuf, len))
+ return NULL;
+
+ while (total < len) {
+ ret = read(fd, inbuf.buffer + total + 4, len - total);
+ if (ret == 0) {
+ DEBUG(10,("read_socket_data: recv of %d returned 0. Error = %s\n", (int)(len - total), strerror(errno) ));
+ return NULL;
+ }
+ if (ret == -1) {
+ DEBUG(0,("read_socket_data: recv failure for %d. Error = %s\n", (int)(len - total), strerror(errno) ));
+ return NULL;
+ }
+ total += ret;
+ }
+
+ q = (GENERIC_PACKET *)talloc(mem_ctx, sizeof(GENERIC_PACKET));
+ p = (struct wins_packet_struct *)talloc(mem_ctx, sizeof(*p));
+ if (q==NULL || p==NULL)
+ return NULL;
+
+ decode_generic_packet(&inbuf, q);
+
+ q->fd=fd;
+
+ p->next = NULL;
+ p->prev = NULL;
+ p->stop_packet = False;
+ p->timestamp = time(NULL);
+ p->fd = fd;
+ p->packet=q;
+
+ return p;
+}
+
+static struct wins_packet_struct *packet_queue = NULL;
+
+/*******************************************************************
+ Queue a packet into a packet queue
+******************************************************************/
+static void queue_packet(struct wins_packet_struct *packet)
+{
+ struct wins_packet_struct *p;
+
+ if (!packet_queue) {
+ packet->prev = NULL;
+ packet->next = NULL;
+ packet_queue = packet;
+ return;
+ }
+
+ /* find the bottom */
+ for (p=packet_queue;p->next;p=p->next)
+ ;
+
+ p->next = packet;
+ packet->next = NULL;
+ packet->prev = p;
+}
+
+/****************************************************************************
+ Listens for NMB or DGRAM packets, and queues them.
+ return True if the socket is dead
+***************************************************************************/
+static BOOL listen_for_wins_packets(void)
+{
+ int num_interfaces = iface_count();
+ fd_set fds;
+ int i, num, s, new_s;
+ struct timeval timeout;
+
+ if(listen_set == NULL) {
+ if(!create_listen_fdset()) {
+ DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n"));
+ return True;
+ }
+ }
+
+ memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set));
+
+ timeout.tv_sec = NMBD_SELECT_LOOP;
+ timeout.tv_usec = 0;
+
+ /* Prepare for the select - allow certain signals. */
+
+ BlockSignals(False, SIGTERM);
+
+ num = sys_select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+
+ /* We can only take signals when we are in the select - block them again here. */
+
+ BlockSignals(True, SIGTERM);
+
+ if(num == -1)
+ return False;
+
+ for (; num > 0; num--) {
+ s = -1;
+ /* check the sockets we are only listening on, waiting to accept */
+ for (i=0; i<num_interfaces; i++) {
+ struct sockaddr addr;
+ socklen_t in_addrlen = sizeof(addr);
+
+ if(FD_ISSET(sock_array[i], &fds)) {
+ s = sock_array[i];
+ /* Clear this so we don't look at it again. */
+ FD_CLR(sock_array[i], &fds);
+
+ /* accept and add the new socket to the listen set */
+ new_s=accept(s, &addr, &in_addrlen);
+
+ if (new_s < 0)
+ continue;
+
+ DEBUG(5,("listen_for_wins_packets: new connection, old: %d, new : %d\n", s, new_s));
+
+ set_socket_options(new_s, "SO_KEEPALIVE");
+ set_socket_options(new_s, lp_socket_options());
+ FD_SET(new_s, listen_set);
+ add_fd_to_sock_array(new_s);
+ }
+ }
+
+ /*
+ * check for the sockets we are waiting data from
+ * either client sending datas
+ * or reply to our requests
+ */
+ for (i=num_interfaces; i<listen_number; i++) {
+ if(FD_ISSET(sock_array[i], &fds)) {
+ struct wins_packet_struct *packet = read_wins_packet(sock_array[i], timeout.tv_sec);
+ if (packet) {
+ packet->fd = sock_array[i];
+ queue_packet(packet);
+ }
+ DEBUG(2,("listen_for_wins_packets: some data on fd %d\n", sock_array[i]));
+ FD_CLR(sock_array[i], &fds);
+ break;
+ }
+
+ }
+
+ }
+
+ return False;
+}
+
+
+/*******************************************************************
+ Run elements off the packet queue till its empty
+******************************************************************/
+
+static void run_wins_packet_queue(void)
+{
+ struct wins_packet_struct *p;
+
+ while ((p = packet_queue)) {
+ packet_queue = p->next;
+ if (packet_queue)
+ packet_queue->prev = NULL;
+ p->next = p->prev = NULL;
+
+ construct_reply(p);
+
+ /* if it was a stop assoc, close the connection */
+ if (p->stop_packet) {
+ FD_CLR(p->fd, listen_set);
+ remove_fd_from_sock_array(p->fd);
+ close(p->fd);
+ }
+ }
+}
+
+/**************************************************************************** **
+ The main select loop.
+ **************************************************************************** */
+static void process(void)
+{
+
+ while( True ) {
+ time_t t = time(NULL);
+
+ /* check for internal messages */
+ message_dispatch();
+
+ if(listen_for_wins_packets())
+ return;
+
+ run_wins_packet_queue();
+
+ run_pull_replication(t);
+
+ run_push_replication(t);
+
+ /*
+ * Reload the services file if we got a sighup.
+ */
+
+ if(reload_after_sighup) {
+ reload_services( True );
+ reopen_logs();
+ reload_after_sighup = False;
+ }
+
+ /* free temp memory */
+ talloc_destroy_pool(mem_ctx);
+
+ /* free up temp memory */
+ lp_talloc_free();
+ }
+} /* process */
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ extern char *optarg;
+ /* shall I run as a daemon */
+ BOOL is_daemon = False;
+ BOOL interactive = False;
+ BOOL specified_logfile = False;
+ BOOL Fork = True;
+ BOOL log_stdout = False;
+ int opt;
+ pstring logfile;
+
+#ifdef HAVE_SET_AUTH_PARAMETERS
+ set_auth_parameters(argc,argv);
+#endif
+
+ /* this is for people who can't start the program correctly */
+ while (argc > 1 && (*argv[1] != '-')) {
+ argv++;
+ argc--;
+ }
+
+ while ( EOF != (opt = getopt(argc, argv, "FSO:l:s:d:Dp:h?Vaiof:")) )
+ switch (opt) {
+ case 'F':
+ Fork = False;
+ break;
+ case 'S':
+ log_stdout = True;
+ break;
+ case 'O':
+ lp_set_cmdline("socket options", optarg);
+ break;
+
+ case 's':
+ pstrcpy(dyn_CONFIGFILE,optarg);
+ break;
+
+ case 'l':
+ specified_logfile = True;
+ slprintf(logfile, sizeof(logfile)-1, "%s/log.wrepld", optarg);
+ lp_set_logfile(logfile);
+ break;
+
+ case 'i':
+ interactive = True;
+ Fork = False;
+ log_stdout = True;
+ break;
+
+ case 'D':
+ is_daemon = True;
+ break;
+
+ case 'd':
+ if (*optarg == 'A')
+ DEBUGLEVEL = 10000;
+ else
+ DEBUGLEVEL = atoi(optarg);
+ break;
+
+ case 'p':
+ wins_port = atoi(optarg);
+ break;
+
+ case 'h':
+ case '?':
+ usage(argv[0]);
+ exit(0);
+ break;
+
+ case 'V':
+ d_printf("Version %s\n",VERSION);
+ exit(0);
+ break;
+ default:
+ DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n"));
+ usage(argv[0]);
+ exit(1);
+ }
+ if (log_stdout && Fork) {
+ d_printf("Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n");
+ usage(argv[0]);
+ exit(1);
+ }
+
+#ifdef HAVE_SETLUID
+ /* needed for SecureWare on SCO */
+ setluid(0);
+#endif
+
+ sec_init();
+
+ load_case_tables();
+
+ if(!specified_logfile) {
+ slprintf(logfile, sizeof(logfile)-1, "%s/log.wrepld",
+ dyn_LOGFILEBASE);
+ lp_set_logfile(logfile);
+ }
+
+ set_remote_machine_name("wrepld");
+
+ 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, False);
+
+ /* make absolutely sure we run as root - to handle cases where people
+ are crazy enough to have it setuid */
+
+ gain_root_privilege();
+ gain_root_group_privilege();
+
+ fault_setup((void (*)(void *))exit_server);
+ CatchSignal(SIGTERM , SIGNAL_CAST dflt_sig);
+
+ /* 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);
+
+ /* we want total control over the permissions on created files,
+ so set our umask to 0 */
+ umask(0);
+
+ reopen_logs();
+
+ DEBUG(1,( "wrepld version %s started.\n", VERSION));
+ DEBUGADD(1,( "Copyright Andrew Tridgell and the Samba Team 1992-2002\n"));
+
+ DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
+ (int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid()));
+
+ 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);
+ }
+
+ /*
+ * Do this before reload_services.
+ */
+
+ if (!reload_services(False))
+ return(-1);
+
+ if (!init_names())
+ return -1;
+
+#ifdef WITH_PROFILE
+ if (!profile_setup(False)) {
+ DEBUG(0,("ERROR: failed to setup profiling\n"));
+ return -1;
+ }
+#endif
+
+ CatchSignal(SIGHUP,SIGNAL_CAST sig_hup);
+
+ DEBUG(3,( "loaded services\n"));
+
+ if (!is_daemon && !is_a_socket(0)) {
+ DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+ is_daemon = True;
+ }
+
+ if (is_daemon && !interactive) {
+ DEBUG( 3, ( "Becoming a daemon.\n" ) );
+ become_daemon(Fork);
+ }
+
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (interactive)
+ setpgid( (pid_t)0, (pid_t)0);
+#endif
+
+ if (!directory_exist(lp_lockdir(), NULL)) {
+ mkdir(lp_lockdir(), 0755);
+ }
+
+ if (is_daemon) {
+ pidfile_create("wrepld");
+ }
+
+ if (!message_init()) {
+ exit(1);
+ }
+
+ /* Initialise the memory context */
+ mem_ctx=talloc_init("wins repl talloc ctx");
+
+ /* initialise the global partners table */
+ partner_count=init_wins_partner_table();
+
+ /* We can only take signals in the select. */
+ BlockSignals( True, SIGTERM );
+
+ process();
+
+ exit_server("normal exit");
+ return(0);
+}