/*
 * The Spread Toolkit.
 *     
 * The contents of this file are subject to the Spread Open-Source
 * License, Version 1.0 (the ``License''); you may not use
 * this file except in compliance with the License.  You may obtain a
 * copy of the License at:
 *
 * http://www.spread.org/license/
 *
 * or in the file ``license.txt'' found in this distribution.
 *
 * Software distributed under the License is distributed on an AS IS basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 * for the specific language governing rights and limitations under the 
 * License.
 *
 * The Creators of Spread are:
 *  Yair Amir, Michal Miskin-Amir, Jonathan Stanton.
 *
 *  Copyright (C) 1993-2004 Spread Concepts LLC <spread@spreadconcepts.com>
 *
 *  All Rights Reserved.
 *
 * Major Contributor(s):
 * ---------------
 *    Cristina Nita-Rotaru crisn@cs.purdue.edu - group communication security.
 *    Theo Schlossnagle    jesus@omniti.com - Perl, skiplists, autoconf.
 *    Dan Schoenblum       dansch@cnds.jhu.edu - Java interface.
 *    John Schultz         jschultz@cnds.jhu.edu - contribution to process group membership.
 *
 */


/*
// example for a file:
// 4
// 3 132.27.1.0 [4803]
//	harpo	[132.28.33.22]
//	hazard
//	hal
// 4 132.28.3.0 3377
//	bih
//	binoc
// 	bbl
//	bbc
// 2 125.32.0.0 3355
// 	rb
//	rc
// 2 132.27.1.0 
//      harry
//	harmony
*/

#include "arch.h"

#ifndef	ARCH_PC_WIN95

#include <netdb.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#else 	/* ARCH_PC_WIN95 */

#include <winsock.h>

#endif	/* ARCH_PC_WIN95 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#include <assert.h>

#include "configuration.h"

#define ext_conf_body
#include "conf_body.h"
#undef  ext_conf_body

#include "alarm.h"
#include "memory.h"
#include "spread_params.h"

static	proc		My;

/* True means allow dangerous monitor commands like partition and flow control
 * to be handled. 
 * False means to ignore requests for those actions. THIS IS THE SAFE SETTING
 */
static  bool    EnableDangerousMonitor = FALSE;

static  port_reuse SocketPortReuse = port_reuse_auto;

static  char    *RuntimeDir = NULL;

static	char	*User = NULL;

static	char	*Group = NULL;

static  int     Link_Protocol;

