zbackup/config.cc

230 lines
5.5 KiB
C++
Raw Normal View History

// 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
2014-12-29 17:24:09 +03:00
#include <sys/wait.h>
2014-12-30 17:13:48 +03:00
#include <cerrno>
#include "zbackup_base.hh"
#include "zbackup.pb.h"
#include "sptr.hh"
2014-12-29 17:24:09 +03:00
#include "config.hh"
2014-12-29 17:24:09 +03:00
#include "file.hh"
#include "debug.hh"
2014-12-29 17:24:09 +03:00
#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
ZConfig::ZConfig( string const & storageDir, string const & password ):
ZBackupBase( storageDir, password, true )
{
}
string ZConfig::toString( google::protobuf::Message const & message )
{
std::string str;
google::protobuf::TextFormat::PrintToString( message, &str );
return str;
}
2014-12-30 17:13:48 +03:00
void ZConfig::show()
{
printf( "%s", toString( extendedStorageInfo.config() ).c_str() );
}
2014-12-30 17:13:48 +03:00
bool ZConfig::edit()
2014-12-29 17:24:09 +03:00
{
2014-12-30 17:13:48 +03:00
string configData( toString( extendedStorageInfo.config() ) );
/* Turn off signals. */
(void) signal( SIGHUP, SIG_IGN );
(void) signal( SIGINT, SIG_IGN );
(void) signal( SIGQUIT, SIG_IGN );
2014-12-29 17:24:09 +03:00
sptr< TemporaryFile > tmpFile = tmpMgr.makeTemporaryFile();
const char * tmpFileName = tmpFile->getFileName().c_str();
2014-12-30 17:13:48 +03:00
sptr< File> tmpConfig = new File( tmpFileName, File::WriteOnly );
tmpConfig->writeRecords( configData.c_str(), configData.size(), 1 );
2014-12-29 17:24:09 +03:00
again:
2014-12-30 17:13:48 +03:00
tmpConfig->rewind();
if ( tmpConfig->error() )
2014-12-29 17:24:09 +03:00
{
2014-12-30 17:13:48 +03:00
verbosePrintf( "Error while writing config to %s\n", tmpFileName );
2014-12-29 17:24:09 +03:00
fatal:
2014-12-30 17:13:48 +03:00
tmpFile.reset();
2014-12-29 17:24:09 +03:00
exit( EXIT_FAILURE );
}
2014-12-30 17:13:48 +03:00
char * editorEnv;
string editor;
if ( ( ( editorEnv = getenv( "VISUAL" ) ) == NULL || *editorEnv == '\0' ) &&
( ( editorEnv = getenv( "EDITOR" ) ) == NULL || *editorEnv == '\0' ) )
editor.assign( EDITOR );
else
editor.assign( editorEnv );
2014-12-29 17:24:09 +03:00
/* 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;
2014-12-30 17:13:48 +03:00
pid_t pid, xpid;
2014-12-29 17:24:09 +03:00
switch ( pid = fork() )
{
case -1:
perror( "fork" );
goto fatal;
case 0:
/* child */
execlp( _PATH_BSHELL, _PATH_BSHELL, "-c", shellArgs.c_str(), (char *) 0 );
2014-12-30 17:13:48 +03:00
perror( editor.c_str() );
2014-12-29 17:24:09 +03:00
exit( EXIT_FAILURE );
/*NOTREACHED*/
default:
/* parent */
break;
}
/* parent */
2014-12-30 17:13:48 +03:00
int waiter;
2014-12-29 17:24:09 +03:00
for ( ; ; )
{
xpid = waitpid( pid, &waiter, 0 );
if ( xpid == -1 )
{
if ( errno != EINTR )
2014-12-30 17:13:48 +03:00
verbosePrintf( "waitpid() failed waiting for PID %ld from \"%s\": %s\n",
(long) pid, editor.c_str(), strerror( errno ) );
2014-12-29 17:24:09 +03:00
}
else
if (xpid != pid)
{
2014-12-30 17:13:48 +03:00
verbosePrintf( "wrong PID (%ld != %ld) from \"%s\"\n",
(long) xpid, (long) pid, editor.c_str() );
2014-12-29 17:24:09 +03:00
goto fatal;
}
else
if ( WIFEXITED( waiter ) && WEXITSTATUS( waiter ) )
{
2014-12-30 17:13:48 +03:00
verbosePrintf( "\"%s\" exited with status %d\n",
editor.c_str(), WEXITSTATUS( waiter ) );
2014-12-29 17:24:09 +03:00
goto fatal;
}
else
if ( WIFSIGNALED( waiter ) )
{
2014-12-30 17:13:48 +03:00
verbosePrintf( "\"%s\" killed; signal %d (%score dumped)\n",
editor.c_str(), WTERMSIG( waiter ),
2014-12-29 17:24:09 +03:00
WCOREDUMP( waiter ) ? "" : "no ");
goto fatal;
}
else
break;
}
(void) signal( SIGHUP, SIG_DFL );
(void) signal( SIGINT, SIG_DFL );
(void) signal( SIGQUIT, SIG_DFL );
2014-12-30 17:13:48 +03:00
tmpConfig->close();
tmpConfig = new File( tmpFileName, File::ReadOnly );
2014-12-29 17:24:09 +03:00
2014-12-30 17:13:48 +03:00
string newConfigData;
newConfigData.resize( tmpConfig->size() );
tmpConfig->read( &newConfigData[ 0 ], newConfigData.size() );
ConfigInfo newConfig;
bool isChanged = false;
2014-12-29 17:24:09 +03:00
2014-12-30 17:13:48 +03:00
int ret = 0;
if ( !parse( newConfigData, &newConfig ) )
2014-12-29 17:24:09 +03:00
ret = -1;
2014-12-30 17:13:48 +03:00
else
{
if ( toString( extendedStorageInfo.config() ) == toString( newConfig ) )
{
verbosePrintf( "No changes made to config\n" );
goto end;
}
else
verbosePrintf( "Updating configuration...\n" );
2014-12-29 17:24:09 +03:00
}
switch ( ret )
{
case 0:
2014-12-30 17:13:48 +03:00
goto success;
2014-12-29 17:24:09 +03:00
case -1:
for ( ; ; )
{
printf( "Do you want to retry the same edit? " );
fflush( stdout );
2014-12-30 17:13:48 +03:00
string input;
input.resize( MAX_TEMPSTR );
if ( fgets( &input[ 0 ], input.size(), stdin ) == 0L )
2014-12-29 17:24:09 +03:00
continue;
2014-12-30 17:13:48 +03:00
switch ( input[ 0 ] )
2014-12-29 17:24:09 +03:00
{
case 'y':
case 'Y':
goto again;
case 'n':
case 'N':
goto abandon;
default:
2014-12-30 17:13:48 +03:00
fprintf( stderr, "Enter Y or N\n" );
2014-12-29 17:24:09 +03:00
}
}
/*NOTREACHED*/
case -2:
2014-12-30 17:13:48 +03:00
2014-12-29 17:24:09 +03:00
abandon:
2014-12-30 17:13:48 +03:00
verbosePrintf( "Configuration is kept intact\n" );
goto end;
success:
extendedStorageInfo.mutable_config()->CopyFrom( newConfig );
verbosePrintf(
"Configuration successfully updated!\n"
"Updated configuration:\n\n%s", toString( extendedStorageInfo.config() ).c_str() );
isChanged = true;
goto end;
2014-12-29 17:24:09 +03:00
default:
2014-12-30 17:13:48 +03:00
verbosePrintf( "panic: bad switch()\n" );
2014-12-29 17:24:09 +03:00
goto fatal;
}
2014-12-30 17:13:48 +03:00
end:
tmpConfig.reset();
tmpFile.reset();
2014-12-29 17:24:09 +03:00
2014-12-30 17:13:48 +03:00
return isChanged;
2014-12-29 17:24:09 +03:00
}
bool ZConfig::parse( const string & str, google::protobuf::Message * mutable_message )
{
return google::protobuf::TextFormat::ParseFromString( str, mutable_message );
}