ZConfig code cleanup

master
Vladimir Stackov 2014-12-30 17:13:48 +03:00
parent f86de5662a
commit ffbe4ba00d
6 changed files with 151 additions and 137 deletions

View File

@ -70,7 +70,7 @@ void ChunkIndex::loadIndex( IndexProcessor & ip )
}
catch( std::exception & e )
{
fprintf( stderr, "error: %s\n", e.what() );
verbosePrintf( "error: %s\n", e.what() );
continue;
}
}

200
config.cc
View File

@ -1,15 +1,12 @@
// 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 <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <utime.h>
#include <cerrno>
#include "zbackup_base.hh"
#include "zbackup.pb.h"
#include "sptr.hh"
#include "config.hh"
#include "file.hh"
@ -40,62 +37,43 @@ string ZConfig::toString( google::protobuf::Message const & message )
return str;
}
void ZConfig::print()
void ZConfig::show()
{
printf( "%s", toString( extendedStorageInfo.config() ).c_str() );
}
void ZConfig::edit()
bool ZConfig::edit()
{
// TODO: Rewrite that copy-paste from cronie on C++
char q[MAX_TEMPSTR], *editor;
struct stat statbuf;
struct utimbuf utimebuf;
int waiter, ret;
pid_t pid, xpid;
FILE *legacyTmpNewConfig;
string configData( toString( extendedStorageInfo.config() ) );
/* 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();
if ( !( legacyTmpNewConfig = fopen( tmpFileName, "w" ) ) )
{
if ( errno != ENOENT )
{
perror( tmpFileName );
exit( EXIT_FAILURE );
}
}
fputs( toString( extendedStorageInfo.config() ).c_str(), legacyTmpNewConfig );
if ( fflush( legacyTmpNewConfig ) < EXIT_SUCCESS )
{
perror( tmpFileName );
exit( EXIT_FAILURE );
}
/* Set it to 1970 */
utimebuf.actime = 0;
utimebuf.modtime = 0;
utime( tmpFileName, &utimebuf );
sptr< File> tmpConfig = new File( tmpFileName, File::WriteOnly );
tmpConfig->writeRecords( configData.c_str(), configData.size(), 1 );
again:
rewind( legacyTmpNewConfig );
if ( ferror( legacyTmpNewConfig ) )
tmpConfig->rewind();
if ( tmpConfig->error() )
{
fprintf( stderr, "Error while writing new config to %s\n", tmpFileName );
verbosePrintf( "Error while writing config to %s\n", tmpFileName );
fatal:
unlink( tmpFileName );
tmpFile.reset();
exit( EXIT_FAILURE );
}
if ( ( ( editor = getenv( "VISUAL" )) == NULL || *editor == '\0' ) &&
( ( editor = getenv("EDITOR")) == NULL || *editor == '\0') )
{
editor = EDITOR;
}
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
@ -110,6 +88,8 @@ fatal:
shellArgs += " ";
shellArgs += tmpFileName;
pid_t pid, xpid;
switch ( pid = fork() )
{
case -1:
@ -118,7 +98,7 @@ fatal:
case 0:
/* child */
execlp( _PATH_BSHELL, _PATH_BSHELL, "-c", shellArgs.c_str(), (char *) 0 );
perror( editor );
perror( editor.c_str() );
exit( EXIT_FAILURE );
/*NOTREACHED*/
default:
@ -127,36 +107,35 @@ fatal:
}
/* parent */
int waiter;
for ( ; ; )
{
xpid = waitpid( pid, &waiter, 0 );
if ( xpid == -1 )
{
if ( errno != EINTR )
fprintf(stderr,
"waitpid() failed waiting for PID %ld from \"%s\": %s\n",
(long) pid, editor, strerror( errno ) );
verbosePrintf( "waitpid() failed waiting for PID %ld from \"%s\": %s\n",
(long) pid, editor.c_str(), strerror( errno ) );
}
else
if (xpid != pid)
{
fprintf( stderr, "wrong PID (%ld != %ld) from \"%s\"\n",
(long) xpid, (long) pid, editor);
verbosePrintf( "wrong PID (%ld != %ld) from \"%s\"\n",
(long) xpid, (long) pid, editor.c_str() );
goto fatal;
}
else
if ( WIFEXITED( waiter ) && WEXITSTATUS( waiter ) )
{
fprintf(stderr, "\"%s\" exited with status %d\n",
editor, WEXITSTATUS( waiter ) );
verbosePrintf( "\"%s\" exited with status %d\n",
editor.c_str(), WEXITSTATUS( waiter ) );
goto fatal;
}
else
if ( WIFSIGNALED( waiter ) )
{
fprintf(stderr,
"\"%s\" killed; signal %d (%score dumped)\n",
editor, WTERMSIG( waiter ),
verbosePrintf( "\"%s\" killed; signal %d (%score dumped)\n",
editor.c_str(), WTERMSIG( waiter ),
WCOREDUMP( waiter ) ? "" : "no ");
goto fatal;
}
@ -167,71 +146,45 @@ fatal:
(void) signal( SIGINT, SIG_DFL );
(void) signal( SIGQUIT, SIG_DFL );
/* lstat doesn't make any harm, because
* the file is stat'ed only when config is touched
*/
if ( lstat( tmpFileName, &statbuf ) < 0 )
{
perror( "lstat" );
goto fatal;
}
tmpConfig->close();
tmpConfig = new File( tmpFileName, File::ReadOnly );
if ( !S_ISREG( statbuf.st_mode ) )
{
fprintf( stderr, "Illegal config\n" );
goto remove;
}
string newConfigData;
newConfigData.resize( tmpConfig->size() );
tmpConfig->read( &newConfigData[ 0 ], newConfigData.size() );
ConfigInfo newConfig;
bool isChanged = false;
if ( statbuf.st_mtime == 0 )
{
fprintf( stderr, "No changes made to config\n" );
goto remove;
}
fprintf( stderr, "Installing new config\n" );
fclose( legacyTmpNewConfig );
if ( !( legacyTmpNewConfig = fopen( tmpFileName, "r+" ) ) )
{
perror("cannot read new config");
goto remove;
}
if ( legacyTmpNewConfig == 0L )
{
perror("fopen");
goto fatal;
}
try
{
File tmpNewConfig( tmpFileName, File::Update );
ConfigInfo newConfig;
string newConfigData;
newConfigData.resize( tmpNewConfig.size() );
tmpNewConfig.read( &newConfigData[ 0 ], newConfigData.size() );
parse( newConfigData, &newConfig );
tmpNewConfig.close();
ret = 0;
}
catch ( std::exception & e )
{
int ret = 0;
if ( !parse( newConfigData, &newConfig ) )
ret = -1;
else
{
if ( toString( extendedStorageInfo.config() ) == toString( newConfig ) )
{
verbosePrintf( "No changes made to config\n" );
goto end;
}
else
verbosePrintf( "Updating configuration...\n" );
}
switch ( ret )
{
case 0:
break;
goto success;
case -1:
for ( ; ; )
{
printf( "Do you want to retry the same edit? " );
fflush( stdout );
q[ 0 ] = '\0';
if ( fgets( q, sizeof q, stdin ) == 0L )
string input;
input.resize( MAX_TEMPSTR );
if ( fgets( &input[ 0 ], input.size(), stdin ) == 0L )
continue;
switch ( q[ 0 ] )
switch ( input[ 0 ] )
{
case 'y':
case 'Y':
@ -240,27 +193,34 @@ fatal:
case 'N':
goto abandon;
default:
fprintf(stderr, "Enter Y or N\n");
fprintf( stderr, "Enter Y or N\n" );
}
}
/*NOTREACHED*/
case -2:
abandon:
fprintf( stderr, "edits left in %s\n", tmpFileName );
goto done;
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;
default:
fprintf( stderr, "panic: bad switch() in replace_cmd()\n" );
verbosePrintf( "panic: bad switch()\n" );
goto fatal;
}
remove:
tmpFile.reset();
done:
verbosePrintf(
"Configuration successfully updated!\n"
"Current repo configuration:\n" );
end:
tmpConfig.reset();
tmpFile.reset();
// printf( "%s", toString( extendedStorageInfo.config() ).c_str() );
return isChanged;
}
bool ZConfig::parse( const string & str, google::protobuf::Message * mutable_message )

View File

@ -5,14 +5,8 @@
#define CONFIG_HH_INCLUDED__
#include <string>
#include <vector>
#include <google/protobuf/text_format.h>
#include "zbackup_base.hh"
#include "zbackup.pb.h"
#include "sptr.hh"
using std::string;
class ZConfig: public ZBackupBase
@ -21,10 +15,11 @@ public:
ZConfig( string const & storageDir, string const & password );
// Print current configuration to screen
void print();
void show();
// Edit current configuration in default editor
void edit();
// returns true if configuration is changed
bool edit();
private:
string toString( google::protobuf::Message const & message );

40
file.cc
View File

@ -6,6 +6,7 @@
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <sstream>
#ifdef __APPLE__
#include <sys/socket.h>
#else
@ -104,6 +105,32 @@ void File::open( char const * filename, OpenMode mode ) throw( exCantOpen )
throw exCantOpen( std::string( filename ) + ": " + strerror( errno ) );
}
void File::open( int fd, OpenMode mode ) throw( exCantOpen )
{
char const * m;
switch( mode )
{
case Update:
m = "r+b";
break;
case WriteOnly:
m = "wb";
break;
default:
m = "rb";
}
f = fdopen( fd, m );
if ( !f )
{
std::ostringstream strFd;
strFd << fd;
throw exCantOpen( "fd#" + strFd.str() + ": " + strerror( errno ) );
}
}
File::File( char const * filename, OpenMode mode ) throw( exCantOpen ):
writeBuffer( 0 )
{
@ -116,6 +143,12 @@ File::File( std::string const & filename, OpenMode mode )
open( filename.c_str(), mode );
}
File::File( int fd, OpenMode mode )
throw( exCantOpen ): writeBuffer( 0 )
{
open( fd, mode );
}
void File::read( void * buf, size_t size ) throw( exReadError, exWriteError )
{
if ( !size )
@ -302,6 +335,13 @@ bool File::eof() throw( exWriteError )
return feof( f );
}
int File::error() throw( exReadError )
{
int result = ferror( f );
return result;
}
FILE * File::file() throw( exWriteError )
{
flushWriteBuffer();

View File

@ -45,6 +45,9 @@ public:
File( std::string const & filename, OpenMode )
throw( exCantOpen );
File( int fd, OpenMode )
throw( exCantOpen );
/// Reads the number of bytes to the buffer, throws an error if it
/// failed to fill the whole buffer (short read, i/o error etc)
void read( void * buf, size_t size ) throw( exReadError, exWriteError );
@ -108,6 +111,9 @@ public:
/// Returns true if end-of-file condition is set
bool eof() throw( exWriteError );
/// Returns ferror
int error() throw( exReadError );
/// Returns the underlying FILE * record, so other operations can be
/// performed on it
FILE * file() throw( exWriteError );
@ -153,6 +159,7 @@ public:
private:
void open( char const * filename, OpenMode ) throw( exCantOpen );
void open( int fd, OpenMode ) throw( exCantOpen );
void flushWriteBuffer() throw( exWriteError );
void releaseWriteBuffer() throw( exWriteError );
};

View File

@ -491,7 +491,7 @@ int main( int argc, char *argv[] )
"Comes with no warranty. Licensed under GNU GPLv2 or later + OpenSSL.\n"
"Visit the project's home page at http://zbackup.org/\n\n"
"Usage: %s [flags] <command> [command args]\n"
"Usage: %s [flags] <command [action]> [command args]\n"
" Flags: --non-encrypted|--password-file <file>\n"
" password flag should be specified twice if import/export/passwd\n"
" command specified\n"
@ -515,8 +515,8 @@ int main( int argc, char *argv[] )
" gc <storage path> - performs chunk garbage collection\n"
" passwd <storage path> - changes repository info file passphrase\n"
" info <storage path> - shows information about storage\n"
" config <storage path> [edit|print] - performs configuration"
" manipulations\n"
" config [show|edit] <storage path> - performs configuration\n"
" manipulations (default is print)\n"
" For export/import storage path must be valid (initialized) storage\n"
"", *argv,
defaultThreads, defaultCacheSizeMb );
@ -676,18 +676,30 @@ int main( int argc, char *argv[] )
// Show repo info
if ( args.size() < 2 || args.size() > 3 )
{
fprintf( stderr, "Usage: %s %s <storage path>\n",
fprintf( stderr, "Usage: %s %s [show|edit] <storage path>\n",
*argv, args[ 0 ] );
return EXIT_FAILURE;
}
ZConfig zc( ZBackupBase::deriveStorageDirFromBackupsFile( args[ 1 ], true ),
int fieldStor = 1;
int fieldAct = 2;
if ( args.size() == 3 )
{
fieldStor = 2;
fieldAct = 1;
}
ZConfig zc( ZBackupBase::deriveStorageDirFromBackupsFile( args[ fieldStor ], true ),
passwords[ 0 ] );
if ( args.size() > 2 && strcmp( args[ 2 ], "edit" ) == 0 )
zc.edit();
if ( args.size() > 2 && strcmp( args[ fieldAct ], "edit" ) == 0 )
{
if ( zc.edit() )
zc.saveExtendedStorageInfo();
}
else
zc.print();
zc.show();
}
else
{