int		Conf_init( char *file_name, char *my_name )
{
        struct hostent  *host_ptr;
	char	machine_name[256];
	char	ip[16];
	int	i,j;
        unsigned int name_len;
        char    configfile_location[MAXPATHLEN];
#if 0
        int     s;
	char	line[132];
	char	buf[132];
	int	full;
	char	optional_id[20];
	int	iret;
	char	*ret;
	int	p;
	int32	i1,i2,i3,i4;
#endif
	Num_procs = 0;
	/* init Config, Config_procs from file
	   init My from host
	 */
        configfile_location[0] = '\0';
        strcat(configfile_location, SPREAD_ETCDIR);
        strcat(configfile_location, "/spread.conf");

	if (NULL != (yyin = fopen(file_name,"r")) )
                Alarm( PRINT, "Conf_init: using file: %s\n", file_name);
	if (yyin == NULL)
		if (NULL != (yyin = fopen(configfile_location, "r")) )
                        Alarm( PRINT, "Conf_init: using file: %s\n", configfile_location);
	if (yyin == NULL)
		Alarm( EXIT, "Conf_init: error opening config file %s\n",
			file_name);

	yyparse();
#if 0
	do{
		ret = fgets(line,132,fp);
		if (ret == NULL) 
			Alarm( EXIT, "Conf_init: no number of segments \n");
		full = sscanf( line, "%s", buf );
	}while( line[0] == '#' || full <= 0 ); 

	sscanf(line,"%d",&Config.num_segments);
	Alarm( CONF, "Conf_init: %d segments\n", Config.num_segments );
	for ( s=0; s < Config.num_segments; s++ )
	{
		do{
			ret = fgets(line,132,fp);
			if (ret == NULL) 
				Alarm( EXIT, "Conf_init: no segment data line \n");
			full = sscanf( line, "%s", buf );
		}while( line[0] == '#' || full <= 0 ); 

		for(i=0; i< 3; i++)
		{
			ret = strchr(line, '.' );
			if ( ret == NULL)
				Alarm( EXIT, "Conf_init: error in bcast addr\n");
			*ret = ' ';
		}
		iret = sscanf(line,"%d%d%d%d%d%hd",
			    	&Config.segments[s].num_procs,
				&i1,&i2,&i3,&i4,
			    	&Config.segments[s].port);
		if( iret == 5 ) Config.segments[s].port = 4803;
		else if( iret < 6 ) 
			Alarm( EXIT, "Conf_init: not a valid segment line: %s\n",
					line);

		Alarm( CONF, "Conf_init: segment %d: with %d procs, (%d.%d.%d.%d, %hd)\n",
			    s, Config.segments[s].num_procs,
			    i1,i2,i3,i4,
			    Config.segments[s].port );

		Config.segments[s].bcast_address = 
			( (i1 << 24 ) | (i2 << 16) | (i3 << 8) | i4 );

		for ( p=0; p < Config.segments[s].num_procs; p++ )
		{
		    do{
			ret = fgets(line,132,fp);
			if (ret == NULL) 
				Alarm( EXIT, "Conf_init: no proc line\n");
			full = sscanf( line, "%s", buf );
		    }while( line[0] == '#' || full <= 0 ); 

		    /* %19 is MAX_PROC_NAME-1 for the null */

		    iret = sscanf(line,"%19s%s",Config_procs[Num_procs].name,
					optional_id);
		    if( iret == 1 )
		    {
			host_ptr = gethostbyname(Config_procs[Num_procs].name);

			if ( host_ptr == 0)
				Alarm( EXIT, "Conf_init: no such host %s\n",
					Config_procs[Num_procs].name);

        		memcpy(&Config_procs[Num_procs].id, host_ptr->h_addr_list[0], 
			       sizeof(int32) );
			Config_procs[Num_procs].id = 
				htonl( Config_procs[Num_procs].id );
			i1= ( Config_procs[Num_procs].id & 0xff000000 ) >> 24;
			i2= ( Config_procs[Num_procs].id & 0x00ff0000 ) >> 16;
			i3= ( Config_procs[Num_procs].id & 0x0000ff00 ) >>  8;
			i4= Config_procs[Num_procs].id & 0x000000ff;
		    }else if( iret == 2 ){
			for(i=0; i< 3; i++)
			{
			    ret = strchr(optional_id, '.' );
			    if ( ret == NULL)
			        Alarm( EXIT, 
	"Conf_init: error in id or not a valid host line: %s\n",line);
			    *ret = ' ';
			}
			sscanf(optional_id,"%d%d%d%d",&i1,&i2,&i3,&i4);
			Config_procs[Num_procs].id = 
				( (i1 << 24 ) | (i2 << 16) | (i3 << 8) | i4 );
		    }else Alarm( EXIT, "Conf_init: not a valid host line: %s\n",
					line);

 		    Config_procs[Num_procs].port = Config.segments[s].port;
 		    Config_procs[Num_procs].seg_index = s;
 		    Config_procs[Num_procs].index_in_seg = p;
		    Config.segments[s].proc_ids[p] = 
				Config_procs[Num_procs].id;

		    Alarm( CONF, "Conf_init: \t\t%-20s\t%d.%d.%d.%d\n",
				Config_procs[Num_procs].name, i1,i2,i3,i4 );

		    Num_procs++;
		}
	}
#endif

        fclose(yyin);

        /* Test for localhost segemnt defined with other non-localhost segments.
         * That is an invalid configuration 
         */
        if ( Config.num_segments > 1 ) {
            int found_localhost = 0;
            int found_nonlocal = 0;
            for ( i=0; i < Config.num_segments; i++) {
                if ( ((Config.segments[i].bcast_address & 0xff000000) >> 24) == 127 ) {
                    found_localhost = 1;
                } else {
                    found_nonlocal = 1;
                }
            }
            if (found_nonlocal && found_localhost) {
                /* Both localhost and non-localhost segments exist. This is a non-functional config.*/
                Alarmp( SPLOG_PRINT, PRINT, "Conf_init: Invalid configuration:\n");
                Conf_print( &Config );
                Alarmp( SPLOG_PRINT, PRINT, "\n");
                Alarmp( SPLOG_FATAL, CONF, "Conf_init: Localhost segments can not be used along with regular network address segments.\nMost likely you need to remove or comment out the \nSpread_Segment 127.0.0.255 {...}\n section of your configuration file.\n");
            }
        }

	if( my_name == NULL ){
		gethostname(machine_name,sizeof(machine_name)); 
		host_ptr = gethostbyname(machine_name);
		if( host_ptr == 0 )
			Alarm( EXIT, "Conf_init: could not get my ip address (my name is %s)\n",
				machine_name );
                if (host_ptr->h_addrtype != AF_INET)
                        Alarm(EXIT, "Conf_init: Sorry, cannot handle addr types other than IPv4\n");
                if (host_ptr->h_length != 4)
                        Alarm(EXIT, "Conf_init: Bad IPv4 address length\n");
	
		i = -1;	/* in case host_ptr->h_length == 0 */
                for (j = 0; host_ptr->h_addr_list[j] != NULL; j++) {
                        memcpy(&My.id, host_ptr->h_addr_list[j], sizeof(struct in_addr));
			My.id = ntohl( My.id );
			i = Conf_proc_by_id( My.id, &My );
			if( i >= 0 ) break;
                }
		if( i < 0 ) Alarm( EXIT,
			"Conf_init: My proc id (%d.%d.%d.%d) is not in configuration\n", IP1(My.id),IP2(My.id),IP3(My.id),IP4(My.id) );

	}else if( ! strcmp( my_name, "Monitor" ) ){
		gethostname(machine_name,sizeof(machine_name)); 
		host_ptr = gethostbyname(machine_name);

		if( host_ptr == 0 )
			Alarm( EXIT, "Conf_init: no such monitor host %s\n",
				machine_name );

        	memcpy(&My.id, host_ptr->h_addr_list[0], 
			sizeof(int32) );
		My.id = ntohl( My.id );

		name_len = strlen( machine_name );
		if( name_len > sizeof(My.name) ) name_len = sizeof(My.name);
		memcpy(My.name, machine_name, name_len );
		Alarm( CONF, "Conf_init: My name: %s, id: %d\n",
			My.name, My.id );
		return( 1 );
	}else{
		name_len = strlen( my_name );
		if( name_len > sizeof(My.name) ) name_len = sizeof(My.name);
		memcpy(My.name, my_name, name_len );
		i = Conf_proc_by_name( My.name, &My );
		if( i < 0  ) Alarm( EXIT,
				"Conf_init: My proc %s is not in configuration \n",
				My.name);

	}

	Conf_id_to_str( My.id, ip );
	Alarm( CONF, "Conf_init: My name: %s, id: %s, port: %hd\n",
		My.name, ip, My.port );

	return( 0 );
}

