mirror of https://github.com/vitalif/zbackup
spawnEditor was moved to ZBackupBase
parent
479b5b3c71
commit
bee7efc9e5
214
config.cc
214
config.cc
|
@ -1,34 +1,22 @@
|
|||
// Copyright (c) 2012-2014 Konstantin Isakov <ikm@zbackup.org> and ZBackup contributors, see CONTRIBUTORS
|
||||
// Part of ZBackup. Licensed under GNU GPLv2 or later + OpenSSL, see LICENSE
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <cerrno>
|
||||
|
||||
#include "zbackup_base.hh"
|
||||
#include "zbackup.pb.h"
|
||||
#include "sptr.hh"
|
||||
|
||||
#include "config.hh"
|
||||
#include "file.hh"
|
||||
#include "debug.hh"
|
||||
|
||||
#if defined(_PATH_VI)
|
||||
# define EDITOR _PATH_VI
|
||||
#else
|
||||
# define EDITOR "/bin/vi"
|
||||
#endif
|
||||
|
||||
#ifndef _PATH_BSHELL
|
||||
# define _PATH_BSHELL "/bin/sh"
|
||||
#endif
|
||||
|
||||
#define MAX_TEMPSTR 131072
|
||||
#include "config.hh"
|
||||
|
||||
ZConfig::ZConfig( string const & storageDir, string const & password ):
|
||||
ZBackupBase( storageDir, password, true )
|
||||
{
|
||||
}
|
||||
|
||||
bool ZConfig::parse( const string & str, google::protobuf::Message * mutable_message )
|
||||
{
|
||||
return google::protobuf::TextFormat::ParseFromString( str, mutable_message );
|
||||
}
|
||||
|
||||
string ZConfig::toString( google::protobuf::Message const & message )
|
||||
{
|
||||
std::string str;
|
||||
|
@ -42,188 +30,34 @@ void ZConfig::show()
|
|||
printf( "%s", toString( extendedStorageInfo.config() ).c_str() );
|
||||
}
|
||||
|
||||
bool ZConfig::edit()
|
||||
bool ZConfig::validate( const string & configData, const string & newConfigData )
|
||||
{
|
||||
ConfigInfo newConfig;
|
||||
return parse( newConfigData, &newConfig );
|
||||
}
|
||||
|
||||
bool ZConfig::editInteractively()
|
||||
{
|
||||
string configData( toString( extendedStorageInfo.config() ) );
|
||||
string newConfigData( configData );
|
||||
|
||||
/* Turn off signals. */
|
||||
(void) signal( SIGHUP, SIG_IGN );
|
||||
(void) signal( SIGINT, SIG_IGN );
|
||||
(void) signal( SIGQUIT, SIG_IGN );
|
||||
|
||||
sptr< TemporaryFile > tmpFile = tmpMgr.makeTemporaryFile();
|
||||
const char * tmpFileName = tmpFile->getFileName().c_str();
|
||||
|
||||
sptr< File> tmpConfig = new File( tmpFileName, File::WriteOnly );
|
||||
tmpConfig->writeRecords( configData.c_str(), configData.size(), 1 );
|
||||
|
||||
again:
|
||||
tmpConfig->rewind();
|
||||
if ( tmpConfig->error() )
|
||||
{
|
||||
verbosePrintf( "Error while writing config to %s\n", tmpFileName );
|
||||
fatal:
|
||||
tmpFile.reset();
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
|
||||
char * editorEnv;
|
||||
string editor;
|
||||
if ( ( ( editorEnv = getenv( "VISUAL" ) ) == NULL || *editorEnv == '\0' ) &&
|
||||
( ( editorEnv = getenv( "EDITOR" ) ) == NULL || *editorEnv == '\0' ) )
|
||||
editor.assign( EDITOR );
|
||||
else
|
||||
editor.assign( editorEnv );
|
||||
|
||||
/* we still have the file open. editors will generally rewrite the
|
||||
* original file rather than renaming/unlinking it and starting a
|
||||
* new one; even backup files are supposed to be made by copying
|
||||
* rather than by renaming. if some editor does not support this,
|
||||
* then don't use it. the security problems are more severe if we
|
||||
* close and reopen the file around the edit.
|
||||
*/
|
||||
|
||||
string shellArgs;
|
||||
shellArgs += editor;
|
||||
shellArgs += " ";
|
||||
shellArgs += tmpFileName;
|
||||
|
||||
pid_t pid, xpid;
|
||||
|
||||
switch ( pid = fork() )
|
||||
{
|
||||
case -1:
|
||||
perror( "fork" );
|
||||
goto fatal;
|
||||
case 0:
|
||||
/* child */
|
||||
execlp( _PATH_BSHELL, _PATH_BSHELL, "-c", shellArgs.c_str(), (char *) 0 );
|
||||
perror( editor.c_str() );
|
||||
exit( EXIT_FAILURE );
|
||||
/*NOTREACHED*/
|
||||
default:
|
||||
/* parent */
|
||||
break;
|
||||
}
|
||||
|
||||
/* parent */
|
||||
int waiter;
|
||||
for ( ; ; )
|
||||
{
|
||||
xpid = waitpid( pid, &waiter, 0 );
|
||||
if ( xpid == -1 )
|
||||
{
|
||||
if ( errno != EINTR )
|
||||
verbosePrintf( "waitpid() failed waiting for PID %ld from \"%s\": %s\n",
|
||||
(long) pid, editor.c_str(), strerror( errno ) );
|
||||
}
|
||||
else
|
||||
if (xpid != pid)
|
||||
{
|
||||
verbosePrintf( "wrong PID (%ld != %ld) from \"%s\"\n",
|
||||
(long) xpid, (long) pid, editor.c_str() );
|
||||
goto fatal;
|
||||
}
|
||||
else
|
||||
if ( WIFEXITED( waiter ) && WEXITSTATUS( waiter ) )
|
||||
{
|
||||
verbosePrintf( "\"%s\" exited with status %d\n",
|
||||
editor.c_str(), WEXITSTATUS( waiter ) );
|
||||
goto fatal;
|
||||
}
|
||||
else
|
||||
if ( WIFSIGNALED( waiter ) )
|
||||
{
|
||||
verbosePrintf( "\"%s\" killed; signal %d (%score dumped)\n",
|
||||
editor.c_str(), WTERMSIG( waiter ),
|
||||
WCOREDUMP( waiter ) ? "" : "no ");
|
||||
goto fatal;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
(void) signal( SIGHUP, SIG_DFL );
|
||||
(void) signal( SIGINT, SIG_DFL );
|
||||
(void) signal( SIGQUIT, SIG_DFL );
|
||||
|
||||
tmpConfig->close();
|
||||
tmpConfig = new File( tmpFileName, File::ReadOnly );
|
||||
|
||||
string newConfigData;
|
||||
newConfigData.resize( tmpConfig->size() );
|
||||
tmpConfig->read( &newConfigData[ 0 ], newConfigData.size() );
|
||||
if ( !spawnEditor( newConfigData, &validate ) )
|
||||
return false;
|
||||
ConfigInfo newConfig;
|
||||
bool isChanged = false;
|
||||
|
||||
int ret = 0;
|
||||
if ( !parse( newConfigData, &newConfig ) )
|
||||
ret = -1;
|
||||
else
|
||||
return false;
|
||||
if ( toString( extendedStorageInfo.config() ) == toString( newConfig ) )
|
||||
{
|
||||
if ( toString( extendedStorageInfo.config() ) == toString( newConfig ) )
|
||||
{
|
||||
verbosePrintf( "No changes made to config\n" );
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
verbosePrintf( "Updating configuration...\n" );
|
||||
verbosePrintf( "No changes made to config\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ( ret )
|
||||
{
|
||||
case 0:
|
||||
goto success;
|
||||
case -1:
|
||||
for ( ; ; )
|
||||
{
|
||||
printf( "Do you want to retry the same edit? " );
|
||||
fflush( stdout );
|
||||
verbosePrintf( "Updating configuration...\n" );
|
||||
|
||||
string input;
|
||||
input.resize( MAX_TEMPSTR );
|
||||
if ( fgets( &input[ 0 ], input.size(), stdin ) == 0L )
|
||||
continue;
|
||||
|
||||
switch ( input[ 0 ] )
|
||||
{
|
||||
case 'y':
|
||||
case 'Y':
|
||||
goto again;
|
||||
case 'n':
|
||||
case 'N':
|
||||
goto abandon;
|
||||
default:
|
||||
fprintf( stderr, "Enter Y or N\n" );
|
||||
}
|
||||
}
|
||||
/*NOTREACHED*/
|
||||
case -2:
|
||||
|
||||
abandon:
|
||||
verbosePrintf( "Configuration is kept intact\n" );
|
||||
goto end;
|
||||
|
||||
success:
|
||||
extendedStorageInfo.mutable_config()->CopyFrom( newConfig );
|
||||
verbosePrintf(
|
||||
extendedStorageInfo.mutable_config()->CopyFrom( newConfig );
|
||||
verbosePrintf(
|
||||
"Configuration successfully updated!\n"
|
||||
"Updated configuration:\n\n%s", toString( extendedStorageInfo.config() ).c_str() );
|
||||
isChanged = true;
|
||||
goto end;
|
||||
|
||||
default:
|
||||
verbosePrintf( "panic: bad switch()\n" );
|
||||
goto fatal;
|
||||
}
|
||||
|
||||
end:
|
||||
tmpConfig.reset();
|
||||
tmpFile.reset();
|
||||
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
bool ZConfig::parse( const string & str, google::protobuf::Message * mutable_message )
|
||||
{
|
||||
return google::protobuf::TextFormat::ParseFromString( str, mutable_message );
|
||||
return true;
|
||||
}
|
||||
|
|
10
config.hh
10
config.hh
|
@ -17,13 +17,17 @@ public:
|
|||
// Print current configuration to screen
|
||||
void show();
|
||||
|
||||
// Edit current configuration in default editor
|
||||
// Edit current configuration
|
||||
// returns true if configuration is changed
|
||||
bool edit();
|
||||
bool editInteractively();
|
||||
|
||||
// Validator for user-supplied configuration
|
||||
static bool validate( const string &, const string & );
|
||||
|
||||
static bool parse( const string & str, google::protobuf::Message * mutable_message );
|
||||
|
||||
private:
|
||||
string toString( google::protobuf::Message const & message );
|
||||
bool parse( const string & str, google::protobuf::Message * mutable_message );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -695,7 +695,7 @@ int main( int argc, char *argv[] )
|
|||
|
||||
if ( args.size() > 2 && strcmp( args[ fieldAct ], "edit" ) == 0 )
|
||||
{
|
||||
if ( zc.edit() )
|
||||
if ( zc.editInteractively() )
|
||||
zc.saveExtendedStorageInfo();
|
||||
}
|
||||
else
|
||||
|
|
194
zbackup_base.cc
194
zbackup_base.cc
|
@ -1,12 +1,25 @@
|
|||
// Copyright (c) 2012-2014 Konstantin Isakov <ikm@zbackup.org> and ZBackup contributors, see CONTRIBUTORS
|
||||
// Part of ZBackup. Licensed under GNU GPLv2 or later + OpenSSL, see LICENSE
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <cerrno>
|
||||
|
||||
#include "zbackup_base.hh"
|
||||
|
||||
#include "storage_info_file.hh"
|
||||
#include "compression.hh"
|
||||
#include "debug.hh"
|
||||
|
||||
#if defined(_PATH_VI)
|
||||
# define EDITOR _PATH_VI
|
||||
#else
|
||||
# define EDITOR "/bin/vi"
|
||||
#endif
|
||||
|
||||
#ifndef _PATH_BSHELL
|
||||
# define _PATH_BSHELL "/bin/sh"
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
|
||||
Paths::Paths( string const & storageDir ): storageDir( storageDir )
|
||||
|
@ -75,11 +88,13 @@ StorageInfo ZBackupBase::loadStorageInfo()
|
|||
return storageInfo;
|
||||
}
|
||||
|
||||
ExtendedStorageInfo ZBackupBase::loadExtendedStorageInfo( EncryptionKey const & encryptionkey )
|
||||
ExtendedStorageInfo ZBackupBase::loadExtendedStorageInfo(
|
||||
EncryptionKey const & encryptionkey )
|
||||
{
|
||||
ExtendedStorageInfo extendedStorageInfo;
|
||||
|
||||
ExtendedStorageInfoFile::load( getExtendedStorageInfoPath(), encryptionkey, extendedStorageInfo );
|
||||
ExtendedStorageInfoFile::load( getExtendedStorageInfoPath(), encryptionkey,
|
||||
extendedStorageInfo );
|
||||
|
||||
return extendedStorageInfo;
|
||||
}
|
||||
|
@ -180,5 +195,178 @@ void ZBackupBase::setPassword( string const & password )
|
|||
|
||||
void ZBackupBase::saveExtendedStorageInfo()
|
||||
{
|
||||
ExtendedStorageInfoFile::save( getExtendedStorageInfoPath(), encryptionkey, extendedStorageInfo );
|
||||
ExtendedStorageInfoFile::save( getExtendedStorageInfoPath(), encryptionkey,
|
||||
extendedStorageInfo );
|
||||
}
|
||||
|
||||
bool ZBackupBase::spawnEditor( string & data, bool( * validator )
|
||||
( string const &, string const & ) )
|
||||
{
|
||||
// Based on ideas found in cronie-1.4.4-12.el6.i686
|
||||
// Initially it was just a copy-paste from edit_cmd (crontab.c)
|
||||
|
||||
/* Turn off signals. */
|
||||
(void) signal( SIGHUP, SIG_IGN );
|
||||
(void) signal( SIGINT, SIG_IGN );
|
||||
(void) signal( SIGQUIT, SIG_IGN );
|
||||
|
||||
sptr< TemporaryFile > tmpFile = tmpMgr.makeTemporaryFile();
|
||||
const char * tmpFileName = tmpFile->getFileName().c_str();
|
||||
|
||||
sptr< File> tmpDataFile = new File( tmpFileName, File::WriteOnly );
|
||||
tmpDataFile->writeRecords( data.c_str(), data.size(), 1 );
|
||||
|
||||
again:
|
||||
tmpDataFile->rewind();
|
||||
if ( tmpDataFile->error() )
|
||||
{
|
||||
verbosePrintf( "Error while writing data to %s\n", tmpFileName );
|
||||
fatal:
|
||||
tmpFile.reset();
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
|
||||
char * editorEnv;
|
||||
string editor;
|
||||
if ( ( ( editorEnv = getenv( "VISUAL" ) ) == NULL || *editorEnv == '\0' ) &&
|
||||
( ( editorEnv = getenv( "EDITOR" ) ) == NULL || *editorEnv == '\0' ) )
|
||||
editor.assign( EDITOR );
|
||||
else
|
||||
editor.assign( editorEnv );
|
||||
|
||||
/* we still have the file open. editors will generally rewrite the
|
||||
* original file rather than renaming/unlinking it and starting a
|
||||
* new one; even backup files are supposed to be made by copying
|
||||
* rather than by renaming. if some editor does not support this,
|
||||
* then don't use it. the security problems are more severe if we
|
||||
* close and reopen the file around the edit.
|
||||
*/
|
||||
|
||||
string shellArgs;
|
||||
shellArgs += editor;
|
||||
shellArgs += " ";
|
||||
shellArgs += tmpFileName;
|
||||
|
||||
pid_t pid, xpid;
|
||||
|
||||
switch ( pid = fork() )
|
||||
{
|
||||
case -1:
|
||||
perror( "fork" );
|
||||
goto fatal;
|
||||
case 0:
|
||||
/* child */
|
||||
dPrintf( "Spawning editor: %s %s %s %s\n", _PATH_BSHELL, _PATH_BSHELL,
|
||||
"-c", shellArgs.c_str() );
|
||||
execlp( _PATH_BSHELL, _PATH_BSHELL, "-c", shellArgs.c_str(), (char *) 0 );
|
||||
perror( editor.c_str() );
|
||||
exit( EXIT_FAILURE );
|
||||
/*NOTREACHED*/
|
||||
default:
|
||||
/* parent */
|
||||
break;
|
||||
}
|
||||
|
||||
/* parent */
|
||||
int waiter;
|
||||
for ( ; ; )
|
||||
{
|
||||
xpid = waitpid( pid, &waiter, 0 );
|
||||
if ( xpid == -1 )
|
||||
{
|
||||
if ( errno != EINTR )
|
||||
verbosePrintf( "waitpid() failed waiting for PID %ld from \"%s\": %s\n",
|
||||
(long) pid, editor.c_str(), strerror( errno ) );
|
||||
}
|
||||
else
|
||||
if (xpid != pid)
|
||||
{
|
||||
verbosePrintf( "wrong PID (%ld != %ld) from \"%s\"\n",
|
||||
(long) xpid, (long) pid, editor.c_str() );
|
||||
goto fatal;
|
||||
}
|
||||
else
|
||||
if ( WIFEXITED( waiter ) && WEXITSTATUS( waiter ) )
|
||||
{
|
||||
verbosePrintf( "\"%s\" exited with status %d\n",
|
||||
editor.c_str(), WEXITSTATUS( waiter ) );
|
||||
goto fatal;
|
||||
}
|
||||
else
|
||||
if ( WIFSIGNALED( waiter ) )
|
||||
{
|
||||
verbosePrintf( "\"%s\" killed; signal %d (%score dumped)\n",
|
||||
editor.c_str(), WTERMSIG( waiter ),
|
||||
WCOREDUMP( waiter ) ? "" : "no ");
|
||||
goto fatal;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
(void) signal( SIGHUP, SIG_DFL );
|
||||
(void) signal( SIGINT, SIG_DFL );
|
||||
(void) signal( SIGQUIT, SIG_DFL );
|
||||
|
||||
tmpDataFile->close();
|
||||
tmpDataFile = new File( tmpFileName, File::ReadOnly );
|
||||
|
||||
string newData;
|
||||
newData.resize( tmpDataFile->size() );
|
||||
tmpDataFile->read( &newData[ 0 ], newData.size() );
|
||||
bool isChanged = false;
|
||||
|
||||
bool valid = validator( data, newData );
|
||||
|
||||
switch ( valid )
|
||||
{
|
||||
case true:
|
||||
goto success;
|
||||
case false:
|
||||
for ( ; ; )
|
||||
{
|
||||
fprintf( stderr, "Supplied data is not valid\n" );
|
||||
fflush( stderr );
|
||||
printf( "Do you want to retry the same edit? " );
|
||||
fflush( stdout );
|
||||
|
||||
string input;
|
||||
input.resize( 131072 ); // Should I choose another magic value?
|
||||
if ( fgets( &input[ 0 ], input.size(), stdin ) == 0L )
|
||||
continue;
|
||||
|
||||
switch ( input[ 0 ] )
|
||||
{
|
||||
case 'y':
|
||||
case 'Y':
|
||||
goto again;
|
||||
case 'n':
|
||||
case 'N':
|
||||
goto abandon;
|
||||
default:
|
||||
fprintf( stderr, "Enter Y or N\n" );
|
||||
}
|
||||
}
|
||||
/*NOTREACHED*/
|
||||
case -2:
|
||||
|
||||
abandon:
|
||||
verbosePrintf( "Data is kept intact\n" );
|
||||
goto end;
|
||||
|
||||
success:
|
||||
isChanged = true;
|
||||
data.assign( newData );
|
||||
goto end;
|
||||
|
||||
default:
|
||||
verbosePrintf( "panic: bad switch()\n" );
|
||||
goto fatal;
|
||||
}
|
||||
|
||||
end:
|
||||
tmpDataFile.reset();
|
||||
tmpFile.reset();
|
||||
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,10 @@ public:
|
|||
|
||||
void setPassword( std::string const & password );
|
||||
|
||||
// returns true if data is changed
|
||||
bool spawnEditor( std::string & data, bool( * validator )
|
||||
( string const &, string const & ) );
|
||||
|
||||
StorageInfo storageInfo;
|
||||
EncryptionKey encryptionkey;
|
||||
ExtendedStorageInfo extendedStorageInfo;
|
||||
|
@ -69,5 +73,4 @@ private:
|
|||
ExtendedStorageInfo loadExtendedStorageInfo( EncryptionKey const & );
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue