1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-25 23:21:54 +03:00
samba-mirror/source/wrepld/server.c
0001-01-01 00:00:00 +00:00

747 lines
18 KiB
C

/*
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 fstring global_myworkgroup;
extern pstring global_myname;
extern pstring user_socket_options;
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(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);
}
/****************************************************************************
initialise connect, service and file structs
****************************************************************************/
static void init_structs(void )
{
/*
* Set the machine NETBIOS name if not already
* set from the config file.
*/
if (!*global_myname) {
char *p;
fstrcpy( global_myname, myhostname() );
p = strchr_m( global_myname, '.' );
if (p)
*p = 0;
}
strupper( global_myname );
}
/****************************************************************************
usage on the program
****************************************************************************/
static void usage(char *pname)
{
d_printf("Usage: %s [-DaioPh?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-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,user_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,user_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, user_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;
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, "O:l:s:d:Dp:h?Vaiof:")) )
switch (opt) {
case 'O':
pstrcpy(user_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;
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);
}
#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],interactive);
/* 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);
init_structs();
#ifdef WITH_PROFILE
if (!profile_setup(False)) {
DEBUG(0,("ERROR: failed to setup profiling\n"));
return -1;
}
#endif
fstrcpy(global_myworkgroup, lp_workgroup());
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();
}
#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_named("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);
}