configuration	Conf()
{
	return Config;
}

proc	Conf_my()
{
	return	My;
}

void    Conf_set_link_protocol(int protocol)
{
        if (protocol < 0 || protocol >= MAX_PROTOCOLS) {
                Alarm(PRINT, "Conf_set_link_protocol: Illegal protocol type %d\n", protocol);
                return;
        }
        Link_Protocol = protocol;
}

int     Conf_get_link_protocol(void)
{
        return(Link_Protocol);
}


int	Conf_proc_by_id( int32u id, proc *p )
{
	int	i,j;

	for ( i=0; i < Num_procs; i++ )
	{
                for ( j=0; j < Config_procs[i].num_if; j++)
                {
                        if ( Config_procs[i].ifc[j].ip == id )
                        {
                                *p =  Config_procs[i] ;
                                return( i );
                        }
                }
	}
	return( -1 );
}

int 	Conf_proc_by_name( char *name, proc *p )
{
	int	i;

	for ( i=0; i < Num_procs; i++ )
	{
		if ( strcmp( Config_procs[i].name, name ) == 0 )
		{
			*p = Config_procs[i];
			return( i );
		}
	}
	return( -1 );
}

int	Conf_id_in_seg( segment *seg, int32u id )
{
	int 	i,j;

	for ( j=0; j < seg->num_procs; j++ )
	{
                for ( i=0; i < seg->procs[j]->num_if; i++)
                {
                        if ( seg->procs[j]->ifc[i].ip == id )
                                return( j );
                }
	}
	return( -1 );
}
static  int     Conf_proc_ref_by_id( int32u id, proc **p )
{
	int	i,j;

	for ( i=0; i < Num_procs; i++ )
	{
                for ( j=0; j < Config_procs[i].num_if; j++)
                {
                        if ( Config_procs[i].ifc[j].ip == id )
                        {
                                *p = &Config_procs[i];
                                return( i );
                        }
                }
	}
	return( -1 );
}

