2014-12-11 10:50:15 +03:00
|
|
|
// Copyright (c) 2012-2014 Konstantin Isakov <ikm@zbackup.org> and ZBackup contributors, see CONTRIBUTORS
|
2014-11-27 19:20:41 +03:00
|
|
|
// Part of ZBackup. Licensed under GNU GPLv2 or later + OpenSSL, see LICENSE
|
|
|
|
|
2014-12-30 19:27:31 +03:00
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <cerrno>
|
2015-03-27 23:56:46 +03:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <signal.h>
|
2014-12-30 19:27:31 +03:00
|
|
|
|
2014-11-27 19:20:41 +03:00
|
|
|
#include "zbackup_base.hh"
|
|
|
|
|
|
|
|
#include "storage_info_file.hh"
|
2014-12-09 15:21:51 +03:00
|
|
|
#include "compression.hh"
|
2014-12-26 14:16:44 +03:00
|
|
|
#include "debug.hh"
|
2014-11-27 19:20:41 +03:00
|
|
|
|
2015-01-13 18:14:25 +03:00
|
|
|
// TODO: make configurable by cmake
|
2015-01-25 19:44:15 +03:00
|
|
|
#if defined(PATH_VI)
|
|
|
|
# define EDITOR PATH_VI
|
2014-12-30 19:27:31 +03:00
|
|
|
#else
|
|
|
|
# define EDITOR "/bin/vi"
|
|
|
|
#endif
|
|
|
|
|
2015-01-25 19:44:15 +03:00
|
|
|
#ifndef PATH_BSHELL
|
|
|
|
# define PATH_BSHELL "/bin/sh"
|
2014-12-30 19:27:31 +03:00
|
|
|
#endif
|
|
|
|
|
2014-11-27 19:20:41 +03:00
|
|
|
using std::string;
|
|
|
|
|
|
|
|
Paths::Paths( string const & storageDir ): storageDir( storageDir )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-08-20 12:01:09 +03:00
|
|
|
Paths::Paths( string const & storageDir, Config const & config ):
|
|
|
|
storageDir( storageDir ), config( config )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-11-27 19:20:41 +03:00
|
|
|
string Paths::getTmpPath()
|
|
|
|
{
|
2015-08-20 12:01:09 +03:00
|
|
|
if ( config.runtime.pathsRespectTmp )
|
|
|
|
{
|
|
|
|
char * tmpdir;
|
|
|
|
if ( ( ( tmpdir = getenv( "TMPDIR" ) ) != NULL && *tmpdir != '\0' ) )
|
|
|
|
return string( tmpdir );
|
|
|
|
}
|
2014-11-27 19:20:41 +03:00
|
|
|
return string( Dir::addPath( storageDir, "tmp" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
string Paths::getBundlesPath()
|
|
|
|
{
|
|
|
|
return string( Dir::addPath( storageDir, "bundles" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
string Paths::getStorageInfoPath()
|
|
|
|
{
|
|
|
|
return string( Dir::addPath( storageDir, "info" ) );
|
|
|
|
}
|
|
|
|
|
2014-12-26 14:16:44 +03:00
|
|
|
string Paths::getExtendedStorageInfoPath()
|
|
|
|
{
|
|
|
|
return string( Dir::addPath( storageDir, "info_extended" ) );
|
|
|
|
}
|
|
|
|
|
2014-11-27 19:20:41 +03:00
|
|
|
string Paths::getIndexPath()
|
|
|
|
{
|
|
|
|
return string( Dir::addPath( storageDir, "index" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
string Paths::getBackupsPath()
|
|
|
|
{
|
|
|
|
return string( Dir::addPath( storageDir, "backups" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
ZBackupBase::ZBackupBase( string const & storageDir, string const & password ):
|
|
|
|
Paths( storageDir ), storageInfo( loadStorageInfo() ),
|
|
|
|
encryptionkey( password, storageInfo.has_encryption_key() ?
|
|
|
|
&storageInfo.encryption_key() : 0 ),
|
2014-12-26 14:16:44 +03:00
|
|
|
extendedStorageInfo( loadExtendedStorageInfo( encryptionkey ) ),
|
2014-11-27 19:20:41 +03:00
|
|
|
tmpMgr( getTmpPath() ),
|
2015-01-19 15:19:45 +03:00
|
|
|
chunkIndex( encryptionkey, tmpMgr, getIndexPath(), false ),
|
|
|
|
config( extendedStorageInfo.mutable_config() )
|
2014-11-27 19:20:41 +03:00
|
|
|
{
|
2015-01-18 03:42:52 +03:00
|
|
|
propagateUpdate();
|
2015-01-20 15:46:08 +03:00
|
|
|
dPrintf("%s for %s is instantiated and initialized\n", __CLASS,
|
2015-01-19 15:19:45 +03:00
|
|
|
storageDir.c_str() );
|
2014-11-27 19:20:41 +03:00
|
|
|
}
|
|
|
|
|
2015-01-17 20:08:15 +03:00
|
|
|
ZBackupBase::ZBackupBase( string const & storageDir, string const & password,
|
2015-01-18 03:42:52 +03:00
|
|
|
Config & configIn ):
|
2015-08-20 12:01:09 +03:00
|
|
|
Paths( storageDir, configIn ), storageInfo( loadStorageInfo() ),
|
2015-01-17 20:08:15 +03:00
|
|
|
encryptionkey( password, storageInfo.has_encryption_key() ?
|
|
|
|
&storageInfo.encryption_key() : 0 ),
|
|
|
|
extendedStorageInfo( loadExtendedStorageInfo( encryptionkey ) ),
|
|
|
|
tmpMgr( getTmpPath() ),
|
|
|
|
chunkIndex( encryptionkey, tmpMgr, getIndexPath(), false ),
|
2015-01-19 15:19:45 +03:00
|
|
|
config( configIn, extendedStorageInfo.mutable_config() )
|
2015-01-17 20:08:15 +03:00
|
|
|
{
|
2015-01-18 03:42:52 +03:00
|
|
|
propagateUpdate();
|
2015-01-20 15:46:08 +03:00
|
|
|
dPrintf("%s for %s is instantiated and initialized\n", __CLASS,
|
2015-01-19 15:19:45 +03:00
|
|
|
storageDir.c_str() );
|
2015-01-17 20:08:15 +03:00
|
|
|
}
|
|
|
|
|
2014-11-27 19:20:41 +03:00
|
|
|
ZBackupBase::ZBackupBase( string const & storageDir, string const & password,
|
|
|
|
bool prohibitChunkIndexLoading ):
|
|
|
|
Paths( storageDir ), storageInfo( loadStorageInfo() ),
|
|
|
|
encryptionkey( password, storageInfo.has_encryption_key() ?
|
|
|
|
&storageInfo.encryption_key() : 0 ),
|
2014-12-26 14:16:44 +03:00
|
|
|
extendedStorageInfo( loadExtendedStorageInfo( encryptionkey ) ),
|
2014-11-27 19:20:41 +03:00
|
|
|
tmpMgr( getTmpPath() ),
|
2015-01-19 15:19:45 +03:00
|
|
|
chunkIndex( encryptionkey, tmpMgr, getIndexPath(), prohibitChunkIndexLoading ),
|
|
|
|
config( extendedStorageInfo.mutable_config() )
|
2014-11-27 19:20:41 +03:00
|
|
|
{
|
2015-01-18 03:42:52 +03:00
|
|
|
propagateUpdate();
|
2015-01-20 15:46:08 +03:00
|
|
|
dPrintf("%s for %s is instantiated and initialized\n", __CLASS,
|
2015-01-19 15:19:45 +03:00
|
|
|
storageDir.c_str() );
|
2015-01-17 20:08:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ZBackupBase::ZBackupBase( string const & storageDir, string const & password,
|
2015-01-18 03:42:52 +03:00
|
|
|
Config & configIn, bool prohibitChunkIndexLoading ):
|
2015-08-20 12:01:09 +03:00
|
|
|
Paths( storageDir, configIn ), storageInfo( loadStorageInfo() ),
|
2015-01-17 20:08:15 +03:00
|
|
|
encryptionkey( password, storageInfo.has_encryption_key() ?
|
|
|
|
&storageInfo.encryption_key() : 0 ),
|
|
|
|
extendedStorageInfo( loadExtendedStorageInfo( encryptionkey ) ),
|
|
|
|
tmpMgr( getTmpPath() ),
|
|
|
|
chunkIndex( encryptionkey, tmpMgr, getIndexPath(), prohibitChunkIndexLoading ),
|
2015-01-19 15:19:45 +03:00
|
|
|
config( configIn, extendedStorageInfo.mutable_config() )
|
2015-01-17 20:08:15 +03:00
|
|
|
{
|
2015-01-18 03:42:52 +03:00
|
|
|
propagateUpdate();
|
2015-01-20 15:46:08 +03:00
|
|
|
dPrintf("%s for %s is instantiated and initialized\n", __CLASS,
|
2015-01-19 15:19:45 +03:00
|
|
|
storageDir.c_str() );
|
2014-11-27 19:20:41 +03:00
|
|
|
}
|
|
|
|
|
2015-01-23 14:32:40 +03:00
|
|
|
// Update all internal variables according to real configuration
|
2015-01-19 17:29:01 +03:00
|
|
|
// Dunno why someone need to store duplicate information
|
|
|
|
// in deduplication utility
|
2015-01-18 03:42:52 +03:00
|
|
|
void ZBackupBase::propagateUpdate()
|
|
|
|
{
|
2015-01-19 17:29:01 +03:00
|
|
|
const_sptr< Compression::CompressionMethod > compression =
|
2015-01-18 03:42:52 +03:00
|
|
|
Compression::CompressionMethod::findCompression(
|
2015-01-19 17:29:01 +03:00
|
|
|
config.GET_STORABLE( bundle, compression_method ) );
|
2015-01-18 03:42:52 +03:00
|
|
|
Compression::CompressionMethod::selectedCompression = compression;
|
|
|
|
}
|
|
|
|
|
2014-11-27 19:20:41 +03:00
|
|
|
StorageInfo ZBackupBase::loadStorageInfo()
|
|
|
|
{
|
|
|
|
StorageInfo storageInfo;
|
|
|
|
|
|
|
|
StorageInfoFile::load( getStorageInfoPath(), storageInfo );
|
|
|
|
|
|
|
|
return storageInfo;
|
|
|
|
}
|
|
|
|
|
2014-12-30 19:27:31 +03:00
|
|
|
ExtendedStorageInfo ZBackupBase::loadExtendedStorageInfo(
|
|
|
|
EncryptionKey const & encryptionkey )
|
2014-12-26 14:16:44 +03:00
|
|
|
{
|
2015-01-20 05:20:44 +03:00
|
|
|
try
|
|
|
|
{
|
|
|
|
ExtendedStorageInfo extendedStorageInfo;
|
|
|
|
|
|
|
|
ExtendedStorageInfoFile::load( getExtendedStorageInfoPath(), encryptionkey,
|
|
|
|
extendedStorageInfo );
|
|
|
|
|
|
|
|
return extendedStorageInfo;
|
|
|
|
}
|
|
|
|
catch ( UnbufferedFile::exCantOpen & ex )
|
|
|
|
{
|
|
|
|
verbosePrintf( "Can't open extended storage info (info_extended)!\n"
|
2015-01-20 05:29:07 +03:00
|
|
|
"Attempting to start repo migration.\n" );
|
|
|
|
|
|
|
|
if ( !File::exists( getExtendedStorageInfoPath() ) )
|
|
|
|
{
|
|
|
|
ExtendedStorageInfo extendedStorageInfo;
|
|
|
|
Config config( extendedStorageInfo.mutable_config() );
|
2015-02-06 10:36:38 +03:00
|
|
|
config.reset_storable();
|
2015-01-20 05:29:07 +03:00
|
|
|
config.SET_STORABLE( chunk, max_size, storageInfo.chunk_max_size() );
|
|
|
|
config.SET_STORABLE( bundle, max_payload_size,
|
|
|
|
storageInfo.bundle_max_payload_size() );
|
|
|
|
config.SET_STORABLE( bundle, compression_method,
|
|
|
|
storageInfo.default_compression_method() );
|
|
|
|
|
|
|
|
ExtendedStorageInfoFile::save( getExtendedStorageInfoPath(), encryptionkey,
|
|
|
|
extendedStorageInfo );
|
|
|
|
|
|
|
|
verbosePrintf( "Done.\n" );
|
|
|
|
|
|
|
|
return loadExtendedStorageInfo( encryptionkey );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf( stderr, "info_extended exists but can't be opened!\n"
|
|
|
|
"Please check file permissions.\n" );
|
|
|
|
}
|
2015-01-20 05:20:44 +03:00
|
|
|
}
|
2014-12-26 14:16:44 +03:00
|
|
|
}
|
|
|
|
|
2014-11-27 19:20:41 +03:00
|
|
|
void ZBackupBase::initStorage( string const & storageDir,
|
|
|
|
string const & password,
|
2015-01-23 15:08:02 +03:00
|
|
|
bool isEncrypted,
|
|
|
|
Config const & configIn )
|
2014-11-27 19:20:41 +03:00
|
|
|
{
|
|
|
|
StorageInfo storageInfo;
|
2014-12-26 14:16:44 +03:00
|
|
|
ExtendedStorageInfo extendedStorageInfo;
|
2015-01-20 15:46:08 +03:00
|
|
|
Config config( extendedStorageInfo.mutable_config() );
|
|
|
|
config.reset_storable();
|
2015-01-23 15:08:02 +03:00
|
|
|
config.storable->MergeFrom( *configIn.storable );
|
2014-12-26 14:16:44 +03:00
|
|
|
|
2014-12-15 14:13:34 +03:00
|
|
|
EncryptionKey encryptionkey = EncryptionKey::noKey();
|
2014-11-27 19:20:41 +03:00
|
|
|
|
|
|
|
if ( isEncrypted )
|
|
|
|
EncryptionKey::generate( password,
|
2014-12-15 14:13:34 +03:00
|
|
|
*storageInfo.mutable_encryption_key(),
|
|
|
|
encryptionkey );
|
2014-11-27 19:20:41 +03:00
|
|
|
|
|
|
|
Paths paths( storageDir );
|
|
|
|
|
|
|
|
if ( !Dir::exists( storageDir ) )
|
|
|
|
Dir::create( storageDir );
|
|
|
|
|
|
|
|
if ( !Dir::exists( paths.getBundlesPath() ) )
|
|
|
|
Dir::create( paths.getBundlesPath() );
|
|
|
|
|
|
|
|
if ( !Dir::exists( paths.getBackupsPath() ) )
|
|
|
|
Dir::create( paths.getBackupsPath() );
|
|
|
|
|
|
|
|
if ( !Dir::exists( paths.getIndexPath() ) )
|
|
|
|
Dir::create( paths.getIndexPath() );
|
|
|
|
|
|
|
|
string storageInfoPath( paths.getStorageInfoPath() );
|
2014-12-26 14:16:44 +03:00
|
|
|
string extendedStorageInfoPath( paths.getExtendedStorageInfoPath() );
|
2014-11-27 19:20:41 +03:00
|
|
|
|
|
|
|
if ( File::exists( storageInfoPath ) )
|
|
|
|
throw exWontOverwrite( storageInfoPath );
|
|
|
|
|
2014-12-26 14:16:44 +03:00
|
|
|
encryptionkey = EncryptionKey( password, storageInfo.has_encryption_key() ?
|
|
|
|
&storageInfo.encryption_key() : 0 );
|
|
|
|
|
2014-11-27 19:20:41 +03:00
|
|
|
StorageInfoFile::save( storageInfoPath, storageInfo );
|
2014-12-26 14:16:44 +03:00
|
|
|
ExtendedStorageInfoFile::save( extendedStorageInfoPath, encryptionkey, extendedStorageInfo );
|
2014-11-27 19:20:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
string ZBackupBase::deriveStorageDirFromBackupsFile( string const &
|
|
|
|
backupsFile, bool allowOutside )
|
|
|
|
{
|
|
|
|
// TODO: handle cases when there's a backup/ folder within the backup/ folder
|
|
|
|
// correctly
|
|
|
|
if ( allowOutside )
|
|
|
|
return Dir::getRealPath( backupsFile );
|
|
|
|
|
|
|
|
string realPath = Dir::getRealPath( Dir::getDirName( backupsFile ) );
|
|
|
|
size_t pos;
|
|
|
|
if ( realPath.size() >= 8 && strcmp( realPath.c_str() + realPath.size() - 8,
|
|
|
|
"/backups") == 0 )
|
|
|
|
pos = realPath.size() - 8;
|
|
|
|
else
|
|
|
|
pos = realPath.rfind( "/backups/" );
|
|
|
|
if ( pos == string::npos )
|
|
|
|
throw exCantDeriveStorageDir( backupsFile );
|
|
|
|
else
|
|
|
|
return realPath.substr( 0, pos );
|
|
|
|
}
|
2014-12-09 15:21:51 +03:00
|
|
|
|
2014-12-15 14:13:34 +03:00
|
|
|
void ZBackupBase::setPassword( string const & password )
|
|
|
|
{
|
|
|
|
EncryptionKey::generate( password,
|
|
|
|
*storageInfo.mutable_encryption_key(), encryptionkey );
|
|
|
|
|
|
|
|
StorageInfoFile::save( getStorageInfoPath(), storageInfo );
|
|
|
|
|
|
|
|
EncryptionKey encryptionkey( password, storageInfo.has_encryption_key() ?
|
|
|
|
&storageInfo.encryption_key() : 0 );
|
|
|
|
}
|
|
|
|
|
2014-12-26 14:16:44 +03:00
|
|
|
void ZBackupBase::saveExtendedStorageInfo()
|
|
|
|
{
|
2014-12-30 19:27:31 +03:00
|
|
|
ExtendedStorageInfoFile::save( getExtendedStorageInfoPath(), encryptionkey,
|
|
|
|
extendedStorageInfo );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ZBackupBase::spawnEditor( string & data, bool( * validator )
|
|
|
|
( string const &, string const & ) )
|
|
|
|
{
|
2015-01-13 18:14:25 +03:00
|
|
|
// Based on ideas found in cronie-1.4.4-12.el6
|
2014-12-30 19:27:31 +03:00
|
|
|
// 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 */
|
2015-01-25 19:44:15 +03:00
|
|
|
dPrintf( "Spawning editor: %s %s %s %s\n", PATH_BSHELL, PATH_BSHELL,
|
2014-12-30 19:27:31 +03:00
|
|
|
"-c", shellArgs.c_str() );
|
2015-01-25 19:44:15 +03:00
|
|
|
execlp( PATH_BSHELL, PATH_BSHELL, "-c", shellArgs.c_str(), (char *) 0 );
|
2014-12-30 19:27:31 +03:00
|
|
|
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':
|
2014-12-30 21:59:06 +03:00
|
|
|
verbosePrintf( "Data is kept intact\n" );
|
|
|
|
goto end;
|
2014-12-30 19:27:31 +03:00
|
|
|
default:
|
|
|
|
fprintf( stderr, "Enter Y or N\n" );
|
|
|
|
}
|
|
|
|
}
|
2014-12-30 21:59:06 +03:00
|
|
|
}
|
2014-12-30 19:27:31 +03:00
|
|
|
|
|
|
|
success:
|
2014-12-30 21:59:06 +03:00
|
|
|
isChanged = true;
|
|
|
|
data.assign( newData );
|
2014-12-30 19:27:31 +03:00
|
|
|
|
|
|
|
end:
|
|
|
|
tmpDataFile.reset();
|
|
|
|
tmpFile.reset();
|
|
|
|
|
|
|
|
return isChanged;
|
2014-12-26 14:16:44 +03:00
|
|
|
}
|
2015-01-23 14:32:40 +03:00
|
|
|
|
|
|
|
bool ZBackupBase::editConfigInteractively()
|
|
|
|
{
|
|
|
|
string configData( Config::toString( *config.storable ) );
|
|
|
|
|
|
|
|
if ( !spawnEditor( configData, &Config::validateProto ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ConfigInfo newConfig;
|
|
|
|
Config::parseProto( configData, &newConfig );
|
|
|
|
if ( Config::toString( *config.storable ) ==
|
|
|
|
Config::toString( newConfig ) )
|
|
|
|
{
|
|
|
|
verbosePrintf( "No changes made to config\n" );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
verbosePrintf( "Updating configuration...\n" );
|
|
|
|
config.storable->MergeFrom( newConfig );
|
|
|
|
verbosePrintf(
|
|
|
|
"Configuration successfully updated!\n"
|
|
|
|
"Updated configuration:\n%s", Config::toString( *config.storable ).c_str() );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|