/*******************************************************************************
* Copyright (c) 2007, Perforce Software, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE
* SOFTWARE, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*******************************************************************************/
#define NEED_GETUID
#include "stdhdrs.h"
#include "strbuf.h"
#include "vararray.h"
#include "strdict.h"
#include "strtable.h"
#include "error.h"
#include "errorlog.h"
#include "filesys.h"
#include "p4dctlerr.h"
#include "p4dctldebug.h"
#include "parsesupp.h"
#include "cmdline.h"
#include "varlist.h"
#include "server.h"
#include "config.h"
//
// Our global config pointer
//
Config * parsed_config = 0;
//-------------------------------------------------------------------------------
// Local types
//-------------------------------------------------------------------------------
struct ServerSpec
{
VarList * settings;
VarList * env;
};
//------------------------------------------------------------------------------
// Config class implementation
//------------------------------------------------------------------------------
int
Config::DropPrivs( Error *e )
{
uid_t uid = getuid();
if( !uid ) return 1;
if( seteuid( uid ) < 0 )
e->Sys( "seteuid", "" );
return !e->Test();
}
int
Config::RestorePrivs( Error *e )
{
if( seteuid( 0 ) < 0 )
e->Sys( "seteuid", "root" );
return !e->Test();
}
Config::Config()
{
env = new VarList;
settings = new VarList;
}
Config::~Config()
{
for( int i = 0; i < servers.Count(); i++ )
delete (Server *)servers.Get( i );
delete env;
delete settings;
}
void
Config::Parse( const char *cfg_file, Error *e )
{
yyin = fopen( cfg_file, "r" );
if( !yyin )
{
e->Sys( "fopen", cfg_file );
return;
}
fname = cfg_file;
//
// Ensure the results of the parse go into this object.
//
parsed_config = this;
if( yyparse() )
{
e->Set( CtlErr::ConfigParseError ) << cfg_file << lineno;
return;
}
// Now merge global settings into each server's config.
this->MergeGlobalSettings();
if( DEBUG_PARSE )
this->Dump();
parsed_config = 0;
if( !ServerCount() )
{
e->Set( CtlErr::NoServersConfigured );
return;
}
}
//
// Get servers by number
Server *
Config::GetServer( int i ) const
{
if( i < servers.Count() )
return (Server *)servers.Get( i );
return 0;
}
//
// Get server by name
//
Server *
Config::GetServer( StrPtr &name )
{
for( int i = 0; i < servers.Count(); i++ )
{
Server *s = GetServer( i );
if( name == s->Name() )
return s;
}
return 0;
}
Server *
Config::GetServer( const char *name )
{
StrBuf n( name );
return GetServer( n );
}
//
// Merge the global settings into each server's
// own settings dictionary
void
Config::MergeGlobalSettings()
{
for( int i = 0; i < servers.Count(); i++ )
{
Server *s = GetServer( i );
s->MergeSettings( settings );
}
}
//
// Add a server to the configuration
//
Server *
Config::AddServer( const char *name, int type, VarList *settings,
VarList *_env )
{
VarList *env = !_env ? new VarList : _env;
Server *s = 0;
switch( type )
{
case SERVER_P4D:
s = new P4D( name, settings, env, this );
servers.Put( s );
break;
case SERVER_P4P:
s = new P4P( name, settings, env, this );
servers.Put( s );
break;
case SERVER_P4WEB:
s = new P4Web( name, settings, env, this );
servers.Put( s );
break;
case SERVER_P4FTP:
s = new P4Ftp( name, settings, env, this );
servers.Put( s );
break;
case SERVER_P4BROKER:
s = new P4Broker( name, settings, env, this );
servers.Put( s );
break;
case SERVER_P4DTG:
s = new P4Dtg( name, settings, env, this );
servers.Put( s );
break;
case SERVER_OTHER:
s = new OtherServer( name, settings, env, this );
servers.Put( s );
break;
default:
fprintf( stderr, "Warning: No support for servers of type %d\n", type);
}
return s;
}
int
Config::ServerType( const char *t )
{
if( !strcmp( "p4d", t ) ) return SERVER_P4D;
if( !strcmp( "p4p", t ) ) return SERVER_P4P;
if( !strcmp( "p4web", t ) ) return SERVER_P4WEB;
if( !strcmp( "p4ftp", t ) ) return SERVER_P4FTP;
if( !strcmp( "p4broker", t ) ) return SERVER_P4BROKER;
if( !strcmp( "p4dtg", t ) ) return SERVER_P4DTG;
if( !strcmp( "other", t ) ) return SERVER_OTHER;
return SERVER_NONE;
}
void
Config::SetGlobalEnvironment( VarList *vars )
{
env->Import( vars );
delete vars;
}
void
Config::SetGlobalSettings( VarList *vars )
{
settings->Import( vars );
delete vars;
}
void
Config::Dump()
{
Server *s;
printf( "Global Environment\n" );
printf( "------------------\n\n" );
env->Dump();
printf( "\nGlobal Settings\n" );
printf( "---------------\n\n" );
settings->Dump();
printf( "\nServer Definitions\n" );
printf( "------------------\n\n" );
for( int i = 0; i < servers.Count(); i++ )
{
s = (Server *)servers.Get( i );
s->Dump();
printf( "\n" );
}
}
//------------------------------------------------------------------------------
// C/C++ shims for the parser
//------------------------------------------------------------------------------
//
// Returns a VarList
//
void *
MakeVar( char *var, char *val )
{
VarList *vl = new VarList;
vl->SetVar( var, val );
free( var );
free( val );
return (void *) vl;
}
//
// Add a variable to an existing list. Both inputs here are VarLists in
// fact - merge the first into the second, delete the second and return
// the first.
//
void *
AddVar( void *listA, void *listB )
{
VarList *a = (VarList *)listA;
VarList *b = (VarList *)listB;
a->Import( b );
delete b;
return (void *)a;
}
//
// Make a new Server object
//
void *
MakeServer( char *name, int type, void *server_spec )
{
ServerSpec *spec = (ServerSpec *)server_spec;
Server *s = 0;
if( ! (type & SERVER_ALL) )
{
StrBuf t;
t << "Invalid server type: " << type;
yyerror( t.Text() ); // This sucker exits...
return 0;
}
s = parsed_config->AddServer( name, type, spec->settings, spec->env );
free( name );
delete spec;
return (void *) s;
}
//
// Add an environment list to the global configuration
//
void
AddEnviron( void *varList )
{
parsed_config->SetGlobalEnvironment( (VarList *)varList );
}
//
// Add a settings list to the global configuration
//
void
AddSettings( void *varList )
{
parsed_config->SetGlobalSettings( (VarList *)varList );
}
void
AddServerSettings( void *server_spec, void *settings)
{
ServerSpec *spec = (ServerSpec *)server_spec;
VarList *stgs = (VarList *)settings;
spec->settings->Import( stgs );
delete stgs;
}
void *
MakeServerSpec( void *settings, void *env )
{
ServerSpec *spec = new ServerSpec;
spec->settings = (VarList *)settings;
spec->env = (VarList*)env;
return spec;
}