diff options
author | Andrew Tridgell <tridge@samba.org> | 2003-08-13 01:53:07 +0000 |
---|---|---|
committer | Andrew Tridgell <tridge@samba.org> | 2003-08-13 01:53:07 +0000 |
commit | ef2e26c91b80556af033d3335e55f5dfa6fff31d (patch) | |
tree | faa21bfd7e7b5247250b47c7891dc1a5ebee6be9 /source4/wrepld/server.c | |
download | samba-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.c | 737 |
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); +} |