2019-02-25 14:48:22 +01:00
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2015-07-21 16:10:13 +02:00
2015-08-28 17:58:29 +02:00
# include "remote/configpackageutility.hpp"
2019-04-16 16:37:38 +02:00
# include "remote/apilistener.hpp"
2015-07-21 16:10:13 +02:00
# include "base/application.hpp"
# include "base/exception.hpp"
# include "base/utility.hpp"
# include <boost/algorithm/string.hpp>
# include <boost/regex.hpp>
# include <algorithm>
# include <fstream>
using namespace icinga ;
2018-01-04 04:25:35 +01:00
String ConfigPackageUtility : : GetPackageDir ( )
2015-07-21 16:10:13 +02:00
{
2018-08-09 15:37:23 +02:00
return Configuration : : DataDir + " /api/packages " ;
2015-07-21 16:10:13 +02:00
}
2015-08-28 17:58:29 +02:00
void ConfigPackageUtility : : CreatePackage ( const String & name )
2015-07-21 16:10:13 +02:00
{
2015-08-28 17:58:29 +02:00
String path = GetPackageDir ( ) + " / " + name ;
2015-07-21 16:10:13 +02:00
if ( Utility : : PathExists ( path ) )
2015-08-28 17:58:29 +02:00
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Package already exists. " ) ) ;
2015-07-21 16:10:13 +02:00
Utility : : MkDirP ( path , 0700 ) ;
2015-08-28 17:58:29 +02:00
WritePackageConfig ( name ) ;
2015-07-21 16:10:13 +02:00
}
2015-08-28 17:58:29 +02:00
void ConfigPackageUtility : : DeletePackage ( const String & name )
2015-07-21 16:10:13 +02:00
{
2015-08-28 17:58:29 +02:00
String path = GetPackageDir ( ) + " / " + name ;
2015-07-21 16:10:13 +02:00
if ( ! Utility : : PathExists ( path ) )
2015-08-28 17:58:29 +02:00
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Package does not exist. " ) ) ;
2015-07-21 16:10:13 +02:00
2019-04-16 16:37:38 +02:00
ApiListener : : Ptr listener = ApiListener : : GetInstance ( ) ;
/* config packages without API make no sense. */
if ( ! listener )
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " No ApiListener instance configured. " ) ) ;
listener - > RemoveActivePackageStage ( name ) ;
2015-07-21 16:10:13 +02:00
Utility : : RemoveDirRecursive ( path ) ;
Application : : RequestRestart ( ) ;
}
2018-01-04 04:25:35 +01:00
std : : vector < String > ConfigPackageUtility : : GetPackages ( )
2015-07-21 16:10:13 +02:00
{
2018-03-07 13:27:31 +01:00
String packageDir = GetPackageDir ( ) ;
2015-08-28 17:58:29 +02:00
std : : vector < String > packages ;
2018-03-07 13:27:31 +01:00
/* Package directory does not exist, no packages have been created thus far. */
if ( ! Utility : : PathExists ( packageDir ) )
return packages ;
Utility : : Glob ( packageDir + " /* " , std : : bind ( & ConfigPackageUtility : : CollectDirNames ,
2017-12-19 15:50:05 +01:00
_1 , std : : ref ( packages ) ) , GlobDirectory ) ;
2018-03-07 13:27:31 +01:00
2015-08-28 17:58:29 +02:00
return packages ;
2015-07-21 16:10:13 +02:00
}
2015-08-28 17:58:29 +02:00
void ConfigPackageUtility : : CollectDirNames ( const String & path , std : : vector < String > & dirs )
2015-07-21 16:10:13 +02:00
{
2017-11-30 08:19:58 +01:00
dirs . emplace_back ( Utility : : BaseName ( path ) ) ;
2015-07-21 16:10:13 +02:00
}
2015-08-28 17:58:29 +02:00
bool ConfigPackageUtility : : PackageExists ( const String & name )
2015-08-18 14:21:55 +02:00
{
2015-08-28 17:58:29 +02:00
return Utility : : PathExists ( GetPackageDir ( ) + " / " + name ) ;
2015-08-18 14:21:55 +02:00
}
2015-08-28 17:58:29 +02:00
String ConfigPackageUtility : : CreateStage ( const String & packageName , const Dictionary : : Ptr & files )
2015-07-21 16:10:13 +02:00
{
String stageName = Utility : : NewUniqueID ( ) ;
2015-08-28 17:58:29 +02:00
String path = GetPackageDir ( ) + " / " + packageName ;
2015-07-21 16:10:13 +02:00
if ( ! Utility : : PathExists ( path ) )
2015-08-28 17:58:29 +02:00
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Package does not exist. " ) ) ;
2015-07-21 16:10:13 +02:00
path + = " / " + stageName ;
Utility : : MkDirP ( path , 0700 ) ;
2015-08-11 12:59:26 +02:00
Utility : : MkDirP ( path + " /conf.d " , 0700 ) ;
Utility : : MkDirP ( path + " /zones.d " , 0700 ) ;
2015-08-28 17:58:29 +02:00
WriteStageConfig ( packageName , stageName ) ;
2015-07-21 16:10:13 +02:00
2015-07-24 16:05:13 +02:00
bool foundDotDot = false ;
2015-09-22 17:58:12 +02:00
2015-08-18 14:21:55 +02:00
if ( files ) {
ObjectLock olock ( files ) ;
2016-08-25 06:19:44 +02:00
for ( const Dictionary : : Pair & kv : files ) {
2015-08-18 14:21:55 +02:00
if ( ContainsDotDot ( kv . first ) ) {
foundDotDot = true ;
break ;
}
2015-09-22 17:58:12 +02:00
2015-08-18 14:21:55 +02:00
String filePath = path + " / " + kv . first ;
2015-09-22 17:58:12 +02:00
2015-08-28 17:58:29 +02:00
Log ( LogInformation , " ConfigPackageUtility " )
2017-12-19 15:50:05 +01:00
< < " Updating configuration file: " < < filePath ;
2015-09-22 17:58:12 +02:00
2016-02-09 15:53:40 +01:00
// Pass the directory and generate a dir tree, if it does not already exist
2015-08-18 14:21:55 +02:00
Utility : : MkDirP ( Utility : : DirName ( filePath ) , 0750 ) ;
std : : ofstream fp ( filePath . CStr ( ) , std : : ofstream : : out | std : : ostream : : binary | std : : ostream : : trunc ) ;
fp < < kv . second ;
fp . close ( ) ;
2015-07-24 16:05:13 +02:00
}
2015-07-21 16:10:13 +02:00
}
2015-07-24 16:05:13 +02:00
if ( foundDotDot ) {
Utility : : RemoveDirRecursive ( path ) ;
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Path must not contain '..'. " ) ) ;
}
2015-07-21 16:10:13 +02:00
return stageName ;
}
2015-08-28 17:58:29 +02:00
void ConfigPackageUtility : : WritePackageConfig ( const String & packageName )
2015-07-21 16:10:13 +02:00
{
2015-08-28 17:58:29 +02:00
String stageName = GetActiveStage ( packageName ) ;
2015-07-21 16:10:13 +02:00
2015-08-28 17:58:29 +02:00
String includePath = GetPackageDir ( ) + " / " + packageName + " /include.conf " ;
2015-07-21 16:10:13 +02:00
std : : ofstream fpInclude ( includePath . CStr ( ) , std : : ofstream : : out | std : : ostream : : binary | std : : ostream : : trunc ) ;
fpInclude < < " include \" */include.conf \" \n " ;
fpInclude . close ( ) ;
2015-08-28 17:58:29 +02:00
String activePath = GetPackageDir ( ) + " / " + packageName + " /active.conf " ;
2015-07-21 16:10:13 +02:00
std : : ofstream fpActive ( activePath . CStr ( ) , std : : ofstream : : out | std : : ostream : : binary | std : : ostream : : trunc ) ;
fpActive < < " if (!globals.contains( \" ActiveStages \" )) { \n "
2017-12-19 15:50:05 +01:00
< < " globals.ActiveStages = {} \n "
< < " } \n "
< < " \n "
< < " if (globals.contains( \" ActiveStageOverride \" )) { \n "
< < " var arr = ActiveStageOverride.split( \" : \" ) \n "
< < " if (arr[0] == \" " < < packageName < < " \" ) { \n "
< < " if (arr.len() < 2) { \n "
< < " log(LogCritical, \" Config \" , \" Invalid value for ActiveStageOverride \" ) \n "
< < " } else { \n "
< < " ActiveStages[ \" " < < packageName < < " \" ] = arr[1] \n "
< < " } \n "
< < " } \n "
< < " } \n "
< < " \n "
< < " if (!ActiveStages.contains( \" " < < packageName < < " \" )) { \n "
< < " ActiveStages[ \" " < < packageName < < " \" ] = \" " < < stageName < < " \" \n "
< < " } \n " ;
2015-07-21 16:10:13 +02:00
fpActive . close ( ) ;
}
2015-08-28 17:58:29 +02:00
void ConfigPackageUtility : : WriteStageConfig ( const String & packageName , const String & stageName )
2015-07-21 16:10:13 +02:00
{
2015-08-28 17:58:29 +02:00
String path = GetPackageDir ( ) + " / " + packageName + " / " + stageName + " /include.conf " ;
2015-07-21 16:10:13 +02:00
std : : ofstream fp ( path . CStr ( ) , std : : ofstream : : out | std : : ostream : : binary | std : : ostream : : trunc ) ;
fp < < " include \" ../active.conf \" \n "
2017-12-19 15:50:05 +01:00
< < " if (ActiveStages[ \" " < < packageName < < " \" ] == \" " < < stageName < < " \" ) { \n "
< < " include_recursive \" conf.d \" \n "
< < " include_zones \" " < < packageName < < " \" , \" zones.d \" \n "
< < " } \n " ;
2015-07-21 16:10:13 +02:00
fp . close ( ) ;
}
2015-08-28 17:58:29 +02:00
void ConfigPackageUtility : : ActivateStage ( const String & packageName , const String & stageName )
2015-07-21 16:10:13 +02:00
{
2019-04-16 16:37:38 +02:00
SetActiveStage ( packageName , stageName ) ;
2015-07-21 16:10:13 +02:00
2015-08-28 17:58:29 +02:00
WritePackageConfig ( packageName ) ;
2015-07-21 16:10:13 +02:00
}
2017-09-03 16:15:40 +02:00
void ConfigPackageUtility : : TryActivateStageCallback ( const ProcessResult & pr , const String & packageName , const String & stageName , bool reload )
2015-07-21 16:10:13 +02:00
{
2015-08-28 17:58:29 +02:00
String logFile = GetPackageDir ( ) + " / " + packageName + " / " + stageName + " /startup.log " ;
2015-07-21 16:10:13 +02:00
std : : ofstream fpLog ( logFile . CStr ( ) , std : : ofstream : : out | std : : ostream : : binary | std : : ostream : : trunc ) ;
fpLog < < pr . Output ;
fpLog . close ( ) ;
2015-08-28 17:58:29 +02:00
String statusFile = GetPackageDir ( ) + " / " + packageName + " / " + stageName + " /status " ;
2015-07-21 16:10:13 +02:00
std : : ofstream fpStatus ( statusFile . CStr ( ) , std : : ofstream : : out | std : : ostream : : binary | std : : ostream : : trunc ) ;
fpStatus < < pr . ExitStatus ;
fpStatus . close ( ) ;
/* validation went fine, activate stage and reload */
if ( pr . ExitStatus = = 0 ) {
2017-09-20 16:40:02 +02:00
{
boost : : mutex : : scoped_lock lock ( GetStaticMutex ( ) ) ;
ActivateStage ( packageName , stageName ) ;
}
2017-09-03 16:15:40 +02:00
if ( reload )
Application : : RequestRestart ( ) ;
2015-07-21 16:10:13 +02:00
} else {
2015-08-28 17:58:29 +02:00
Log ( LogCritical , " ConfigPackageUtility " )
2017-12-19 15:50:05 +01:00
< < " Config validation failed for package ' "
< < packageName < < " ' and stage ' " < < stageName < < " '. " ;
2015-07-21 16:10:13 +02:00
}
}
2017-09-03 16:15:40 +02:00
void ConfigPackageUtility : : AsyncTryActivateStage ( const String & packageName , const String & stageName , bool reload )
2015-07-21 16:10:13 +02:00
{
2017-02-21 10:59:43 +01:00
VERIFY ( Application : : GetArgC ( ) > = 1 ) ;
2015-07-21 16:10:13 +02:00
// prepare arguments
2018-01-11 11:17:38 +01:00
Array : : Ptr args = new Array ( {
Application : : GetExePath ( Application : : GetArgV ( ) [ 0 ] ) ,
} ) ;
2015-07-21 16:10:13 +02:00
2018-08-22 14:47:57 +02:00
// copy all arguments of parent process
2018-09-27 20:30:19 +02:00
for ( int i = 1 ; i < Application : : GetArgC ( ) ; i + + ) {
String argV = Application : : GetArgV ( ) [ i ] ;
if ( argV = = " -d " | | argV = = " --daemonize " )
continue ;
args - > Add ( argV ) ;
2018-08-22 14:47:57 +02:00
}
2018-09-27 20:30:19 +02:00
2018-08-22 14:47:57 +02:00
// add arguments for validation
args - > Add ( " --validate " ) ;
args - > Add ( " --define " ) ;
args - > Add ( " ActiveStageOverride= " + packageName + " : " + stageName ) ;
2015-07-21 16:10:13 +02:00
Process : : Ptr process = new Process ( Process : : PrepareCommand ( args ) ) ;
2019-04-15 16:56:30 +02:00
process - > SetTimeout ( Application : : GetReloadTimeout ( ) ) ;
2017-11-21 11:52:55 +01:00
process - > Run ( std : : bind ( & TryActivateStageCallback , _1 , packageName , stageName , reload ) ) ;
2015-07-21 16:10:13 +02:00
}
2015-08-28 17:58:29 +02:00
void ConfigPackageUtility : : DeleteStage ( const String & packageName , const String & stageName )
2015-07-21 16:10:13 +02:00
{
2015-08-28 17:58:29 +02:00
String path = GetPackageDir ( ) + " / " + packageName + " / " + stageName ;
2015-07-21 16:10:13 +02:00
if ( ! Utility : : PathExists ( path ) )
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Stage does not exist. " ) ) ;
2015-08-28 17:58:29 +02:00
if ( GetActiveStage ( packageName ) = = stageName )
2015-07-21 16:10:13 +02:00
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Active stage cannot be deleted. " ) ) ;
Utility : : RemoveDirRecursive ( path ) ;
}
2015-08-28 17:58:29 +02:00
std : : vector < String > ConfigPackageUtility : : GetStages ( const String & packageName )
2015-07-21 16:10:13 +02:00
{
std : : vector < String > stages ;
2017-11-23 06:51:48 +01:00
Utility : : Glob ( GetPackageDir ( ) + " / " + packageName + " /* " , std : : bind ( & ConfigPackageUtility : : CollectDirNames , _1 , std : : ref ( stages ) ) , GlobDirectory ) ;
2015-07-21 16:10:13 +02:00
return stages ;
}
2019-04-16 16:37:38 +02:00
String ConfigPackageUtility : : GetActiveStageFromFile ( const String & packageName )
2015-07-21 16:10:13 +02:00
{
2019-04-16 16:37:38 +02:00
/* Lock the transaction, reading this only happens on startup or when something really is broken. */
boost : : mutex : : scoped_lock lock ( GetStaticMutex ( ) ) ;
2015-08-28 17:58:29 +02:00
String path = GetPackageDir ( ) + " / " + packageName + " /active-stage " ;
2015-07-21 16:10:13 +02:00
std : : ifstream fp ;
fp . open ( path . CStr ( ) ) ;
String stage ;
std : : getline ( fp , stage . GetData ( ) ) ;
fp . close ( ) ;
if ( fp . fail ( ) )
2019-04-16 16:37:38 +02:00
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Cannot detect active stage for package ' " + packageName + " '. Broken config package, check the troubleshooting documentation. " ) ) ;
2015-07-21 16:10:13 +02:00
2015-08-27 18:06:20 +02:00
return stage . Trim ( ) ;
2015-07-21 16:10:13 +02:00
}
2019-04-26 14:51:28 +02:00
void ConfigPackageUtility : : SetActiveStageToFile ( const String & packageName , const String & stageName )
{
boost : : mutex : : scoped_lock lock ( GetStaticMutex ( ) ) ;
String activeStagePath = GetPackageDir ( ) + " / " + packageName + " /active-stage " ;
std : : ofstream fpActiveStage ( activeStagePath . CStr ( ) , std : : ofstream : : out | std : : ostream : : binary | std : : ostream : : trunc ) ; //TODO: fstream exceptions
fpActiveStage < < stageName ;
fpActiveStage . close ( ) ;
}
2019-04-16 16:37:38 +02:00
String ConfigPackageUtility : : GetActiveStage ( const String & packageName )
{
ApiListener : : Ptr listener = ApiListener : : GetInstance ( ) ;
/* config packages without API make no sense. */
if ( ! listener )
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " No ApiListener instance configured. " ) ) ;
String activeStage ;
/* First use runtime state. */
try {
activeStage = listener - > GetActivePackageStage ( packageName ) ;
} catch ( const std : : exception & ex ) {
/* Fallback to reading the file, happens on restarts. */
activeStage = GetActiveStageFromFile ( packageName ) ;
/* When we've read something, correct memory. */
if ( ! activeStage . IsEmpty ( ) )
listener - > SetActivePackageStage ( packageName , activeStage ) ;
else
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Cannot detect active stage for package ' " + packageName + " '. Broken config package, check the troubleshooting documentation. " ) ) ;
}
return activeStage ;
}
void ConfigPackageUtility : : SetActiveStage ( const String & packageName , const String & stageName )
{
ApiListener : : Ptr listener = ApiListener : : GetInstance ( ) ;
/* config packages without API make no sense. */
if ( ! listener )
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " No ApiListener instance configured. " ) ) ;
listener - > SetActivePackageStage ( packageName , stageName ) ;
/* Also update the marker on disk for restarts. */
2019-04-26 14:51:28 +02:00
SetActiveStageToFile ( packageName , stageName ) ;
2019-04-16 16:37:38 +02:00
}
2015-07-21 16:10:13 +02:00
2015-08-28 17:58:29 +02:00
std : : vector < std : : pair < String , bool > > ConfigPackageUtility : : GetFiles ( const String & packageName , const String & stageName )
2015-07-21 16:10:13 +02:00
{
std : : vector < std : : pair < String , bool > > paths ;
2017-11-23 06:51:48 +01:00
Utility : : GlobRecursive ( GetPackageDir ( ) + " / " + packageName + " / " + stageName , " * " , std : : bind ( & ConfigPackageUtility : : CollectPaths , _1 , std : : ref ( paths ) ) , GlobDirectory | GlobFile ) ;
2015-07-21 16:10:13 +02:00
return paths ;
}
2015-08-28 17:58:29 +02:00
void ConfigPackageUtility : : CollectPaths ( const String & path , std : : vector < std : : pair < String , bool > > & paths )
2015-07-21 16:10:13 +02:00
{
# ifndef _WIN32
struct stat statbuf ;
int rc = lstat ( path . CStr ( ) , & statbuf ) ;
if ( rc < 0 )
BOOST_THROW_EXCEPTION ( posix_error ( )
2017-12-19 15:50:05 +01:00
< < boost : : errinfo_api_function ( " lstat " )
< < boost : : errinfo_errno ( errno )
< < boost : : errinfo_file_name ( path ) ) ;
2015-08-03 10:17:12 +02:00
2017-11-30 08:19:58 +01:00
paths . emplace_back ( path , S_ISDIR ( statbuf . st_mode ) ) ;
2015-07-21 16:10:13 +02:00
# else /* _WIN32 */
struct _stat statbuf ;
int rc = _stat ( path . CStr ( ) , & statbuf ) ;
if ( rc < 0 )
BOOST_THROW_EXCEPTION ( posix_error ( )
2017-12-19 15:50:05 +01:00
< < boost : : errinfo_api_function ( " _stat " )
< < boost : : errinfo_errno ( errno )
< < boost : : errinfo_file_name ( path ) ) ;
2015-07-21 16:10:13 +02:00
2017-11-30 08:19:58 +01:00
paths . emplace_back ( path , ( ( statbuf . st_mode & S_IFMT ) = = S_IFDIR ) ) ;
2015-08-03 10:17:12 +02:00
# endif /* _WIN32 */
2015-07-21 16:10:13 +02:00
}
2015-08-28 17:58:29 +02:00
bool ConfigPackageUtility : : ContainsDotDot ( const String & path )
2015-07-21 16:10:13 +02:00
{
2018-01-04 18:24:45 +01:00
std : : vector < String > tokens = path . Split ( " / \\ " ) ;
2015-07-21 16:10:13 +02:00
2016-08-25 06:19:44 +02:00
for ( const String & part : tokens ) {
2015-07-21 16:10:13 +02:00
if ( part = = " .. " )
return true ;
}
return false ;
}
2015-08-28 17:58:29 +02:00
bool ConfigPackageUtility : : ValidateName ( const String & name )
2015-07-21 16:10:13 +02:00
{
if ( name . IsEmpty ( ) )
return false ;
/* check for path injection */
if ( ContainsDotDot ( name ) )
return false ;
boost : : regex expr ( " ^[^a-zA-Z0-9_ \\ -]*$ " , boost : : regex : : icase ) ;
boost : : smatch what ;
return ( ! boost : : regex_search ( name . GetData ( ) , what , expr ) ) ;
}
2018-01-04 04:25:35 +01:00
boost : : mutex & ConfigPackageUtility : : GetStaticMutex ( )
2017-09-20 16:40:02 +02:00
{
static boost : : mutex mutex ;
return mutex ;
}