int     Conf_append_id_to_seg( segment *seg, int32u id)
{
        proc *p;
        if (Conf_proc_ref_by_id(id, &p) != -1)
        {
                seg->procs[seg->num_procs] = p;
                seg->num_procs++;
                return( 0 );
        } 
        return( -1 );
}
int	Conf_id_in_conf( configuration *config, int32u id )
{
	int 	i;

	for ( i=0; i < config->num_segments; i++ )
                if ( Conf_id_in_seg(&(config->segments[i]), id) >= 0 )
                        return( i );
	return( -1 );
}

int	Conf_num_procs( configuration *config )
{
	int 	i,ret;

	ret = 0;
	for ( i=0; i < config->num_segments; i++ )
		ret += config->segments[i].num_procs;

	return( ret );
}

int32u	Conf_leader( configuration *config )
{
        int i;

        for( i=0; i < config->num_segments; i++ )
        {
                if( config->segments[i].num_procs > 0 )
                        return( config->segments[i].procs[0]->id );
        }
        Alarm( EXIT, "Conf_leader: Empty configuration %c",Conf_print(config));
	return( -1 );
}

int32u	Conf_last( configuration *config )
{
        int i,j;

        for( i = config->num_segments-1; i >= 0; i-- )
        {
                if( config->segments[i].num_procs > 0 )
		{
			j = config->segments[i].num_procs-1;
                        return( config->segments[i].procs[j]->id );
		}
        }
        Alarm( EXIT, "Conf_last: Empty configuration %c",Conf_print(config));
	return( -1 );
}

int32u	Conf_seg_leader( configuration *config, int16 seg_index )
{
	if( config->segments[seg_index].num_procs > 0 )
	{
		return( config->segments[seg_index].procs[0]->id );
	}
        Alarm( EXIT, "Conf_seg_leader: Empty segment %d in Conf %c",
		seg_index, Conf_print(config));
	return( -1 );
}

int32u	Conf_seg_last( configuration *config, int16 seg_index )
{
	int	j;

	if( config->segments[seg_index].num_procs > 0 )
	{
		j = config->segments[seg_index].num_procs-1;
		return( config->segments[seg_index].procs[j]->id );
	}
        Alarm( EXIT, "Conf_seg_leader: Empty segment %d in Conf %c",
		seg_index, Conf_print(config));
        return(-1);
}

int	Conf_num_procs_in_seg( configuration *config, int16 seg_index )
{
	return( config->segments[seg_index].num_procs );
}

void	Conf_id_to_str( int32u id, char *str )
{
	int32u	i1,i2,i3,i4;

	i1 = (id & 0xff000000) >> 24;
	i2 = (id & 0x00ff0000) >> 16;
	i3 = (id & 0x0000ff00) >> 8;
	i4 = (id & 0x000000ff);
	sprintf( str, "%u.%u.%u.%u", i1, i2, i3, i4 );
}

