2014-12-26 17:06:50 +03:00
|
|
|
// 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
|
|
|
|
2014-12-26 17:06:50 +03:00
|
|
|
#include "config.hh"
|
2014-12-29 17:24:09 +03:00
|
|
|
#include "file.hh"
|
2014-12-26 17:06:50 +03:00
|
|
|
#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
|
|
|
|
|
2014-12-26 17:06:50 +03:00
|
|
|
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()
|
2014-12-26 17:06:50 +03:00
|
|
|
{
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-12-26 17:06:50 +03:00
|
|
|
bool ZConfig::parse( const string & str, google::protobuf::Message * mutable_message )
|
|
|
|
{
|
|
|
|
return google::protobuf::TextFormat::ParseFromString( str, mutable_message );
|
|
|
|
}
|