char	Conf_print(configuration *config)
{
	int 	s,p,ret;
	char	ip[16];
	proc	pr;

	Alarm( PRINT, "--------------------\n" );
	Alarm( PRINT, "Configuration at %s is:\n", My.name );
	Alarm( PRINT, "Num Segments %d\n",config->num_segments );
	for ( s=0; s < config->num_segments; s++ )
	{
		Conf_id_to_str( config->segments[s].bcast_address, ip );
		Alarm( PRINT, "\t%d\t%-16s  %hd\n",
			config->segments[s].num_procs, ip,
			config->segments[s].port );
		for( p=0; p < config->segments[s].num_procs; p++)
		{
			ret = Conf_proc_by_id( config->segments[s].procs[p]->id,
					&pr );	
			Conf_id_to_str( pr.id, ip );
			Alarm( PRINT, "\t\t%-20s\t%-16s\n", pr.name, ip );
		}
	}
	Alarm( PRINT, "====================" );
	return( '\n' );
}

bool    Conf_get_dangerous_monitor_state(void)
{
        return(EnableDangerousMonitor);
}

void    Conf_set_dangerous_monitor_state(bool new_state)
{
        if (new_state == FALSE) {
                Alarm(PRINT, "disabling Dangerous Monitor Commands!\n");
        } else if (new_state == TRUE) {
                Alarm(PRINT, "ENABLING Dangerous Monitor Commands! Make sure Spread network is secured\n");
        } else {
                /* invalid setting */
                return;
        }
        EnableDangerousMonitor = new_state;
}

port_reuse Conf_get_port_reuse_type(void)
{
        return(SocketPortReuse);
}

void    Conf_set_port_reuse_type(port_reuse state)
{
        switch (state)
        {
        case port_reuse_auto:
                Alarm(PRINT, "Setting SO_REUSEADDR to auto\n");
                break;
        case port_reuse_on:
                Alarm(PRINT, "Setting SO_REUSEADDR to always on -- make sure Spread daemon host is secured!\n");
                break;
        case port_reuse_off:
                Alarm(PRINT, "Setting SO_REUSEADDR to always off\n");
                break;
        default:
                /* Inavlid type -- ignored */
                return;
        }
        SocketPortReuse = state;
}

static void set_param_if_valid(char **param, char *value, char *description, unsigned int max_value_len)
{
        if (value != NULL && *value != '\0')
        {
                unsigned int len = strlen(value);
                char *old_value = *param;
                char *buf;
                if (len > max_value_len)
                {
                    Alarm(EXIT, "set_param_if_valid: value string too long\n");
                }
                buf = Mem_alloc(len + 1);
                if (buf == NULL)
                {
                        Alarm(EXIT, "set_param_if_valid: Out of memory\n");
                }
                strncpy(buf, value, len);
                buf[len] = '\0';

                *param = buf;
                if (old_value != NULL)
                {
                    dispose(old_value);
                }
                Alarm(PRINT, "Set %s to '%s'\n", description, value);
        }
        else
        {
                Alarm(DEBUG, "Ignored invalid %s\n", description);
        }
}

char    *Conf_get_runtime_dir(void)
{
        return (RuntimeDir != NULL ? RuntimeDir : SP_RUNTIME_DIR);
}

void    Conf_set_runtime_dir(char *dir)
{
        set_param_if_valid(&RuntimeDir, dir, "runtime directory", MAXPATHLEN);
}

char    *Conf_get_user(void)
{
        return (User != NULL ? User : SP_USER);
}

void    Conf_set_user(char *user)
{
        set_param_if_valid(&User, user, "user name", 32);
}

char    *Conf_get_group(void)
{
        return (Group != NULL ? Group : SP_GROUP);
}

void    Conf_set_group(char *group)
{
        set_param_if_valid(&Group, group, "group name", 32);
}


syntax highlighted by Code2HTML, v. 0.9.1