summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore37
-rw-r--r--.svnrevision2
-rw-r--r--LICENSE.txt4
-rw-r--r--bin/bbackupctl/bbackupctl.cpp19
-rw-r--r--bin/bbackupd/BackupClientContext.cpp27
-rw-r--r--bin/bbackupd/BackupClientContext.h19
-rw-r--r--bin/bbackupd/BackupClientDeleteList.cpp80
-rw-r--r--bin/bbackupd/BackupClientDeleteList.h34
-rw-r--r--bin/bbackupd/BackupClientDirectoryRecord.cpp576
-rw-r--r--bin/bbackupd/BackupClientDirectoryRecord.h159
-rw-r--r--bin/bbackupd/BackupClientInodeToIDMap.cpp11
-rw-r--r--bin/bbackupd/BackupDaemon.cpp1699
-rw-r--r--bin/bbackupd/BackupDaemon.h148
-rw-r--r--bin/bbackupd/BackupDaemonInterface.h164
-rw-r--r--bin/bbackupd/Win32ServiceFunctions.cpp8
-rwxr-xr-xbin/bbackupd/bbackupd-config.in6
-rw-r--r--bin/bbackupd/bbackupd.cpp2
-rw-r--r--bin/bbackupd/win32/NotifySysAdmin.vbs62
-rw-r--r--bin/bbackupd/win32/bbackupd.conf2
-rw-r--r--bin/bbackupquery/BackupQueries.cpp1240
-rw-r--r--bin/bbackupquery/BackupQueries.h280
-rw-r--r--bin/bbackupquery/BoxBackupCompareParams.h107
-rw-r--r--bin/bbackupquery/bbackupquery.cpp79
-rw-r--r--bin/bbackupquery/documentation.txt22
-rw-r--r--bin/bbstoreaccounts/bbstoreaccounts.cpp112
-rw-r--r--bin/bbstored/BBStoreDHousekeeping.cpp16
-rw-r--r--bin/bbstored/BackupCommands.cpp165
-rw-r--r--bin/bbstored/BackupConstants.h2
-rw-r--r--bin/bbstored/BackupStoreContext.cpp (renamed from bin/bbstored/BackupContext.cpp)211
-rw-r--r--bin/bbstored/BackupStoreContext.h (renamed from bin/bbstored/BackupContext.h)50
-rw-r--r--bin/bbstored/BackupStoreDaemon.cpp34
-rw-r--r--bin/bbstored/BackupStoreDaemon.h15
-rw-r--r--bin/bbstored/HousekeepStoreAccount.cpp274
-rw-r--r--bin/bbstored/HousekeepStoreAccount.h3
-rw-r--r--bin/bbstored/backupprotocol.txt8
-rwxr-xr-xbin/bbstored/bbstored-config.in4
-rw-r--r--bin/bbstored/bbstored.cpp2
-rwxr-xr-xcleanupforcvs.pl4
-rw-r--r--configure.ac105
-rwxr-xr-xcontrib/bbadmin/accounts.cgi580
-rw-r--r--contrib/bbadmin/apache.conf14
-rw-r--r--contrib/bbadmin/bb.css70
-rw-r--r--contrib/bbreporter/LICENSE674
-rwxr-xr-xcontrib/bbreporter/bbreporter.py538
-rw-r--r--contrib/debian/bbackupd.in63
-rw-r--r--contrib/debian/bbstored.in57
-rw-r--r--contrib/mac_osx/org.boxbackup.bbackupd.plist.in20
-rw-r--r--contrib/mac_osx/org.boxbackup.bbstored.plist.in21
-rw-r--r--contrib/redhat/bbackupd.in8
-rw-r--r--contrib/redhat/bbstored.in8
-rw-r--r--contrib/rpm/boxbackup.spec20
-rwxr-xr-xcontrib/solaris/bbackupd-smf-method.in2
-rwxr-xr-xcontrib/solaris/bbstored-smf-method.in2
-rw-r--r--contrib/suse/bbackupd.in4
-rw-r--r--contrib/suse/bbstored.in4
-rwxr-xr-xcontrib/windows/installer/boxbackup.mpi.in3392
-rwxr-xr-xcontrib/windows/installer/tools/InstallService.bat3
-rwxr-xr-xcontrib/windows/installer/tools/KillBackupProcess.bat3
-rwxr-xr-xcontrib/windows/installer/tools/QueryOutputAll.bat5
-rwxr-xr-xcontrib/windows/installer/tools/QueryOutputCurrent.bat5
-rwxr-xr-xcontrib/windows/installer/tools/ReloadConfig.bat3
-rwxr-xr-xcontrib/windows/installer/tools/RemoteControl.exebin0 -> 156536 bytes
-rwxr-xr-xcontrib/windows/installer/tools/RemoveService.bat3
-rwxr-xr-xcontrib/windows/installer/tools/RestartService.bat5
-rwxr-xr-xcontrib/windows/installer/tools/ShowUsage.bat3
-rwxr-xr-xcontrib/windows/installer/tools/StartService.bat3
-rwxr-xr-xcontrib/windows/installer/tools/StopService.bat3
-rwxr-xr-xcontrib/windows/installer/tools/Sync.bat3
-rw-r--r--distribution/COMMON-MANIFEST.txt17
-rw-r--r--distribution/boxbackup/CONTACT.txt6
-rw-r--r--distribution/boxbackup/DISTRIBUTION-MANIFEST.txt42
-rw-r--r--distribution/boxbackup/DOCUMENTATION.txt2
-rw-r--r--distribution/boxbackup/VERSION.txt2
-rw-r--r--docs/Makefile129
-rw-r--r--docs/api-notes/INDEX.txt (renamed from docs/backup/INDEX.txt)10
-rw-r--r--docs/api-notes/Win32_Clients.txt (renamed from docs/backup/Win32_Clients.txt)0
-rw-r--r--docs/api-notes/backup_encryption.txt (renamed from docs/backup/backup_encryption.txt)0
-rw-r--r--docs/api-notes/bin_bbackupd.txt (renamed from docs/backup/bin_bbackupd.txt)0
-rw-r--r--docs/api-notes/bin_bbstored.txt (renamed from docs/backup/bin_bbstored.txt)0
-rw-r--r--docs/api-notes/common/lib_common.txt (renamed from docs/common/lib_common.txt)0
-rw-r--r--docs/api-notes/common/lib_common/BoxTime.txt (renamed from docs/common/lib_common/BoxTime.txt)0
-rw-r--r--docs/api-notes/common/lib_common/CollectInBufferStream.txt (renamed from docs/common/lib_common/CollectInBufferStream.txt)0
-rw-r--r--docs/api-notes/common/lib_common/Configuration.txt (renamed from docs/common/lib_common/Configuration.txt)0
-rw-r--r--docs/api-notes/common/lib_common/Conversion.txt (renamed from docs/common/lib_common/Conversion.txt)0
-rw-r--r--docs/api-notes/common/lib_common/ExcludeList.txt (renamed from docs/common/lib_common/ExcludeList.txt)0
-rw-r--r--docs/api-notes/common/lib_common/FdGetLine.txt (renamed from docs/common/lib_common/FdGetLine.txt)0
-rw-r--r--docs/api-notes/common/lib_common/Guards.txt (renamed from docs/common/lib_common/Guards.txt)0
-rw-r--r--docs/api-notes/common/lib_common/IOStream.txt (renamed from docs/common/lib_common/IOStream.txt)0
-rw-r--r--docs/api-notes/common/lib_common/IOStreamGetLine.txt (renamed from docs/common/lib_common/IOStreamGetLine.txt)0
-rw-r--r--docs/api-notes/common/lib_common/MainHelper.txt (renamed from docs/common/lib_common/MainHelper.txt)0
-rw-r--r--docs/api-notes/common/lib_common/WaitForEvent.txt (renamed from docs/common/lib_common/WaitForEvent.txt)0
-rw-r--r--docs/api-notes/common/lib_common/xStream.txt (renamed from docs/common/lib_common/xStream.txt)0
-rw-r--r--docs/api-notes/common/lib_compress.txt (renamed from docs/common/lib_compress.txt)0
-rw-r--r--docs/api-notes/common/lib_compress/CompressStream.txt (renamed from docs/common/lib_compress/CompressStream.txt)0
-rw-r--r--docs/api-notes/common/lib_crypto.txt (renamed from docs/common/lib_crypto.txt)0
-rw-r--r--docs/api-notes/common/lib_crypto/CipherContext.txt (renamed from docs/common/lib_crypto/CipherContext.txt)0
-rw-r--r--docs/api-notes/common/lib_crypto/RollingChecksum.txt (renamed from docs/common/lib_crypto/RollingChecksum.txt)0
-rw-r--r--docs/api-notes/common/lib_server.txt (renamed from docs/common/lib_server.txt)0
-rw-r--r--docs/api-notes/common/lib_server/Daemon.txt (renamed from docs/common/lib_server/Daemon.txt)0
-rw-r--r--docs/api-notes/common/lib_server/Protocol.txt (renamed from docs/common/lib_server/Protocol.txt)0
-rw-r--r--docs/api-notes/common/lib_server/ServerStream.txt (renamed from docs/common/lib_server/ServerStream.txt)0
-rw-r--r--docs/api-notes/common/lib_server/ServerTLS.txt (renamed from docs/common/lib_server/ServerTLS.txt)0
-rw-r--r--docs/api-notes/common/lib_server/SocketStream.txt (renamed from docs/common/lib_server/SocketStream.txt)0
-rw-r--r--docs/api-notes/common/lib_server/SocketStreamTLS.txt (renamed from docs/common/lib_server/SocketStreamTLS.txt)0
-rw-r--r--docs/api-notes/common/lib_server/TLSContext.txt (renamed from docs/common/lib_server/TLSContext.txt)0
-rw-r--r--docs/api-notes/common/memory_leaks.txt (renamed from docs/common/memory_leaks.txt)0
-rw-r--r--docs/api-notes/encrypt_rsync.txt (renamed from docs/backup/encrypt_rsync.txt)0
-rw-r--r--docs/api-notes/lib_backupclient.txt (renamed from docs/backup/lib_backupclient.txt)0
-rw-r--r--docs/api-notes/lib_backupstore.txt (renamed from docs/backup/lib_backupstore.txt)0
-rw-r--r--docs/api-notes/raidfile/RaidFileRead.txt (renamed from docs/raidfile/lib_raidfile/RaidFileRead.txt)0
-rw-r--r--docs/api-notes/raidfile/RaidFileWrite.txt (renamed from docs/raidfile/lib_raidfile/RaidFileWrite.txt)0
-rw-r--r--docs/api-notes/raidfile/lib_raidfile.txt (renamed from docs/raidfile/lib_raidfile.txt)0
-rw-r--r--docs/api-notes/win32_build_on_cygwin_using_mingw.txt107
-rw-r--r--docs/api-notes/win32_build_on_linux_using_mingw.txt108
-rw-r--r--docs/api-notes/windows_porting.txt (renamed from docs/backup/windows_porting.txt)0
-rw-r--r--docs/backup/win32_build_on_cygwin_using_mingw.txt53
-rw-r--r--docs/backup/win32_build_on_linux_using_mingw.txt64
-rw-r--r--docs/docbook/adminguide.xml (renamed from documentation/adminguide.xml)46
-rw-r--r--docs/docbook/bb-book.xsl (renamed from documentation/bb-book.xsl)0
-rw-r--r--docs/docbook/bb-man.xsl.tmpl (renamed from documentation/bb-man.xsl)2
-rw-r--r--docs/docbook/bb-nochunk-book.xsl (renamed from documentation/bb-nochunk-book.xsl)0
-rw-r--r--docs/docbook/bbackupctl.xml205
-rw-r--r--docs/docbook/bbackupd-config.xml153
-rw-r--r--docs/docbook/bbackupd.conf.xml479
-rw-r--r--docs/docbook/bbackupd.xml209
-rw-r--r--docs/docbook/bbackupquery.xml506
-rw-r--r--docs/docbook/bbstoreaccounts.xml386
-rw-r--r--docs/docbook/bbstored-certs.xml180
-rw-r--r--docs/docbook/bbstored-config.xml148
-rw-r--r--docs/docbook/bbstored.conf.xml211
-rw-r--r--docs/docbook/bbstored.xml90
-rw-r--r--docs/docbook/html/bbdoc-man.css (renamed from documentation/html/bbdoc-man.css)0
-rw-r--r--docs/docbook/html/bbdoc.css (renamed from documentation/html/bbdoc.css)0
-rw-r--r--docs/docbook/html/favicon.icobin0 -> 67646 bytes
-rw-r--r--docs/docbook/html/images/arrow.png (renamed from documentation/html/images/arrow.png)bin197 -> 197 bytes
-rw-r--r--docs/docbook/html/images/bblogo.png (renamed from documentation/html/images/bblogo.png)bin5882 -> 5882 bytes
-rw-r--r--docs/docbook/html/images/stepahead.png (renamed from documentation/html/images/stepahead.png)bin298 -> 298 bytes
-rw-r--r--docs/docbook/instguide.xml (renamed from documentation/instguide.xml)10
-rw-r--r--docs/docbook/raidfile-config.xml198
-rw-r--r--docs/docbook/raidfile.conf.xml143
-rw-r--r--docs/images/bblogo-alpha.xcfbin0 -> 93980 bytes
-rw-r--r--docs/tools/generate_except_xml.pl (renamed from documentation/generate_except_xml.pl)6
-rw-r--r--documentation/Makefile66
-rw-r--r--documentation/bbackupctl.xml147
-rw-r--r--documentation/bbackupquery.xml380
-rw-r--r--documentation/bbstoreaccounts.xml290
-rw-r--r--documentation/bbstored-certs.xml125
-rw-r--r--documentation/bbstored-config.xml140
-rw-r--r--documentation/raidfile-config.xml143
-rw-r--r--infrastructure/BoxPlatform.pm.in50
-rw-r--r--infrastructure/buildenv-testmain-template.cpp103
-rw-r--r--infrastructure/m4/ax_bswap64.m412
-rw-r--r--infrastructure/m4/ax_check_define_pragma.m46
-rw-r--r--infrastructure/m4/ax_check_dirent_d_type.m412
-rw-r--r--infrastructure/m4/ax_check_malloc_workaround.m412
-rw-r--r--infrastructure/m4/ax_check_nonaligned_access.m418
-rw-r--r--infrastructure/m4/ax_check_syscall_lseek.m412
-rw-r--r--infrastructure/m4/ax_func_syscall.m423
-rwxr-xr-xinfrastructure/makebuildenv.pl.in125
-rwxr-xr-xinfrastructure/makedistribution.pl.in67
-rwxr-xr-xinfrastructure/makeparcels.pl.in185
-rw-r--r--infrastructure/msvc/2003/bbackupctl.vcproj4
-rw-r--r--infrastructure/msvc/2003/bbackupd.vcproj4
-rw-r--r--infrastructure/msvc/2003/boxquery.vcproj4
-rw-r--r--infrastructure/msvc/2003/common.vcproj4
-rw-r--r--infrastructure/msvc/2003/win32test.vcproj2
-rw-r--r--infrastructure/msvc/2005/bbackupctl.vcproj4
-rw-r--r--infrastructure/msvc/2005/bbackupd.vcproj4
-rw-r--r--infrastructure/msvc/2005/boxquery.vcproj6
-rw-r--r--infrastructure/msvc/2005/common.vcproj4
-rw-r--r--infrastructure/msvc/2005/win32test.vcproj4
-rw-r--r--infrastructure/parcelpath.pl17
-rw-r--r--infrastructure/printversion.pl12
-rw-r--r--lib/backupclient/BackupClientCryptoKeys.cpp52
-rw-r--r--lib/backupclient/BackupClientCryptoKeys.h2
-rw-r--r--lib/backupclient/BackupClientFileAttributes.cpp64
-rw-r--r--lib/backupclient/BackupClientFileAttributes.h8
-rw-r--r--lib/backupclient/BackupClientRestore.cpp289
-rw-r--r--lib/backupclient/BackupClientRestore.h19
-rw-r--r--lib/backupclient/BackupDaemonConfigVerify.cpp97
-rw-r--r--lib/backupclient/BackupStoreFile.cpp27
-rw-r--r--lib/backupclient/BackupStoreFile.h17
-rw-r--r--lib/backupclient/BackupStoreFileDiff.cpp69
-rw-r--r--lib/backupclient/BackupStoreFileEncodeStream.cpp69
-rw-r--r--lib/backupclient/BackupStoreFileEncodeStream.h10
-rw-r--r--lib/backupclient/BackupStoreFilename.cpp32
-rw-r--r--lib/backupclient/BackupStoreFilename.h24
-rw-r--r--lib/backupclient/BackupStoreFilenameClear.cpp23
-rw-r--r--lib/backupclient/BackupStoreObjectDump.cpp21
-rw-r--r--lib/backupclient/RunStatusProvider.h29
-rw-r--r--lib/backupstore/BackupStoreAccountDatabase.cpp4
-rw-r--r--lib/backupstore/BackupStoreCheck.cpp3
-rw-r--r--lib/backupstore/BackupStoreCheck.h4
-rw-r--r--lib/backupstore/BackupStoreCheck2.cpp271
-rw-r--r--lib/backupstore/BackupStoreCheckData.cpp15
-rw-r--r--lib/backupstore/BackupStoreConfigVerify.cpp17
-rw-r--r--lib/backupstore/BackupStoreInfo.cpp4
-rw-r--r--lib/backupstore/StoreStructure.h2
-rw-r--r--lib/common/Box.h59
-rw-r--r--lib/common/BoxException.h1
-rw-r--r--lib/common/BoxPlatform.h37
-rw-r--r--lib/common/BoxTime.cpp46
-rw-r--r--lib/common/BoxTime.h3
-rw-r--r--lib/common/Configuration.cpp335
-rw-r--r--lib/common/Configuration.h76
-rw-r--r--lib/common/DebugAssertFailed.cpp4
-rw-r--r--lib/common/DebugMemLeakFinder.cpp58
-rw-r--r--lib/common/DebugPrintf.cpp4
-rw-r--r--lib/common/EventWatchFilesystemObject.cpp43
-rw-r--r--lib/common/FdGetLine.h2
-rw-r--r--lib/common/FileModificationTime.h23
-rw-r--r--lib/common/FileStream.cpp110
-rw-r--r--lib/common/FileStream.h21
-rw-r--r--lib/common/Guards.h4
-rw-r--r--lib/common/IOStream.cpp36
-rw-r--r--lib/common/IOStream.h10
-rw-r--r--lib/common/IOStreamGetLine.h2
-rw-r--r--lib/common/Logging.cpp233
-rw-r--r--lib/common/Logging.h138
-rw-r--r--lib/common/NamedLock.cpp8
-rw-r--r--lib/common/PartialReadStream.cpp3
-rw-r--r--lib/common/ReadLoggingStream.cpp28
-rw-r--r--lib/common/ReadLoggingStream.h19
-rw-r--r--lib/common/SelfFlushingStream.h71
-rw-r--r--lib/common/Test.cpp418
-rw-r--r--lib/common/Test.h430
-rw-r--r--lib/common/Timer.cpp308
-rw-r--r--lib/common/Timer.h36
-rw-r--r--lib/common/Utils.cpp179
-rw-r--r--lib/common/Utils.h5
-rw-r--r--lib/common/WaitForEvent.h2
-rwxr-xr-xlib/common/makeexception.pl.in12
-rw-r--r--lib/compress/Compress.h8
-rw-r--r--lib/compress/CompressStream.cpp4
-rw-r--r--lib/crypto/CipherContext.cpp15
-rw-r--r--lib/httpserver/HTTPException.txt16
-rw-r--r--lib/httpserver/HTTPQueryDecoder.cpp159
-rw-r--r--lib/httpserver/HTTPQueryDecoder.h47
-rw-r--r--lib/httpserver/HTTPRequest.cpp783
-rw-r--r--lib/httpserver/HTTPRequest.h174
-rw-r--r--lib/httpserver/HTTPResponse.cpp648
-rw-r--r--lib/httpserver/HTTPResponse.h175
-rw-r--r--lib/httpserver/HTTPServer.cpp249
-rw-r--r--lib/httpserver/HTTPServer.h79
-rw-r--r--lib/httpserver/Makefile.extra7
-rw-r--r--lib/httpserver/S3Client.cpp243
-rw-r--r--lib/httpserver/S3Client.h72
-rw-r--r--lib/httpserver/cdecode.cpp92
-rw-r--r--lib/httpserver/cdecode.h28
-rw-r--r--lib/httpserver/cencode.cpp113
-rw-r--r--lib/httpserver/cencode.h32
-rw-r--r--lib/httpserver/decode.h77
-rw-r--r--lib/httpserver/encode.h87
-rw-r--r--lib/intercept/intercept.cpp141
-rw-r--r--lib/intercept/intercept.h21
-rw-r--r--lib/raidfile/RaidFileController.cpp17
-rw-r--r--lib/raidfile/RaidFileRead.cpp8
-rw-r--r--lib/raidfile/RaidFileUtil.cpp58
-rw-r--r--lib/raidfile/RaidFileWrite.cpp11
-rw-r--r--lib/server/Daemon.cpp328
-rw-r--r--lib/server/Daemon.h28
-rw-r--r--lib/server/OverlappedIO.h42
-rw-r--r--lib/server/Protocol.cpp23
-rw-r--r--lib/server/ProtocolUncertainStream.cpp21
-rw-r--r--lib/server/SSLLib.cpp44
-rw-r--r--lib/server/SSLLib.h4
-rw-r--r--lib/server/ServerControl.cpp227
-rw-r--r--lib/server/ServerControl.h188
-rw-r--r--lib/server/ServerStream.h22
-rw-r--r--lib/server/ServerTLS.h12
-rw-r--r--lib/server/Socket.cpp15
-rw-r--r--lib/server/Socket.h6
-rw-r--r--lib/server/SocketListen.h85
-rw-r--r--lib/server/SocketStream.cpp101
-rw-r--r--lib/server/SocketStream.h3
-rw-r--r--lib/server/SocketStreamTLS.cpp21
-rw-r--r--lib/server/SocketStreamTLS.h3
-rw-r--r--lib/server/TLSContext.cpp14
-rw-r--r--lib/server/WinNamedPipeListener.h232
-rw-r--r--lib/server/WinNamedPipeStream.cpp153
-rw-r--r--lib/server/WinNamedPipeStream.h7
-rwxr-xr-xlib/server/makeprotocol.pl.in161
-rw-r--r--lib/win32/emu.cpp363
-rw-r--r--lib/win32/emu.h83
-rwxr-xr-xlib/win32/getopt_long.cpp (renamed from lib/win32/getopt_long.cxx)2
-rw-r--r--modules.txt4
-rw-r--r--parcels.txt25
-rwxr-xr-xruntest.pl.in21
-rw-r--r--test/backupdiff/testbackupdiff.cpp83
-rw-r--r--test/backupstore/testbackupstore.cpp52
-rw-r--r--test/backupstorefix/testbackupstorefix.cpp9
-rwxr-xr-xtest/backupstorefix/testfiles/testbackupstorefix.pl.in16
-rw-r--r--test/basicserver/testbasicserver.cpp4
-rw-r--r--test/bbackupd/Makefile.extra15
-rw-r--r--test/bbackupd/testbbackupd.cpp1747
-rw-r--r--test/bbackupd/testfiles/bbackupd-exclude.conf.in47
-rw-r--r--test/bbackupd/testfiles/bbackupd-snapshot.conf.in56
-rw-r--r--test/bbackupd/testfiles/bbackupd-symlink.conf.in55
-rw-r--r--test/bbackupd/testfiles/bbackupd-temploc.conf3
-rw-r--r--test/bbackupd/testfiles/bbackupd.conf.in1
-rw-r--r--test/bbackupd/testfiles/bbstored.conf2
-rwxr-xr-xtest/bbackupd/testfiles/extcheck1.pl.in5
-rwxr-xr-xtest/bbackupd/testfiles/extcheck2.pl.in5
-rwxr-xr-xtest/bbackupd/testfiles/notifyscript.pl.in11
-rw-r--r--test/common/testcommon.cpp57
-rw-r--r--test/compress/testcompress.cpp3
-rw-r--r--test/httpserver/testfiles/httpserver.conf8
-rw-r--r--test/httpserver/testfiles/photos/puppy.jpg1
-rwxr-xr-xtest/httpserver/testfiles/testrequests.pl143
-rw-r--r--test/httpserver/testhttpserver.cpp661
-rw-r--r--test/raidfile/testraidfile.cpp3
311 files changed, 22828 insertions, 6502 deletions
diff --git a/.hgignore b/.hgignore
new file mode 100644
index 00000000..c21cefc6
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,37 @@
+.svn
+BoxConfig.h
+BoxConfig.h.in
+BoxPlatform.pm
+BoxPortsAndFiles.h
+ExceptionCodes.txt
+Makefile
+_main.cpp
+_t
+_t-gdb
+aclocal.m4
+autogen_*
+autom4te.cache
+bbackupd-config
+bbackupd.conf
+bbstored-certs
+bbstored-config
+config.log
+config.status
+configure
+debug
+extcheck1.pl
+extcheck2.pl
+local
+makebuildenv.pl
+makedistribution.pl
+makedocumentation.pl
+makeexception.pl
+makeparcels.pl
+makeprotocol.pl
+notifyscript.pl
+parcels
+raidfile-config
+release
+runtest.pl
+syncallowscript.pl
+testbackupstorefix.pl
diff --git a/.svnrevision b/.svnrevision
index 4adb23b1..951ba797 100644
--- a/.svnrevision
+++ b/.svnrevision
@@ -1 +1 @@
-2072
+2491
diff --git a/LICENSE.txt b/LICENSE.txt
index d08aa509..d53bfd71 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
-Box Backup, http://www.fluffy.co.uk/boxbackup
+Box Backup, http://www.boxbackup.org/
-Copyright (c) 2003-2007 Ben Summers and contributors. All rights reserved.
+Copyright (c) 2003-2008 Ben Summers and contributors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
diff --git a/bin/bbackupctl/bbackupctl.cpp b/bin/bbackupctl/bbackupctl.cpp
index 3795cbed..2c41f614 100644
--- a/bin/bbackupctl/bbackupctl.cpp
+++ b/bin/bbackupctl/bbackupctl.cpp
@@ -15,6 +15,8 @@
#include <unistd.h>
#endif
+#include <cstdlib>
+
#include "MainHelper.h"
#include "BoxPortsAndFiles.h"
#include "BackupDaemonConfigVerify.h"
@@ -61,9 +63,7 @@ int main(int argc, const char *argv[])
MAINHELPER_START
-#if defined WIN32 && ! defined NDEBUG
- ::openlog("Box Backup (bbackupctl)", 0, 0);
-#endif
+ Logging::SetProgramName("bbackupctl");
// Filename for configuration file?
std::string configFilename;
@@ -256,7 +256,8 @@ int main(int argc, const char *argv[])
case SyncAndWaitForEnd:
{
// send a sync command
- std::string cmd("force-sync\n");
+ commandName = "force-sync";
+ std::string cmd = commandName + "\n";
connection.Write(cmd.c_str(), cmd.size());
connection.WriteAllBuffered();
@@ -336,13 +337,17 @@ int main(int argc, const char *argv[])
{
if(!quiet)
{
- BOX_INFO("Succeeded.");
+ BOX_INFO("Control command "
+ "sent: " <<
+ commandName);
}
finished = true;
}
else if(line == "error")
{
- BOX_ERROR("Check command spelling");
+ BOX_ERROR("Control command failed: " <<
+ commandName << ". Check "
+ "command spelling.");
returnCode = 1;
finished = true;
}
@@ -352,7 +357,7 @@ int main(int argc, const char *argv[])
MAINHELPER_END
-#if defined WIN32 && ! defined NDEBUG
+#if defined WIN32 && ! defined BOX_RELEASE_BUILD
closelog();
#endif
diff --git a/bin/bbackupd/BackupClientContext.cpp b/bin/bbackupd/BackupClientContext.cpp
index 4b4efd90..b978f54c 100644
--- a/bin/bbackupd/BackupClientContext.cpp
+++ b/bin/bbackupd/BackupClientContext.cpp
@@ -41,17 +41,20 @@
// --------------------------------------------------------------------------
BackupClientContext::BackupClientContext
(
- BackupDaemon &rDaemon,
+ LocationResolver &rResolver,
TLSContext &rTLSContext,
const std::string &rHostname,
+ int Port,
int32_t AccountNumber,
bool ExtendedLogging,
bool ExtendedLogToFile,
- std::string ExtendedLogFile
+ std::string ExtendedLogFile,
+ ProgressNotifier& rProgressNotifier
)
- : mrDaemon(rDaemon),
+ : mrResolver(rResolver),
mrTLSContext(rTLSContext),
mHostname(rHostname),
+ mPort(Port),
mAccountNumber(AccountNumber),
mpSocket(0),
mpConnection(0),
@@ -66,8 +69,9 @@ BackupClientContext::BackupClientContext
mStorageLimitExceeded(false),
mpExcludeFiles(0),
mpExcludeDirs(0),
- mKeepAliveTimer(0),
- mbIsManaged(false)
+ mKeepAliveTimer(0, "KeepAliveTime"),
+ mbIsManaged(false),
+ mrProgressNotifier(rProgressNotifier)
{
}
@@ -129,7 +133,8 @@ BackupProtocolClient &BackupClientContext::GetConnection()
mHostname << "'...");
// Connect!
- mpSocket->Open(mrTLSContext, Socket::TypeINET, mHostname.c_str(), BOX_PORT_BBSTORED);
+ mpSocket->Open(mrTLSContext, Socket::TypeINET,
+ mHostname.c_str(), mPort);
// And create a procotol object
mpConnection = new BackupProtocolClient(*mpSocket);
@@ -146,8 +151,8 @@ BackupProtocolClient &BackupClientContext::GetConnection()
if (!mpExtendedLogFileHandle)
{
- BOX_ERROR("Failed to open extended log "
- "file: " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to open extended "
+ "log file: " << mExtendedLogFile);
}
else
{
@@ -465,7 +470,7 @@ bool BackupClientContext::FindFilename(int64_t ObjectID, int64_t ContainingDirec
{
// Location name -- look up in daemon's records
std::string locPath;
- if(!mrDaemon.FindLocationPathName(elementName.GetClearFilename(), locPath))
+ if(!mrResolver.FindLocationPathName(elementName.GetClearFilename(), locPath))
{
// Didn't find the location... so can't give the local filename
return false;
@@ -504,7 +509,7 @@ void BackupClientContext::SetKeepAliveTime(int iSeconds)
{
mKeepAliveTime = iSeconds < 0 ? 0 : iSeconds;
BOX_TRACE("Set keep-alive time to " << mKeepAliveTime << " seconds");
- mKeepAliveTimer = Timer(mKeepAliveTime);
+ mKeepAliveTimer = Timer(mKeepAliveTime, "KeepAliveTime");
}
// --------------------------------------------------------------------------
@@ -564,7 +569,7 @@ void BackupClientContext::DoKeepAlive()
BOX_TRACE("KeepAliveTime reached, sending keep-alive message");
mpConnection->QueryGetIsAlive();
- mKeepAliveTimer = Timer(mKeepAliveTime);
+ mKeepAliveTimer = Timer(mKeepAliveTime, "KeepAliveTime");
}
int BackupClientContext::GetMaximumDiffingTime()
diff --git a/bin/bbackupd/BackupClientContext.h b/bin/bbackupd/BackupClientContext.h
index 152d8556..4665df2b 100644
--- a/bin/bbackupd/BackupClientContext.h
+++ b/bin/bbackupd/BackupClientContext.h
@@ -12,6 +12,8 @@
#include "BoxTime.h"
#include "BackupClientDeleteList.h"
+#include "BackupClientDirectoryRecord.h"
+#include "BackupDaemonInterface.h"
#include "BackupStoreFile.h"
#include "ExcludeList.h"
#include "Timer.h"
@@ -25,6 +27,7 @@ class BackupStoreFilenameClear;
#include <string>
+
// --------------------------------------------------------------------------
//
// Class
@@ -38,13 +41,15 @@ class BackupClientContext : public DiffTimer
public:
BackupClientContext
(
- BackupDaemon &rDaemon,
+ LocationResolver &rResolver,
TLSContext &rTLSContext,
const std::string &rHostname,
+ int32_t Port,
int32_t AccountNumber,
bool ExtendedLogging,
bool ExtendedLogToFile,
- std::string ExtendedLogFile
+ std::string ExtendedLogFile,
+ ProgressNotifier &rProgressNotifier
);
virtual ~BackupClientContext();
private:
@@ -69,6 +74,7 @@ public:
int64_t GetClientStoreMarker() const {return mClientStoreMarker;}
bool StorageLimitExceeded() {return mStorageLimitExceeded;}
+ void SetStorageLimitExceeded() {mStorageLimitExceeded = true;}
// --------------------------------------------------------------------------
//
@@ -197,10 +203,16 @@ public:
virtual int GetMaximumDiffingTime();
virtual bool IsManaged() { return mbIsManaged; }
+ ProgressNotifier& GetProgressNotifier() const
+ {
+ return mrProgressNotifier;
+ }
+
private:
- BackupDaemon &mrDaemon;
+ LocationResolver &mrResolver;
TLSContext &mrTLSContext;
std::string mHostname;
+ int mPort;
int32_t mAccountNumber;
SocketStreamTLS *mpSocket;
BackupProtocolClient *mpConnection;
@@ -219,6 +231,7 @@ private:
bool mbIsManaged;
int mKeepAliveTime;
int mMaximumDiffingTime;
+ ProgressNotifier &mrProgressNotifier;
};
#endif // BACKUPCLIENTCONTEXT__H
diff --git a/bin/bbackupd/BackupClientDeleteList.cpp b/bin/bbackupd/BackupClientDeleteList.cpp
index f6d8e0dc..b9b5b53e 100644
--- a/bin/bbackupd/BackupClientDeleteList.cpp
+++ b/bin/bbackupd/BackupClientDeleteList.cpp
@@ -42,21 +42,38 @@ BackupClientDeleteList::~BackupClientDeleteList()
{
}
+BackupClientDeleteList::FileToDelete::FileToDelete(int64_t DirectoryID,
+ const BackupStoreFilename& rFilename,
+ const std::string& rLocalPath)
+: mDirectoryID(DirectoryID),
+ mFilename(rFilename),
+ mLocalPath(rLocalPath)
+{ }
+
+BackupClientDeleteList::DirToDelete::DirToDelete(int64_t ObjectID,
+ const std::string& rLocalPath)
+: mObjectID(ObjectID),
+ mLocalPath(rLocalPath)
+{ }
+
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupClientDeleteList::AddDirectoryDelete(int64_t)
+// Name: BackupClientDeleteList::AddDirectoryDelete(int64_t,
+// const BackupStoreFilename&)
// Purpose: Add a directory to the list of directories to be deleted.
// Created: 10/11/03
//
// --------------------------------------------------------------------------
-void BackupClientDeleteList::AddDirectoryDelete(int64_t ObjectID)
+void BackupClientDeleteList::AddDirectoryDelete(int64_t ObjectID,
+ const std::string& rLocalPath)
{
// Only add the delete to the list if it's not in the "no delete" set
- if(mDirectoryNoDeleteList.find(ObjectID) == mDirectoryNoDeleteList.end())
+ if(mDirectoryNoDeleteList.find(ObjectID) ==
+ mDirectoryNoDeleteList.end())
{
// Not in the list, so should delete it
- mDirectoryList.push_back(ObjectID);
+ mDirectoryList.push_back(DirToDelete(ObjectID, rLocalPath));
}
}
@@ -64,18 +81,22 @@ void BackupClientDeleteList::AddDirectoryDelete(int64_t ObjectID)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupClientDeleteList::AddFileDelete(int64_t, BackupStoreFilenameClear &)
+// Name: BackupClientDeleteList::AddFileDelete(int64_t,
+// const BackupStoreFilename &)
// Purpose:
// Created: 10/11/03
//
// --------------------------------------------------------------------------
-void BackupClientDeleteList::AddFileDelete(int64_t DirectoryID, const BackupStoreFilename &rFilename)
+void BackupClientDeleteList::AddFileDelete(int64_t DirectoryID,
+ const BackupStoreFilename &rFilename, const std::string& rLocalPath)
{
// Try to find it in the no delete list
- std::vector<std::pair<int64_t, BackupStoreFilename> >::iterator delEntry(mFileNoDeleteList.begin());
+ std::vector<std::pair<int64_t, BackupStoreFilename> >::iterator
+ delEntry(mFileNoDeleteList.begin());
while(delEntry != mFileNoDeleteList.end())
{
- if((delEntry)->first == DirectoryID && (delEntry)->second == rFilename)
+ if((delEntry)->first == DirectoryID
+ && (delEntry)->second == rFilename)
{
// Found!
break;
@@ -86,7 +107,8 @@ void BackupClientDeleteList::AddFileDelete(int64_t DirectoryID, const BackupStor
// Only add it to the delete list if it wasn't in the no delete list
if(delEntry == mFileNoDeleteList.end())
{
- mFileList.push_back(std::pair<int64_t, BackupStoreFilename>(DirectoryID, rFilename));
+ mFileList.push_back(FileToDelete(DirectoryID, rFilename,
+ rLocalPath));
}
}
@@ -113,18 +135,24 @@ void BackupClientDeleteList::PerformDeletions(BackupClientContext &rContext)
BackupProtocolClient &connection(rContext.GetConnection());
// Do the deletes
- for(std::vector<int64_t>::iterator i(mDirectoryList.begin()); i != mDirectoryList.end(); ++i)
+ for(std::vector<DirToDelete>::iterator i(mDirectoryList.begin());
+ i != mDirectoryList.end(); ++i)
{
- connection.QueryDeleteDirectory(*i);
+ connection.QueryDeleteDirectory(i->mObjectID);
+ rContext.GetProgressNotifier().NotifyDirectoryDeleted(
+ i->mObjectID, i->mLocalPath);
}
// Clear the directory list
mDirectoryList.clear();
// Delete the files
- for(std::vector<std::pair<int64_t, BackupStoreFilename> >::iterator i(mFileList.begin()); i != mFileList.end(); ++i)
+ for(std::vector<FileToDelete>::iterator i(mFileList.begin());
+ i != mFileList.end(); ++i)
{
- connection.QueryDeleteFile(i->first, i->second);
+ connection.QueryDeleteFile(i->mDirectoryID, i->mFilename);
+ rContext.GetProgressNotifier().NotifyFileDeleted(
+ i->mDirectoryID, i->mLocalPath);
}
}
@@ -140,7 +168,15 @@ void BackupClientDeleteList::PerformDeletions(BackupClientContext &rContext)
void BackupClientDeleteList::StopDirectoryDeletion(int64_t ObjectID)
{
// First of all, is it in the delete vector?
- std::vector<int64_t>::iterator delEntry(std::find(mDirectoryList.begin(), mDirectoryList.end(), ObjectID));
+ std::vector<DirToDelete>::iterator delEntry(mDirectoryList.begin());
+ for(; delEntry != mDirectoryList.end(); delEntry++)
+ {
+ if(delEntry->mObjectID == ObjectID)
+ {
+ // Found!
+ break;
+ }
+ }
if(delEntry != mDirectoryList.end())
{
// erase this entry
@@ -148,7 +184,8 @@ void BackupClientDeleteList::StopDirectoryDeletion(int64_t ObjectID)
}
else
{
- // Haven't been asked to delete it yet, put it in the no delete list
+ // Haven't been asked to delete it yet, put it in the
+ // no delete list
mDirectoryNoDeleteList.insert(ObjectID);
}
}
@@ -162,13 +199,15 @@ void BackupClientDeleteList::StopDirectoryDeletion(int64_t ObjectID)
// Created: 19/11/03
//
// --------------------------------------------------------------------------
-void BackupClientDeleteList::StopFileDeletion(int64_t DirectoryID, const BackupStoreFilename &rFilename)
+void BackupClientDeleteList::StopFileDeletion(int64_t DirectoryID,
+ const BackupStoreFilename &rFilename)
{
// Find this in the delete list
- std::vector<std::pair<int64_t, BackupStoreFilename> >::iterator delEntry(mFileList.begin());
+ std::vector<FileToDelete>::iterator delEntry(mFileList.begin());
while(delEntry != mFileList.end())
{
- if((delEntry)->first == DirectoryID && (delEntry)->second == rFilename)
+ if(delEntry->mDirectoryID == DirectoryID
+ && delEntry->mFilename == rFilename)
{
// Found!
break;
@@ -186,10 +225,5 @@ void BackupClientDeleteList::StopFileDeletion(int64_t DirectoryID, const BackupS
// Haven't been asked to delete it yet, put it in the no delete list
mFileNoDeleteList.push_back(std::pair<int64_t, BackupStoreFilename>(DirectoryID, rFilename));
}
-
}
-
-
-
-
diff --git a/bin/bbackupd/BackupClientDeleteList.h b/bin/bbackupd/BackupClientDeleteList.h
index 5940cf50..b0fbf51a 100644
--- a/bin/bbackupd/BackupClientDeleteList.h
+++ b/bin/bbackupd/BackupClientDeleteList.h
@@ -28,22 +28,46 @@ class BackupClientContext;
// --------------------------------------------------------------------------
class BackupClientDeleteList
{
+private:
+ class FileToDelete
+ {
+ public:
+ int64_t mDirectoryID;
+ BackupStoreFilename mFilename;
+ std::string mLocalPath;
+ FileToDelete(int64_t DirectoryID,
+ const BackupStoreFilename& rFilename,
+ const std::string& rLocalPath);
+ };
+
+ class DirToDelete
+ {
+ public:
+ int64_t mObjectID;
+ std::string mLocalPath;
+ DirToDelete(int64_t ObjectID, const std::string& rLocalPath);
+ };
+
public:
BackupClientDeleteList();
~BackupClientDeleteList();
- void AddDirectoryDelete(int64_t ObjectID);
- void AddFileDelete(int64_t DirectoryID, const BackupStoreFilename &rFilename);
+ void AddDirectoryDelete(int64_t ObjectID,
+ const std::string& rLocalPath);
+ void AddFileDelete(int64_t DirectoryID,
+ const BackupStoreFilename &rFilename,
+ const std::string& rLocalPath);
void StopDirectoryDeletion(int64_t ObjectID);
- void StopFileDeletion(int64_t DirectoryID, const BackupStoreFilename &rFilename);
+ void StopFileDeletion(int64_t DirectoryID,
+ const BackupStoreFilename &rFilename);
void PerformDeletions(BackupClientContext &rContext);
private:
- std::vector<int64_t> mDirectoryList;
+ std::vector<DirToDelete> mDirectoryList;
std::set<int64_t> mDirectoryNoDeleteList; // note: things only get in this list if they're not present in mDirectoryList when they are 'added'
- std::vector<std::pair<int64_t, BackupStoreFilename> > mFileList;
+ std::vector<FileToDelete> mFileList;
std::vector<std::pair<int64_t, BackupStoreFilename> > mFileNoDeleteList;
};
diff --git a/bin/bbackupd/BackupClientDirectoryRecord.cpp b/bin/bbackupd/BackupClientDirectoryRecord.cpp
index 0a0703c2..b8d42d47 100644
--- a/bin/bbackupd/BackupClientDirectoryRecord.cpp
+++ b/bin/bbackupd/BackupClientDirectoryRecord.cpp
@@ -2,7 +2,8 @@
//
// File
// Name: BackupClientDirectoryRecord.cpp
-// Purpose: Implementation of record about directory for backup client
+// Purpose: Implementation of record about directory for
+// backup client
// Created: 2003/10/08
//
// --------------------------------------------------------------------------
@@ -100,16 +101,27 @@ void BackupClientDirectoryRecord::DeleteSubDirectories()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::SyncParams &, int64_t, const std::string &, bool)
-// Purpose: Syncronise, recusively, a local directory with the server.
+// Name: BackupClientDirectoryRecord::SyncDirectory(i
+// BackupClientDirectoryRecord::SyncParams &,
+// int64_t, const std::string &,
+// const std::string &, bool)
+// Purpose: Recursively synchronise a local directory
+// with the server.
// Created: 2003/10/08
//
// --------------------------------------------------------------------------
-void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::SyncParams &rParams, int64_t ContainingDirectoryID,
- const std::string &rLocalPath, bool ThisDirHasJustBeenCreated)
+void BackupClientDirectoryRecord::SyncDirectory(
+ BackupClientDirectoryRecord::SyncParams &rParams,
+ int64_t ContainingDirectoryID,
+ const std::string &rLocalPath,
+ const std::string &rRemotePath,
+ bool ThisDirHasJustBeenCreated)
{
+ BackupClientContext& rContext(rParams.mrContext);
+ ProgressNotifier& rNotifier(rContext.GetProgressNotifier());
+
// Signal received by daemon?
- if(rParams.mrDaemon.StopRun())
+ if(rParams.mrRunStatusProvider.StopRun())
{
// Yes. Stop now.
THROW_EXCEPTION(BackupStoreException, SignalReceived)
@@ -118,49 +130,66 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// Start by making some flag changes, marking this sync as not done,
// and on the immediate sub directories.
mSyncDone = false;
- for(std::map<std::string, BackupClientDirectoryRecord *>::iterator i = mSubDirectories.begin();
+ for(std::map<std::string, BackupClientDirectoryRecord *>::iterator
+ i = mSubDirectories.begin();
i != mSubDirectories.end(); ++i)
{
i->second->mSyncDone = false;
}
- // Work out the time in the future after which the file should be uploaded regardless.
- // This is a simple way to avoid having too many problems with file servers when they have
- // clients with badly out of sync clocks.
- rParams.mUploadAfterThisTimeInTheFuture = GetCurrentBoxTime() + rParams.mMaxFileTimeInFuture;
+ // Work out the time in the future after which the file should
+ // be uploaded regardless. This is a simple way to avoid having
+ // too many problems with file servers when they have clients
+ // with badly out of sync clocks.
+ rParams.mUploadAfterThisTimeInTheFuture = GetCurrentBoxTime() +
+ rParams.mMaxFileTimeInFuture;
- // Build the current state checksum to compare against while getting info from dirs
- // Note checksum is used locally only, so byte order isn't considered.
+ // Build the current state checksum to compare against while
+ // getting info from dirs. Note checksum is used locally only,
+ // so byte order isn't considered.
MD5Digest currentStateChecksum;
+ EMU_STRUCT_STAT dest_st;
// Stat the directory, to get attribute info
+ // If it's a symbolic link, we want the link target here
+ // (as we're about to back up the contents of the directory)
{
- struct stat st;
- if(::stat(rLocalPath.c_str(), &st) != 0)
+ if(EMU_STAT(rLocalPath.c_str(), &dest_st) != 0)
{
- // The directory has probably been deleted, so just ignore this error.
- // In a future scan, this deletion will be noticed, deleted from server, and this object deleted.
- rParams.GetProgressNotifier().NotifyDirStatFailed(
- this, rLocalPath, strerror(errno));
+ // The directory has probably been deleted, so
+ // just ignore this error. In a future scan, this
+ // deletion will be noticed, deleted from server,
+ // and this object deleted.
+ rNotifier.NotifyDirStatFailed(this, rLocalPath,
+ strerror(errno));
return;
}
- // Store inode number in map so directories are tracked in case they're renamed
+ // Store inode number in map so directories are tracked
+ // in case they're renamed
{
- BackupClientInodeToIDMap &idMap(rParams.mrContext.GetNewIDMap());
- idMap.AddToMap(st.st_ino, mObjectID, ContainingDirectoryID);
+ BackupClientInodeToIDMap &idMap(
+ rParams.mrContext.GetNewIDMap());
+ idMap.AddToMap(dest_st.st_ino, mObjectID,
+ ContainingDirectoryID);
}
// Add attributes to checksum
- currentStateChecksum.Add(&st.st_mode, sizeof(st.st_mode));
- currentStateChecksum.Add(&st.st_uid, sizeof(st.st_uid));
- currentStateChecksum.Add(&st.st_gid, sizeof(st.st_gid));
+ currentStateChecksum.Add(&dest_st.st_mode,
+ sizeof(dest_st.st_mode));
+ currentStateChecksum.Add(&dest_st.st_uid,
+ sizeof(dest_st.st_uid));
+ currentStateChecksum.Add(&dest_st.st_gid,
+ sizeof(dest_st.st_gid));
// Inode to be paranoid about things moving around
- currentStateChecksum.Add(&st.st_ino, sizeof(st.st_ino));
+ currentStateChecksum.Add(&dest_st.st_ino,
+ sizeof(dest_st.st_ino));
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
- currentStateChecksum.Add(&st.st_flags, sizeof(st.st_flags));
+ currentStateChecksum.Add(&dest_st.st_flags,
+ sizeof(dest_st.st_flags));
#endif
StreamableMemBlock xattr;
- BackupClientFileAttributes::FillExtendedAttr(xattr, rLocalPath.c_str());
+ BackupClientFileAttributes::FillExtendedAttr(xattr,
+ rLocalPath.c_str());
currentStateChecksum.Add(xattr.GetBuffer(), xattr.GetSize());
}
@@ -170,13 +199,13 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
std::vector<std::string> files;
bool downloadDirectoryRecordBecauseOfFutureFiles = false;
- struct stat dir_st;
- if(::lstat(rLocalPath.c_str(), &dir_st) != 0)
+ EMU_STRUCT_STAT link_st;
+ if(EMU_LSTAT(rLocalPath.c_str(), &link_st) != 0)
{
// Report the error (logs and
// eventual email to administrator)
- rParams.GetProgressNotifier().NotifyFileStatFailed(this,
- rLocalPath, strerror(errno));
+ rNotifier.NotifyFileStatFailed(this, rLocalPath,
+ strerror(errno));
// FIXME move to NotifyFileStatFailed()
SetErrorWhenReadingFilesystemObject(rParams,
@@ -192,8 +221,7 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
DIR *dirHandle = 0;
try
{
- rParams.GetProgressNotifier().NotifyScanDirectory(
- this, rLocalPath);
+ rNotifier.NotifyScanDirectory(this, rLocalPath);
dirHandle = ::opendir(rLocalPath.c_str());
if(dirHandle == 0)
@@ -202,17 +230,19 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// eventual email to administrator)
if (errno == EACCES)
{
- rParams.GetProgressNotifier().NotifyDirListFailed(
- this, rLocalPath, "Access denied");
+ rNotifier.NotifyDirListFailed(this,
+ rLocalPath, "Access denied");
}
else
{
- rParams.GetProgressNotifier().NotifyDirListFailed(this,
+ rNotifier.NotifyDirListFailed(this,
rLocalPath, strerror(errno));
}
- // Report the error (logs and eventual email to administrator)
- SetErrorWhenReadingFilesystemObject(rParams, rLocalPath.c_str());
+ // Report the error (logs and eventual email
+ // to administrator)
+ SetErrorWhenReadingFilesystemObject(rParams,
+ rLocalPath.c_str());
// Ignore this directory for now.
return;
}
@@ -228,14 +258,17 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
::memset(&checksum_info, 0, sizeof(checksum_info));
struct dirent *en = 0;
- struct stat st;
+ EMU_STRUCT_STAT file_st;
std::string filename;
while((en = ::readdir(dirHandle)) != 0)
{
rParams.mrContext.DoKeepAlive();
- // Don't need to use LinuxWorkaround_FinishDirentStruct(en, rLocalPath.c_str());
- // on Linux, as a stat is performed to get all this info
+ // Don't need to use
+ // LinuxWorkaround_FinishDirentStruct(en,
+ // rLocalPath.c_str());
+ // on Linux, as a stat is performed to
+ // get all this info
if(en->d_name[0] == '.' &&
(en->d_name[1] == '\0' || (en->d_name[1] == '.' && en->d_name[2] == '\0')))
@@ -259,11 +292,11 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// prefer S_IFREG, S_IFDIR...
int type = en->d_type;
#else
- if(::lstat(filename.c_str(), &st) != 0)
+ if(EMU_LSTAT(filename.c_str(), &file_st) != 0)
{
// Report the error (logs and
// eventual email to administrator)
- rParams.GetProgressNotifier().NotifyFileStatFailed(this,
+ rNotifier.NotifyFileStatFailed(this,
filename, strerror(errno));
// FIXME move to NotifyFileStatFailed()
@@ -274,19 +307,18 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
continue;
}
- if(st.st_dev != dir_st.st_dev)
+ if(file_st.st_dev != dest_st.st_dev)
{
if(!(rParams.mrContext.ExcludeDir(
filename)))
{
- rParams.GetProgressNotifier()
- .NotifyMountPointSkipped(
- this, filename);
+ rNotifier.NotifyMountPointSkipped(
+ this, filename);
}
continue;
}
- int type = st.st_mode & S_IFMT;
+ int type = file_st.st_mode & S_IFMT;
#endif
if(type == S_IFREG || type == S_IFLNK)
@@ -296,8 +328,7 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// Exclude it?
if(rParams.mrContext.ExcludeFile(filename))
{
- rParams.GetProgressNotifier()
- .NotifyFileExcluded(
+ rNotifier.NotifyFileExcluded(
this,
filename);
@@ -315,8 +346,7 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// Exclude it?
if(rParams.mrContext.ExcludeDir(filename))
{
- rParams.GetProgressNotifier()
- .NotifyDirExcluded(
+ rNotifier.NotifyDirExcluded(
this,
filename);
@@ -327,19 +357,22 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// Store on list
dirs.push_back(std::string(en->d_name));
}
+ else if (type == S_IFSOCK || type == S_IFIFO)
+ {
+ // removed notification for these types
+ // see Debian bug 479145, no objections
+ }
else
{
if(rParams.mrContext.ExcludeFile(filename))
{
- rParams.GetProgressNotifier()
- .NotifyFileExcluded(
+ rNotifier.NotifyFileExcluded(
this,
filename);
}
else
{
- rParams.GetProgressNotifier()
- .NotifyUnsupportedFileType(
+ rNotifier.NotifyUnsupportedFileType(
this, filename);
SetErrorWhenReadingFilesystemObject(
rParams, filename.c_str());
@@ -354,10 +387,9 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
#ifdef WIN32
// We didn't stat the file before,
// but now we need the information.
- if(::lstat(filename.c_str(), &st) != 0)
+ if(emu_stat(filename.c_str(), &file_st) != 0)
{
- rParams.GetProgressNotifier()
- .NotifyFileStatFailed(this,
+ rNotifier.NotifyFileStatFailed(this,
filename,
strerror(errno));
@@ -370,18 +402,17 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
continue;
}
- if(st.st_dev != dir_st.st_dev)
+ if(file_st.st_dev != link_st.st_dev)
{
- rParams.GetProgressNotifier()
- .NotifyMountPointSkipped(this,
+ rNotifier.NotifyMountPointSkipped(this,
filename);
continue;
}
#endif
- checksum_info.mModificationTime = FileModificationTime(st);
- checksum_info.mAttributeModificationTime = FileAttrModificationTime(st);
- checksum_info.mSize = st.st_size;
+ checksum_info.mModificationTime = FileModificationTime(file_st);
+ checksum_info.mAttributeModificationTime = FileAttrModificationTime(file_st);
+ checksum_info.mSize = file_st.st_size;
currentStateChecksum.Add(&checksum_info, sizeof(checksum_info));
currentStateChecksum.Add(en->d_name, strlen(en->d_name));
@@ -394,7 +425,7 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// Log that this has happened
if(!rParams.mHaveLoggedWarningAboutFutureFileTimes)
{
- rParams.GetProgressNotifier().NotifyFileModifiedInFuture(
+ rNotifier.NotifyFileModifiedInFuture(
this, filename);
rParams.mHaveLoggedWarningAboutFutureFileTimes = true;
}
@@ -468,7 +499,8 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
}
// Do the directory reading
- bool updateCompleteSuccess = UpdateItems(rParams, rLocalPath, pdirOnStore, entriesLeftOver, files, dirs);
+ bool updateCompleteSuccess = UpdateItems(rParams, rLocalPath,
+ rRemotePath, pdirOnStore, entriesLeftOver, files, dirs);
// LAST THING! (think exception safety)
// Store the new checksum -- don't fetch things unnecessarily in the future
@@ -604,11 +636,18 @@ void BackupClientDirectoryRecord::UpdateAttributes(BackupClientDirectoryRecord::
// Created: 2003/10/09
//
// --------------------------------------------------------------------------
-bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncParams &rParams,
- const std::string &rLocalPath, BackupStoreDirectory *pDirOnStore,
+bool BackupClientDirectoryRecord::UpdateItems(
+ BackupClientDirectoryRecord::SyncParams &rParams,
+ const std::string &rLocalPath,
+ const std::string &rRemotePath,
+ BackupStoreDirectory *pDirOnStore,
std::vector<BackupStoreDirectory::Entry *> &rEntriesLeftOver,
- std::vector<std::string> &rFiles, const std::vector<std::string> &rDirs)
+ std::vector<std::string> &rFiles,
+ const std::vector<std::string> &rDirs)
{
+ BackupClientContext& rContext(rParams.mrContext);
+ ProgressNotifier& rNotifier(rContext.GetProgressNotifier());
+
bool allUpdatedSuccessfully = true;
// Decrypt all the directory entries.
@@ -634,7 +673,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
f != rFiles.end(); ++f)
{
// Send keep-alive message if needed
- rParams.mrContext.DoKeepAlive();
+ rContext.DoKeepAlive();
// Filename of this file
std::string filename(MakeFullPath(rLocalPath, *f));
@@ -648,10 +687,10 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// BLOCK
{
// Stat the file
- struct stat st;
- if(::lstat(filename.c_str(), &st) != 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_LSTAT(filename.c_str(), &st) != 0)
{
- rParams.GetProgressNotifier().NotifyFileStatFailed(this,
+ rNotifier.NotifyFileStatFailed(this,
filename, strerror(errno));
// Report the error (logs and
@@ -689,7 +728,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
if((en != 0) && ((en->GetFlags() & BackupStoreDirectory::Entry::Flags_File) == 0))
{
// Directory exists in the place of this file -- sort it out
- RemoveDirectoryInPlaceOfFile(rParams, pDirOnStore, en->GetObjectID(), *f);
+ RemoveDirectoryInPlaceOfFile(rParams, pDirOnStore,
+ en, *f);
en = 0;
}
@@ -701,7 +741,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// 2) It's not in the store
// Do we know about the inode number?
- const BackupClientInodeToIDMap &idMap(rParams.mrContext.GetCurrentIDMap());
+ const BackupClientInodeToIDMap &idMap(rContext.GetCurrentIDMap());
int64_t renameObjectID = 0, renameInDirectory = 0;
if(idMap.Lookup(inodeNum, renameObjectID, renameInDirectory))
{
@@ -711,24 +751,24 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
bool isCurrentVersion = false;
box_time_t srvModTime = 0, srvAttributesHash = 0;
BackupStoreFilenameClear oldLeafname;
- if(rParams.mrContext.FindFilename(renameObjectID, renameInDirectory, localPotentialOldName, isDir, isCurrentVersion, &srvModTime, &srvAttributesHash, &oldLeafname))
+ if(rContext.FindFilename(renameObjectID, renameInDirectory, localPotentialOldName, isDir, isCurrentVersion, &srvModTime, &srvAttributesHash, &oldLeafname))
{
// Only interested if it's a file and the latest version
if(!isDir && isCurrentVersion)
{
// Check that the object we found in the ID map doesn't exist on disc
- struct stat st;
- if(::stat(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT)
{
// Doesn't exist locally, but does exist on the server.
// Therefore we can safely rename it to this new file.
// Get the connection to the server
- BackupProtocolClient &connection(rParams.mrContext.GetConnection());
+ BackupProtocolClient &connection(rContext.GetConnection());
// Only do this step if there is room on the server.
// This step will be repeated later when there is space available
- if(!rParams.mrContext.StorageLimitExceeded())
+ if(!rContext.StorageLimitExceeded())
{
// Rename the existing files (ie include old versions) on the server
connection.QueryMoveObject(renameObjectID, renameInDirectory, mObjectID /* move to this directory */,
@@ -736,7 +776,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
storeFilename);
// Stop the attempt to delete the file in the original location
- BackupClientDeleteList &rdelList(rParams.mrContext.GetDeleteList());
+ BackupClientDeleteList &rdelList(rContext.GetDeleteList());
rdelList.StopFileDeletion(renameInDirectory, oldLeafname);
// Create new entry in the directory for it
@@ -799,13 +839,15 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
if (pDirOnStore != 0 && en == 0)
{
doUpload = true;
- BOX_TRACE(filename << ": will upload "
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will upload "
"(not on server)");
}
else if (modTime >= rParams.mSyncPeriodStart)
{
doUpload = true;
- BOX_TRACE(filename << ": will upload "
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will upload "
"(modified since last sync)");
}
}
@@ -823,7 +865,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
> rParams.mMaxUploadWait)
{
doUpload = true;
- BOX_TRACE(filename << ": will upload "
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will upload "
"(continually modified)");
}
@@ -840,7 +883,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
en->GetModificationTime() != modTime)
{
doUpload = true;
- BOX_TRACE(filename << ": will upload "
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will upload "
"(mod time changed)");
}
@@ -852,64 +896,121 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
rParams.mUploadAfterThisTimeInTheFuture)
{
doUpload = true;
- BOX_TRACE(filename << ": will upload "
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will upload "
"(mod time in the future)");
}
}
-
- if (!doUpload)
+
+ if (en != 0 && en->GetModificationTime() == modTime)
{
- BOX_TRACE(filename << ": will not upload "
- "(no reason to upload, mod time is "
- << modTime << " versus sync period "
- << rParams.mSyncPeriodStart << " to "
- << rParams.mSyncPeriodEnd << ")");
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will not upload "
+ "(not modified since last upload)");
+ }
+ else if (!doUpload)
+ {
+ if (modTime > rParams.mSyncPeriodEnd)
+ {
+ box_time_t now = GetCurrentBoxTime();
+ int age = BoxTimeToSeconds(now -
+ modTime);
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will not upload "
+ "(modified too recently: "
+ "only " << age << "seconds ago)");
+ }
+ else
+ {
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will not upload "
+ "(mod time is " << modTime <<
+ " which is outside sync window, "
+ << rParams.mSyncPeriodStart << " to "
+ << rParams.mSyncPeriodEnd << ")");
+ }
}
+ bool fileSynced = true;
+
if (doUpload)
{
+ // Upload needed, don't mark sync success until
+ // we've actually done it
+ fileSynced = false;
+
// Make sure we're connected -- must connect here so we know whether
// the storage limit has been exceeded, and hence whether or not
// to actually upload the file.
- rParams.mrContext.GetConnection();
+ rContext.GetConnection();
// Only do this step if there is room on the server.
// This step will be repeated later when there is space available
- if(!rParams.mrContext.StorageLimitExceeded())
+ if(!rContext.StorageLimitExceeded())
{
- // Upload the file to the server, recording the object ID it returns
- bool noPreviousVersionOnServer = ((pDirOnStore != 0) && (en == 0));
+ // Upload the file to the server, recording the
+ // object ID it returns
+ bool noPreviousVersionOnServer =
+ ((pDirOnStore != 0) && (en == 0));
- // Surround this in a try/catch block, to catch errrors, but still continue
+ // Surround this in a try/catch block, to
+ // catch errors, but still continue
bool uploadSuccess = false;
try
{
- latestObjectID = UploadFile(rParams, filename, storeFilename, fileSize, modTime, attributesHash, noPreviousVersionOnServer);
- uploadSuccess = true;
+ latestObjectID = UploadFile(rParams,
+ filename, storeFilename,
+ fileSize, modTime,
+ attributesHash,
+ noPreviousVersionOnServer);
+
+ if (latestObjectID == 0)
+ {
+ // storage limit exceeded
+ rParams.mrContext.SetStorageLimitExceeded();
+ uploadSuccess = false;
+ allUpdatedSuccessfully = false;
+ }
+ else
+ {
+ uploadSuccess = true;
+ }
}
catch(ConnectionException &e)
{
- // Connection errors should just be passed on to the main handler, retries
- // would probably just cause more problems.
- rParams.GetProgressNotifier()
- .NotifyFileUploadException(
- this, filename, e);
+ // Connection errors should just be
+ // passed on to the main handler,
+ // retries would probably just cause
+ // more problems.
+ rNotifier.NotifyFileUploadException(
+ this, filename, e);
throw;
}
catch(BoxException &e)
{
- // an error occured -- make return code false, to show error in directory
+ if (e.GetType() == BackupStoreException::ExceptionType &&
+ e.GetSubType() == BackupStoreException::SignalReceived)
+ {
+ // abort requested, pass the
+ // exception on up.
+ throw;
+ }
+
+ // an error occured -- make return
+ // code false, to show error in directory
allUpdatedSuccessfully = false;
// Log it.
SetErrorWhenReadingFilesystemObject(rParams, filename.c_str());
- rParams.GetProgressNotifier()
- .NotifyFileUploadException(
- this, filename, e);
+ rNotifier.NotifyFileUploadException(
+ this, filename, e);
}
- // Update structures if the file was uploaded successfully.
+ // Update structures if the file was uploaded
+ // successfully.
if(uploadSuccess)
{
+ fileSynced = true;
+
// delete from pending entries
if(pendingFirstSeenTime != 0 && mpPendingEntries != 0)
{
@@ -919,28 +1020,41 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
}
else
{
- rParams.GetProgressNotifier().NotifyFileSkippedServerFull(this,
+ rNotifier.NotifyFileSkippedServerFull(this,
filename);
}
}
else if(en != 0 && en->GetAttributesHash() != attributesHash)
{
// Attributes have probably changed, upload them again.
- // If the attributes have changed enough, the directory hash will have changed too,
- // and so the dir will have been downloaded, and the entry will be available.
+ // If the attributes have changed enough, the directory
+ // hash will have changed too, and so the dir will have
+ // been downloaded, and the entry will be available.
// Get connection
- BackupProtocolClient &connection(rParams.mrContext.GetConnection());
+ BackupProtocolClient &connection(rContext.GetConnection());
// Only do this step if there is room on the server.
- // This step will be repeated later when there is space available
- if(!rParams.mrContext.StorageLimitExceeded())
+ // This step will be repeated later when there is
+ // space available
+ if(!rContext.StorageLimitExceeded())
{
- // Update store
- BackupClientFileAttributes attr;
- attr.ReadAttributes(filename.c_str(), false /* put mod times in the attributes, please */);
- MemBlockStream attrStream(attr);
- connection.QuerySetReplacementFileAttributes(mObjectID, attributesHash, storeFilename, attrStream);
+ try
+ {
+ // Update store
+ BackupClientFileAttributes attr;
+ attr.ReadAttributes(filename.c_str(), false /* put mod times in the attributes, please */);
+ MemBlockStream attrStream(attr);
+ connection.QuerySetReplacementFileAttributes(mObjectID, attributesHash, storeFilename, attrStream);
+ fileSynced = true;
+ }
+ catch (BoxException &e)
+ {
+ BOX_ERROR("Failed to read or store "
+ "file attributes for '" <<
+ filename << "', will try "
+ "again later");
+ }
}
}
@@ -976,36 +1090,56 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
if(fileSize >= rParams.mFileTrackingSizeThreshold)
{
// Get the map
- BackupClientInodeToIDMap &idMap(rParams.mrContext.GetNewIDMap());
+ BackupClientInodeToIDMap &idMap(rContext.GetNewIDMap());
// Need to get an ID from somewhere...
if(latestObjectID != 0)
{
// Use this one
+ BOX_TRACE("Storing uploaded file ID " <<
+ inodeNum << " (" << filename << ") "
+ "in ID map as object " <<
+ latestObjectID << " with parent " <<
+ mObjectID);
idMap.AddToMap(inodeNum, latestObjectID, mObjectID /* containing directory */);
}
else
{
// Don't know it -- haven't sent anything to the store, and didn't get a listing.
// Look it up in the current map, and if it's there, use that.
- const BackupClientInodeToIDMap &currentIDMap(rParams.mrContext.GetCurrentIDMap());
+ const BackupClientInodeToIDMap &currentIDMap(rContext.GetCurrentIDMap());
int64_t objid = 0, dirid = 0;
if(currentIDMap.Lookup(inodeNum, objid, dirid))
{
// Found
+ if (dirid != mObjectID)
+ {
+ BOX_WARNING("Found conflicting parent ID for file ID " << inodeNum << " (" << filename << "): expected " << mObjectID << " but found " << dirid << " (same directory used in two different locations?)");
+ }
+
ASSERT(dirid == mObjectID);
+
// NOTE: If the above assert fails, an inode number has been reused by the OS,
// or there is a problem somewhere. If this happened on a short test run, look
// into it. However, in a long running process this may happen occasionally and
- // not indiciate anything wrong.
+ // not indicate anything wrong.
// Run the release version for real life use, where this check is not made.
- idMap.AddToMap(inodeNum, objid, mObjectID /* containing directory */);
+ BOX_TRACE("Storing found file ID " <<
+ inodeNum << " (" << filename <<
+ ") in ID map as object " <<
+ objid << " with parent " <<
+ mObjectID);
+ idMap.AddToMap(inodeNum, objid,
+ mObjectID /* containing directory */);
}
}
}
- rParams.GetProgressNotifier().NotifyFileSynchronised(this,
- filename, fileSize);
+ if (fileSynced)
+ {
+ rNotifier.NotifyFileSynchronised(this, filename,
+ fileSize);
+ }
}
// Erase contents of files to save space when recursing
@@ -1014,7 +1148,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// Delete the pending entries, if the map is entry
if(mpPendingEntries != 0 && mpPendingEntries->size() == 0)
{
- TRACE1("Deleting mpPendingEntries from dir ID %lld\n", mObjectID);
+ BOX_TRACE("Deleting mpPendingEntries from dir ID " <<
+ BOX_FORMAT_OBJECTID(mObjectID));
delete mpPendingEntries;
mpPendingEntries = 0;
}
@@ -1024,7 +1159,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
d != rDirs.end(); ++d)
{
// Send keep-alive message if needed
- rParams.mrContext.DoKeepAlive();
+ rContext.DoKeepAlive();
// Get the local filename
std::string dirname(MakeFullPath(rLocalPath, *d));
@@ -1044,21 +1179,27 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// Check that the entry which might have been found is in fact a directory
if((en != 0) && ((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) == 0))
{
- // Entry exists, but is not a directory. Bad. Get rid of it.
- BackupProtocolClient &connection(rParams.mrContext.GetConnection());
+ // Entry exists, but is not a directory. Bad.
+ // Get rid of it.
+ BackupProtocolClient &connection(rContext.GetConnection());
connection.QueryDeleteFile(mObjectID /* in directory */, storeFilename);
+ rNotifier.NotifyFileDeleted(en->GetObjectID(),
+ storeFilename.GetClearFilename());
// Nothing found
en = 0;
}
- // Flag for having created directory, so can optimise the recusive call not to
- // read it again, because we know it's empty.
+ // Flag for having created directory, so can optimise the
+ // recursive call not to read it again, because we know
+ // it's empty.
bool haveJustCreatedDirOnServer = false;
// Next, see if it's in the list of sub directories
BackupClientDirectoryRecord *psubDirRecord = 0;
- std::map<std::string, BackupClientDirectoryRecord *>::iterator e(mSubDirectories.find(*d));
+ std::map<std::string, BackupClientDirectoryRecord *>::iterator
+ e(mSubDirectories.find(*d));
+
if(e != mSubDirectories.end())
{
// In the list, just use this pointer
@@ -1080,7 +1221,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// No. Exists on the server, and we know about it from the listing.
subDirObjectID = en->GetObjectID();
}
- else if(rParams.mrContext.StorageLimitExceeded())
+ else if(rContext.StorageLimitExceeded())
// know we've got a connection if we get this far,
// as dir will have been modified.
{
@@ -1098,29 +1239,47 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
box_time_t attrModTime = 0;
InodeRefType inodeNum = 0;
BackupClientFileAttributes attr;
- attr.ReadAttributes(dirname.c_str(), true /* directories have zero mod times */,
- 0 /* not interested in mod time */, &attrModTime, 0 /* not file size */,
- &inodeNum);
+ bool failedToReadAttributes = false;
+
+ try
+ {
+ attr.ReadAttributes(dirname.c_str(),
+ true /* directories have zero mod times */,
+ 0 /* not interested in mod time */,
+ &attrModTime, 0 /* not file size */,
+ &inodeNum);
+ }
+ catch (BoxException &e)
+ {
+ BOX_WARNING("Failed to read attributes "
+ "of directory, cannot check "
+ "for rename, assuming new: '"
+ << dirname << "'");
+ failedToReadAttributes = true;
+ }
// Check to see if the directory been renamed
// First, do we have a record in the ID map?
int64_t renameObjectID = 0, renameInDirectory = 0;
bool renameDir = false;
- const BackupClientInodeToIDMap &idMap(rParams.mrContext.GetCurrentIDMap());
- if(idMap.Lookup(inodeNum, renameObjectID, renameInDirectory))
+ const BackupClientInodeToIDMap &idMap(
+ rContext.GetCurrentIDMap());
+
+ if(!failedToReadAttributes && idMap.Lookup(inodeNum,
+ renameObjectID, renameInDirectory))
{
// Look up on the server to get the name, to build the local filename
std::string localPotentialOldName;
bool isDir = false;
bool isCurrentVersion = false;
- if(rParams.mrContext.FindFilename(renameObjectID, renameInDirectory, localPotentialOldName, isDir, isCurrentVersion))
+ if(rContext.FindFilename(renameObjectID, renameInDirectory, localPotentialOldName, isDir, isCurrentVersion))
{
// Only interested if it's a directory
if(isDir && isCurrentVersion)
{
// Check that the object doesn't exist already
- struct stat st;
- if(::stat(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT)
{
// Doesn't exist locally, but does exist on the server.
// Therefore we can safely rename it.
@@ -1131,7 +1290,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
}
// Get connection
- BackupProtocolClient &connection(rParams.mrContext.GetConnection());
+ BackupProtocolClient &connection(rContext.GetConnection());
// Don't do a check for storage limit exceeded here, because if we get to this
// stage, a connection will have been opened, and the status known, so the check
@@ -1151,7 +1310,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
connection.QueryChangeDirAttributes(renameObjectID, attrModTime, attrStream);
// Stop it being deleted later
- BackupClientDeleteList &rdelList(rParams.mrContext.GetDeleteList());
+ BackupClientDeleteList &rdelList(
+ rContext.GetDeleteList());
rdelList.StopDirectoryDeletion(renameObjectID);
// This is the ID for the renamed directory
@@ -1188,12 +1348,14 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
}
}
- ASSERT(psubDirRecord != 0 || rParams.mrContext.StorageLimitExceeded());
+ ASSERT(psubDirRecord != 0 || rContext.StorageLimitExceeded());
if(psubDirRecord)
{
// Sync this sub directory too
- psubDirRecord->SyncDirectory(rParams, mObjectID, dirname, haveJustCreatedDirOnServer);
+ psubDirRecord->SyncDirectory(rParams, mObjectID,
+ dirname, rRemotePath + "/" + *d,
+ haveJustCreatedDirOnServer);
}
// Zero pointer in rEntriesLeftOver, if we have a pointer to zero
@@ -1222,20 +1384,27 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// to a list, which is actually deleted at the very end of the session.
// If there's an error during the process, it doesn't matter if things
// aren't actually deleted, as the whole state will be reset anyway.
- BackupClientDeleteList &rdel(rParams.mrContext.GetDeleteList());
+ BackupClientDeleteList &rdel(rContext.GetDeleteList());
+
+ BackupStoreFilenameClear clear(en->GetName());
+ std::string localName = MakeFullPath(rLocalPath,
+ clear.GetClearFilename());
// Delete this entry -- file or directory?
if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_File) != 0)
{
// Set a pending deletion for the file
- rdel.AddFileDelete(mObjectID, en->GetName());
+ rdel.AddFileDelete(mObjectID, en->GetName(),
+ localName);
}
else if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) != 0)
{
// Set as a pending deletion for the directory
- rdel.AddDirectoryDelete(en->GetObjectID());
+ rdel.AddDirectoryDelete(en->GetObjectID(),
+ localName);
- // If there's a directory record for it in the sub directory map, delete it now
+ // If there's a directory record for it in
+ // the sub directory map, delete it now
BackupStoreFilenameClear dirname(en->GetName());
std::map<std::string, BackupClientDirectoryRecord *>::iterator e(mSubDirectories.find(dirname.GetClearFilename()));
if(e != mSubDirectories.end())
@@ -1249,8 +1418,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
rLocalPath,
dirname.GetClearFilename());
- TRACE1("Deleted directory record for "
- "%s\n", name.c_str());
+ BOX_TRACE("Deleted directory record "
+ "for " << name);
}
}
}
@@ -1270,14 +1439,24 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// Created: 9/7/04
//
// --------------------------------------------------------------------------
-void BackupClientDirectoryRecord::RemoveDirectoryInPlaceOfFile(SyncParams &rParams, BackupStoreDirectory *pDirOnStore, int64_t ObjectID, const std::string &rFilename)
+void BackupClientDirectoryRecord::RemoveDirectoryInPlaceOfFile(
+ SyncParams &rParams,
+ BackupStoreDirectory* pDirOnStore,
+ BackupStoreDirectory::Entry* pEntry,
+ const std::string &rFilename)
{
// First, delete the directory
BackupProtocolClient &connection(rParams.mrContext.GetConnection());
- connection.QueryDeleteDirectory(ObjectID);
+ connection.QueryDeleteDirectory(pEntry->GetObjectID());
+
+ BackupStoreFilenameClear clear(pEntry->GetName());
+ rParams.mrContext.GetProgressNotifier().NotifyDirectoryDeleted(
+ pEntry->GetObjectID(), clear.GetClearFilename());
// Then, delete any directory record
- std::map<std::string, BackupClientDirectoryRecord *>::iterator e(mSubDirectories.find(rFilename));
+ std::map<std::string, BackupClientDirectoryRecord *>::iterator
+ e(mSubDirectories.find(rFilename));
+
if(e != mSubDirectories.end())
{
// A record exists for this, remove it
@@ -1294,16 +1473,30 @@ void BackupClientDirectoryRecord::RemoveDirectoryInPlaceOfFile(SyncParams &rPara
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::SyncParams &, const std::string &, const BackupStoreFilename &, int64_t, box_time_t, box_time_t, bool)
-// Purpose: Private. Upload a file to the server -- may send a patch instead of the whole thing
+// Name: BackupClientDirectoryRecord::UploadFile(
+// BackupClientDirectoryRecord::SyncParams &,
+// const std::string &,
+// const BackupStoreFilename &,
+// int64_t, box_time_t, box_time_t, bool)
+// Purpose: Private. Upload a file to the server. May send
+// a patch instead of the whole thing
// Created: 20/1/04
//
// --------------------------------------------------------------------------
-int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::SyncParams &rParams, const std::string &rFilename, const BackupStoreFilename &rStoreFilename,
- int64_t FileSize, box_time_t ModificationTime, box_time_t AttributesHash, bool NoPreviousVersionOnServer)
+int64_t BackupClientDirectoryRecord::UploadFile(
+ BackupClientDirectoryRecord::SyncParams &rParams,
+ const std::string &rFilename,
+ const BackupStoreFilename &rStoreFilename,
+ int64_t FileSize,
+ box_time_t ModificationTime,
+ box_time_t AttributesHash,
+ bool NoPreviousVersionOnServer)
{
+ BackupClientContext& rContext(rParams.mrContext);
+ ProgressNotifier& rNotifier(rContext.GetProgressNotifier());
+
// Get the connection
- BackupProtocolClient &connection(rParams.mrContext.GetConnection());
+ BackupProtocolClient &connection(rContext.GetConnection());
// Info
int64_t objID = 0;
@@ -1312,8 +1505,10 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
// Use a try block to catch store full errors
try
{
- // Might an old version be on the server, and is the file size over the diffing threshold?
- if(!NoPreviousVersionOnServer && FileSize >= rParams.mDiffingUploadSizeThreshold)
+ // Might an old version be on the server, and is the file
+ // size over the diffing threshold?
+ if(!NoPreviousVersionOnServer &&
+ FileSize >= rParams.mDiffingUploadSizeThreshold)
{
// YES -- try to do diff, if possible
// First, query the server to see if there's an old version available
@@ -1323,7 +1518,7 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
if(diffFromID != 0)
{
// Found an old version
- rParams.GetProgressNotifier().NotifyFileUploadingPatch(this,
+ rNotifier.NotifyFileUploadingPatch(this,
rFilename);
// Get the index
@@ -1333,7 +1528,7 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
// Diff the file
//
- rParams.mrContext.ManageDiffProcess();
+ rContext.ManageDiffProcess();
bool isCompletelyDifferent = false;
std::auto_ptr<IOStream> patchStream(
@@ -1342,11 +1537,11 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
mObjectID, /* containing directory */
rStoreFilename, diffFromID, *blockIndexStream,
connection.GetTimeout(),
- &rParams.mrContext, // DiffTimer implementation
+ &rContext, // DiffTimer implementation
0 /* not interested in the modification time */,
&isCompletelyDifferent));
- rParams.mrContext.UnManageDiffProcess();
+ rContext.UnManageDiffProcess();
//
// Upload the patch to the store
@@ -1354,6 +1549,9 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
std::auto_ptr<BackupProtocolClientSuccess> stored(connection.QueryStoreFile(mObjectID, ModificationTime,
AttributesHash, isCompletelyDifferent?(0):(diffFromID), rStoreFilename, *patchStream));
+ // Get object ID from the result
+ objID = stored->GetObjectID();
+
// Don't attempt to upload it again!
doNormalUpload = false;
}
@@ -1362,13 +1560,14 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
if(doNormalUpload)
{
// below threshold or nothing to diff from, so upload whole
- rParams.GetProgressNotifier().NotifyFileUploading(this,
- rFilename);
+ rNotifier.NotifyFileUploading(this, rFilename);
// Prepare to upload, getting a stream which will encode the file as we go along
std::auto_ptr<IOStream> upload(
BackupStoreFile::EncodeFile(rFilename.c_str(),
- mObjectID, rStoreFilename));
+ mObjectID, rStoreFilename, NULL,
+ &rParams,
+ &(rParams.mrRunStatusProvider)));
// Send to store
std::auto_ptr<BackupProtocolClientSuccess> stored(
@@ -1384,9 +1583,10 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
}
catch(BoxException &e)
{
- rParams.mrContext.UnManageDiffProcess();
+ rContext.UnManageDiffProcess();
- if(e.GetType() == ConnectionException::ExceptionType && e.GetSubType() == ConnectionException::Protocol_UnexpectedReply)
+ if(e.GetType() == ConnectionException::ExceptionType &&
+ e.GetSubType() == ConnectionException::Protocol_UnexpectedReply)
{
// Check and see what error the protocol has,
// this is more useful to users than the exception.
@@ -1397,11 +1597,15 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
&& subtype == BackupProtocolClientError::Err_StorageLimitExceeded)
{
// The hard limit was exceeded on the server, notify!
- rParams.mrDaemon.NotifySysadmin(BackupDaemon::NotifyEvent_StoreFull);
+ rParams.mrSysadminNotifier.NotifySysadmin(
+ SysadminNotifier::StoreFull);
+ // return an error code instead of
+ // throwing an exception that we
+ // can't debug.
+ return 0;
}
- rParams.GetProgressNotifier()
- .NotifyFileUploadServerError(
- this, rFilename, type, subtype);
+ rNotifier.NotifyFileUploadServerError(this,
+ rFilename, type, subtype);
}
}
@@ -1409,7 +1613,7 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
throw;
}
- rParams.GetProgressNotifier().NotifyFileUploaded(this, rFilename, FileSize);
+ rNotifier.NotifyFileUploaded(this, rFilename, FileSize);
// Return the new object ID of this file
return objID;
@@ -1450,16 +1654,20 @@ void BackupClientDirectoryRecord::SetErrorWhenReadingFilesystemObject(BackupClie
// Created: 8/3/04
//
// --------------------------------------------------------------------------
-BackupClientDirectoryRecord::SyncParams::SyncParams(BackupDaemon &rDaemon,
- ProgressNotifier &rProgressNotifier, BackupClientContext &rContext)
- : mrProgressNotifier(rProgressNotifier),
- mSyncPeriodStart(0),
+BackupClientDirectoryRecord::SyncParams::SyncParams(
+ RunStatusProvider &rRunStatusProvider,
+ SysadminNotifier &rSysadminNotifier,
+ ProgressNotifier &rProgressNotifier,
+ BackupClientContext &rContext)
+ : mSyncPeriodStart(0),
mSyncPeriodEnd(0),
mMaxUploadWait(0),
mMaxFileTimeInFuture(99999999999999999LL),
mFileTrackingSizeThreshold(16*1024),
mDiffingUploadSizeThreshold(16*1024),
- mrDaemon(rDaemon),
+ mrRunStatusProvider(rRunStatusProvider),
+ mrSysadminNotifier(rSysadminNotifier),
+ mrProgressNotifier(rProgressNotifier),
mrContext(rContext),
mReadErrorsOnFilesystemObjects(false),
mUploadAfterThisTimeInTheFuture(99999999999999999LL),
diff --git a/bin/bbackupd/BackupClientDirectoryRecord.h b/bin/bbackupd/BackupClientDirectoryRecord.h
index 9e4dda7a..fce3fc04 100644
--- a/bin/bbackupd/BackupClientDirectoryRecord.h
+++ b/bin/bbackupd/BackupClientDirectoryRecord.h
@@ -13,10 +13,13 @@
#include <string>
#include <map>
-#include "BoxTime.h"
#include "BackupClientFileAttributes.h"
+#include "BackupDaemonInterface.h"
#include "BackupStoreDirectory.h"
+#include "BoxTime.h"
#include "MD5Digest.h"
+#include "ReadLoggingStream.h"
+#include "RunStatusProvider.h"
class Archive;
class BackupClientContext;
@@ -25,82 +28,6 @@ class BackupDaemon;
// --------------------------------------------------------------------------
//
// Class
-// Name: ProgressNotifier
-// Purpose: Provides methods for the backup library to inform the user
-// interface about its progress with the backup
-// Created: 2005/11/20
-//
-// --------------------------------------------------------------------------
-class BackupClientDirectoryRecord;
-
-class ProgressNotifier
-{
- public:
- virtual ~ProgressNotifier() { }
- virtual void NotifyScanDirectory(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyDirStatFailed(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- const std::string& rErrorMsg) = 0;
- virtual void NotifyFileStatFailed(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- const std::string& rErrorMsg) = 0;
- virtual void NotifyDirListFailed(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- const std::string& rErrorMsg) = 0;
- virtual void NotifyMountPointSkipped(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileExcluded(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyDirExcluded(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyUnsupportedFileType(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileReadFailed(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- const std::string& rErrorMsg) = 0;
- virtual void NotifyFileModifiedInFuture(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileSkippedServerFull(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileUploadException(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- const BoxException& rException) = 0;
- virtual void NotifyFileUploadServerError(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- int type, int subtype) = 0;
- virtual void NotifyFileUploading(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileUploadingPatch(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileUploaded(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- int64_t FileSize) = 0;
- virtual void NotifyFileSynchronised(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- int64_t FileSize) = 0;
-};
-
-// --------------------------------------------------------------------------
-//
-// Class
// Name: BackupClientDirectoryRecord
// Purpose: Implementation of record about directory for backup client
// Created: 2003/10/08
@@ -132,11 +59,12 @@ public:
// Created: 8/3/04
//
// --------------------------------------------------------------------------
- class SyncParams
+ class SyncParams : public ReadLoggingStream::Logger
{
public:
SyncParams(
- BackupDaemon &rDaemon,
+ RunStatusProvider &rRunStatusProvider,
+ SysadminNotifier &rSysadminNotifier,
ProgressNotifier &rProgressNotifier,
BackupClientContext &rContext);
~SyncParams();
@@ -144,7 +72,6 @@ public:
// No copying
SyncParams(const SyncParams&);
SyncParams &operator=(const SyncParams&);
- ProgressNotifier &mrProgressNotifier;
public:
// Data members are public, as accessors are not justified here
@@ -154,7 +81,9 @@ public:
box_time_t mMaxFileTimeInFuture;
int32_t mFileTrackingSizeThreshold;
int32_t mDiffingUploadSizeThreshold;
- BackupDaemon &mrDaemon;
+ RunStatusProvider &mrRunStatusProvider;
+ SysadminNotifier &mrSysadminNotifier;
+ ProgressNotifier &mrProgressNotifier;
BackupClientContext &mrContext;
bool mReadErrorsOnFilesystemObjects;
@@ -162,41 +91,79 @@ public:
box_time_t mUploadAfterThisTimeInTheFuture;
bool mHaveLoggedWarningAboutFutureFileTimes;
+ bool StopRun() { return mrRunStatusProvider.StopRun(); }
+ void NotifySysadmin(SysadminNotifier::EventCode Event)
+ {
+ mrSysadminNotifier.NotifySysadmin(Event);
+ }
ProgressNotifier& GetProgressNotifier() const
{
return mrProgressNotifier;
}
+
+ /* ReadLoggingStream::Logger implementation */
+ virtual void Log(int64_t readSize, int64_t offset,
+ int64_t length, box_time_t elapsed, box_time_t finish)
+ {
+ mrProgressNotifier.NotifyReadProgress(readSize, offset,
+ length, elapsed, finish);
+ }
+ virtual void Log(int64_t readSize, int64_t offset,
+ int64_t length)
+ {
+ mrProgressNotifier.NotifyReadProgress(readSize, offset,
+ length);
+ }
+ virtual void Log(int64_t readSize, int64_t offset)
+ {
+ mrProgressNotifier.NotifyReadProgress(readSize, offset);
+ }
};
- void SyncDirectory(SyncParams &rParams, int64_t ContainingDirectoryID, const std::string &rLocalPath,
+ void SyncDirectory(SyncParams &rParams,
+ int64_t ContainingDirectoryID,
+ const std::string &rLocalPath,
+ const std::string &rRemotePath,
bool ThisDirHasJustBeenCreated = false);
private:
void DeleteSubDirectories();
BackupStoreDirectory *FetchDirectoryListing(SyncParams &rParams);
- void UpdateAttributes(SyncParams &rParams, BackupStoreDirectory *pDirOnStore, const std::string &rLocalPath);
- bool UpdateItems(SyncParams &rParams, const std::string &rLocalPath, BackupStoreDirectory *pDirOnStore,
+ void UpdateAttributes(SyncParams &rParams,
+ BackupStoreDirectory *pDirOnStore,
+ const std::string &rLocalPath);
+ bool UpdateItems(SyncParams &rParams, const std::string &rLocalPath,
+ const std::string &rRemotePath,
+ BackupStoreDirectory *pDirOnStore,
std::vector<BackupStoreDirectory::Entry *> &rEntriesLeftOver,
- std::vector<std::string> &rFiles, const std::vector<std::string> &rDirs);
- int64_t UploadFile(SyncParams &rParams, const std::string &rFilename, const BackupStoreFilename &rStoreFilename,
- int64_t FileSize, box_time_t ModificationTime, box_time_t AttributesHash, bool NoPreviousVersionOnServer);
- void SetErrorWhenReadingFilesystemObject(SyncParams &rParams, const char *Filename);
- void RemoveDirectoryInPlaceOfFile(SyncParams &rParams, BackupStoreDirectory *pDirOnStore, int64_t ObjectID, const std::string &rFilename);
+ std::vector<std::string> &rFiles,
+ const std::vector<std::string> &rDirs);
+ int64_t UploadFile(SyncParams &rParams,
+ const std::string &rFilename,
+ const BackupStoreFilename &rStoreFilename,
+ int64_t FileSize, box_time_t ModificationTime,
+ box_time_t AttributesHash, bool NoPreviousVersionOnServer);
+ void SetErrorWhenReadingFilesystemObject(SyncParams &rParams,
+ const char *Filename);
+ void RemoveDirectoryInPlaceOfFile(SyncParams &rParams,
+ BackupStoreDirectory* pDirOnStore,
+ BackupStoreDirectory::Entry* pEntry,
+ const std::string &rFilename);
private:
- int64_t mObjectID;
+ int64_t mObjectID;
std::string mSubDirName;
- bool mInitialSyncDone;
- bool mSyncDone;
+ bool mInitialSyncDone;
+ bool mSyncDone;
// Checksum of directory contents and attributes, used to detect changes
uint8_t mStateChecksum[MD5Digest::DigestLength];
- std::map<std::string, box_time_t> *mpPendingEntries;
- std::map<std::string, BackupClientDirectoryRecord *> mSubDirectories;
+ std::map<std::string, box_time_t> *mpPendingEntries;
+ std::map<std::string, BackupClientDirectoryRecord *> mSubDirectories;
// mpPendingEntries is a pointer rather than simple a member
- // variables, because most of the time it'll be empty. This would waste a lot
- // of memory because of STL allocation policies.
+ // variable, because most of the time it'll be empty. This would
+ // waste a lot of memory because of STL allocation policies.
};
#endif // BACKUPCLIENTDIRECTORYRECORD__H
diff --git a/bin/bbackupd/BackupClientInodeToIDMap.cpp b/bin/bbackupd/BackupClientInodeToIDMap.cpp
index 0d4fd507..b9f56c5a 100644
--- a/bin/bbackupd/BackupClientInodeToIDMap.cpp
+++ b/bin/bbackupd/BackupClientInodeToIDMap.cpp
@@ -217,13 +217,16 @@ void BackupClientInodeToIDMap::AddToMap(InodeRefType InodeRef, int64_t ObjectID,
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupClientInodeToIDMap::Lookup(InodeRefType, int64_t &, int64_t &) const
-// Purpose: Looks up an inode in the map, returning true if it exists, and the object
-// ids of it and the directory it's in the reference arguments.
+// Name: BackupClientInodeToIDMap::Lookup(InodeRefType,
+// int64_t &, int64_t &) const
+// Purpose: Looks up an inode in the map, returning true if it
+// exists, and the object ids of it and the directory
+// it's in the reference arguments.
// Created: 11/11/03
//
// --------------------------------------------------------------------------
-bool BackupClientInodeToIDMap::Lookup(InodeRefType InodeRef, int64_t &rObjectIDOut, int64_t &rInDirectoryOut) const
+bool BackupClientInodeToIDMap::Lookup(InodeRefType InodeRef,
+ int64_t &rObjectIDOut, int64_t &rInDirectoryOut) const
{
#ifdef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION
std::map<InodeRefType, std::pair<int64_t, int64_t> >::const_iterator i(mMap.find(InodeRef));
diff --git a/bin/bbackupd/BackupDaemon.cpp b/bin/bbackupd/BackupDaemon.cpp
index e762bbdc..3615b848 100644
--- a/bin/bbackupd/BackupDaemon.cpp
+++ b/bin/bbackupd/BackupDaemon.cpp
@@ -10,6 +10,7 @@
#include "Box.h"
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
@@ -47,36 +48,34 @@
#include "BoxPortsAndFiles.h"
#include "SSLLib.h"
-#include "TLSContext.h"
-#include "BackupDaemon.h"
-#include "BackupDaemonConfigVerify.h"
+#include "autogen_BackupProtocolClient.h"
+#include "autogen_ClientException.h"
+#include "autogen_ConversionException.h"
+#include "Archive.h"
#include "BackupClientContext.h"
+#include "BackupClientCryptoKeys.h"
#include "BackupClientDirectoryRecord.h"
-#include "BackupStoreDirectory.h"
#include "BackupClientFileAttributes.h"
-#include "BackupStoreFilenameClear.h"
#include "BackupClientInodeToIDMap.h"
-#include "autogen_BackupProtocolClient.h"
-#include "autogen_ConversionException.h"
-#include "BackupClientCryptoKeys.h"
-#include "BannerText.h"
+#include "BackupClientMakeExcludeList.h"
+#include "BackupDaemon.h"
+#include "BackupDaemonConfigVerify.h"
+#include "BackupStoreConstants.h"
+#include "BackupStoreDirectory.h"
+#include "BackupStoreException.h"
#include "BackupStoreFile.h"
-#include "Random.h"
+#include "BackupStoreFilenameClear.h"
+#include "BannerText.h"
+#include "Conversion.h"
#include "ExcludeList.h"
-#include "BackupClientMakeExcludeList.h"
-#include "IOStreamGetLine.h"
-#include "Utils.h"
#include "FileStream.h"
-#include "BackupStoreException.h"
-#include "BackupStoreConstants.h"
-#include "LocalProcessStream.h"
#include "IOStreamGetLine.h"
-#include "Conversion.h"
-#include "Archive.h"
-#include "Timer.h"
+#include "LocalProcessStream.h"
#include "Logging.h"
-#include "autogen_ClientException.h"
+#include "Random.h"
+#include "Timer.h"
+#include "Utils.h"
#ifdef WIN32
#include "Win32ServiceFunctions.h"
@@ -93,25 +92,6 @@ static const time_t MAX_SLEEP_TIME = 1024;
// This prevents repetative cycles of load on the server
#define SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY 6
-#ifdef WIN32
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: HelperThread()
-// Purpose: Background thread function, called by Windows,
-// calls the BackupDaemon's RunHelperThread method
-// to listen for and act on control communications
-// Created: 18/2/04
-//
-// --------------------------------------------------------------------------
-unsigned int WINAPI HelperThread(LPVOID lpParam)
-{
- ((BackupDaemon *)lpParam)->RunHelperThread();
-
- return 0;
-}
-#endif
-
// --------------------------------------------------------------------------
//
// Function
@@ -122,9 +102,23 @@ unsigned int WINAPI HelperThread(LPVOID lpParam)
// --------------------------------------------------------------------------
BackupDaemon::BackupDaemon()
: mState(BackupDaemon::State_Initialising),
- mpCommandSocketInfo(0),
+ mDeleteRedundantLocationsAfter(0),
+ mLastNotifiedEvent(SysadminNotifier::MAX),
mDeleteUnusedRootDirEntriesAfter(0),
- mLogAllFileAccess(false)
+ mClientStoreMarker(BackupClientContext::ClientStoreMarker_NotKnown),
+ mStorageLimitExceeded(false),
+ mReadErrorsOnFilesystemObjects(false),
+ mLastSyncTime(0),
+ mNextSyncTime(0),
+ mCurrentSyncStartTime(0),
+ mUpdateStoreInterval(0),
+ mDeleteStoreObjectInfoFile(false),
+ mDoSyncForcedByPreviousSyncError(false),
+ mLogAllFileAccess(false),
+ mpProgressNotifier(this),
+ mpLocationResolver(this),
+ mpRunStatusProvider(this),
+ mpSysadminNotifier(this)
#ifdef WIN32
, mInstallService(false),
mRemoveService(false),
@@ -134,40 +128,6 @@ BackupDaemon::BackupDaemon()
{
// Only ever one instance of a daemon
SSLLib::Initialise();
-
- // Initialise notification sent status
- for(int l = 0; l < NotifyEvent__MAX; ++l)
- {
- mNotificationsSent[l] = false;
- }
-
- #ifdef WIN32
- // Create the event object to signal from main thread to
- // worker when new messages are queued to be sent to the
- // command socket.
-
- mhMessageToSendEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if(mhMessageToSendEvent == INVALID_HANDLE_VALUE)
- {
- BOX_ERROR("Failed to create event object: error " <<
- GetLastError());
- exit(1);
- }
-
- // Create the event object to signal from worker to main thread
- // when a command has been received on the command socket.
-
- mhCommandReceivedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if(mhCommandReceivedEvent == INVALID_HANDLE_VALUE)
- {
- BOX_ERROR("Failed to create event object: error " <<
- GetLastError());
- exit(1);
- }
-
- // Create the critical section to protect the message queue
- InitializeCriticalSection(&mMessageQueueLock);
- #endif
}
// --------------------------------------------------------------------------
@@ -182,12 +142,6 @@ BackupDaemon::~BackupDaemon()
{
DeleteAllLocations();
DeleteAllIDMaps();
-
- if(mpCommandSocketInfo != 0)
- {
- delete mpCommandSocketInfo;
- mpCommandSocketInfo = 0;
- }
}
// --------------------------------------------------------------------------
@@ -262,12 +216,12 @@ void BackupDaemon::SetupInInitialProcess()
if(GetConfiguration().KeyExists("CommandSocket"))
{
BOX_WARNING(
- "==============================================================================\n"
- "SECURITY WARNING: This platform cannot check the credentials of connections to\n"
- "the command socket. This is a potential DoS security problem.\n"
- "Remove the CommandSocket directive from the bbackupd.conf file if bbackupctl\n"
- "is not used.\n"
- "==============================================================================\n"
+ "==============================================================================\n"
+ "SECURITY WARNING: This platform cannot check the credentials of connections to\n"
+ "the command socket. This is a potential DoS security problem.\n"
+ "Remove the CommandSocket directive from the bbackupd.conf file if bbackupctl\n"
+ "is not used.\n"
+ "==============================================================================\n"
);
}
}
@@ -294,7 +248,7 @@ void BackupDaemon::DeleteAllLocations()
// Clear the contents of the map, so it is empty
mLocations.clear();
- // And delete everything from the assoicated mount vector
+ // And delete everything from the associated mount vector
mIDMapMounts.clear();
}
@@ -322,6 +276,7 @@ int BackupDaemon::ProcessOption(signed int option)
case 'S':
{
mServiceName = optarg;
+ Logging::SetProgramName(mServiceName);
return 0;
}
@@ -356,8 +311,6 @@ int BackupDaemon::Main(const std::string &rConfigFileName)
return RemoveService(mServiceName);
}
- Logging::SetProgramName("Box Backup (" + mServiceName + ")");
-
int returnCode;
if (mRunAsService)
@@ -377,220 +330,6 @@ int BackupDaemon::Main(const std::string &rConfigFileName)
return returnCode;
}
-
-void BackupDaemon::RunHelperThread(void)
-{
- const Configuration &conf(GetConfiguration());
- mpCommandSocketInfo = new CommandSocketInfo;
- WinNamedPipeStream& rSocket(mpCommandSocketInfo->mListeningSocket);
-
- // loop until the parent process exits, or we decide
- // to kill the thread ourselves
- while (!IsTerminateWanted())
- {
- try
- {
- std::string socket = conf.GetKeyValue("CommandSocket");
- rSocket.Accept(socket);
- }
- catch (BoxException &e)
- {
- BOX_ERROR("Failed to open command socket: " <<
- e.what());
- SetTerminateWanted();
- break; // this is fatal to listening thread
- }
- catch(std::exception &e)
- {
- BOX_ERROR("Failed to open command socket: " <<
- e.what());
- SetTerminateWanted();
- break; // this is fatal to listening thread
- }
- catch(...)
- {
- BOX_ERROR("Failed to open command socket: "
- "unknown error");
- SetTerminateWanted();
- break; // this is fatal to listening thread
- }
-
- try
- {
- // Errors here do not kill the thread,
- // only the current connection.
-
- // This next section comes from Ben's original function
- // Log
- BOX_INFO("Connection from command socket");
-
- // Send a header line summarising the configuration
- // and current state
- char summary[256];
- size_t summarySize = sprintf(summary,
- "bbackupd: %d %d %d %d\nstate %d\n",
- conf.GetKeyValueBool("AutomaticBackup"),
- conf.GetKeyValueInt("UpdateStoreInterval"),
- conf.GetKeyValueInt("MinimumFileAge"),
- conf.GetKeyValueInt("MaxUploadWait"),
- mState);
-
- rSocket.Write(summary, summarySize);
- rSocket.Write("ping\n", 5);
-
- // old queued messages are not useful
- EnterCriticalSection(&mMessageQueueLock);
- mMessageList.clear();
- ResetEvent(mhMessageToSendEvent);
- LeaveCriticalSection(&mMessageQueueLock);
-
- IOStreamGetLine readLine(rSocket);
- std::string command;
-
- while (rSocket.IsConnected() && !IsTerminateWanted())
- {
- HANDLE handles[2];
- handles[0] = mhMessageToSendEvent;
- handles[1] = rSocket.GetReadableEvent();
-
- BOX_TRACE("Received command '" << command
- << "' over command socket");
-
- DWORD result = WaitForMultipleObjects(
- sizeof(handles)/sizeof(*handles),
- handles, FALSE, 1000);
-
- if(result == 0)
- {
- ResetEvent(mhMessageToSendEvent);
-
- EnterCriticalSection(&mMessageQueueLock);
- try
- {
- while (mMessageList.size() > 0)
- {
- std::string message = *(mMessageList.begin());
- mMessageList.erase(mMessageList.begin());
- printf("Sending '%s' to waiting client... ", message.c_str());
- message += "\n";
- rSocket.Write(message.c_str(),
- message.length());
-
- printf("done.\n");
- }
- }
- catch (...)
- {
- LeaveCriticalSection(&mMessageQueueLock);
- throw;
- }
- LeaveCriticalSection(&mMessageQueueLock);
- continue;
- }
- else if(result == WAIT_TIMEOUT)
- {
- continue;
- }
- else if(result != 1)
- {
- BOX_ERROR("WaitForMultipleObjects returned invalid result " << result);
- continue;
- }
-
- if(!readLine.GetLine(command))
- {
- BOX_ERROR("Failed to read line");
- continue;
- }
-
- BOX_INFO("Received command " << command <<
- " from client");
-
- bool sendOK = false;
- bool sendResponse = true;
- bool disconnect = false;
-
- // Command to process!
- if(command == "quit" || command == "")
- {
- // Close the socket.
- disconnect = true;
- sendResponse = false;
- }
- else if(command == "sync")
- {
- // Sync now!
- this->mDoSyncFlagOut = true;
- this->mSyncIsForcedOut = false;
- sendOK = true;
- SetEvent(mhCommandReceivedEvent);
- }
- else if(command == "force-sync")
- {
- // Sync now (forced -- overrides any SyncAllowScript)
- this->mDoSyncFlagOut = true;
- this->mSyncIsForcedOut = true;
- sendOK = true;
- SetEvent(mhCommandReceivedEvent);
- }
- else if(command == "reload")
- {
- // Reload the configuration
- SetReloadConfigWanted();
- sendOK = true;
- SetEvent(mhCommandReceivedEvent);
- }
- else if(command == "terminate")
- {
- // Terminate the daemon cleanly
- SetTerminateWanted();
- sendOK = true;
- SetEvent(mhCommandReceivedEvent);
- }
- else
- {
- BOX_ERROR("Received unknown command "
- "'" << command << "' "
- "from client");
- sendResponse = true;
- sendOK = false;
- }
-
- // Send a response back?
- if(sendResponse)
- {
- const char* response = sendOK ? "ok\n" : "error\n";
- rSocket.Write(
- response, strlen(response));
- }
-
- if(disconnect)
- {
- break;
- }
- }
-
- rSocket.Close();
- }
- catch(BoxException &e)
- {
- BOX_ERROR("Communication error with "
- "control client: " << e.what());
- }
- catch(std::exception &e)
- {
- BOX_ERROR("Internal error in command socket "
- "thread: " << e.what());
- }
- catch(...)
- {
- BOX_ERROR("Communication error with control client");
- }
- }
-
- CloseHandle(mhCommandReceivedEvent);
- CloseHandle(mhMessageToSendEvent);
-}
#endif
// --------------------------------------------------------------------------
@@ -606,36 +345,29 @@ void BackupDaemon::Run()
// initialise global timer mechanism
Timers::Init();
- #ifdef WIN32
- // Create a thread to handle the named pipe
- HANDLE hThread;
- unsigned int dwThreadId;
-
- hThread = (HANDLE) _beginthreadex(
- NULL, // default security attributes
- 0, // use default stack size
- HelperThread, // thread function
- this, // argument to thread function
- 0, // use default creation flags
- &dwThreadId); // returns the thread identifier
- #else
+ #ifndef WIN32
// Ignore SIGPIPE so that if a command connection is broken,
// the daemon doesn't terminate.
::signal(SIGPIPE, SIG_IGN);
+ #endif
- // Create a command socket?
- const Configuration &conf(GetConfiguration());
- if(conf.KeyExists("CommandSocket"))
- {
- // Yes, create a local UNIX socket
- mpCommandSocketInfo = new CommandSocketInfo;
- const char *socketName =
- conf.GetKeyValue("CommandSocket").c_str();
+ // Create a command socket?
+ const Configuration &conf(GetConfiguration());
+ if(conf.KeyExists("CommandSocket"))
+ {
+ // Yes, create a local UNIX socket
+ mapCommandSocketInfo.reset(new CommandSocketInfo);
+ const char *socketName =
+ conf.GetKeyValue("CommandSocket").c_str();
+ #ifdef WIN32
+ mapCommandSocketInfo->mListeningSocket.Listen(
+ socketName);
+ #else
::unlink(socketName);
- mpCommandSocketInfo->mListeningSocket.Listen(
+ mapCommandSocketInfo->mListeningSocket.Listen(
Socket::TypeUNIX, socketName);
- }
- #endif // !WIN32
+ #endif
+ }
// Handle things nicely on exceptions
try
@@ -644,16 +376,11 @@ void BackupDaemon::Run()
}
catch(...)
{
- #ifdef WIN32
- // Don't delete the socket, as the helper thread
- // is probably still using it. Let Windows clean
- // up after us.
- #else
- if(mpCommandSocketInfo != 0)
+ if(mapCommandSocketInfo.get())
{
try
{
- delete mpCommandSocketInfo;
+ mapCommandSocketInfo.reset();
}
catch(std::exception &e)
{
@@ -666,91 +393,63 @@ void BackupDaemon::Run()
BOX_WARNING("Error closing command socket "
"after exception, ignored.");
}
- mpCommandSocketInfo = 0;
}
- #endif // WIN32
Timers::Cleanup();
throw;
}
- #ifndef WIN32
- // Clean up
- if(mpCommandSocketInfo != 0)
- {
- delete mpCommandSocketInfo;
- mpCommandSocketInfo = 0;
- }
- #endif
-
+ // Clean up
+ mapCommandSocketInfo.reset();
Timers::Cleanup();
}
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupDaemon::Run2()
-// Purpose: Run function for daemon (second stage)
-// Created: 2003/10/08
-//
-// --------------------------------------------------------------------------
-void BackupDaemon::Run2()
+void BackupDaemon::InitCrypto()
{
// Read in the certificates creating a TLS context
- TLSContext tlsContext;
const Configuration &conf(GetConfiguration());
std::string certFile(conf.GetKeyValue("CertificateFile"));
std::string keyFile(conf.GetKeyValue("PrivateKeyFile"));
std::string caFile(conf.GetKeyValue("TrustedCAsFile"));
- tlsContext.Initialise(false /* as client */, certFile.c_str(), keyFile.c_str(), caFile.c_str());
+ mTlsContext.Initialise(false /* as client */, certFile.c_str(),
+ keyFile.c_str(), caFile.c_str());
// Set up the keys for various things
BackupClientCryptoKeys_Setup(conf.GetKeyValue("KeysFile").c_str());
+}
- // Setup various timings
- int maximumDiffingTime = 600;
- int keepAliveTime = 60;
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BackupDaemon::Run2()
+// Purpose: Run function for daemon (second stage)
+// Created: 2003/10/08
+//
+// --------------------------------------------------------------------------
+void BackupDaemon::Run2()
+{
+ InitCrypto();
- // max diffing time, keep-alive time
- if(conf.KeyExists("MaximumDiffingTime"))
- {
- maximumDiffingTime = conf.GetKeyValueInt("MaximumDiffingTime");
- }
- if(conf.KeyExists("KeepAliveTime"))
- {
- keepAliveTime = conf.GetKeyValueInt("KeepAliveTime");
- }
+ const Configuration &conf(GetConfiguration());
// How often to connect to the store (approximate)
- box_time_t updateStoreInterval = SecondsToBoxTime(conf.GetKeyValueInt("UpdateStoreInterval"));
+ mUpdateStoreInterval = SecondsToBoxTime(
+ conf.GetKeyValueInt("UpdateStoreInterval"));
// But are we connecting automatically?
bool automaticBackup = conf.GetKeyValueBool("AutomaticBackup");
- // The minimum age a file needs to be before it will be considered for uploading
- box_time_t minimumFileAge = SecondsToBoxTime(conf.GetKeyValueInt("MinimumFileAge"));
-
- // The maximum time we'll wait to upload a file, regardless of how often it's modified
- box_time_t maxUploadWait = SecondsToBoxTime(conf.GetKeyValueInt("MaxUploadWait"));
- // Adjust by subtracting the minimum file age, so is relative to sync period end in comparisons
- maxUploadWait = (maxUploadWait > minimumFileAge)?(maxUploadWait - minimumFileAge):(0);
-
// When the next sync should take place -- which is ASAP
- box_time_t nextSyncTime = 0;
+ mNextSyncTime = 0;
// When the last sync started (only updated if the store was not full when the sync ended)
- box_time_t lastSyncTime = 0;
+ mLastSyncTime = 0;
// --------------------------------------------------------------------------------------------
- // And what's the current client store marker?
- int64_t clientStoreMarker =
- BackupClientContext::ClientStoreMarker_NotKnown;
- // haven't contacted the store yet
-
- bool deleteStoreObjectInfoFile = DeserializeStoreObjectInfo(
- clientStoreMarker, lastSyncTime, nextSyncTime);
+ mDeleteStoreObjectInfoFile = DeserializeStoreObjectInfo(
+ mLastSyncTime, mNextSyncTime);
// --------------------------------------------------------------------------------------------
@@ -758,91 +457,98 @@ void BackupDaemon::Run2()
// Set state
SetState(State_Idle);
+ mDoSyncForcedByPreviousSyncError = false;
+
// Loop around doing backups
do
{
// Flags used below
bool storageLimitExceeded = false;
bool doSync = false;
- bool doSyncForcedByCommand = false;
+ bool mDoSyncForcedByCommand = false;
// Is a delay necessary?
+ box_time_t currentTime;
+
+ do
{
- box_time_t currentTime;
- do
+ // Check whether we should be stopping,
+ // and don't run a sync if so.
+ if(StopRun()) break;
+
+ currentTime = GetCurrentBoxTime();
+
+ // Pause a while, but no more than
+ // MAX_SLEEP_TIME seconds (use the conditional
+ // because times are unsigned)
+ box_time_t requiredDelay =
+ (mNextSyncTime < currentTime)
+ ? (0)
+ : (mNextSyncTime - currentTime);
+
+ // If there isn't automatic backup happening,
+ // set a long delay. And limit delays at the
+ // same time.
+ if(!automaticBackup && !mDoSyncForcedByPreviousSyncError)
{
- // Check whether we should be stopping,
- // and don't run a sync if so.
- if(StopRun()) break;
-
- currentTime = GetCurrentBoxTime();
-
- // Pause a while, but no more than
- // MAX_SLEEP_TIME seconds (use the conditional
- // because times are unsigned)
- box_time_t requiredDelay =
- (nextSyncTime < currentTime)
- ? (0)
- : (nextSyncTime - currentTime);
-
- // If there isn't automatic backup happening,
- // set a long delay. And limit delays at the
- // same time.
- if(!automaticBackup || requiredDelay >
- SecondsToBoxTime(MAX_SLEEP_TIME))
+ requiredDelay = SecondsToBoxTime(MAX_SLEEP_TIME);
+ }
+ else if(requiredDelay > SecondsToBoxTime(MAX_SLEEP_TIME))
+ {
+ requiredDelay = SecondsToBoxTime(MAX_SLEEP_TIME);
+ }
+
+ // Only delay if necessary
+ if(requiredDelay > 0)
+ {
+ // Sleep somehow. There are choices
+ // on how this should be done,
+ // depending on the state of the
+ // control connection
+ if(mapCommandSocketInfo.get() != 0)
{
- requiredDelay = SecondsToBoxTime(
- MAX_SLEEP_TIME);
+ // A command socket exists,
+ // so sleep by waiting on it
+ WaitOnCommandSocket(requiredDelay,
+ doSync, mDoSyncForcedByCommand);
}
-
- // Only delay if necessary
- if(requiredDelay > 0)
+ else
{
- // Sleep somehow. There are choices
- // on how this should be done,
- // depending on the state of the
- // control connection
- if(mpCommandSocketInfo != 0)
- {
- // A command socket exists,
- // so sleep by waiting on it
- WaitOnCommandSocket(
- requiredDelay, doSync,
- doSyncForcedByCommand);
- }
- else
- {
- // No command socket or
- // connection, just do a
- // normal sleep
- time_t sleepSeconds =
- BoxTimeToSeconds(
- requiredDelay);
- ::sleep((sleepSeconds <= 0)
- ? 1
- : sleepSeconds);
- }
+ // No command socket or
+ // connection, just do a
+ // normal sleep
+ time_t sleepSeconds =
+ BoxTimeToSeconds(requiredDelay);
+ ::sleep((sleepSeconds <= 0)
+ ? 1 : sleepSeconds);
}
-
- } while((!automaticBackup || (currentTime < nextSyncTime)) && !doSync && !StopRun());
+ }
+
+ if ((automaticBackup || mDoSyncForcedByPreviousSyncError)
+ && currentTime >= mNextSyncTime)
+ {
+ doSync = true;
+ }
}
+ while(!doSync && !StopRun());
// Time of sync start, and if it's time for another sync
// (and we're doing automatic syncs), set the flag
- box_time_t currentSyncStartTime = GetCurrentBoxTime();
- if(automaticBackup && currentSyncStartTime >= nextSyncTime)
+ mCurrentSyncStartTime = GetCurrentBoxTime();
+ if((automaticBackup || mDoSyncForcedByPreviousSyncError) &&
+ mCurrentSyncStartTime >= mNextSyncTime)
{
doSync = true;
}
// Use a script to see if sync is allowed now?
- if(!doSyncForcedByCommand && doSync && !StopRun())
+ if(!mDoSyncForcedByCommand && doSync && !StopRun())
{
int d = UseScriptToSeeIfSyncAllowed();
if(d > 0)
{
// Script has asked for a delay
- nextSyncTime = GetCurrentBoxTime() +
+ mNextSyncTime = GetCurrentBoxTime() +
SecondsToBoxTime(d);
doSync = false;
}
@@ -852,383 +558,448 @@ void BackupDaemon::Run2()
// to be stopping)
if(doSync && !StopRun())
{
- // Touch a file to record times in filesystem
- TouchFileInWorkingDir("last_sync_start");
+ RunSyncNowWithExceptionHandling();
+ }
- // Tell anything connected to the command socket
- SendSyncStartOrFinish(true /* start */);
-
- // Reset statistics on uploads
- BackupStoreFile::ResetStats();
-
- // Calculate the sync period of files to examine
- box_time_t syncPeriodStart = lastSyncTime;
- box_time_t syncPeriodEnd = currentSyncStartTime -
- minimumFileAge;
+ // Set state
+ SetState(storageLimitExceeded?State_StorageLimitExceeded:State_Idle);
- if(syncPeriodStart >= syncPeriodEnd &&
- syncPeriodStart - syncPeriodEnd < minimumFileAge)
- {
- // This can happen if we receive a force-sync
- // command less than minimumFileAge after
- // the last sync. Deal with it by moving back
- // syncPeriodStart, which should not do any
- // damage.
- syncPeriodStart = syncPeriodEnd -
- SecondsToBoxTime(1);
- }
+ } while(!StopRun());
+
+ // Make sure we have a clean start next time round (if restart)
+ DeleteAllLocations();
+ DeleteAllIDMaps();
+}
- if(syncPeriodStart >= syncPeriodEnd)
- {
- BOX_ERROR("Invalid (negative) sync period: "
- "perhaps your clock is going "
- "backwards (" << syncPeriodStart <<
- " to " << syncPeriodEnd << ")");
- THROW_EXCEPTION(ClientException,
- ClockWentBackwards);
- }
+void BackupDaemon::RunSyncNowWithExceptionHandling()
+{
+ OnBackupStart();
- // Check logic
- ASSERT(syncPeriodEnd > syncPeriodStart);
- // Paranoid check on sync times
- if(syncPeriodStart >= syncPeriodEnd) continue;
-
- // Adjust syncPeriodEnd to emulate snapshot
- // behaviour properly
- box_time_t syncPeriodEndExtended = syncPeriodEnd;
- // Using zero min file age?
- if(minimumFileAge == 0)
- {
- // Add a year on to the end of the end time,
- // to make sure we sync files which are
- // modified after the scan run started.
- // Of course, they may be eligible to be
- // synced again the next time round,
- // but this should be OK, because the changes
- // only upload should upload no data.
- syncPeriodEndExtended += SecondsToBoxTime(
- (time_t)(356*24*3600));
- }
+ // Do sync
+ bool errorOccurred = false;
+ int errorCode = 0, errorSubCode = 0;
+ const char* errorString = "unknown";
- // Delete the serialised store object file,
- // so that we don't try to reload it after a
- // partially completed backup
- if(deleteStoreObjectInfoFile &&
- !DeleteStoreObjectInfo())
- {
- BOX_ERROR("Failed to delete the "
- "StoreObjectInfoFile, backup cannot "
- "continue safely.");
- THROW_EXCEPTION(ClientException,
- FailedToDeleteStoreObjectInfoFile);
- }
+ try
+ {
+ RunSyncNow();
+ }
+ catch(BoxException &e)
+ {
+ errorOccurred = true;
+ errorString = e.what();
+ errorCode = e.GetType();
+ errorSubCode = e.GetSubType();
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Internal error during backup run: " << e.what());
+ errorOccurred = true;
+ errorString = e.what();
+ }
+ catch(...)
+ {
+ // TODO: better handling of exceptions here...
+ // need to be very careful
+ errorOccurred = true;
+ }
- // In case the backup throws an exception,
- // we should not try to delete the store info
- // object file again.
- deleteStoreObjectInfoFile = false;
-
- // Do sync
- bool errorOccurred = false;
- int errorCode = 0, errorSubCode = 0;
- const char* errorString = "unknown";
+ // do not retry immediately without a good reason
+ mDoSyncForcedByPreviousSyncError = false;
+
+ if(errorOccurred)
+ {
+ // Is it a berkely db failure?
+ bool isBerkelyDbFailure = false;
- try
- {
- // Set state and log start
- SetState(State_Connected);
- BOX_NOTICE("Beginning scan of local files");
+ if (errorCode == BackupStoreException::ExceptionType
+ && errorSubCode == BackupStoreException::BerkelyDBFailure)
+ {
+ isBerkelyDbFailure = true;
+ }
- std::string extendedLogFile;
- if (conf.KeyExists("ExtendedLogFile"))
- {
- extendedLogFile = conf.GetKeyValue(
- "ExtendedLogFile");
- }
-
- if (conf.KeyExists("LogAllFileAccess"))
- {
- mLogAllFileAccess =
- conf.GetKeyValueBool(
- "LogAllFileAccess");
- }
-
- // Then create a client context object (don't
- // just connect, as this may be unnecessary)
- BackupClientContext clientContext
- (
- *this,
- tlsContext,
- conf.GetKeyValue("StoreHostname"),
- conf.GetKeyValueInt("AccountNumber"),
- conf.GetKeyValueBool("ExtendedLogging"),
- conf.KeyExists("ExtendedLogFile"),
- extendedLogFile
- );
-
- // Set up the sync parameters
- BackupClientDirectoryRecord::SyncParams params(
- *this, *this, clientContext);
- params.mSyncPeriodStart = syncPeriodStart;
- params.mSyncPeriodEnd = syncPeriodEndExtended;
- // use potentially extended end time
- params.mMaxUploadWait = maxUploadWait;
- params.mFileTrackingSizeThreshold =
- conf.GetKeyValueInt(
- "FileTrackingSizeThreshold");
- params.mDiffingUploadSizeThreshold =
- conf.GetKeyValueInt(
- "DiffingUploadSizeThreshold");
- params.mMaxFileTimeInFuture =
- SecondsToBoxTime(
- conf.GetKeyValueInt(
- "MaxFileTimeInFuture"));
- mDeleteRedundantLocationsAfter =
- conf.GetKeyValueInt(
- "DeleteRedundantLocationsAfter");
-
- clientContext.SetMaximumDiffingTime(maximumDiffingTime);
- clientContext.SetKeepAliveTime(keepAliveTime);
-
- // Set store marker
- clientContext.SetClientStoreMarker(clientStoreMarker);
-
- // Set up the locations, if necessary --
- // need to do it here so we have a
- // (potential) connection to use
- if(mLocations.empty())
- {
- const Configuration &locations(
- conf.GetSubConfiguration(
- "BackupLocations"));
-
- // Make sure all the directory records
- // are set up
- SetupLocations(clientContext, locations);
- }
-
- // Get some ID maps going
- SetupIDMapsForSync();
-
- // Delete any unused directories?
- DeleteUnusedRootDirEntries(clientContext);
-
- // Notify administrator
- NotifySysadmin(NotifyEvent_BackupStart);
-
- // Go through the records, syncing them
- for(std::vector<Location *>::const_iterator
- i(mLocations.begin());
- i != mLocations.end(); ++i)
- {
- // Set current and new ID map pointers
- // in the context
- clientContext.SetIDMaps(mCurrentIDMaps[(*i)->mIDMapIndex], mNewIDMaps[(*i)->mIDMapIndex]);
-
- // Set exclude lists (context doesn't
- // take ownership)
- clientContext.SetExcludeLists(
- (*i)->mpExcludeFiles,
- (*i)->mpExcludeDirs);
-
- // Sync the directory
- (*i)->mpDirectoryRecord->SyncDirectory(
- params,
- BackupProtocolClientListDirectory::RootDirectory,
- (*i)->mPath);
+ if(isBerkelyDbFailure)
+ {
+ // Delete corrupt files
+ DeleteCorruptBerkelyDbFiles();
+ }
- // Unset exclude lists (just in case)
- clientContext.SetExcludeLists(0, 0);
- }
-
- // Errors reading any files?
- if(params.mReadErrorsOnFilesystemObjects)
- {
- // Notify administrator
- NotifySysadmin(NotifyEvent_ReadError);
- }
- else
- {
- // Unset the read error flag, so the // error is reported again if it
- // happens again
- mNotificationsSent[NotifyEvent_ReadError] = false;
- }
-
- // Perform any deletions required -- these are
- // delayed until the end to allow renaming to
- // happen neatly.
- clientContext.PerformDeletions();
+ // Clear state data
+ // Go back to beginning of time
+ mLastSyncTime = 0;
+ mClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown; // no store marker, so download everything
+ DeleteAllLocations();
+ DeleteAllIDMaps();
- // Close any open connection
- clientContext.CloseAnyOpenConnection();
-
- // Get the new store marker
- clientStoreMarker = clientContext.GetClientStoreMarker();
-
- // Check the storage limit
- if(clientContext.StorageLimitExceeded())
- {
- // Tell the sysadmin about this
- NotifySysadmin(NotifyEvent_StoreFull);
- }
- else
- {
- // The start time of the next run is
- // the end time of this run.
- // This is only done if the storage
- // limit wasn't exceeded (as things
- // won't have been done properly if
- // it was)
- lastSyncTime = syncPeriodEnd;
-
- // unflag the storage full notify flag
- // so that next time the store is full,
- // an alert will be sent
- mNotificationsSent[NotifyEvent_StoreFull] = false;
- }
-
- // Calculate when the next sync run should be
- nextSyncTime = currentSyncStartTime +
- updateStoreInterval +
- Random::RandomInt(updateStoreInterval >>
+ // Handle restart?
+ if(StopRun())
+ {
+ BOX_NOTICE("Exception (" << errorCode
+ << "/" << errorSubCode
+ << ") due to signal");
+ OnBackupFinish();
+ return;
+ }
+
+ NotifySysadmin(SysadminNotifier::BackupError);
+
+ // If the Berkely db files get corrupted,
+ // delete them and try again immediately.
+ if(isBerkelyDbFailure)
+ {
+ BOX_ERROR("Berkely db inode map files corrupted, "
+ "deleting and restarting scan. Renamed files "
+ "and directories will not be tracked until "
+ "after this scan.");
+ ::sleep(1);
+ }
+ else
+ {
+ // Not restart/terminate, pause and retry
+ // Notify administrator
+ SetState(State_Error);
+ BOX_ERROR("Exception caught (" << errorString <<
+ " " << errorCode << "/" << errorSubCode <<
+ "), reset state and waiting to retry...");
+ ::sleep(10);
+ mNextSyncTime = mCurrentSyncStartTime +
+ SecondsToBoxTime(100) +
+ Random::RandomInt(mUpdateStoreInterval >>
SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY);
-
- // Commit the ID Maps
- CommitIDMapsAfterSync();
+ }
+ }
+ // Notify system administrator about the final state of the backup
+ else if(mReadErrorsOnFilesystemObjects)
+ {
+ NotifySysadmin(SysadminNotifier::ReadError);
+ }
+ else if(mStorageLimitExceeded)
+ {
+ NotifySysadmin(SysadminNotifier::StoreFull);
+ }
+ else
+ {
+ NotifySysadmin(SysadminNotifier::BackupOK);
+ }
+
+ // If we were retrying after an error, and this backup succeeded,
+ // then now would be a good time to stop :-)
+ mDoSyncForcedByPreviousSyncError = errorOccurred;
- // Log
- BOX_NOTICE("Finished scan of local files");
+ OnBackupFinish();
+}
- // Notify administrator
- NotifySysadmin(NotifyEvent_BackupFinish);
+void BackupDaemon::RunSyncNow()
+{
+ // Delete the serialised store object file,
+ // so that we don't try to reload it after a
+ // partially completed backup
+ if(mDeleteStoreObjectInfoFile &&
+ !DeleteStoreObjectInfo())
+ {
+ BOX_ERROR("Failed to delete the StoreObjectInfoFile, "
+ "backup cannot continue safely.");
+ THROW_EXCEPTION(ClientException,
+ FailedToDeleteStoreObjectInfoFile);
+ }
- // --------------------------------------------------------------------------------------------
+ // In case the backup throws an exception,
+ // we should not try to delete the store info
+ // object file again.
+ mDeleteStoreObjectInfoFile = false;
- // We had a successful backup, save the store
- // info. If we save successfully, we must
- // delete the file next time we start a backup
+ const Configuration &conf(GetConfiguration());
- deleteStoreObjectInfoFile =
- SerializeStoreObjectInfo(
- clientStoreMarker,
- lastSyncTime, nextSyncTime);
+ std::auto_ptr<FileLogger> fileLogger;
- // --------------------------------------------------------------------------------------------
- }
- catch(BoxException &e)
- {
- errorOccurred = true;
- errorString = e.what();
- errorCode = e.GetType();
- errorSubCode = e.GetSubType();
- }
- catch(std::exception &e)
- {
- BOX_ERROR("Internal error during "
- "backup run: " << e.what());
- errorOccurred = true;
- errorString = e.what();
- }
- catch(...)
- {
- // TODO: better handling of exceptions here...
- // need to be very careful
- errorOccurred = true;
- }
-
- if(errorOccurred)
- {
- // Is it a berkely db failure?
- bool isBerkelyDbFailure = false;
+ if (conf.KeyExists("LogFile"))
+ {
+ Log::Level level = Log::INFO;
+ if (conf.KeyExists("LogFileLevel"))
+ {
+ level = Logging::GetNamedLevel(
+ conf.GetKeyValue("LogFileLevel"));
+ }
+ fileLogger.reset(new FileLogger(conf.GetKeyValue("LogFile"),
+ level));
+ }
- if (errorCode == BackupStoreException::ExceptionType
- && errorSubCode == BackupStoreException::BerkelyDBFailure)
- {
- isBerkelyDbFailure = true;
- }
+ std::string extendedLogFile;
+ if (conf.KeyExists("ExtendedLogFile"))
+ {
+ extendedLogFile = conf.GetKeyValue("ExtendedLogFile");
+ }
+
+ if (conf.KeyExists("LogAllFileAccess"))
+ {
+ mLogAllFileAccess = conf.GetKeyValueBool("LogAllFileAccess");
+ }
+
+ // Then create a client context object (don't
+ // just connect, as this may be unnecessary)
+ BackupClientContext clientContext
+ (
+ *mpLocationResolver,
+ mTlsContext,
+ conf.GetKeyValue("StoreHostname"),
+ conf.GetKeyValueInt("StorePort"),
+ conf.GetKeyValueInt("AccountNumber"),
+ conf.GetKeyValueBool("ExtendedLogging"),
+ conf.KeyExists("ExtendedLogFile"),
+ extendedLogFile, *mpProgressNotifier
+ );
+
+ // The minimum age a file needs to be before it will be
+ // considered for uploading
+ box_time_t minimumFileAge = SecondsToBoxTime(
+ conf.GetKeyValueInt("MinimumFileAge"));
- if(isBerkelyDbFailure)
- {
- // Delete corrupt files
- DeleteCorruptBerkelyDbFiles();
- }
+ // The maximum time we'll wait to upload a file, regardless
+ // of how often it's modified
+ box_time_t maxUploadWait = SecondsToBoxTime(
+ conf.GetKeyValueInt("MaxUploadWait"));
+ // Adjust by subtracting the minimum file age, so is relative
+ // to sync period end in comparisons
+ if (maxUploadWait > minimumFileAge)
+ {
+ maxUploadWait -= minimumFileAge;
+ }
+ else
+ {
+ maxUploadWait = 0;
+ }
- // Clear state data
- syncPeriodStart = 0;
- // go back to beginning of time
- clientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown; // no store marker, so download everything
- DeleteAllLocations();
- DeleteAllIDMaps();
+ // Calculate the sync period of files to examine
+ box_time_t syncPeriodStart = mLastSyncTime;
+ box_time_t syncPeriodEnd = GetCurrentBoxTime() - minimumFileAge;
- // Handle restart?
- if(StopRun())
- {
- BOX_NOTICE("Exception (" << errorCode
- << "/" << errorSubCode
- << ") due to signal");
- return;
- }
+ if(syncPeriodStart >= syncPeriodEnd &&
+ syncPeriodStart - syncPeriodEnd < minimumFileAge)
+ {
+ // This can happen if we receive a force-sync command less
+ // than minimumFileAge after the last sync. Deal with it by
+ // moving back syncPeriodStart, which should not do any
+ // damage.
+ syncPeriodStart = syncPeriodEnd -
+ SecondsToBoxTime(1);
+ }
- // If the Berkely db files get corrupted, delete them and try again immediately
- if(isBerkelyDbFailure)
- {
- BOX_ERROR("Berkely db inode map files corrupted, deleting and restarting scan. Renamed files and directories will not be tracked until after this scan.");
- ::sleep(1);
- }
- else
- {
- // Not restart/terminate, pause and retry
- // Notify administrator
- NotifySysadmin(NotifyEvent_BackupError);
- SetState(State_Error);
- BOX_ERROR("Exception caught ("
- << errorString
- << " " << errorCode
- << "/" << errorSubCode
- << "), reset state and "
- "waiting to retry...");
- ::sleep(10);
- nextSyncTime = currentSyncStartTime +
- SecondsToBoxTime(90) +
- Random::RandomInt(
- updateStoreInterval >>
- SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY);
- }
- }
+ if(syncPeriodStart >= syncPeriodEnd)
+ {
+ BOX_ERROR("Invalid (negative) sync period: "
+ "perhaps your clock is going "
+ "backwards (" << syncPeriodStart <<
+ " to " << syncPeriodEnd << ")");
+ THROW_EXCEPTION(ClientException,
+ ClockWentBackwards);
+ }
- // Log the stats
- BOX_NOTICE("File statistics: total file size uploaded "
- << BackupStoreFile::msStats.mBytesInEncodedFiles
- << ", bytes already on server "
- << BackupStoreFile::msStats.mBytesAlreadyOnServer
- << ", encoded size "
- << BackupStoreFile::msStats.mTotalFileStreamSize);
- BackupStoreFile::ResetStats();
+ // Check logic
+ ASSERT(syncPeriodEnd > syncPeriodStart);
+ // Paranoid check on sync times
+ if(syncPeriodStart >= syncPeriodEnd) return;
+
+ // Adjust syncPeriodEnd to emulate snapshot
+ // behaviour properly
+ box_time_t syncPeriodEndExtended = syncPeriodEnd;
+
+ // Using zero min file age?
+ if(minimumFileAge == 0)
+ {
+ // Add a year on to the end of the end time,
+ // to make sure we sync files which are
+ // modified after the scan run started.
+ // Of course, they may be eligible to be
+ // synced again the next time round,
+ // but this should be OK, because the changes
+ // only upload should upload no data.
+ syncPeriodEndExtended += SecondsToBoxTime(
+ (time_t)(356*24*3600));
+ }
+
+ // Set up the sync parameters
+ BackupClientDirectoryRecord::SyncParams params(*mpRunStatusProvider,
+ *mpSysadminNotifier, *mpProgressNotifier, clientContext);
+ params.mSyncPeriodStart = syncPeriodStart;
+ params.mSyncPeriodEnd = syncPeriodEndExtended;
+ // use potentially extended end time
+ params.mMaxUploadWait = maxUploadWait;
+ params.mFileTrackingSizeThreshold =
+ conf.GetKeyValueInt("FileTrackingSizeThreshold");
+ params.mDiffingUploadSizeThreshold =
+ conf.GetKeyValueInt("DiffingUploadSizeThreshold");
+ params.mMaxFileTimeInFuture =
+ SecondsToBoxTime(conf.GetKeyValueInt("MaxFileTimeInFuture"));
+ mDeleteRedundantLocationsAfter =
+ conf.GetKeyValueInt("DeleteRedundantLocationsAfter");
+ mStorageLimitExceeded = false;
+ mReadErrorsOnFilesystemObjects = false;
- // Tell anything connected to the command socket
- SendSyncStartOrFinish(false /* finish */);
+ // Setup various timings
+ int maximumDiffingTime = 600;
+ int keepAliveTime = 60;
- // Touch a file to record times in filesystem
- TouchFileInWorkingDir("last_sync_finish");
- }
+ // max diffing time, keep-alive time
+ if(conf.KeyExists("MaximumDiffingTime"))
+ {
+ maximumDiffingTime = conf.GetKeyValueInt("MaximumDiffingTime");
+ }
+ if(conf.KeyExists("KeepAliveTime"))
+ {
+ keepAliveTime = conf.GetKeyValueInt("KeepAliveTime");
+ }
+
+ clientContext.SetMaximumDiffingTime(maximumDiffingTime);
+ clientContext.SetKeepAliveTime(keepAliveTime);
+
+ // Set store marker
+ clientContext.SetClientStoreMarker(mClientStoreMarker);
+
+ // Set up the locations, if necessary --
+ // need to do it here so we have a
+ // (potential) connection to use
+ if(mLocations.empty())
+ {
+ const Configuration &locations(
+ conf.GetSubConfiguration(
+ "BackupLocations"));
- // Set state
- SetState(storageLimitExceeded?State_StorageLimitExceeded:State_Idle);
+ // Make sure all the directory records
+ // are set up
+ SetupLocations(clientContext, locations);
+ }
+
+ mpProgressNotifier->NotifyIDMapsSetup(clientContext);
+
+ // Get some ID maps going
+ SetupIDMapsForSync();
- } while(!StopRun());
+ // Delete any unused directories?
+ DeleteUnusedRootDirEntries(clientContext);
+
+ // Go through the records, syncing them
+ for(std::vector<Location *>::const_iterator
+ i(mLocations.begin());
+ i != mLocations.end(); ++i)
+ {
+ // Set current and new ID map pointers
+ // in the context
+ clientContext.SetIDMaps(mCurrentIDMaps[(*i)->mIDMapIndex],
+ mNewIDMaps[(*i)->mIDMapIndex]);
- // Make sure we have a clean start next time round (if restart)
- DeleteAllLocations();
- DeleteAllIDMaps();
+ // Set exclude lists (context doesn't
+ // take ownership)
+ clientContext.SetExcludeLists(
+ (*i)->mpExcludeFiles,
+ (*i)->mpExcludeDirs);
+
+ // Sync the directory
+ (*i)->mpDirectoryRecord->SyncDirectory(
+ params,
+ BackupProtocolClientListDirectory::RootDirectory,
+ (*i)->mPath, std::string("/") + (*i)->mName);
+
+ // Unset exclude lists (just in case)
+ clientContext.SetExcludeLists(0, 0);
+ }
+
+ // Perform any deletions required -- these are
+ // delayed until the end to allow renaming to
+ // happen neatly.
+ clientContext.PerformDeletions();
+
+ // Close any open connection
+ clientContext.CloseAnyOpenConnection();
+
+ // Get the new store marker
+ mClientStoreMarker = clientContext.GetClientStoreMarker();
+ mStorageLimitExceeded = clientContext.StorageLimitExceeded();
+ mReadErrorsOnFilesystemObjects =
+ params.mReadErrorsOnFilesystemObjects;
+
+ if(!mStorageLimitExceeded)
+ {
+ // The start time of the next run is the end time of this
+ // run. This is only done if the storage limit wasn't
+ // exceeded (as things won't have been done properly if
+ // it was)
+ mLastSyncTime = syncPeriodEnd;
+ }
+
+ // Commit the ID Maps
+ CommitIDMapsAfterSync();
+
+ // Calculate when the next sync run should be
+ mNextSyncTime = mCurrentSyncStartTime +
+ mUpdateStoreInterval +
+ Random::RandomInt(mUpdateStoreInterval >>
+ SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY);
+
+ // --------------------------------------------------------------------------------------------
+
+ // We had a successful backup, save the store
+ // info. If we save successfully, we must
+ // delete the file next time we start a backup
+
+ mDeleteStoreObjectInfoFile =
+ SerializeStoreObjectInfo(mLastSyncTime,
+ mNextSyncTime);
+
+ // --------------------------------------------------------------------------------------------
}
+void BackupDaemon::OnBackupStart()
+{
+ // Touch a file to record times in filesystem
+ TouchFileInWorkingDir("last_sync_start");
+
+ // Reset statistics on uploads
+ BackupStoreFile::ResetStats();
+
+ // Tell anything connected to the command socket
+ SendSyncStartOrFinish(true /* start */);
+
+ // Notify administrator
+ NotifySysadmin(SysadminNotifier::BackupStart);
+
+ // Set state and log start
+ SetState(State_Connected);
+ BOX_NOTICE("Beginning scan of local files");
+}
+
+void BackupDaemon::OnBackupFinish()
+{
+ // Log
+ BOX_NOTICE("Finished scan of local files");
+
+ // Log the stats
+ BOX_NOTICE("File statistics: total file size uploaded "
+ << BackupStoreFile::msStats.mBytesInEncodedFiles
+ << ", bytes already on server "
+ << BackupStoreFile::msStats.mBytesAlreadyOnServer
+ << ", encoded size "
+ << BackupStoreFile::msStats.mTotalFileStreamSize);
+
+ // Reset statistics again
+ BackupStoreFile::ResetStats();
+
+ // Notify administrator
+ NotifySysadmin(SysadminNotifier::BackupFinish);
+
+ // Tell anything connected to the command socket
+ SendSyncStartOrFinish(false /* finish */);
+
+ // Touch a file to record times in filesystem
+ TouchFileInWorkingDir("last_sync_finish");
+}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupDaemon::UseScriptToSeeIfSyncAllowed()
-// Purpose: Private. Use a script to see if the sync should be allowed (if configured)
-// Returns -1 if it's allowed, time in seconds to wait otherwise.
+// Purpose: Private. Use a script to see if the sync should be
+// allowed now (if configured). Returns -1 if it's
+// allowed, time in seconds to wait otherwise.
// Created: 21/6/04
//
// --------------------------------------------------------------------------
@@ -1250,7 +1021,8 @@ int BackupDaemon::UseScriptToSeeIfSyncAllowed()
pid_t pid = 0;
try
{
- std::auto_ptr<IOStream> pscript(LocalProcessStream(conf.GetKeyValue("SyncAllowScript").c_str(), pid));
+ std::auto_ptr<IOStream> pscript(LocalProcessStream(
+ conf.GetKeyValue("SyncAllowScript").c_str(), pid));
// Read in the result
IOStreamGetLine getLine(*pscript);
@@ -1323,33 +1095,13 @@ int BackupDaemon::UseScriptToSeeIfSyncAllowed()
// --------------------------------------------------------------------------
void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFlagOut, bool &SyncIsForcedOut)
{
-#ifdef WIN32
- DWORD requiredDelayMs = BoxTimeToMilliSeconds(RequiredDelay);
-
- DWORD result = WaitForSingleObject(mhCommandReceivedEvent,
- (DWORD)requiredDelayMs);
-
- if(result == WAIT_OBJECT_0)
- {
- DoSyncFlagOut = this->mDoSyncFlagOut;
- SyncIsForcedOut = this->mSyncIsForcedOut;
- ResetEvent(mhCommandReceivedEvent);
- }
- else if(result == WAIT_TIMEOUT)
+ ASSERT(mapCommandSocketInfo.get());
+ if(!mapCommandSocketInfo.get())
{
- DoSyncFlagOut = false;
- SyncIsForcedOut = false;
- }
- else
- {
- BOX_ERROR("Unexpected result from WaitForSingleObject: "
- "error " << GetLastError());
+ // failure case isn't too bad
+ ::sleep(1);
+ return;
}
-
- return;
-#else // ! WIN32
- ASSERT(mpCommandSocketInfo != 0);
- if(mpCommandSocketInfo == 0) {::sleep(1); return;} // failure case isn't too bad
BOX_TRACE("Wait on command socket, delay = " << RequiredDelay);
@@ -1362,12 +1114,12 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
if(timeout == INFTIM) timeout = 100000;
// Wait for socket connection, or handle a command?
- if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
{
// No connection, listen for a new one
- mpCommandSocketInfo->mpConnectedSocket.reset(mpCommandSocketInfo->mListeningSocket.Accept(timeout).release());
+ mapCommandSocketInfo->mpConnectedSocket.reset(mapCommandSocketInfo->mListeningSocket.Accept(timeout).release());
- if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
{
// If a connection didn't arrive, there was a timeout, which means we've
// waited long enough and it's time to go.
@@ -1386,7 +1138,7 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
{
uid_t remoteEUID = 0xffff;
gid_t remoteEGID = 0xffff;
- if(mpCommandSocketInfo->mpConnectedSocket->GetPeerCredentials(remoteEUID, remoteEGID))
+ if(mapCommandSocketInfo->mpConnectedSocket->GetPeerCredentials(remoteEUID, remoteEGID))
{
// Credentials are available -- check UID
if(remoteEUID == ::getuid())
@@ -1403,7 +1155,7 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
{
// Dump the connection
BOX_ERROR("Incoming command connection from peer had different user ID than this process, or security check could not be completed.");
- mpCommandSocketInfo->mpConnectedSocket.reset();
+ mapCommandSocketInfo->mpConnectedSocket.reset();
return;
}
else
@@ -1420,7 +1172,7 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
conf.GetKeyValueInt("MinimumFileAge"),
conf.GetKeyValueInt("MaxUploadWait"),
mState);
- mpCommandSocketInfo->mpConnectedSocket->Write(summary, summarySize);
+ mapCommandSocketInfo->mpConnectedSocket->Write(summary, summarySize);
// Set the timeout to something very small, so we don't wait too long on waiting
// for any incoming data
@@ -1430,22 +1182,22 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
}
// So there must be a connection now.
- ASSERT(mpCommandSocketInfo->mpConnectedSocket.get() != 0);
+ ASSERT(mapCommandSocketInfo->mpConnectedSocket.get() != 0);
// Is there a getline object ready?
- if(mpCommandSocketInfo->mpGetLine == 0)
+ if(mapCommandSocketInfo->mpGetLine == 0)
{
// Create a new one
- mpCommandSocketInfo->mpGetLine = new IOStreamGetLine(*(mpCommandSocketInfo->mpConnectedSocket.get()));
+ mapCommandSocketInfo->mpGetLine = new IOStreamGetLine(*(mapCommandSocketInfo->mpConnectedSocket.get()));
}
// Ping the remote side, to provide errors which will mean the socket gets closed
- mpCommandSocketInfo->mpConnectedSocket->Write("ping\n", 5);
+ mapCommandSocketInfo->mpConnectedSocket->Write("ping\n", 5);
// Wait for a command or something on the socket
std::string command;
- while(mpCommandSocketInfo->mpGetLine != 0 && !mpCommandSocketInfo->mpGetLine->IsEOF()
- && mpCommandSocketInfo->mpGetLine->GetLine(command, false /* no preprocessing */, timeout))
+ while(mapCommandSocketInfo->mpGetLine != 0 && !mapCommandSocketInfo->mpGetLine->IsEOF()
+ && mapCommandSocketInfo->mpGetLine->GetLine(command, false /* no preprocessing */, timeout))
{
BOX_TRACE("Receiving command '" << command
<< "' over command socket");
@@ -1490,7 +1242,7 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
// Send a response back?
if(sendResponse)
{
- mpCommandSocketInfo->mpConnectedSocket->Write(sendOK?"ok\n":"error\n", sendOK?3:6);
+ mapCommandSocketInfo->mpConnectedSocket->Write(sendOK?"ok\n":"error\n", sendOK?3:6);
}
// Set timeout to something very small, so this just checks for data which is waiting
@@ -1498,18 +1250,39 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
}
// Close on EOF?
- if(mpCommandSocketInfo->mpGetLine != 0 && mpCommandSocketInfo->mpGetLine->IsEOF())
+ if(mapCommandSocketInfo->mpGetLine != 0 && mapCommandSocketInfo->mpGetLine->IsEOF())
{
CloseCommandConnection();
}
}
+ catch(ConnectionException &ce)
+ {
+ BOX_NOTICE("Failed to write to command socket: " << ce.what());
+
+ // If an error occurs, and there is a connection active,
+ // just close that connection and continue. Otherwise,
+ // let the error propagate.
+
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
+ {
+ throw; // thread will die
+ }
+ else
+ {
+ // Close socket and ignore error
+ CloseCommandConnection();
+ }
+ }
catch(std::exception &e)
{
- BOX_ERROR("Internal error in command socket thread: "
- << e.what());
- // If an error occurs, and there is a connection active, just close that
- // connection and continue. Otherwise, let the error propagate.
- if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
+ BOX_ERROR("Failed to write to command socket: " <<
+ e.what());
+
+ // If an error occurs, and there is a connection active,
+ // just close that connection and continue. Otherwise,
+ // let the error propagate.
+
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
{
throw; // thread will die
}
@@ -1521,9 +1294,13 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
}
catch(...)
{
- // If an error occurs, and there is a connection active, just close that
- // connection and continue. Otherwise, let the error propagate.
- if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
+ BOX_ERROR("Failed to write to command socket: unknown error");
+
+ // If an error occurs, and there is a connection active,
+ // just close that connection and continue. Otherwise,
+ // let the error propagate.
+
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
{
throw; // thread will die
}
@@ -1533,7 +1310,6 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
CloseCommandConnection();
}
}
-#endif // WIN32
}
@@ -1547,17 +1323,16 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
// --------------------------------------------------------------------------
void BackupDaemon::CloseCommandConnection()
{
-#ifndef WIN32
try
{
BOX_TRACE("Closing command connection");
- if(mpCommandSocketInfo->mpGetLine)
+ if(mapCommandSocketInfo->mpGetLine)
{
- delete mpCommandSocketInfo->mpGetLine;
- mpCommandSocketInfo->mpGetLine = 0;
+ delete mapCommandSocketInfo->mpGetLine;
+ mapCommandSocketInfo->mpGetLine = 0;
}
- mpCommandSocketInfo->mpConnectedSocket.reset();
+ mapCommandSocketInfo->mpConnectedSocket.reset();
}
catch(std::exception &e)
{
@@ -1568,7 +1343,6 @@ void BackupDaemon::CloseCommandConnection()
{
// Ignore any errors
}
-#endif
}
@@ -1586,27 +1360,15 @@ void BackupDaemon::SendSyncStartOrFinish(bool SendStart)
// The bbackupctl program can't rely on a state change, because it
// may never change if the server doesn't need to be contacted.
- if(mpCommandSocketInfo != NULL &&
-#ifdef WIN32
- mpCommandSocketInfo->mListeningSocket.IsConnected()
-#else
- mpCommandSocketInfo->mpConnectedSocket.get() != 0
-#endif
- )
+ if(mapCommandSocketInfo.get() &&
+ mapCommandSocketInfo->mpConnectedSocket.get() != 0)
{
std::string message = SendStart ? "start-sync" : "finish-sync";
try
{
-#ifdef WIN32
- EnterCriticalSection(&mMessageQueueLock);
- mMessageList.push_back(message);
- SetEvent(mhMessageToSendEvent);
- LeaveCriticalSection(&mMessageQueueLock);
-#else
message += "\n";
- mpCommandSocketInfo->mpConnectedSocket->Write(
+ mapCommandSocketInfo->mpConnectedSocket->Write(
message.c_str(), message.size());
-#endif
}
catch(std::exception &e)
{
@@ -1668,14 +1430,20 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
// Just a check to make sure it's right.
DeleteAllLocations();
- // Going to need a copy of the root directory. Get a connection, and fetch it.
+ // Going to need a copy of the root directory. Get a connection,
+ // and fetch it.
BackupProtocolClient &connection(rClientContext.GetConnection());
- // Ask server for a list of everything in the root directory, which is a directory itself
- std::auto_ptr<BackupProtocolClientSuccess> dirreply(connection.QueryListDirectory(
+ // Ask server for a list of everything in the root directory,
+ // which is a directory itself
+ std::auto_ptr<BackupProtocolClientSuccess> dirreply(
+ connection.QueryListDirectory(
BackupProtocolClientListDirectory::RootDirectory,
- BackupProtocolClientListDirectory::Flags_Dir, // only directories
- BackupProtocolClientListDirectory::Flags_Deleted | BackupProtocolClientListDirectory::Flags_OldVersion, // exclude old/deleted stuff
+ // only directories
+ BackupProtocolClientListDirectory::Flags_Dir,
+ // exclude old/deleted stuff
+ BackupProtocolClientListDirectory::Flags_Deleted |
+ BackupProtocolClientListDirectory::Flags_OldVersion,
false /* no attributes */));
// Retrieve the directory from the stream following
@@ -1755,33 +1523,41 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
#endif // HAVE_STRUCT_MNTENT_MNT_DIR
// Check sorting and that things are as we expect
ASSERT(mountPoints.size() > 0);
-#ifndef NDEBUG
+#ifndef BOX_RELEASE_BUILD
{
std::set<std::string, mntLenCompare>::reverse_iterator i(mountPoints.rbegin());
ASSERT(*i == "/");
}
-#endif // n NDEBUG
+#endif // n BOX_RELEASE_BUILD
#endif // n HAVE_STRUCT_STATFS_F_MNTONNAME || n HAVE_STRUCT_STATVFS_F_MNTONNAME
#endif // HAVE_MOUNTS
// Then... go through each of the entries in the configuration,
// making sure there's a directory created for it.
- for(std::list<std::pair<std::string, Configuration> >::const_iterator i = rLocationsConf.mSubConfigurations.begin();
- i != rLocationsConf.mSubConfigurations.end(); ++i)
- {
- BOX_TRACE("new location: " << i->first);
+ std::vector<std::string> locNames =
+ rLocationsConf.GetSubConfigurationNames();
+
+ for(std::vector<std::string>::iterator
+ pLocName = locNames.begin();
+ pLocName != locNames.end();
+ pLocName++)
+ {
+ const Configuration& rConfig(
+ rLocationsConf.GetSubConfiguration(*pLocName));
+ BOX_TRACE("new location: " << *pLocName);
+
// Create a record for it
std::auto_ptr<Location> apLoc(new Location);
try
{
// Setup names in the location record
- apLoc->mName = i->first;
- apLoc->mPath = i->second.GetKeyValue("Path");
+ apLoc->mName = *pLocName;
+ apLoc->mPath = rConfig.GetKeyValue("Path");
// Read the exclude lists from the Configuration
- apLoc->mpExcludeFiles = BackupClientMakeExcludeList_Files(i->second);
- apLoc->mpExcludeDirs = BackupClientMakeExcludeList_Dirs(i->second);
+ apLoc->mpExcludeFiles = BackupClientMakeExcludeList_Files(rConfig);
+ apLoc->mpExcludeDirs = BackupClientMakeExcludeList_Dirs(rConfig);
// Does this exist on the server?
// Remove from dir object early, so that if we fail
@@ -1814,10 +1590,9 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
if(::statfs(apLoc->mPath.c_str(), &s) != 0)
#endif // HAVE_STRUCT_STATVFS_F_MNTONNAME
{
- BOX_WARNING("Failed to stat location "
+ BOX_LOG_SYS_WARNING("Failed to stat location "
"path '" << apLoc->mPath <<
- "' (" << strerror(errno) <<
- "), skipping location '" <<
+ "', skipping location '" <<
apLoc->mName << "'");
continue;
}
@@ -1929,7 +1704,8 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
// Create and store the directory object for the root of this location
ASSERT(oid != 0);
- BackupClientDirectoryRecord *precord = new BackupClientDirectoryRecord(oid, i->first);
+ BackupClientDirectoryRecord *precord =
+ new BackupClientDirectoryRecord(oid, *pLocName);
apLoc->mpDirectoryRecord.reset(precord);
// Push it back on the vector of locations
@@ -2007,8 +1783,8 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
// --------------------------------------------------------------------------
void BackupDaemon::SetupIDMapsForSync()
{
- // Need to do different things depending on whether it's an in memory implementation,
- // or whether it's all stored on disc.
+ // Need to do different things depending on whether it's an
+ // in memory implementation, or whether it's all stored on disc.
#ifdef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION
@@ -2016,8 +1792,8 @@ void BackupDaemon::SetupIDMapsForSync()
DeleteIDMapVector(mNewIDMaps);
FillIDMapVector(mNewIDMaps, true /* new maps */);
- // Then make sure that the current maps have objects, even if they are empty
- // (for the very first run)
+ // Then make sure that the current maps have objects,
+ // even if they are empty (for the very first run)
if(mCurrentIDMaps.empty())
{
FillIDMapVector(mCurrentIDMaps, false /* current maps */);
@@ -2191,9 +1967,8 @@ void BackupDaemon::CommitIDMapsAfterSync()
#endif
if(::rename(newmap.c_str(), target.c_str()) != 0)
{
- BOX_ERROR("failed to rename ID map: " << newmap
- << " to " << target << ": "
- << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to rename ID map: " <<
+ newmap << " to " << target);
THROW_EXCEPTION(CommonException, OSFileError)
}
}
@@ -2281,20 +2056,14 @@ void BackupDaemon::SetState(int State)
sprintf(newState, "state %d", State);
std::string message = newState;
-#ifdef WIN32
- EnterCriticalSection(&mMessageQueueLock);
- mMessageList.push_back(newState);
- SetEvent(mhMessageToSendEvent);
- LeaveCriticalSection(&mMessageQueueLock);
-#else
message += "\n";
- if(mpCommandSocketInfo == 0)
+ if(!mapCommandSocketInfo.get())
{
return;
}
- if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
{
return;
}
@@ -2302,22 +2071,27 @@ void BackupDaemon::SetState(int State)
// Something connected to the command socket, tell it about the new state
try
{
- mpCommandSocketInfo->mpConnectedSocket->Write(message.c_str(),
+ mapCommandSocketInfo->mpConnectedSocket->Write(message.c_str(),
message.length());
}
+ catch(ConnectionException &ce)
+ {
+ BOX_NOTICE("Failed to write state to command socket: " <<
+ ce.what());
+ CloseCommandConnection();
+ }
catch(std::exception &e)
{
- BOX_ERROR("Internal error while writing state "
- "to command socket: " << e.what());
+ BOX_ERROR("Failed to write state to command socket: " <<
+ e.what());
CloseCommandConnection();
}
catch(...)
{
- BOX_ERROR("Internal error while writing state "
- "to command socket: unknown error");
+ BOX_ERROR("Failed to write state to command socket: "
+ "unknown error");
CloseCommandConnection();
}
-#endif
}
@@ -2351,7 +2125,7 @@ void BackupDaemon::TouchFileInWorkingDir(const char *Filename)
// Created: 25/2/04
//
// --------------------------------------------------------------------------
-void BackupDaemon::NotifySysadmin(int Event)
+void BackupDaemon::NotifySysadmin(SysadminNotifier::EventCode Event)
{
static const char *sEventNames[] =
{
@@ -2360,31 +2134,47 @@ void BackupDaemon::NotifySysadmin(int Event)
"backup-error",
"backup-start",
"backup-finish",
+ "backup-ok",
0
};
- BOX_TRACE("sizeof(sEventNames) == " << sizeof(sEventNames));
- BOX_TRACE("sizeof(*sEventNames) == " << sizeof(*sEventNames));
- BOX_TRACE("NotifyEvent__MAX == " << NotifyEvent__MAX);
- ASSERT((sizeof(sEventNames)/sizeof(*sEventNames)) == NotifyEvent__MAX + 1);
+ // BOX_TRACE("sizeof(sEventNames) == " << sizeof(sEventNames));
+ // BOX_TRACE("sizeof(*sEventNames) == " << sizeof(*sEventNames));
+ // BOX_TRACE("NotifyEvent__MAX == " << NotifyEvent__MAX);
+ ASSERT((sizeof(sEventNames)/sizeof(*sEventNames)) == SysadminNotifier::MAX + 1);
- BOX_TRACE("BackupDaemon::NotifySysadmin() called, event = " <<
- sEventNames[Event]);
-
- if(Event < 0 || Event >= NotifyEvent__MAX)
+ if(Event < 0 || Event >= SysadminNotifier::MAX)
{
+ BOX_ERROR("BackupDaemon::NotifySysadmin() called for "
+ "invalid event code " << Event);
THROW_EXCEPTION(BackupStoreException,
BadNotifySysadminEventCode);
}
- // Don't send lots of repeated messages
- if(mNotificationsSent[Event] &&
- Event != NotifyEvent_BackupStart &&
- Event != NotifyEvent_BackupFinish)
+ BOX_TRACE("BackupDaemon::NotifySysadmin() called, event = " <<
+ sEventNames[Event]);
+
+ if(!GetConfiguration().KeyExists("NotifyAlways") ||
+ !GetConfiguration().GetKeyValueBool("NotifyAlways"))
{
- BOX_WARNING("Suppressing duplicate notification about " <<
- sEventNames[Event]);
- return;
+ // Don't send lots of repeated messages
+ // Note: backup-start and backup-finish will always be
+ // logged, because mLastNotifiedEvent is never set to
+ // these values and therefore they are never "duplicates".
+ if(mLastNotifiedEvent == Event)
+ {
+ if(Event == SysadminNotifier::BackupOK)
+ {
+ BOX_INFO("Suppressing duplicate notification "
+ "about " << sEventNames[Event]);
+ }
+ else
+ {
+ BOX_WARNING("Suppressing duplicate notification "
+ "about " << sEventNames[Event]);
+ }
+ return;
+ }
}
// Is there a notification script?
@@ -2392,10 +2182,10 @@ void BackupDaemon::NotifySysadmin(int Event)
if(!conf.KeyExists("NotifyScript"))
{
// Log, and then return
- if(Event != NotifyEvent_BackupStart &&
- Event != NotifyEvent_BackupFinish)
+ if(Event != SysadminNotifier::BackupStart &&
+ Event != SysadminNotifier::BackupFinish)
{
- BOX_ERROR("Not notifying administrator about event "
+ BOX_INFO("Not notifying administrator about event "
<< sEventNames[Event] << " -- set NotifyScript "
"to do this in future");
}
@@ -2407,20 +2197,22 @@ void BackupDaemon::NotifySysadmin(int Event)
sEventNames[Event]);
// Log what we're about to do
- BOX_NOTICE("About to notify administrator about event "
+ BOX_INFO("About to notify administrator about event "
<< sEventNames[Event] << ", running script '"
<< script << "'");
// Then do it
- if(::system(script.c_str()) != 0)
+ int returnCode = ::system(script.c_str());
+ if(returnCode != 0)
{
- BOX_ERROR("Notify script returned an error code. ('"
- << script << "')");
+ BOX_WARNING("Notify script returned error code: " <<
+ returnCode << " ('" << script << "')");
+ }
+ else if(Event != SysadminNotifier::BackupStart &&
+ Event != SysadminNotifier::BackupFinish)
+ {
+ mLastNotifiedEvent = Event;
}
-
- // Flag that this is done so the administrator isn't constantly
- // bombarded with lots of errors
- mNotificationsSent[Event] = true;
}
@@ -2461,13 +2253,13 @@ void BackupDaemon::DeleteUnusedRootDirEntries(BackupClientContext &rContext)
// Entries to delete, and it's the right time to do so...
BOX_NOTICE("Deleting unused locations from store root...");
BackupProtocolClient &connection(rContext.GetConnection());
- for(std::vector<std::pair<int64_t,std::string> >::iterator i(mUnusedRootDirEntries.begin()); i != mUnusedRootDirEntries.end(); ++i)
+ for(std::vector<std::pair<int64_t,std::string> >::iterator
+ i(mUnusedRootDirEntries.begin());
+ i != mUnusedRootDirEntries.end(); ++i)
{
connection.QueryDeleteDirectory(i->first);
-
- // Log this
- BOX_NOTICE("Deleted " << i->second << " (ID " << i->first
- << ") from store root");
+ rContext.GetProgressNotifier().NotifyFileDeleted(
+ i->first, i->second);
}
// Reset state
@@ -2738,9 +2530,12 @@ BackupDaemon::CommandSocketInfo::~CommandSocketInfo()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time_t theLastSyncTime, box_time_t theNextSyncTime)
-// Purpose: Serializes remote directory and file information into a stream of bytes, using an Archive abstraction.
-//
+// Name: BackupDaemon::SerializeStoreObjectInfo(
+// box_time_t theLastSyncTime,
+// box_time_t theNextSyncTime)
+// Purpose: Serializes remote directory and file information
+// into a stream of bytes, using an Archive
+// abstraction.
// Created: 2005/04/11
//
// --------------------------------------------------------------------------
@@ -2749,7 +2544,8 @@ static const int STOREOBJECTINFO_MAGIC_ID_VALUE = 0x7777525F;
static const std::string STOREOBJECTINFO_MAGIC_ID_STRING = "BBACKUPD-STATE";
static const int STOREOBJECTINFO_VERSION = 2;
-bool BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time_t theLastSyncTime, box_time_t theNextSyncTime) const
+bool BackupDaemon::SerializeStoreObjectInfo(box_time_t theLastSyncTime,
+ box_time_t theNextSyncTime) const
{
if(!GetConfiguration().KeyExists("StoreObjectInfoFile"))
{
@@ -2778,7 +2574,7 @@ bool BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time
anArchive.Write(STOREOBJECTINFO_MAGIC_ID_STRING);
anArchive.Write(STOREOBJECTINFO_VERSION);
anArchive.Write(GetLoadedConfigModifiedTime());
- anArchive.Write(aClientStoreMarker);
+ anArchive.Write(mClientStoreMarker);
anArchive.Write(theLastSyncTime);
anArchive.Write(theNextSyncTime);
@@ -2830,15 +2626,13 @@ bool BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time
}
catch(std::exception &e)
{
- BOX_ERROR("Internal error writing store object "
- "info file (" << StoreObjectInfoFile << "): "
- << e.what());
+ BOX_ERROR("Failed to write StoreObjectInfoFile: " <<
+ StoreObjectInfoFile << ": " << e.what());
}
catch(...)
{
- BOX_ERROR("Internal error writing store object "
- "info file (" << StoreObjectInfoFile << "): "
- "unknown error");
+ BOX_ERROR("Failed to write StoreObjectInfoFile: " <<
+ StoreObjectInfoFile << ": unknown error");
}
return created;
@@ -2847,13 +2641,17 @@ bool BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupDaemon::DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_time_t & theLastSyncTime, box_time_t & theNextSyncTime)
-// Purpose: Deserializes remote directory and file information from a stream of bytes, using an Archive abstraction.
-//
+// Name: BackupDaemon::DeserializeStoreObjectInfo(
+// box_time_t & theLastSyncTime,
+// box_time_t & theNextSyncTime)
+// Purpose: Deserializes remote directory and file information
+// from a stream of bytes, using an Archive
+// abstraction.
// Created: 2005/04/11
//
// --------------------------------------------------------------------------
-bool BackupDaemon::DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_time_t & theLastSyncTime, box_time_t & theNextSyncTime)
+bool BackupDaemon::DeserializeStoreObjectInfo(box_time_t & theLastSyncTime,
+ box_time_t & theNextSyncTime)
{
//
//
@@ -2945,7 +2743,7 @@ bool BackupDaemon::DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_
//
// this is it, go at it
//
- anArchive.Read(aClientStoreMarker);
+ anArchive.Read(mClientStoreMarker);
anArchive.Read(theLastSyncTime);
anArchive.Read(theNextSyncTime);
@@ -3023,7 +2821,7 @@ bool BackupDaemon::DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_
DeleteAllLocations();
- aClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown;
+ mClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown;
theLastSyncTime = 0;
theNextSyncTime = 0;
@@ -3057,9 +2855,10 @@ bool BackupDaemon::DeleteStoreObjectInfo() const
// Check to see if the file exists
if(!FileExists(storeObjectInfoFile.c_str()))
{
- // File doesn't exist -- so can't be deleted. But something isn't quite right, so log a message
- BOX_WARNING("Store object info file did not exist when it "
- "was supposed to. (" << storeObjectInfoFile << ")");
+ // File doesn't exist -- so can't be deleted. But something
+ // isn't quite right, so log a message
+ BOX_WARNING("StoreObjectInfoFile did not exist when it "
+ "was supposed to: " << storeObjectInfoFile);
// Return true to stop things going around in a loop
return true;
@@ -3068,8 +2867,8 @@ bool BackupDaemon::DeleteStoreObjectInfo() const
// Actually delete it
if(::unlink(storeObjectInfoFile.c_str()) != 0)
{
- BOX_ERROR("Failed to delete the old store object info file: "
- << storeObjectInfoFile << ": "<< strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to delete the old "
+ "StoreObjectInfoFile: " << storeObjectInfoFile);
return false;
}
diff --git a/bin/bbackupd/BackupDaemon.h b/bin/bbackupd/BackupDaemon.h
index 62f9c393..0c864abd 100644
--- a/bin/bbackupd/BackupDaemon.h
+++ b/bin/bbackupd/BackupDaemon.h
@@ -14,16 +14,20 @@
#include <string>
#include <memory>
+#include "BackupClientContext.h"
+#include "BackupClientDirectoryRecord.h"
#include "BoxTime.h"
#include "Daemon.h"
-#include "BackupClientDirectoryRecord.h"
+#include "Logging.h"
#include "Socket.h"
#include "SocketListen.h"
#include "SocketStream.h"
-#include "Logging.h"
+#include "TLSContext.h"
+
#include "autogen_BackupProtocolClient.h"
#ifdef WIN32
+ #include "WinNamedPipeListener.h"
#include "WinNamedPipeStream.h"
#endif
@@ -43,7 +47,8 @@ class Archive;
// Created: 2003/10/08
//
// --------------------------------------------------------------------------
-class BackupDaemon : public Daemon, ProgressNotifier
+class BackupDaemon : public Daemon, ProgressNotifier, LocationResolver,
+RunStatusProvider, SysadminNotifier
{
public:
BackupDaemon();
@@ -52,10 +57,10 @@ public:
private:
// methods below do partial (specialized) serialization of
// client state only
- bool SerializeStoreObjectInfo(int64_t aClientStoreMarker,
- box_time_t theLastSyncTime, box_time_t theNextSyncTime) const;
- bool DeserializeStoreObjectInfo(int64_t & aClientStoreMarker,
- box_time_t & theLastSyncTime, box_time_t & theNextSyncTime);
+ bool SerializeStoreObjectInfo(box_time_t theLastSyncTime,
+ box_time_t theNextSyncTime) const;
+ bool DeserializeStoreObjectInfo(box_time_t & theLastSyncTime,
+ box_time_t & theNextSyncTime);
bool DeleteStoreObjectInfo() const;
BackupDaemon(const BackupDaemon &);
@@ -65,6 +70,14 @@ public:
std::string GetOptionString();
int ProcessOption(signed int option);
int Main(const std::string &rConfigFileName);
+
+ // This shouldn't be here, but apparently gcc on
+ // Windows has no idea about inherited methods...
+ virtual int Main(const char *DefaultConfigFile, int argc,
+ const char *argv[])
+ {
+ return Daemon::Main(DefaultConfigFile, argc, argv);
+ }
#endif
void Run();
@@ -88,21 +101,22 @@ public:
int GetState() {return mState;}
// Allow other classes to call this too
- enum
- {
- NotifyEvent_StoreFull = 0,
- NotifyEvent_ReadError,
- NotifyEvent_BackupError,
- NotifyEvent_BackupStart,
- NotifyEvent_BackupFinish,
- NotifyEvent__MAX
- // When adding notifications, remember to add strings to NotifySysadmin()
- };
- void NotifySysadmin(int Event);
+ void NotifySysadmin(SysadminNotifier::EventCode Event);
private:
void Run2();
+public:
+ void InitCrypto();
+ void RunSyncNowWithExceptionHandling();
+ void RunSyncNow();
+ void OnBackupStart();
+ void OnBackupFinish();
+ // TouchFileInWorkingDir is only here for use by Boxi.
+ // This does NOT constitute an API!
+ void TouchFileInWorkingDir(const char *Filename);
+
+private:
void DeleteAllLocations();
void SetupLocations(BackupClientContext &rClientContext, const Configuration &rLocationsConf);
@@ -126,8 +140,6 @@ private:
void CloseCommandConnection();
void SendSyncStartOrFinish(bool SendStart);
- void TouchFileInWorkingDir(const char *Filename);
-
void DeleteUnusedRootDirEntries(BackupClientContext &rContext);
#ifdef PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
@@ -137,7 +149,7 @@ private:
int UseScriptToSeeIfSyncAllowed();
-private:
+public:
class Location
{
public:
@@ -157,7 +169,11 @@ private:
ExcludeList *mpExcludeFiles;
ExcludeList *mpExcludeDirs;
};
-
+
+ typedef const std::vector<Location *> Locations;
+ Locations GetLocations() { return mLocations; }
+
+private:
int mState; // what the daemon is currently doing
std::vector<Location *> mLocations;
@@ -179,7 +195,8 @@ private:
CommandSocketInfo &operator=(const CommandSocketInfo &);
public:
#ifdef WIN32
- WinNamedPipeStream mListeningSocket;
+ WinNamedPipeListener<1 /* listen backlog */> mListeningSocket;
+ std::auto_ptr<WinNamedPipeStream> mpConnectedSocket;
#else
SocketListen<SocketStream, 1 /* listen backlog */> mListeningSocket;
std::auto_ptr<SocketStream> mpConnectedSocket;
@@ -188,23 +205,51 @@ private:
};
// Using a socket?
- CommandSocketInfo *mpCommandSocketInfo;
+ std::auto_ptr<CommandSocketInfo> mapCommandSocketInfo;
// Stop notifications being repeated.
- bool mNotificationsSent[NotifyEvent__MAX];
+ SysadminNotifier::EventCode mLastNotifiedEvent;
// Unused entries in the root directory wait a while before being deleted
box_time_t mDeleteUnusedRootDirEntriesAfter; // time to delete them
std::vector<std::pair<int64_t,std::string> > mUnusedRootDirEntries;
+ int64_t mClientStoreMarker;
+ bool mStorageLimitExceeded;
+ bool mReadErrorsOnFilesystemObjects;
+ box_time_t mLastSyncTime, mNextSyncTime;
+ box_time_t mCurrentSyncStartTime, mUpdateStoreInterval;
+ TLSContext mTlsContext;
+ bool mDeleteStoreObjectInfoFile;
+ bool mDoSyncForcedByPreviousSyncError;
+
public:
bool StopRun() { return this->Daemon::StopRun(); }
+ bool StorageLimitExceeded() { return mStorageLimitExceeded; }
private:
bool mLogAllFileAccess;
+public:
+ ProgressNotifier* GetProgressNotifier() { return mpProgressNotifier; }
+ LocationResolver* GetLocationResolver() { return mpLocationResolver; }
+ RunStatusProvider* GetRunStatusProvider() { return mpRunStatusProvider; }
+ SysadminNotifier* GetSysadminNotifier() { return mpSysadminNotifier; }
+ void SetProgressNotifier (ProgressNotifier* p) { mpProgressNotifier = p; }
+ void SetLocationResolver (LocationResolver* p) { mpLocationResolver = p; }
+ void SetRunStatusProvider(RunStatusProvider* p) { mpRunStatusProvider = p; }
+ void SetSysadminNotifier (SysadminNotifier* p) { mpSysadminNotifier = p; }
+
+private:
+ ProgressNotifier* mpProgressNotifier;
+ LocationResolver* mpLocationResolver;
+ RunStatusProvider* mpRunStatusProvider;
+ SysadminNotifier* mpSysadminNotifier;
+
/* ProgressNotifier implementation */
public:
+ virtual void NotifyIDMapsSetup(BackupClientContext& rContext) { }
+
virtual void NotifyScanDirectory(
const BackupClientDirectoryRecord* pDirRecord,
const std::string& rLocalPath)
@@ -387,7 +432,7 @@ public:
{
if (mLogAllFileAccess)
{
- BOX_INFO("Uploading complete file: " << rLocalPath);
+ BOX_NOTICE("Uploading complete file: " << rLocalPath);
}
}
virtual void NotifyFileUploadingPatch(
@@ -396,7 +441,7 @@ public:
{
if (mLogAllFileAccess)
{
- BOX_INFO("Uploading patch to file: " << rLocalPath);
+ BOX_NOTICE("Uploading patch to file: " << rLocalPath);
}
}
virtual void NotifyFileUploaded(
@@ -406,7 +451,7 @@ public:
{
if (mLogAllFileAccess)
{
- BOX_INFO("Uploaded file: " << rLocalPath);
+ BOX_NOTICE("Uploaded file: " << rLocalPath);
}
}
virtual void NotifyFileSynchronised(
@@ -419,18 +464,51 @@ public:
BOX_INFO("Synchronised file: " << rLocalPath);
}
}
+ virtual void NotifyDirectoryDeleted(
+ int64_t ObjectID,
+ const std::string& rRemotePath)
+ {
+ if (mLogAllFileAccess)
+ {
+ BOX_NOTICE("Deleted directory: " << rRemotePath <<
+ " (ID " << BOX_FORMAT_OBJECTID(ObjectID) <<
+ ")");
+ }
+ }
+ virtual void NotifyFileDeleted(
+ int64_t ObjectID,
+ const std::string& rRemotePath)
+ {
+ if (mLogAllFileAccess)
+ {
+ BOX_NOTICE("Deleted file: " << rRemotePath <<
+ " (ID " << BOX_FORMAT_OBJECTID(ObjectID) <<
+ ")");
+ }
+ }
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset,
+ int64_t length, box_time_t elapsed, box_time_t finish)
+ {
+ BOX_TRACE("Read " << readSize << " bytes at " << offset <<
+ ", " << (length - offset) << " remain, eta " <<
+ BoxTimeToSeconds(finish - elapsed) << "s");
+ }
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset,
+ int64_t length)
+ {
+ BOX_TRACE("Read " << readSize << " bytes at " << offset <<
+ ", " << (length - offset) << " remain");
+ }
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset)
+ {
+ BOX_TRACE("Read " << readSize << " bytes at " << offset <<
+ ", unknown bytes remaining");
+ }
#ifdef WIN32
- public:
- void RunHelperThread(void);
-
private:
- bool mDoSyncFlagOut, mSyncIsForcedOut;
bool mInstallService, mRemoveService, mRunAsService;
std::string mServiceName;
- HANDLE mhMessageToSendEvent, mhCommandReceivedEvent;
- CRITICAL_SECTION mMessageQueueLock;
- std::vector<std::string> mMessageList;
#endif
};
diff --git a/bin/bbackupd/BackupDaemonInterface.h b/bin/bbackupd/BackupDaemonInterface.h
new file mode 100644
index 00000000..5bbdd427
--- /dev/null
+++ b/bin/bbackupd/BackupDaemonInterface.h
@@ -0,0 +1,164 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: BackupDaemonInterface.h
+// Purpose: Interfaces for managing a BackupDaemon
+// Created: 2008/12/30
+//
+// --------------------------------------------------------------------------
+
+#ifndef BACKUPDAEMONINTERFACE__H
+#define BACKUPDAEMONINTERFACE__H
+
+#include <string>
+// #include <map>
+
+// #include "BackupClientFileAttributes.h"
+// #include "BackupStoreDirectory.h"
+#include "BoxTime.h"
+// #include "MD5Digest.h"
+// #include "ReadLoggingStream.h"
+// #include "RunStatusProvider.h"
+
+class Archive;
+class BackupClientContext;
+class BackupDaemon;
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: SysadminNotifier
+// Purpose: Provides a NotifySysadmin() method to send mail to the sysadmin
+// Created: 2005/11/15
+//
+// --------------------------------------------------------------------------
+class SysadminNotifier
+{
+ public:
+ virtual ~SysadminNotifier() { }
+
+ typedef enum
+ {
+ StoreFull = 0,
+ ReadError,
+ BackupError,
+ BackupStart,
+ BackupFinish,
+ BackupOK,
+ MAX
+ // When adding notifications, remember to add
+ // strings to NotifySysadmin()
+ }
+ EventCode;
+
+ virtual void NotifySysadmin(EventCode Event) = 0;
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: ProgressNotifier
+// Purpose: Provides methods for the backup library to inform the user
+// interface about its progress with the backup
+// Created: 2005/11/20
+//
+// --------------------------------------------------------------------------
+
+class BackupClientContext;
+class BackupClientDirectoryRecord;
+
+class ProgressNotifier
+{
+ public:
+ virtual ~ProgressNotifier() { }
+ virtual void NotifyIDMapsSetup(BackupClientContext& rContext) = 0;
+ virtual void NotifyScanDirectory(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyDirStatFailed(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ const std::string& rErrorMsg) = 0;
+ virtual void NotifyFileStatFailed(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ const std::string& rErrorMsg) = 0;
+ virtual void NotifyDirListFailed(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ const std::string& rErrorMsg) = 0;
+ virtual void NotifyMountPointSkipped(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileExcluded(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyDirExcluded(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyUnsupportedFileType(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileReadFailed(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ const std::string& rErrorMsg) = 0;
+ virtual void NotifyFileModifiedInFuture(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileSkippedServerFull(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileUploadException(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ const BoxException& rException) = 0;
+ virtual void NotifyFileUploadServerError(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ int type, int subtype) = 0;
+ virtual void NotifyFileUploading(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileUploadingPatch(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileUploaded(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ int64_t FileSize) = 0;
+ virtual void NotifyFileSynchronised(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ int64_t FileSize) = 0;
+ virtual void NotifyDirectoryDeleted(
+ int64_t ObjectID,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyFileDeleted(
+ int64_t ObjectID,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset,
+ int64_t length, box_time_t elapsed, box_time_t finish) = 0;
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset,
+ int64_t length) = 0;
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset) = 0;
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: LocationResolver
+// Purpose: Interface for classes that can resolve locations to paths,
+// like BackupDaemon
+// Created: 2003/10/08
+//
+// --------------------------------------------------------------------------
+class LocationResolver
+{
+public:
+ virtual ~LocationResolver() { }
+ virtual bool FindLocationPathName(const std::string &rLocationName,
+ std::string &rPathOut) const = 0;
+};
+
+#endif // BACKUPDAEMONINTERFACE__H
diff --git a/bin/bbackupd/Win32ServiceFunctions.cpp b/bin/bbackupd/Win32ServiceFunctions.cpp
index a7bf6bd9..2df914a7 100644
--- a/bin/bbackupd/Win32ServiceFunctions.cpp
+++ b/bin/bbackupd/Win32ServiceFunctions.cpp
@@ -203,12 +203,12 @@ int InstallService(const char* pConfigFileName, const std::string& rServiceName)
{
if (pConfigFileName != NULL)
{
- struct stat st;
+ EMU_STRUCT_STAT st;
if (emu_stat(pConfigFileName, &st) != 0)
{
- BOX_ERROR("Failed to open configuration file '" <<
- pConfigFileName << "': " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to open configuration file "
+ "'" << pConfigFileName << "'");
return 1;
}
@@ -221,7 +221,7 @@ int InstallService(const char* pConfigFileName, const std::string& rServiceName)
}
}
- SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
+ SC_HANDLE scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
if (!scm)
{
diff --git a/bin/bbackupd/bbackupd-config.in b/bin/bbackupd/bbackupd-config.in
index 16ddb75c..925dcc3e 100755
--- a/bin/bbackupd/bbackupd-config.in
+++ b/bin/bbackupd/bbackupd-config.in
@@ -26,7 +26,7 @@ Parameters:
explicitly, using bbackupctl sync
account-num (hexdecimal) and server-hostname
are supplied by the server administrator
- working-dir is usually @localstatedir_expanded@
+ working-dir is usually @localstatedir_expanded@/bbackupd
backup directories is list of directories to back up
__E
@@ -227,7 +227,7 @@ SUBJECT="BACKUP PROBLEM on host $hostname"
SENDTO="$current_username"
if [ "\$1" = "" ]; then
- echo "Usage: $0 <store-full|read-error|backup-error|backup-start|backup-finish>" >&2
+ echo "Usage: \$0 <store-full|read-error|backup-error|backup-start|backup-finish>" >&2
exit 2
elif [ "\$1" = store-full ]; then
$sendmail \$SENDTO <<EOM
@@ -577,7 +577,7 @@ What you need to do now...
more files will be backed up. You want to know about this.
6) Start the backup daemon with the command
- @bindir_expanded@/bbackupd$daemon_args
+ @sbindir_expanded@/bbackupd$daemon_args
in /etc/rc.local, or your local equivalent.
Note that bbackupd must run as root.
__E
diff --git a/bin/bbackupd/bbackupd.cpp b/bin/bbackupd/bbackupd.cpp
index a0f275b3..d334a2df 100644
--- a/bin/bbackupd/bbackupd.cpp
+++ b/bin/bbackupd/bbackupd.cpp
@@ -29,7 +29,7 @@ int main(int argc, const char *argv[])
MAINHELPER_START
- Logging::SetProgramName("Box Backup (bbackupd)");
+ Logging::SetProgramName("bbackupd");
Logging::ToConsole(true);
Logging::ToSyslog (true);
diff --git a/bin/bbackupd/win32/NotifySysAdmin.vbs b/bin/bbackupd/win32/NotifySysAdmin.vbs
index 49082887..712d92da 100644
--- a/bin/bbackupd/win32/NotifySysAdmin.vbs
+++ b/bin/bbackupd/win32/NotifySysAdmin.vbs
@@ -10,44 +10,62 @@ Dim smtpserver
Set WshNet = CreateObject("WScript.Network")
hostname = WshNet.ComputerName
-account = "0a1"
+account = "0x1"
from = "boxbackup@" & hostname
sendto = "admin@example.com"
-subjtmpl = "BACKUP PROBLEM on host " & hostname
smtpserver = "smtp.example.com"
+subjtmpl = "BACKUP PROBLEM on host " & hostname
Set args = WScript.Arguments
If args(0) = "store-full" Then
subject = subjtmpl & " (store full)"
- body = "The store account for "&hostname&" is full." & vbCrLf & vbCrLf & _
- "=============================" & vbCrLf & _
- "FILES ARE NOT BEING BACKED UP" & vbCrLf & _
- "=============================" & vbCrLf & vbCrLf & _
- "Please adjust the limits on account "&account&" on server "&hostname&"." _
- & vbCrLf
+ body = "The store account for "&hostname&" is full." & vbCrLf & _
+ vbCrLf & _
+ "=============================" & vbCrLf & _
+ "FILES ARE NOT BEING BACKED UP" & vbCrLf & _
+ "=============================" & vbCrLf & _
+ vbCrLf & _
+ "Please adjust the limits on account "&account&" on server "&hostname&"." _
+ & vbCrLf
SendMail from,sendto,subject,body
ElseIf args(0) = "read-error" Then
subject = subjtmpl & " (read errors)"
- body = "Errors occured reading some files or directories for backup on "&hostname&"." _
- & vbCrLf & vbCrLf & _
- "===================================" & vbCrLf & _
- "THESE FILES ARE NOT BEING BACKED UP" & vbCrLf & _
- "===================================" & vbCrLf & vbCrLf & _
- "Check the logs on "&hostname&" for the files and directories which caused" & _
- "these errors, and take appropraite action." & vbCrLf & vbCrLf & _
- "Other files are being backed up." & vbCrLf
+ body = "Errors occurred reading some files or directories " & _
+ "for backup on " & hostname & "." & vbCrLf & _
+ vbCrLf & _
+ "===================================" & vbCrLf & _
+ "THESE FILES ARE NOT BEING BACKED UP" & vbCrLf & _
+ "===================================" & vbCrLf & vbCrLf & _
+ "Check the logs on "&hostname&" for the files and " & _
+ "directories which caused" & vbCrLf & _
+ "these errors, and take appropriate action." & vbCrLf & _
+ vbCrLf & _
+ "Other files are being backed up." & vbCrLf
+ SendMail from,sendto,subject,body
+ElseIf args(0) = "backup-error" Then
+ subject = subjtmpl & " (read errors)"
+ body = "An error occurred during the backup on "&hostname&"." _
+ & vbCrLf & vbCrLf & _
+ "==========================" & vbCrLf & _
+ "FILES MAY NOT BE BACKED UP" & vbCrLf & _
+ "==========================" & vbCrLf & _
+ vbCrLf & _
+ "Check the logs on "&hostname&" for more " & _
+ "information about the error, " & vbCrLf & _
+ "and take appropriate action." & vbCrLf
SendMail from,sendto,subject,body
-ElseIf args(0) = "backup-start" Or args(0) = "backup-finish" Then
+ElseIf args(0) = "backup-start" Or args(0) = "backup-finish" _
+ Or args(0) = "backup-ok" Then
' do nothing for these messages by default
Else
subject = subjtmpl & " (unknown)"
body = "The backup daemon on "&hostname&" reported an unknown error." _
- & vbCrLf & vbCrLf & _
- "==========================" & vbCrLf & _
- "FILES MAY NOT BE BACKED UP" & vbCrLf & _
- "==========================" & vbCrLf & vbCrLf & _
- "Please check the logs on "&hostname&"." & vbCrLf
+ & vbCrLf & vbCrLf & _
+ "==========================" & vbCrLf & _
+ "FILES MAY NOT BE BACKED UP" & vbCrLf & _
+ "==========================" & vbCrLf & vbCrLf & _
+ "Please check the logs on "&hostname&"." & vbCrLf
SendMail from,sendto,subject,body
End If
diff --git a/bin/bbackupd/win32/bbackupd.conf b/bin/bbackupd/win32/bbackupd.conf
index 6c987f7d..b0793b29 100644
--- a/bin/bbackupd/win32/bbackupd.conf
+++ b/bin/bbackupd/win32/bbackupd.conf
@@ -173,7 +173,7 @@ Server
# If a directive ends in Regex, then it is a regular expression rather than a
# explicit full pathname. See:
#
-# http://bbdev.fluffy.co.uk/trac/wiki/Win32Regex
+# http://www.boxbackup.org/trac/wiki/Win32Regex
#
# for more information about regular expressions on Windows.
#
diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp
index b6984641..687dcb05 100644
--- a/bin/bbackupquery/BackupQueries.cpp
+++ b/bin/bbackupquery/BackupQueries.cpp
@@ -13,7 +13,6 @@
#include <unistd.h>
#endif
-#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
@@ -25,29 +24,33 @@
#include <dirent.h>
#endif
-#include <set>
+#include <cstring>
#include <limits>
+#include <iostream>
+#include <ostream>
+#include <set>
+#include "BackupClientFileAttributes.h"
+#include "BackupClientMakeExcludeList.h"
+#include "BackupClientRestore.h"
#include "BackupQueries.h"
-#include "Utils.h"
-#include "Configuration.h"
-#include "autogen_BackupProtocolClient.h"
-#include "BackupStoreFilenameClear.h"
#include "BackupStoreDirectory.h"
-#include "IOStream.h"
-#include "BoxTimeToText.h"
-#include "FileStream.h"
+#include "BackupStoreException.h"
#include "BackupStoreFile.h"
-#include "TemporaryDirectory.h"
-#include "FileModificationTime.h"
-#include "BackupClientFileAttributes.h"
+#include "BackupStoreFilenameClear.h"
+#include "BoxTimeToText.h"
#include "CommonException.h"
-#include "BackupClientRestore.h"
-#include "BackupStoreException.h"
+#include "Configuration.h"
#include "ExcludeList.h"
-#include "BackupClientMakeExcludeList.h"
-#include "PathUtils.h"
+#include "FileModificationTime.h"
+#include "FileStream.h"
+#include "IOStream.h"
#include "Logging.h"
+#include "PathUtils.h"
+#include "SelfFlushingStream.h"
+#include "TemporaryDirectory.h"
+#include "Utils.h"
+#include "autogen_BackupProtocolClient.h"
#include "MemLeakFindOn.h"
@@ -68,8 +71,10 @@
// Created: 2003/10/10
//
// --------------------------------------------------------------------------
-BackupQueries::BackupQueries(BackupProtocolClient &rConnection, const Configuration &rConfiguration)
- : mrConnection(rConnection),
+BackupQueries::BackupQueries(BackupProtocolClient &rConnection,
+ const Configuration &rConfiguration, bool readWrite)
+ : mReadWrite(readWrite),
+ mrConnection(rConnection),
mrConfiguration(rConfiguration),
mQuitNow(false),
mRunningAsRoot(false),
@@ -115,7 +120,13 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine)
if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0')
{
// Yes, run shell command
- ::system(Command + 3);
+ int result = ::system(Command + 3);
+ if(result != 0)
+ {
+ BOX_WARNING("System command returned error code " <<
+ result);
+ SetReturnCode(ReturnCode::Command_Error);
+ }
return;
}
@@ -209,28 +220,36 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine)
{ "getobject", "" },
{ "get", "i" },
{ "compare", "alcqAEQ" },
- { "restore", "dri" },
+ { "restore", "drif" },
{ "help", "" },
- { "usage", "" },
+ { "usage", "m" },
{ "undelete", "" },
+ { "delete", "" },
{ NULL, NULL }
};
- #define COMMAND_Quit 0
- #define COMMAND_Exit 1
- #define COMMAND_List 2
- #define COMMAND_pwd 3
- #define COMMAND_cd 4
- #define COMMAND_lcd 5
- #define COMMAND_sh 6
- #define COMMAND_GetObject 7
- #define COMMAND_Get 8
- #define COMMAND_Compare 9
- #define COMMAND_Restore 10
- #define COMMAND_Help 11
- #define COMMAND_Usage 12
- #define COMMAND_Undelete 13
- static const char *alias[] = {"ls", 0};
- static const int aliasIs[] = {COMMAND_List, 0};
+
+ typedef enum
+ {
+ Command_Quit = 0,
+ Command_Exit,
+ Command_List,
+ Command_pwd,
+ Command_cd,
+ Command_lcd,
+ Command_sh,
+ Command_GetObject,
+ Command_Get,
+ Command_Compare,
+ Command_Restore,
+ Command_Help,
+ Command_Usage,
+ Command_Undelete,
+ Command_Delete,
+ }
+ CommandType;
+
+ static const char *alias[] = {"ls", 0};
+ static const int aliasIs[] = {Command_List, 0};
// Work out which command it is...
int cmd = 0;
@@ -284,25 +303,25 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine)
}
}
- if(cmd != COMMAND_Quit && cmd != COMMAND_Exit)
+ if(cmd != Command_Quit && cmd != Command_Exit)
{
// If not a quit command, set the return code to zero
- SetReturnCode(0);
+ SetReturnCode(ReturnCode::Command_OK);
}
// Handle command
switch(cmd)
{
- case COMMAND_Quit:
- case COMMAND_Exit:
+ case Command_Quit:
+ case Command_Exit:
mQuitNow = true;
break;
- case COMMAND_List:
+ case Command_List:
CommandList(args, opts);
break;
- case COMMAND_pwd:
+ case Command_pwd:
{
// Simple implementation, so do it here
BOX_INFO(GetCurrentDirectoryName() << " (" <<
@@ -310,47 +329,52 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine)
}
break;
- case COMMAND_cd:
+ case Command_cd:
CommandChangeDir(args, opts);
break;
- case COMMAND_lcd:
+ case Command_lcd:
CommandChangeLocalDir(args);
break;
- case COMMAND_sh:
+ case Command_sh:
BOX_ERROR("The command to run must be specified as an argument.");
break;
- case COMMAND_GetObject:
+ case Command_GetObject:
CommandGetObject(args, opts);
break;
- case COMMAND_Get:
+ case Command_Get:
CommandGet(args, opts);
break;
- case COMMAND_Compare:
+ case Command_Compare:
CommandCompare(args, opts);
break;
- case COMMAND_Restore:
+ case Command_Restore:
CommandRestore(args, opts);
break;
- case COMMAND_Usage:
- CommandUsage();
+ case Command_Usage:
+ CommandUsage(opts);
break;
- case COMMAND_Help:
+ case Command_Help:
CommandHelp(args);
break;
- case COMMAND_Undelete:
+ case Command_Undelete:
CommandUndelete(args, opts);
break;
+ case Command_Delete:
+ CommandDelete(args, opts);
+ break;
+
default:
+ BOX_ERROR("Unknown command: " << Command);
break;
}
}
@@ -402,6 +426,7 @@ void BackupQueries::CommandList(const std::vector<std::string> &args, const bool
{
BOX_ERROR("Directory '" << args[0] << "' not found "
"on store.");
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
}
@@ -427,11 +452,28 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool
if(!opts[LIST_OPTION_ALLOWDELETED]) excludeFlags |= BackupProtocolClientListDirectory::Flags_Deleted;
// Do communication
- mrConnection.QueryListDirectory(
- DirID,
- BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, // both files and directories
- excludeFlags,
- true /* want attributes */);
+ try
+ {
+ mrConnection.QueryListDirectory(
+ DirID,
+ BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING,
+ // both files and directories
+ excludeFlags,
+ true /* want attributes */);
+ }
+ catch (std::exception &e)
+ {
+ BOX_ERROR("Failed to list directory: " << e.what());
+ SetReturnCode(ReturnCode::Command_Error);
+ return;
+ }
+ catch (...)
+ {
+ BOX_ERROR("Failed to list directory: unknown error");
+ SetReturnCode(ReturnCode::Command_Error);
+ return;
+ }
+
// Retrieve the directory from the stream following
BackupStoreDirectory dir;
@@ -574,15 +616,18 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupQueries::FindDirectoryObjectID(const std::string &)
-// Purpose: Find the object ID of a directory on the store, or return 0 for not found.
-// If pStack != 0, the object is set to the stack of directories.
-// Will start from the current directory stack.
+// Name: BackupQueries::FindDirectoryObjectID(const
+// std::string &)
+// Purpose: Find the object ID of a directory on the store,
+// or return 0 for not found. If pStack != 0, the
+// object is set to the stack of directories.
+// Will start from the current directory stack.
// Created: 2003/10/10
//
// --------------------------------------------------------------------------
-int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, bool AllowOldVersion,
- bool AllowDeletedDirs, std::vector<std::pair<std::string, int64_t> > *pStack)
+int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName,
+ bool AllowOldVersion, bool AllowDeletedDirs,
+ std::vector<std::pair<std::string, int64_t> > *pStack)
{
// Split up string into elements
std::vector<std::string> dirElements;
@@ -747,6 +792,7 @@ void BackupQueries::CommandChangeDir(const std::vector<std::string> &args, const
if(args.size() != 1 || args[0].size() == 0)
{
BOX_ERROR("Incorrect usage. cd [-o] [-d] <directory>");
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -764,6 +810,7 @@ void BackupQueries::CommandChangeDir(const std::vector<std::string> &args, const
if(id == 0)
{
BOX_ERROR("Directory '" << args[0] << "' not found.");
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -785,7 +832,7 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
if(args.size() != 1 || args[0].size() == 0)
{
BOX_ERROR("Incorrect usage. lcd <local-directory>");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -795,7 +842,7 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
if(!ConvertConsoleToUtf8(args[0].c_str(), dirName))
{
BOX_ERROR("Failed to convert path from console encoding.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
int result = ::chdir(dirName.c_str());
@@ -810,11 +857,11 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
}
else
{
- BOX_ERROR("Error changing to directory '" <<
- args[0] << ": " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to change to directory "
+ "'" << args[0] << "'");
}
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -822,9 +869,8 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
char wd[PATH_MAX];
if(::getcwd(wd, PATH_MAX) == 0)
{
- BOX_ERROR("Error getting current directory: " <<
- strerror(errno));
- SetReturnCode(COMMAND_RETURN_ERROR);
+ BOX_LOG_SYS_ERROR("Error getting current directory");
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -832,7 +878,7 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
if(!ConvertUtf8ToConsole(wd, dirName))
{
BOX_ERROR("Failed to convert new path from console encoding.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
BOX_INFO("Local current directory is now '" << dirName << "'.");
@@ -868,8 +914,8 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const
}
// Does file exist?
- struct stat st;
- if(::stat(args[1].c_str(), &st) == 0 || errno != ENOENT)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(args[1].c_str(), &st) == 0 || errno != ENOENT)
{
BOX_ERROR("The local file '" << args[1] << " already exists.");
return;
@@ -907,6 +953,117 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BackupQueries::FindFileID(const std::string&
+// rNameOrIdString, const bool *options,
+// int64_t *pDirIdOut, std::string* pFileNameOut)
+// Purpose: Locate a file on the store (either by name or by
+// object ID, depending on opts['i'], where name can
+// include a path) and return the file ID, placing the
+// directory ID in *pDirIdOut and the filename part
+// of the path (if not looking up by ID and not NULL)
+// in *pFileNameOut.
+// Created: 2008-09-12
+//
+// --------------------------------------------------------------------------
+int64_t BackupQueries::FindFileID(const std::string& rNameOrIdString,
+ const bool *opts, int64_t *pDirIdOut, std::string* pFileNameOut,
+ int16_t flagsInclude, int16_t flagsExclude, int16_t* pFlagsOut)
+{
+ // Find object ID somehow
+ int64_t fileId;
+ int64_t dirId = GetCurrentDirectoryID();
+ std::string fileName = rNameOrIdString;
+
+ if(!opts['i'])
+ {
+ // does this remote filename include a path?
+ std::string::size_type index = fileName.rfind('/');
+ if(index != std::string::npos)
+ {
+ std::string dirName(fileName.substr(0, index));
+ fileName = fileName.substr(index + 1);
+
+ dirId = FindDirectoryObjectID(dirName);
+ if(dirId == 0)
+ {
+ BOX_ERROR("Directory '" << dirName <<
+ "' not found.");
+ return 0;
+ }
+ }
+
+ if(pFileNameOut)
+ {
+ *pFileNameOut = fileName;
+ }
+ }
+
+ BackupStoreFilenameClear fn(fileName);
+
+ // Need to look it up in the current directory
+ mrConnection.QueryListDirectory(
+ dirId, flagsInclude, flagsExclude,
+ true /* do want attributes */);
+
+ // Retrieve the directory from the stream following
+ BackupStoreDirectory dir;
+ std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
+ dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
+ BackupStoreDirectory::Entry *en;
+
+ if(opts['i'])
+ {
+ // Specified as ID.
+ fileId = ::strtoll(rNameOrIdString.c_str(), 0, 16);
+ if(fileId == std::numeric_limits<long long>::min() ||
+ fileId == std::numeric_limits<long long>::max() ||
+ fileId == 0)
+ {
+ BOX_ERROR("Not a valid object ID (specified in hex).");
+ return 0;
+ }
+
+ // Check that the item is actually in the directory
+ en = dir.FindEntryByID(fileId);
+ if(en == 0)
+ {
+ BOX_ERROR("File ID " <<
+ BOX_FORMAT_OBJECTID(fileId) <<
+ " not found in current directory on store.\n"
+ "(You can only access files by ID from the "
+ "current directory.)");
+ return 0;
+ }
+ }
+ else
+ {
+ // Specified by name, find the object in the directory to get the ID
+ BackupStoreDirectory::Iterator i(dir);
+ en = i.FindMatchingClearName(fn);
+ if(en == 0)
+ {
+ BOX_ERROR("Filename '" << rNameOrIdString << "' "
+ "not found in current directory on store.\n"
+ "(Subdirectories in path not searched.)");
+ return 0;
+ }
+
+ fileId = en->GetObjectID();
+ }
+
+ *pDirIdOut = dirId;
+
+ if(pFlagsOut)
+ {
+ *pFlagsOut = en->GetFlags();
+ }
+
+ return fileId;
+}
+
// --------------------------------------------------------------------------
//
@@ -929,120 +1086,72 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts)
}
// Find object ID somehow
- int64_t fileId;
- int64_t dirId = GetCurrentDirectoryID();
+ int64_t fileId, dirId;
std::string localName;
- // BLOCK
- {
#ifdef WIN32
- for (std::vector<std::string>::iterator
- i = args.begin(); i != args.end(); i++)
+ for (std::vector<std::string>::iterator
+ i = args.begin(); i != args.end(); i++)
+ {
+ std::string out;
+ if(!ConvertConsoleToUtf8(i->c_str(), out))
{
- std::string out;
- if(!ConvertConsoleToUtf8(i->c_str(), out))
- {
- BOX_ERROR("Failed to convert encoding.");
- return;
- }
- *i = out;
+ BOX_ERROR("Failed to convert encoding.");
+ return;
}
+ *i = out;
+ }
#endif
- std::string fileName(args[0]);
+ int16_t flagsExclude;
- if(!opts['i'])
- {
- // does this remote filename include a path?
- std::string::size_type index = fileName.rfind('/');
- if(index != std::string::npos)
- {
- std::string dirName(fileName.substr(0, index));
- fileName = fileName.substr(index + 1);
-
- dirId = FindDirectoryObjectID(dirName);
- if(dirId == 0)
- {
- BOX_ERROR("Directory '" << dirName <<
- "' not found.");
- return;
- }
- }
- }
+ if(opts['i'])
+ {
+ // can retrieve anything by ID
+ flagsExclude = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING;
+ }
+ else
+ {
+ // only current versions by name
+ flagsExclude =
+ BackupProtocolClientListDirectory::Flags_OldVersion |
+ BackupProtocolClientListDirectory::Flags_Deleted;
+ }
- BackupStoreFilenameClear fn(fileName);
- // Need to look it up in the current directory
- mrConnection.QueryListDirectory(
- dirId,
- BackupProtocolClientListDirectory::Flags_File, // just files
- (opts['i'])?(BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING):(BackupProtocolClientListDirectory::Flags_OldVersion | BackupProtocolClientListDirectory::Flags_Deleted), // only current versions
- false /* don't want attributes */);
+ fileId = FindFileID(args[0], opts, &dirId, &localName,
+ BackupProtocolClientListDirectory::Flags_File, // just files
+ flagsExclude, NULL /* don't care about flags found */);
- // Retrieve the directory from the stream following
- BackupStoreDirectory dir;
- std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
- dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
+ if (fileId == 0)
+ {
+ // error already reported
+ return;
+ }
- if(opts['i'])
+ if(opts['i'])
+ {
+ // Specified as ID. Must have a local name in the arguments
+ // (check at beginning of function ensures this)
+ localName = args[1];
+ }
+ else
+ {
+ // Specified by name. Local name already set by FindFileID,
+ // but may be overridden by user supplying a second argument.
+ if(args.size() == 2)
{
- // Specified as ID.
- fileId = ::strtoll(args[0].c_str(), 0, 16);
- if(fileId == std::numeric_limits<long long>::min() ||
- fileId == std::numeric_limits<long long>::max() ||
- fileId == 0)
- {
- BOX_ERROR("Not a valid object ID (specified in hex).");
- return;
- }
-
- // Check that the item is actually in the directory
- if(dir.FindEntryByID(fileId) == 0)
- {
- BOX_ERROR("File ID " <<
- BOX_FORMAT_OBJECTID(fileId) <<
- " not found in current "
- "directory on store.\n"
- "(You can only download files by ID "
- "from the current directory.)");
- return;
- }
-
- // Must have a local name in the arguments (check at beginning of function ensures this)
localName = args[1];
}
- else
- {
- // Specified by name, find the object in the directory to get the ID
- BackupStoreDirectory::Iterator i(dir);
- BackupStoreDirectory::Entry *en = i.FindMatchingClearName(fn);
-
- if(en == 0)
- {
- BOX_ERROR("Filename '" << args[0] << "' "
- "not found in current "
- "directory on store.\n"
- "(Subdirectories in path not "
- "searched.)");
- return;
- }
-
- fileId = en->GetObjectID();
-
- // Local name is the last argument, which is either
- // the looked up filename, or a filename specified
- // by the user.
- localName = args[args.size() - 1];
- }
}
// Does local file already exist? (don't want to overwrite)
- struct stat st;
- if(::stat(localName.c_str(), &st) == 0 || errno != ENOENT)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localName.c_str(), &st) == 0 || errno != ENOENT)
{
BOX_ERROR("The local file " << localName << " already exists, "
"will not overwrite it.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -1081,7 +1190,6 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts)
}
}
-
// --------------------------------------------------------------------------
//
// Function
@@ -1090,58 +1198,17 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts)
// Created: 29/1/04
//
// --------------------------------------------------------------------------
-BackupQueries::CompareParams::CompareParams()
- : mQuickCompare(false),
- mIgnoreExcludes(false),
- mIgnoreAttributes(false),
- mDifferences(0),
- mDifferencesExplainedByModTime(0),
- mUncheckedFiles(0),
- mExcludedDirs(0),
- mExcludedFiles(0),
- mpExcludeFiles(0),
- mpExcludeDirs(0),
- mLatestFileUploadTime(0)
-{
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CompareParams::~CompareParams()
-// Purpose: Destructor
-// Created: 29/1/04
-//
-// --------------------------------------------------------------------------
-BackupQueries::CompareParams::~CompareParams()
-{
- DeleteExcludeLists();
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CompareParams::DeleteExcludeLists()
-// Purpose: Delete the include lists contained
-// Created: 29/1/04
-//
-// --------------------------------------------------------------------------
-void BackupQueries::CompareParams::DeleteExcludeLists()
-{
- if(mpExcludeFiles != 0)
- {
- delete mpExcludeFiles;
- mpExcludeFiles = 0;
- }
- if(mpExcludeDirs != 0)
- {
- delete mpExcludeDirs;
- mpExcludeDirs = 0;
- }
-}
-
+BackupQueries::CompareParams::CompareParams(bool QuickCompare,
+ bool IgnoreExcludes, bool IgnoreAttributes,
+ box_time_t LatestFileUploadTime)
+: BoxBackupCompareParams(QuickCompare, IgnoreExcludes, IgnoreAttributes,
+ LatestFileUploadTime),
+ mDifferences(0),
+ mDifferencesExplainedByModTime(0),
+ mUncheckedFiles(0),
+ mExcludedDirs(0),
+ mExcludedFiles(0)
+{ }
// --------------------------------------------------------------------------
//
@@ -1153,24 +1220,19 @@ void BackupQueries::CompareParams::DeleteExcludeLists()
// --------------------------------------------------------------------------
void BackupQueries::CommandCompare(const std::vector<std::string> &args, const bool *opts)
{
- // Parameters, including count of differences
- BackupQueries::CompareParams params;
- params.mQuickCompare = opts['q'];
- params.mQuietCompare = opts['Q'];
- params.mIgnoreExcludes = opts['E'];
- params.mIgnoreAttributes = opts['A'];
+ box_time_t LatestFileUploadTime = GetCurrentBoxTime();
// Try and work out the time before which all files should be on the server
{
std::string syncTimeFilename(mrConfiguration.GetKeyValue("DataDirectory") + DIRECTORY_SEPARATOR_ASCHAR);
syncTimeFilename += "last_sync_start";
// Stat it to get file time
- struct stat st;
- if(::stat(syncTimeFilename.c_str(), &st) == 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(syncTimeFilename.c_str(), &st) == 0)
{
// Files modified after this time shouldn't be on the server, so report errors slightly differently
- params.mLatestFileUploadTime = FileModificationTime(st)
- - SecondsToBoxTime(mrConfiguration.GetKeyValueInt("MinimumFileAge"));
+ LatestFileUploadTime = FileModificationTime(st) -
+ SecondsToBoxTime(mrConfiguration.GetKeyValueInt("MinimumFileAge"));
}
else
{
@@ -1179,8 +1241,16 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b
}
}
+ // Parameters, including count of differences
+ BackupQueries::CompareParams params(opts['q'], // quick compare?
+ opts['E'], // ignore excludes
+ opts['A'], // ignore attributes
+ LatestFileUploadTime);
+
+ params.mQuietCompare = opts['Q'];
+
// Quick compare?
- if(params.mQuickCompare)
+ if(params.QuickCompare())
{
BOX_WARNING("Quick compare used -- file attributes are not "
"checked.");
@@ -1189,11 +1259,16 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b
if(!opts['l'] && opts['a'] && args.size() == 0)
{
// Compare all locations
- const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations"));
- for(std::list<std::pair<std::string, Configuration> >::const_iterator i = locations.mSubConfigurations.begin();
- i != locations.mSubConfigurations.end(); ++i)
+ const Configuration &rLocations(
+ mrConfiguration.GetSubConfiguration("BackupLocations"));
+ std::vector<std::string> locNames =
+ rLocations.GetSubConfigurationNames();
+ for(std::vector<std::string>::iterator
+ pLocName = locNames.begin();
+ pLocName != locNames.end();
+ pLocName++)
{
- CompareLocation(i->first, params);
+ CompareLocation(*pLocName, params);
}
}
else if(opts['l'] && !opts['a'] && args.size() == 1)
@@ -1206,7 +1281,7 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b
// Compare directory to directory
// Can't be bothered to do all the hard work to work out which location it's on, and hence which exclude list
- if(!params.mIgnoreExcludes)
+ if(!params.IgnoreExcludes())
{
BOX_ERROR("Cannot use excludes on directory to directory comparison -- use -E flag to specify ignored excludes.");
return;
@@ -1241,15 +1316,15 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b
{
if (params.mUncheckedFiles != 0)
{
- SetReturnCode(COMPARE_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Compare_Error);
}
else if (params.mDifferences != 0)
{
- SetReturnCode(COMPARE_RETURN_DIFFERENT);
+ SetReturnCode(ReturnCode::Compare_Different);
}
else
{
- SetReturnCode(COMPARE_RETURN_SAME);
+ SetReturnCode(ReturnCode::Compare_Same);
}
}
}
@@ -1263,7 +1338,8 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b
// Created: 2003/10/13
//
// --------------------------------------------------------------------------
-void BackupQueries::CompareLocation(const std::string &rLocation, BackupQueries::CompareParams &rParams)
+void BackupQueries::CompareLocation(const std::string &rLocation,
+ BoxBackupCompareParams &rParams)
{
// Find the location's sub configuration
const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations"));
@@ -1287,45 +1363,36 @@ void BackupQueries::CompareLocation(const std::string &rLocation, BackupQueries:
}
#endif
- try
- {
- // Generate the exclude lists
- if(!rParams.mIgnoreExcludes)
- {
- rParams.mpExcludeFiles = BackupClientMakeExcludeList_Files(loc);
- rParams.mpExcludeDirs = BackupClientMakeExcludeList_Dirs(loc);
- }
-
- // Then get it compared
- Compare(std::string("/") + rLocation,
- loc.GetKeyValue("Path"), rParams);
- }
- catch(...)
+ // Generate the exclude lists
+ if(!rParams.IgnoreExcludes())
{
- // Clean up
- rParams.DeleteExcludeLists();
- throw;
+ rParams.LoadExcludeLists(loc);
}
-
- // Delete exclude lists
- rParams.DeleteExcludeLists();
+
+ // Then get it compared
+ Compare(std::string("/") + rLocation, loc.GetKeyValue("Path"), rParams);
}
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupQueries::Compare(const std::string &, const std::string &, BackupQueries::CompareParams &)
+// Name: BackupQueries::Compare(const std::string &,
+// const std::string &, BackupQueries::CompareParams &)
// Purpose: Compare a store directory against a local directory
// Created: 2003/10/13
//
// --------------------------------------------------------------------------
-void BackupQueries::Compare(const std::string &rStoreDir, const std::string &rLocalDir, BackupQueries::CompareParams &rParams)
+void BackupQueries::Compare(const std::string &rStoreDir,
+ const std::string &rLocalDir, BoxBackupCompareParams &rParams)
{
#ifdef WIN32
+ std::string localDirEncoded;
std::string storeDirEncoded;
+ if(!ConvertConsoleToUtf8(rLocalDir.c_str(), localDirEncoded)) return;
if(!ConvertConsoleToUtf8(rStoreDir.c_str(), storeDirEncoded)) return;
#else
+ const std::string& localDirEncoded(rLocalDir);
const std::string& storeDirEncoded(rStoreDir);
#endif
@@ -1335,19 +1402,22 @@ void BackupQueries::Compare(const std::string &rStoreDir, const std::string &rLo
// Found?
if(dirID == 0)
{
- BOX_WARNING("Local directory '" << rLocalDir << "' exists, "
- "but server directory '" << rStoreDir << "' does not "
- "exist.");
- rParams.mDifferences ++;
+ bool modifiedAfterLastSync = false;
+
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(rLocalDir.c_str(), &st) == 0)
+ {
+ if(FileAttrModificationTime(st) >
+ rParams.LatestFileUploadTime())
+ {
+ modifiedAfterLastSync = true;
+ }
+ }
+
+ rParams.NotifyRemoteFileMissing(localDirEncoded,
+ storeDirEncoded, modifiedAfterLastSync);
return;
}
-
-#ifdef WIN32
- std::string localDirEncoded;
- if(!ConvertConsoleToUtf8(rLocalDir.c_str(), localDirEncoded)) return;
-#else
- std::string localDirEncoded(rLocalDir);
-#endif
// Go!
Compare(dirID, storeDirEncoded, localDirEncoded, rParams);
@@ -1363,56 +1433,36 @@ void BackupQueries::Compare(const std::string &rStoreDir, const std::string &rLo
// Created: 2003/10/13
//
// --------------------------------------------------------------------------
-void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const std::string &rLocalDir, BackupQueries::CompareParams &rParams)
+void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir,
+ const std::string &rLocalDir, BoxBackupCompareParams &rParams)
{
-#ifdef WIN32
- // By this point, rStoreDir and rLocalDir should be in UTF-8 encoding
-
- std::string localDirDisplay;
- std::string storeDirDisplay;
-
- if(!ConvertUtf8ToConsole(rLocalDir.c_str(), localDirDisplay)) return;
- if(!ConvertUtf8ToConsole(rStoreDir.c_str(), storeDirDisplay)) return;
-#else
- const std::string& localDirDisplay(rLocalDir);
- const std::string& storeDirDisplay(rStoreDir);
-#endif
+ rParams.NotifyDirComparing(rLocalDir, rStoreDir);
// Get info on the local directory
- struct stat st;
- if(::lstat(rLocalDir.c_str(), &st) != 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_LSTAT(rLocalDir.c_str(), &st) != 0)
{
// What kind of error?
- if(errno == ENOTDIR)
- {
- BOX_WARNING("Local object '" << localDirDisplay << "' "
- "is a file, server object '" <<
- storeDirDisplay << "' is a directory.");
- rParams.mDifferences ++;
- }
- else if(errno == ENOENT)
+ if(errno == ENOTDIR || errno == ENOENT)
{
- BOX_WARNING("Local directory '" << localDirDisplay <<
- "' does not exist (compared to server "
- "directory '" << storeDirDisplay << "').");
- rParams.mDifferences ++;
+ rParams.NotifyLocalDirMissing(rLocalDir, rStoreDir);
}
else
{
- BOX_WARNING("Failed to access local directory '" <<
- localDirDisplay << ": " << strerror(errno) <<
- "'.");
- rParams.mUncheckedFiles ++;
+ rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir);
}
return;
}
// Get the directory listing from the store
mrConnection.QueryListDirectory(
- DirID,
- BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, // get everything
- BackupProtocolClientListDirectory::Flags_OldVersion | BackupProtocolClientListDirectory::Flags_Deleted, // except for old versions and deleted files
- true /* want attributes */);
+ DirID,
+ BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING,
+ // get everything
+ BackupProtocolClientListDirectory::Flags_OldVersion |
+ BackupProtocolClientListDirectory::Flags_Deleted,
+ // except for old versions and deleted files
+ true /* want attributes */);
// Retrieve the directory from the stream following
BackupStoreDirectory dir;
@@ -1422,8 +1472,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
// Test out the attributes
if(!dir.HasAttributes())
{
- BOX_WARNING("Store directory '" << storeDirDisplay << "' "
- "doesn't have attributes.");
+ rParams.NotifyStoreDirMissingAttributes(rLocalDir, rStoreDir);
}
else
{
@@ -1436,12 +1485,27 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
localAttr.ReadAttributes(rLocalDir.c_str(),
true /* directories have zero mod times */);
- if(!(attr.Compare(localAttr, true, true /* ignore modification times */)))
+ if(attr.Compare(localAttr, true, true /* ignore modification times */))
{
- BOX_WARNING("Local directory '" << localDirDisplay <<
- "' has different attributes to store "
- "directory '" << storeDirDisplay << "'.");
- rParams.mDifferences ++;
+ rParams.NotifyDirCompared(rLocalDir, rStoreDir,
+ false, false /* actually we didn't check :) */);
+ }
+ else
+ {
+ bool modifiedAfterLastSync = false;
+
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(rLocalDir.c_str(), &st) == 0)
+ {
+ if(FileAttrModificationTime(st) >
+ rParams.LatestFileUploadTime())
+ {
+ modifiedAfterLastSync = true;
+ }
+ }
+
+ rParams.NotifyDirCompared(rLocalDir, rStoreDir,
+ true, modifiedAfterLastSync);
}
}
@@ -1449,11 +1513,10 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
DIR *dirhandle = ::opendir(rLocalDir.c_str());
if(dirhandle == 0)
{
- BOX_WARNING("Failed to open local directory '" <<
- localDirDisplay << "': " << strerror(errno));
- rParams.mUncheckedFiles ++;
+ rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir);
return;
}
+
try
{
// Read the files and directories into sets
@@ -1484,8 +1547,8 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
#ifndef HAVE_VALID_DIRENT_D_TYPE
std::string fn(MakeFullPath
(rLocalDir, localDirEn->d_name));
- struct stat st;
- if(::lstat(fn.c_str(), &st) != 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_LSTAT(fn.c_str(), &st) != 0)
{
THROW_EXCEPTION(CommonException, OSFileError)
}
@@ -1518,8 +1581,8 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
// Close directory
if(::closedir(dirhandle) != 0)
{
- BOX_ERROR("Failed to close local directory '" <<
- localDirDisplay << "': " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to close local directory "
+ "'" << rLocalDir << "'");
}
dirhandle = 0;
@@ -1557,36 +1620,30 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
for(std::set<std::pair<std::string, BackupStoreDirectory::Entry *> >::const_iterator i = storeFiles.begin(); i != storeFiles.end(); ++i)
{
const std::string& fileName(i->first);
-#ifdef WIN32
- // File name is also in UTF-8 encoding,
- // need to convert to console
- std::string fileNameDisplay;
- if(!ConvertUtf8ToConsole(i->first.c_str(),
- fileNameDisplay)) return;
-#else
- const std::string& fileNameDisplay(i->first);
-#endif
- std::string localPath(MakeFullPath
- (rLocalDir, fileName));
- std::string localPathDisplay(MakeFullPath
- (localDirDisplay, fileNameDisplay));
- std::string storePathDisplay
- (storeDirDisplay + "/" + fileNameDisplay);
+ std::string localPath(MakeFullPath(rLocalDir, fileName));
+ std::string storePath(rStoreDir + "/" + fileName);
+ rParams.NotifyFileComparing(localPath, storePath);
+
// Does the file exist locally?
string_set_iter_t local(localFiles.find(fileName));
if(local == localFiles.end())
{
// Not found -- report
- BOX_WARNING("Local file '" <<
- localPathDisplay << "' does not exist, "
- "but store file '" <<
- storePathDisplay << "' does.");
- rParams.mDifferences ++;
+ rParams.NotifyLocalFileMissing(localPath,
+ storePath);
}
else
- {
+ {
+ int64_t fileSize = 0;
+
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localPath.c_str(), &st) == 0)
+ {
+ fileSize = st.st_size;
+ }
+
try
{
// Files the same flag?
@@ -1594,8 +1651,10 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
// File modified after last sync flag
bool modifiedAfterLastSync = false;
+
+ bool hasDifferentAttribs = false;
- if(rParams.mQuickCompare)
+ if(rParams.QuickCompare())
{
// Compare file -- fetch it
mrConnection.QueryGetBlockIndexByID(i->second->GetObjectID());
@@ -1640,7 +1699,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
BackupClientFileAttributes localAttr;
box_time_t fileModTime = 0;
localAttr.ReadAttributes(localPath.c_str(), false /* don't zero mod times */, &fileModTime);
- modifiedAfterLastSync = (fileModTime > rParams.mLatestFileUploadTime);
+ modifiedAfterLastSync = (fileModTime > rParams.LatestFileUploadTime());
bool ignoreAttrModTime = true;
#ifdef WIN32
@@ -1649,7 +1708,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
ignoreAttrModTime = false;
#endif
- if(!rParams.mIgnoreAttributes &&
+ if(!rParams.IgnoreAttributes() &&
#ifdef PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE
!fileOnServerStream->IsSymLink() &&
#endif
@@ -1657,128 +1716,43 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
ignoreAttrModTime,
fileOnServerStream->IsSymLink() /* ignore modification time if it's a symlink */))
{
- BOX_WARNING("Local file '" <<
- localPathDisplay <<
- "' has different attributes "
- "to store file '" <<
- storePathDisplay <<
- "'.");
- rParams.mDifferences ++;
- if(modifiedAfterLastSync)
- {
- rParams.mDifferencesExplainedByModTime ++;
- BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)");
- }
- else if(i->second->HasAttributes())
- {
- BOX_INFO("(the file above has had new attributes applied)\n");
- }
+ hasDifferentAttribs = true;
}
// Compare contents, if it's a regular file not a link
// Remember, we MUST read the entire stream from the server.
+ SelfFlushingStream flushObject(*objectStream);
+
if(!fileOnServerStream->IsSymLink())
{
+ SelfFlushingStream flushFile(*fileOnServerStream);
// Open the local file
FileStream l(localPath.c_str());
-
- // Size
- IOStream::pos_type fileSizeLocal = l.BytesLeftToRead();
- IOStream::pos_type fileSizeServer = 0;
-
- // Test the contents
- char buf1[2048];
- char buf2[2048];
- while(fileOnServerStream->StreamDataLeft() && l.StreamDataLeft())
- {
- int size = fileOnServerStream->Read(buf1, sizeof(buf1), mrConnection.GetTimeout());
- fileSizeServer += size;
-
- if(l.Read(buf2, size) != size
- || ::memcmp(buf1, buf2, size) != 0)
- {
- equal = false;
- break;
- }
- }
-
- // Check read all the data from the server and file -- can't be equal if local and remote aren't the same length
- // Can't use StreamDataLeft() test on file, because if it's the same size, it won't know
- // it's EOF yet.
- if(fileOnServerStream->StreamDataLeft() || fileSizeServer != fileSizeLocal)
- {
- equal = false;
- }
-
- // Must always read the entire decoded stream, if it's not a symlink
- if(fileOnServerStream->StreamDataLeft())
- {
- // Absorb all the data remaining
- char buffer[2048];
- while(fileOnServerStream->StreamDataLeft())
- {
- fileOnServerStream->Read(buffer, sizeof(buffer), mrConnection.GetTimeout());
- }
- }
-
- // Must always read the entire encoded stream
- if(objectStream->StreamDataLeft())
- {
- // Absorb all the data remaining
- char buffer[2048];
- while(objectStream->StreamDataLeft())
- {
- objectStream->Read(buffer, sizeof(buffer), mrConnection.GetTimeout());
- }
- }
+ equal = l.CompareWith(*fileOnServerStream,
+ mrConnection.GetTimeout());
}
}
- // Report if not equal.
- if(!equal)
- {
- BOX_WARNING("Local file '" <<
- localPathDisplay << "' "
- "has different contents "
- "to store file '" <<
- storePathDisplay <<
- "'.");
- rParams.mDifferences ++;
- if(modifiedAfterLastSync)
- {
- rParams.mDifferencesExplainedByModTime ++;
- BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)");
- }
- else if(i->second->HasAttributes())
- {
- BOX_INFO("(the file above has had new attributes applied)\n");
- }
- }
+ rParams.NotifyFileCompared(localPath,
+ storePath, fileSize,
+ hasDifferentAttribs, !equal,
+ modifiedAfterLastSync,
+ i->second->HasAttributes());
}
catch(BoxException &e)
{
- BOX_ERROR("Failed to fetch and compare "
- "'" <<
- storePathDisplay.c_str() <<
- "': error " << e.what() <<
- " (" << e.GetType() <<
- "/" << e.GetSubType() << ")");
- rParams.mUncheckedFiles ++;
+ rParams.NotifyDownloadFailed(localPath,
+ storePath, fileSize, e);
}
catch(std::exception &e)
{
- BOX_ERROR("Failed to fetch and compare "
- "'" <<
- storePathDisplay.c_str() <<
- "': " << e.what());
+ rParams.NotifyDownloadFailed(localPath,
+ storePath, fileSize, e);
}
catch(...)
{
- BOX_ERROR("Failed to fetch and compare "
- "'" <<
- storePathDisplay.c_str() <<
- "': unknown error");
- rParams.mUncheckedFiles ++;
+ rParams.NotifyDownloadFailed(localPath,
+ storePath, fileSize);
}
// Remove from set so that we know it's been compared
@@ -1786,53 +1760,34 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
}
}
- // Report any files which exist on the locally, but not on the store
+ // Report any files which exist locally, but not on the store
for(string_set_iter_t i = localFiles.begin(); i != localFiles.end(); ++i)
{
-#ifdef WIN32
- // File name is also in UTF-8 encoding,
- // need to convert to console
- std::string fileNameDisplay;
- if(!ConvertUtf8ToConsole(i->c_str(), fileNameDisplay))
- return;
-#else
- const std::string& fileNameDisplay(*i);
-#endif
-
- std::string localPath(MakeFullPath
- (rLocalDir, *i));
- std::string localPathDisplay(MakeFullPath
- (localDirDisplay, fileNameDisplay));
- std::string storePathDisplay
- (storeDirDisplay + "/" + fileNameDisplay);
+ std::string localPath(MakeFullPath(rLocalDir, *i));
+ std::string storePath(rStoreDir + "/" + *i);
// Should this be ignored (ie is excluded)?
- if(rParams.mpExcludeFiles == 0 ||
- !(rParams.mpExcludeFiles->IsExcluded(localPath)))
+ if(!rParams.IsExcludedFile(localPath))
{
- BOX_WARNING("Local file '" <<
- localPathDisplay <<
- "' exists, but store file '" <<
- storePathDisplay <<
- "' does not.");
- rParams.mDifferences ++;
+ bool modifiedAfterLastSync = false;
- // Check the file modification time
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localPath.c_str(), &st) == 0)
{
- struct stat st;
- if(::stat(localPath.c_str(), &st) == 0)
+ if(FileModificationTime(st) >
+ rParams.LatestFileUploadTime())
{
- if(FileModificationTime(st) > rParams.mLatestFileUploadTime)
- {
- rParams.mDifferencesExplainedByModTime ++;
- BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)");
- }
+ modifiedAfterLastSync = true;
}
}
+
+ rParams.NotifyRemoteFileMissing(localPath,
+ storePath, modifiedAfterLastSync);
}
else
{
- rParams.mExcludedFiles ++;
+ rParams.NotifyExcludedFile(localPath,
+ storePath);
}
}
@@ -1843,99 +1798,69 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
// Now do the directories, recursively to check subdirectories
for(std::set<std::pair<std::string, BackupStoreDirectory::Entry *> >::const_iterator i = storeDirs.begin(); i != storeDirs.end(); ++i)
{
-#ifdef WIN32
- // Directory name is also in UTF-8 encoding,
- // need to convert to console
- std::string subdirNameDisplay;
- if(!ConvertUtf8ToConsole(i->first.c_str(),
- subdirNameDisplay))
- return;
-#else
- const std::string& subdirNameDisplay(i->first);
-#endif
-
- std::string localPath(MakeFullPath
- (rLocalDir, i->first));
- std::string localPathDisplay(MakeFullPath
- (localDirDisplay, subdirNameDisplay));
- std::string storePathDisplay
- (storeDirDisplay + "/" + subdirNameDisplay);
+ std::string localPath(MakeFullPath(rLocalDir, i->first));
+ std::string storePath(rLocalDir + "/" + i->first);
// Does the directory exist locally?
string_set_iter_t local(localDirs.find(i->first));
if(local == localDirs.end() &&
- rParams.mpExcludeDirs != NULL &&
- rParams.mpExcludeDirs->IsExcluded(localPath))
+ rParams.IsExcludedDir(localPath))
{
- // Not found -- report
- BOX_WARNING("Local directory '" <<
- localPathDisplay << "' is excluded, "
- "but store directory '" <<
- storePathDisplay << "' still exists.");
- rParams.mDifferences ++;
+ rParams.NotifyExcludedFileNotDeleted(localPath,
+ storePath);
}
else if(local == localDirs.end())
{
// Not found -- report
- BOX_WARNING("Local directory '" <<
- localPathDisplay << "' does not exist, "
- "but store directory '" <<
- storePathDisplay << "' does.");
- rParams.mDifferences ++;
+ rParams.NotifyRemoteFileMissing(localPath,
+ storePath, false);
}
- else if(rParams.mpExcludeDirs != NULL &&
- rParams.mpExcludeDirs->IsExcluded(localPath))
+ else if(rParams.IsExcludedDir(localPath))
{
// don't recurse into excluded directories
}
else
{
// Compare directory
- Compare(i->second->GetObjectID(), rStoreDir + "/" + i->first, localPath, rParams);
+ Compare(i->second->GetObjectID(),
+ rStoreDir + "/" + i->first,
+ localPath, rParams);
// Remove from set so that we know it's been compared
localDirs.erase(local);
}
}
- // Report any files which exist on the locally, but not on the store
- for(std::set<std::string>::const_iterator i = localDirs.begin(); i != localDirs.end(); ++i)
+ // Report any directories which exist locally, but not on the store
+ for(std::set<std::string>::const_iterator
+ i = localDirs.begin();
+ i != localDirs.end(); ++i)
{
-#ifdef WIN32
- // File name is also in UTF-8 encoding,
- // need to convert to console
- std::string fileNameDisplay;
- if(!ConvertUtf8ToConsole(i->c_str(), fileNameDisplay))
- return;
-#else
- const std::string& fileNameDisplay(*i);
-#endif
-
- std::string localPath(MakeFullPath
- (rLocalDir, *i));
- std::string localPathDisplay(MakeFullPath
- (localDirDisplay, fileNameDisplay));
-
- std::string storePath
- (rStoreDir + "/" + *i);
- std::string storePathDisplay
- (storeDirDisplay + "/" + fileNameDisplay);
+ std::string localPath(MakeFullPath(rLocalDir, *i));
+ std::string storePath(rStoreDir + "/" + *i);
// Should this be ignored (ie is excluded)?
- if(rParams.mpExcludeDirs == 0 || !(rParams.mpExcludeDirs->IsExcluded(localPath)))
+ if(!rParams.IsExcludedDir(localPath))
{
- BOX_WARNING("Local directory '" <<
- localPathDisplay << "' exists, but "
- "store directory '" <<
- storePathDisplay << "' does not.");
- rParams.mDifferences ++;
+ bool modifiedAfterLastSync = false;
+
+ // Check the dir modification time
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localPath.c_str(), &st) == 0 &&
+ FileModificationTime(st) >
+ rParams.LatestFileUploadTime())
+ {
+ modifiedAfterLastSync = true;
+ }
+
+ rParams.NotifyRemoteFileMissing(localPath,
+ storePath, modifiedAfterLastSync);
}
else
{
- rParams.mExcludedDirs ++;
+ rParams.NotifyExcludedDir(localPath, storePath);
}
}
-
}
catch(...)
{
@@ -1943,6 +1868,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
{
::closedir(dirhandle);
}
+ throw;
}
}
@@ -1960,7 +1886,7 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b
// Check arguments
if(args.size() != 2)
{
- BOX_ERROR("Incorrect usage. restore [-d] [-r] [-i] <remote-name> <local-name>");
+ BOX_ERROR("Incorrect usage. restore [-drif] <remote-name> <local-name>");
return;
}
@@ -2023,18 +1949,19 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b
localName.c_str(),
true /* print progress dots */, restoreDeleted,
false /* don't undelete after restore! */,
- opts['r'] /* resume? */);
+ opts['r'] /* resume? */,
+ opts['f'] /* force continue after errors */);
}
catch(std::exception &e)
{
BOX_ERROR("Failed to restore: " << e.what());
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
catch(...)
{
BOX_ERROR("Failed to restore: unknown exception");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -2044,33 +1971,38 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b
BOX_INFO("Restore complete.");
break;
+ case Restore_CompleteWithErrors:
+ BOX_WARNING("Restore complete, but some files could not be "
+ "restored.");
+ break;
+
case Restore_ResumePossible:
- BOX_ERROR("Resume possible -- repeat command with -r flag to resume");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ BOX_ERROR("Resume possible -- repeat command with -r flag "
+ "to resume.");
+ SetReturnCode(ReturnCode::Command_Error);
break;
case Restore_TargetExists:
- BOX_ERROR("The target directory exists. You cannot restore over an existing directory.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ BOX_ERROR("The target directory exists. You cannot restore "
+ "over an existing directory.");
+ SetReturnCode(ReturnCode::Command_Error);
break;
- #ifdef WIN32
case Restore_TargetPathNotFound:
BOX_ERROR("The target directory path does not exist.\n"
"To restore to a directory whose parent "
"does not exist, create the parent first.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
break;
- #endif
case Restore_UnknownError:
BOX_ERROR("Unknown error during restore.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
break;
default:
BOX_ERROR("Unknown restore result " << result << ".");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
break;
}
}
@@ -2131,49 +2063,46 @@ void BackupQueries::CommandHelp(const std::vector<std::string> &args)
// Created: 19/4/04
//
// --------------------------------------------------------------------------
-void BackupQueries::CommandUsage()
+void BackupQueries::CommandUsage(const bool *opts)
{
+ bool MachineReadable = opts['m'];
+
// Request full details from the server
std::auto_ptr<BackupProtocolClientAccountUsage> usage(mrConnection.QueryGetAccountUsage());
// Display each entry in turn
int64_t hardLimit = usage->GetBlocksHardLimit();
int32_t blockSize = usage->GetBlockSize();
- CommandUsageDisplayEntry("Used", usage->GetBlocksUsed(), hardLimit, blockSize);
- CommandUsageDisplayEntry("Old files", usage->GetBlocksInOldFiles(), hardLimit, blockSize);
- CommandUsageDisplayEntry("Deleted files", usage->GetBlocksInDeletedFiles(), hardLimit, blockSize);
- CommandUsageDisplayEntry("Directories", usage->GetBlocksInDirectories(), hardLimit, blockSize);
- CommandUsageDisplayEntry("Soft limit", usage->GetBlocksSoftLimit(), hardLimit, blockSize);
- CommandUsageDisplayEntry("Hard limit", hardLimit, hardLimit, blockSize);
+ CommandUsageDisplayEntry("Used", usage->GetBlocksUsed(), hardLimit,
+ blockSize, MachineReadable);
+ CommandUsageDisplayEntry("Old files", usage->GetBlocksInOldFiles(),
+ hardLimit, blockSize, MachineReadable);
+ CommandUsageDisplayEntry("Deleted files", usage->GetBlocksInDeletedFiles(),
+ hardLimit, blockSize, MachineReadable);
+ CommandUsageDisplayEntry("Directories", usage->GetBlocksInDirectories(),
+ hardLimit, blockSize, MachineReadable);
+ CommandUsageDisplayEntry("Soft limit", usage->GetBlocksSoftLimit(),
+ hardLimit, blockSize, MachineReadable);
+ CommandUsageDisplayEntry("Hard limit", hardLimit, hardLimit, blockSize,
+ MachineReadable);
}
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupQueries::CommandUsageDisplayEntry(const char *, int64_t, int64_t, int32_t)
+// Name: BackupQueries::CommandUsageDisplayEntry(const char *,
+// int64_t, int64_t, int32_t, bool)
// Purpose: Display an entry in the usage table
// Created: 19/4/04
//
// --------------------------------------------------------------------------
-void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size, int64_t HardLimit, int32_t BlockSize)
+void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size,
+int64_t HardLimit, int32_t BlockSize, bool MachineReadable)
{
- // Calculate size in Mb
- double mb = (((double)Size) * ((double)BlockSize)) / ((double)(1024*1024));
- int64_t percent = (Size * 100) / HardLimit;
-
- // Bar graph
- char bar[41];
- unsigned int b = (int)((Size * (sizeof(bar)-1)) / HardLimit);
- if(b > sizeof(bar)-1) {b = sizeof(bar)-1;}
- for(unsigned int l = 0; l < b; l++)
- {
- bar[l] = '*';
- }
- bar[b] = '\0';
-
- // Print the entryj
- ::printf("%14s %10.1fMb %3d%% %s\n", Name, mb, (int32_t)percent, bar);
+ std::cout << FormatUsageLineStart(Name, MachineReadable) <<
+ FormatUsageBar(Size, Size * BlockSize, HardLimit * BlockSize,
+ MachineReadable) << std::endl;
}
@@ -2187,10 +2116,17 @@ void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size, int
// --------------------------------------------------------------------------
void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const bool *opts)
{
+ if (!mReadWrite)
+ {
+ BOX_ERROR("This command requires a read-write connection. "
+ "Please reconnect with the -w option.");
+ return;
+ }
+
// Check arguments
if(args.size() != 1)
{
- BOX_ERROR("Incorrect usage. undelete <directory-name>");
+ BOX_ERROR("Incorrect usage. undelete <name> or undelete -i <object-id>");
return;
}
@@ -2200,23 +2136,133 @@ void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const
#else
const std::string& storeDirEncoded(args[0]);
#endif
-
- // Get directory ID
- int64_t dirID = FindDirectoryObjectID(storeDirEncoded,
- false /* no old versions */, true /* find deleted dirs */);
-
- // Allowable?
- if(dirID == 0)
+
+ // Find object ID somehow
+ int64_t fileId, parentId;
+ std::string fileName;
+ int16_t flagsOut;
+
+ fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName,
+ /* include files and directories */
+ BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING,
+ /* include old and deleted files */
+ BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING,
+ &flagsOut);
+
+ if (fileId == 0)
{
- BOX_ERROR("Directory '" << args[0] << "' not found on server.");
+ // error already reported
return;
}
- if(dirID == BackupProtocolClientListDirectory::RootDirectory)
+
+ // Undelete it on the store
+ try
+ {
+ // Undelete object
+ if(flagsOut & BackupProtocolClientListDirectory::Flags_File)
+ {
+ mrConnection.QueryUndeleteFile(parentId, fileId);
+ }
+ else
+ {
+ mrConnection.QueryUndeleteDirectory(fileId);
+ }
+ }
+ catch (BoxException &e)
+ {
+ BOX_ERROR("Failed to undelete object: " <<
+ e.what());
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to undelete object: " <<
+ e.what());
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to undelete object: unknown error");
+ }
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BackupQueries::CommandDelete(const
+// std::vector<std::string> &, const bool *)
+// Purpose: Deletes a file
+// Created: 23/11/03
+//
+// --------------------------------------------------------------------------
+void BackupQueries::CommandDelete(const std::vector<std::string> &args,
+ const bool *opts)
+{
+ if (!mReadWrite)
+ {
+ BOX_ERROR("This command requires a read-write connection. "
+ "Please reconnect with the -w option.");
+ return;
+ }
+
+ // Check arguments
+ if(args.size() != 1)
{
- BOX_ERROR("Cannot undelete the root directory.");
+ BOX_ERROR("Incorrect usage. delete <name>");
return;
}
- // Undelete
- mrConnection.QueryUndeleteDirectory(dirID);
+#ifdef WIN32
+ std::string storeDirEncoded;
+ if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded)) return;
+#else
+ const std::string& storeDirEncoded(args[0]);
+#endif
+
+ // Find object ID somehow
+ int64_t fileId, parentId;
+ std::string fileName;
+ int16_t flagsOut;
+
+ fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName,
+ /* include files and directories */
+ BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING,
+ /* exclude old and deleted files */
+ BackupProtocolClientListDirectory::Flags_OldVersion |
+ BackupProtocolClientListDirectory::Flags_Deleted,
+ &flagsOut);
+
+ if (fileId == 0)
+ {
+ // error already reported
+ return;
+ }
+
+ BackupStoreFilenameClear fn(fileName);
+
+ // Delete it on the store
+ try
+ {
+ // Delete object
+ if(flagsOut & BackupProtocolClientListDirectory::Flags_File)
+ {
+ mrConnection.QueryDeleteFile(parentId, fn);
+ }
+ else
+ {
+ mrConnection.QueryDeleteDirectory(fileId);
+ }
+ }
+ catch (BoxException &e)
+ {
+ BOX_ERROR("Failed to delete object: " <<
+ e.what());
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to delete object: " <<
+ e.what());
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to delete object: unknown error");
+ }
}
diff --git a/bin/bbackupquery/BackupQueries.h b/bin/bbackupquery/BackupQueries.h
index b2ef8cc2..392aa428 100644
--- a/bin/bbackupquery/BackupQueries.h
+++ b/bin/bbackupquery/BackupQueries.h
@@ -14,6 +14,7 @@
#include <string>
#include "BoxTime.h"
+#include "BoxBackupCompareParams.h"
class BackupProtocolClient;
class Configuration;
@@ -30,7 +31,9 @@ class ExcludeList;
class BackupQueries
{
public:
- BackupQueries(BackupProtocolClient &rConnection, const Configuration &rConfiguration);
+ BackupQueries(BackupProtocolClient &rConnection,
+ const Configuration &rConfiguration,
+ bool readWrite);
~BackupQueries();
private:
BackupQueries(const BackupQueries &);
@@ -54,43 +57,282 @@ private:
void CommandCompare(const std::vector<std::string> &args, const bool *opts);
void CommandRestore(const std::vector<std::string> &args, const bool *opts);
void CommandUndelete(const std::vector<std::string> &args, const bool *opts);
- void CommandUsage();
- void CommandUsageDisplayEntry(const char *Name, int64_t Size, int64_t HardLimit, int32_t BlockSize);
+ void CommandDelete(const std::vector<std::string> &args,
+ const bool *opts);
+ void CommandUsage(const bool *opts);
+ void CommandUsageDisplayEntry(const char *Name, int64_t Size,
+ int64_t HardLimit, int32_t BlockSize, bool MachineReadable);
void CommandHelp(const std::vector<std::string> &args);
// Implementations
- void List(int64_t DirID, const std::string &rListRoot, const bool *opts, bool FirstLevel);
- class CompareParams
+ void List(int64_t DirID, const std::string &rListRoot, const bool *opts,
+ bool FirstLevel);
+
+public:
+ class CompareParams : public BoxBackupCompareParams
{
public:
- CompareParams();
- ~CompareParams();
- void DeleteExcludeLists();
- bool mQuickCompare;
+ CompareParams(bool QuickCompare, bool IgnoreExcludes,
+ bool IgnoreAttributes, box_time_t LatestFileUploadTime);
+
bool mQuietCompare;
- bool mIgnoreExcludes;
- bool mIgnoreAttributes;
int mDifferences;
int mDifferencesExplainedByModTime;
int mUncheckedFiles;
int mExcludedDirs;
int mExcludedFiles;
- const ExcludeList *mpExcludeFiles;
- const ExcludeList *mpExcludeDirs;
- box_time_t mLatestFileUploadTime;
+
+ std::string ConvertForConsole(const std::string& rUtf8String)
+ {
+ #ifdef WIN32
+ std::string output;
+
+ if(!ConvertUtf8ToConsole(rUtf8String.c_str(), output))
+ {
+ BOX_WARNING("Character set conversion failed "
+ "on string: " << rUtf8String);
+ return rUtf8String;
+ }
+
+ return output;
+ #else
+ return rUtf8String;
+ #endif
+ }
+
+ virtual void NotifyLocalDirMissing(const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ BOX_WARNING("Local directory '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "does not exist, but remote directory does.");
+ mDifferences ++;
+ }
+
+ virtual void NotifyLocalDirAccessFailed(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ BOX_LOG_SYS_WARNING("Failed to access local directory "
+ "'" << ConvertForConsole(rLocalPath) << "'");
+ mUncheckedFiles ++;
+ }
+
+ virtual void NotifyStoreDirMissingAttributes(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ BOX_WARNING("Store directory '" <<
+ ConvertForConsole(rRemotePath) << "' "
+ "doesn't have attributes.");
+ }
+
+ virtual void NotifyRemoteFileMissing(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath,
+ bool modifiedAfterLastSync)
+ {
+ BOX_WARNING("Local file '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "exists, but remote file '" <<
+ ConvertForConsole(rRemotePath) << "' "
+ "does not.");
+ mDifferences ++;
+
+ if(modifiedAfterLastSync)
+ {
+ mDifferencesExplainedByModTime ++;
+ BOX_INFO("(the file above was modified after "
+ "the last sync time -- might be "
+ "reason for difference)");
+ }
+ }
+
+ virtual void NotifyLocalFileMissing(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ BOX_WARNING("Remote file '" <<
+ ConvertForConsole(rRemotePath) << "' "
+ "exists, but local file '" <<
+ ConvertForConsole(rLocalPath) << "' does not.");
+ mDifferences ++;
+ }
+
+ virtual void NotifyExcludedFileNotDeleted(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ BOX_WARNING("Local file '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "is excluded, but remote file '" <<
+ ConvertForConsole(rRemotePath) << "' "
+ "still exists.");
+ mDifferences ++;
+ }
+
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ BoxException& rException)
+ {
+ BOX_ERROR("Failed to download remote file '" <<
+ ConvertForConsole(rRemotePath) << "': " <<
+ rException.what() << " (" <<
+ rException.GetType() << "/" <<
+ rException.GetSubType() << ")");
+ mUncheckedFiles ++;
+ }
+
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ std::exception& rException)
+ {
+ BOX_ERROR("Failed to download remote file '" <<
+ ConvertForConsole(rRemotePath) << "': " <<
+ rException.what());
+ mUncheckedFiles ++;
+ }
+
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes)
+ {
+ BOX_ERROR("Failed to download remote file '" <<
+ ConvertForConsole(rRemotePath));
+ mUncheckedFiles ++;
+ }
+
+ virtual void NotifyExcludedFile(const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ mExcludedFiles ++;
+ }
+
+ virtual void NotifyExcludedDir(const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ mExcludedDirs ++;
+ }
+
+ virtual void NotifyDirComparing(const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ }
+
+ virtual void NotifyDirCompared(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath,
+ bool HasDifferentAttributes,
+ bool modifiedAfterLastSync)
+ {
+ if(HasDifferentAttributes)
+ {
+ BOX_WARNING("Local directory '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "has different attributes to "
+ "store directory '" <<
+ ConvertForConsole(rRemotePath) << "'.");
+ mDifferences ++;
+
+ if(modifiedAfterLastSync)
+ {
+ mDifferencesExplainedByModTime ++;
+ BOX_INFO("(the directory above was "
+ "modified after the last sync "
+ "time -- might be reason for "
+ "difference)");
+ }
+ }
+ }
+
+ virtual void NotifyFileComparing(const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ }
+
+ virtual void NotifyFileCompared(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ bool HasDifferentAttributes, bool HasDifferentContents,
+ bool ModifiedAfterLastSync, bool NewAttributesApplied)
+ {
+ int NewDifferences = 0;
+
+ if(HasDifferentAttributes)
+ {
+ BOX_WARNING("Local file '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "has different attributes to "
+ "store file '" <<
+ ConvertForConsole(rRemotePath) << "'.");
+ NewDifferences ++;
+ }
+
+ if(HasDifferentContents)
+ {
+ BOX_WARNING("Local file '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "has different contents to "
+ "store file '" <<
+ ConvertForConsole(rRemotePath) << "'.");
+ NewDifferences ++;
+ }
+
+ if(HasDifferentAttributes || HasDifferentContents)
+ {
+ if(ModifiedAfterLastSync)
+ {
+ mDifferencesExplainedByModTime +=
+ NewDifferences;
+ BOX_INFO("(the file above was modified "
+ "after the last sync time -- "
+ "might be reason for difference)");
+ }
+ else if(NewAttributesApplied)
+ {
+ BOX_INFO("(the file above has had new "
+ "attributes applied)\n");
+ }
+ }
+
+ mDifferences += NewDifferences;
+ }
};
- void CompareLocation(const std::string &rLocation, CompareParams &rParams);
- void Compare(const std::string &rStoreDir, const std::string &rLocalDir, CompareParams &rParams);
- void Compare(int64_t DirID, const std::string &rStoreDir, const std::string &rLocalDir, CompareParams &rParams);
+ void CompareLocation(const std::string &rLocation,
+ BoxBackupCompareParams &rParams);
+ void Compare(const std::string &rStoreDir,
+ const std::string &rLocalDir, BoxBackupCompareParams &rParams);
+ void Compare(int64_t DirID, const std::string &rStoreDir,
+ const std::string &rLocalDir, BoxBackupCompareParams &rParams);
+
+public:
+
+ class ReturnCode
+ {
+ public:
+ enum {
+ Command_OK = 0,
+ Compare_Same = 1,
+ Compare_Different,
+ Compare_Error,
+ Command_Error,
+ } Type;
+ };
+
+private:
// Utility functions
- int64_t FindDirectoryObjectID(const std::string &rDirName, bool AllowOldVersion = false,
- bool AllowDeletedDirs = false, std::vector<std::pair<std::string, int64_t> > *pStack = 0);
+ int64_t FindDirectoryObjectID(const std::string &rDirName,
+ bool AllowOldVersion = false, bool AllowDeletedDirs = false,
+ std::vector<std::pair<std::string, int64_t> > *pStack = 0);
+ int64_t FindFileID(const std::string& rNameOrIdString,
+ const bool *opts, int64_t *pDirIdOut,
+ std::string* pFileNameOut, int16_t flagsInclude,
+ int16_t flagsExclude, int16_t* pFlagsOut);
int64_t GetCurrentDirectoryID();
std::string GetCurrentDirectoryName();
void SetReturnCode(int code) {mReturnCode = code;}
private:
+ bool mReadWrite;
BackupProtocolClient &mrConnection;
const Configuration &mrConfiguration;
bool mQuitNow;
diff --git a/bin/bbackupquery/BoxBackupCompareParams.h b/bin/bbackupquery/BoxBackupCompareParams.h
new file mode 100644
index 00000000..c58759a2
--- /dev/null
+++ b/bin/bbackupquery/BoxBackupCompareParams.h
@@ -0,0 +1,107 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: BoxBackupCompareParams.h
+// Purpose: Parameters and notifiers for a compare operation
+// Created: 2008/12/30
+//
+// --------------------------------------------------------------------------
+
+#ifndef BOXBACKUPCOMPAREPARAMS__H
+#define BOXBACKUPCOMPAREPARAMS__H
+
+#include <memory>
+#include <string>
+
+#include "BoxTime.h"
+#include "ExcludeList.h"
+#include "BackupClientMakeExcludeList.h"
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: BoxBackupCompareParams
+// Purpose: Parameters and notifiers for a compare operation
+// Created: 2003/10/10
+//
+// --------------------------------------------------------------------------
+class BoxBackupCompareParams
+{
+private:
+ std::auto_ptr<const ExcludeList> mapExcludeFiles, mapExcludeDirs;
+ bool mQuickCompare;
+ bool mIgnoreExcludes;
+ bool mIgnoreAttributes;
+ box_time_t mLatestFileUploadTime;
+
+public:
+ BoxBackupCompareParams(bool QuickCompare, bool IgnoreExcludes,
+ bool IgnoreAttributes, box_time_t LatestFileUploadTime)
+ : mQuickCompare(QuickCompare),
+ mIgnoreExcludes(IgnoreExcludes),
+ mIgnoreAttributes(IgnoreAttributes),
+ mLatestFileUploadTime(LatestFileUploadTime)
+ { }
+
+ virtual ~BoxBackupCompareParams() { }
+
+ bool QuickCompare() { return mQuickCompare; }
+ bool IgnoreExcludes() { return mIgnoreExcludes; }
+ bool IgnoreAttributes() { return mIgnoreAttributes; }
+ box_time_t LatestFileUploadTime() { return mLatestFileUploadTime; }
+
+ void LoadExcludeLists(const Configuration& rLoc)
+ {
+ mapExcludeFiles.reset(BackupClientMakeExcludeList_Files(rLoc));
+ mapExcludeDirs.reset(BackupClientMakeExcludeList_Dirs(rLoc));
+ }
+ bool IsExcludedFile(const std::string& rLocalPath)
+ {
+ if (!mapExcludeFiles.get()) return false;
+ return mapExcludeFiles->IsExcluded(rLocalPath);
+ }
+ bool IsExcludedDir(const std::string& rLocalPath)
+ {
+ if (!mapExcludeDirs.get()) return false;
+ return mapExcludeDirs->IsExcluded(rLocalPath);
+ }
+
+ virtual void NotifyLocalDirMissing(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyLocalDirAccessFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyStoreDirMissingAttributes(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyRemoteFileMissing(const std::string& rLocalPath,
+ const std::string& rRemotePath,
+ bool modifiedAfterLastSync) = 0;
+ virtual void NotifyLocalFileMissing(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyExcludedFileNotDeleted(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ BoxException& rException) = 0;
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ std::exception& rException) = 0;
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes) = 0;
+ virtual void NotifyExcludedFile(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyExcludedDir(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyDirComparing(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyDirCompared(const std::string& rLocalPath,
+ const std::string& rRemotePath, bool HasDifferentAttributes,
+ bool modifiedAfterLastSync) = 0;
+ virtual void NotifyFileComparing(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyFileCompared(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ bool HasDifferentAttributes, bool HasDifferentContents,
+ bool modifiedAfterLastSync, bool newAttributesApplied) = 0;
+};
+
+#endif // BOXBACKUPCOMPAREPARAMS__H
diff --git a/bin/bbackupquery/bbackupquery.cpp b/bin/bbackupquery/bbackupquery.cpp
index c9d6b715..33860dcf 100644
--- a/bin/bbackupquery/bbackupquery.cpp
+++ b/bin/bbackupquery/bbackupquery.cpp
@@ -37,6 +37,8 @@
#endif
#endif
+#include <cstdlib>
+
#include "MainHelper.h"
#include "BoxPortsAndFiles.h"
#include "BackupDaemonConfigVerify.h"
@@ -61,10 +63,13 @@ void PrintUsageAndExit()
#ifdef WIN32
"[-u] "
#endif
- "\n\t[-c config_file] [-l log_file] [commands]\n"
+ "\n"
+ "\t[-c config_file] [-o log_file] [-O log_file_level]\n"
+ "\t[-l protocol_log_file] [commands]\n"
+ "\n"
"As many commands as you require.\n"
"If commands are multiple words, remember to enclose the command in quotes.\n"
- "Remember to use quit command if you don't want to drop into interactive mode.\n");
+ "Remember to use the quit command unless you want to end up in interactive mode.\n");
exit(1);
}
@@ -90,7 +95,7 @@ int main(int argc, const char *argv[])
#endif
// Really don't want trace statements happening, even in debug mode
- #ifndef NDEBUG
+ #ifndef BOX_RELEASE_BUILD
BoxDebugTraceOn = false;
#endif
@@ -106,24 +111,26 @@ int main(int argc, const char *argv[])
#endif
// Flags
- bool quiet = false;
bool readWrite = false;
- Logging::SetProgramName("Box Backup (bbackupquery)");
+ Logging::SetProgramName("bbackupquery");
- #ifdef NDEBUG
+ #ifdef BOX_RELEASE_BUILD
int masterLevel = Log::NOTICE; // need an int to do math with
#else
int masterLevel = Log::INFO; // need an int to do math with
#endif
#ifdef WIN32
- const char* validOpts = "qvwuc:l:";
+ const char* validOpts = "qvwuc:l:o:O:W:";
bool unicodeConsole = false;
#else
- const char* validOpts = "qvwc:l:";
+ const char* validOpts = "qvwc:l:o:O:W:";
#endif
+ std::string fileLogFile;
+ Log::Level fileLogLevel = Log::INVALID;
+
// See if there's another entry on the command line
int c;
while((c = getopt(argc, (char * const *)argv, validOpts)) != -1)
@@ -132,9 +139,6 @@ int main(int argc, const char *argv[])
{
case 'q':
{
- // Quiet mode
- quiet = true;
-
if(masterLevel == Log::NOTHING)
{
BOX_FATAL("Too many '-q': "
@@ -159,6 +163,17 @@ int main(int argc, const char *argv[])
}
break;
+ case 'W':
+ {
+ masterLevel = Logging::GetNamedLevel(optarg);
+ if (masterLevel == Log::INVALID)
+ {
+ BOX_FATAL("Invalid logging level");
+ return 2;
+ }
+ }
+ break;
+
case 'w':
// Read/write mode
readWrite = true;
@@ -174,8 +189,24 @@ int main(int argc, const char *argv[])
logFile = ::fopen(optarg, "w");
if(logFile == 0)
{
- BOX_ERROR("Failed to open log file '" <<
- optarg << "': " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to open log file "
+ "'" << optarg << "'");
+ }
+ break;
+
+ case 'o':
+ fileLogFile = optarg;
+ fileLogLevel = Log::EVERYTHING;
+ break;
+
+ case 'O':
+ {
+ fileLogLevel = Logging::GetNamedLevel(optarg);
+ if (fileLogLevel == Log::INVALID)
+ {
+ BOX_FATAL("Invalid logging level");
+ return 2;
+ }
}
break;
@@ -196,6 +227,19 @@ int main(int argc, const char *argv[])
Logging::SetGlobalLevel((Log::Level)masterLevel);
+ std::auto_ptr<FileLogger> fileLogger;
+ if (fileLogLevel != Log::INVALID)
+ {
+ fileLogger.reset(new FileLogger(fileLogFile, fileLogLevel));
+ }
+
+ bool quiet = false;
+ if (masterLevel < Log::NOTICE)
+ {
+ // Quiet mode
+ quiet = true;
+ }
+
// Print banner?
if(!quiet)
{
@@ -260,7 +304,9 @@ int main(int argc, const char *argv[])
// 2. Connect to server
if(!quiet) BOX_INFO("Connecting to store...");
SocketStreamTLS socket;
- socket.Open(tlsContext, Socket::TypeINET, conf.GetKeyValue("StoreHostname").c_str(), BOX_PORT_BBSTORED);
+ socket.Open(tlsContext, Socket::TypeINET,
+ conf.GetKeyValue("StoreHostname").c_str(),
+ conf.GetKeyValueInt("StorePort"));
// 3. Make a protocol, and handshake
if(!quiet) BOX_INFO("Handshake with store...");
@@ -291,7 +337,7 @@ int main(int argc, const char *argv[])
if(!quiet) printf("Login complete.\n\nType \"help\" for a list of commands.\n\n");
// Set up a context for our work
- BackupQueries context(connection, conf);
+ BackupQueries context(connection, conf, readWrite);
// Start running commands... first from the command line
{
@@ -377,7 +423,8 @@ int main(int argc, const char *argv[])
#ifdef WIN32
// Clean up our sockets
- WSACleanup();
+ // FIXME we should do this, but I get an abort() when I try
+ // WSACleanup();
#endif
MAINHELPER_END
diff --git a/bin/bbackupquery/documentation.txt b/bin/bbackupquery/documentation.txt
index 42217edc..d32bf200 100644
--- a/bin/bbackupquery/documentation.txt
+++ b/bin/bbackupquery/documentation.txt
@@ -116,7 +116,7 @@ compare <store-dir-name> <local-dir-name>
This can be used for automated tests.
<
-> restore [-d] [-r] [-i] <directory-name> <local-directory-name>
+> restore [-drif] <directory-name> <local-directory-name>
Restores a directory to the local disc. The local directory specified
must not exist (unless a previous restore is being restarted).
@@ -126,6 +126,7 @@ compare <store-dir-name> <local-dir-name>
-d -- restore a deleted directory or deleted files inside
-r -- resume an interrupted restoration
-i -- directory name is actually an ID
+ -f -- force restore to continue if errors are encountered
If a restore operation is interrupted for any reason, it can be restarted
using the -r switch. Restore progress information is saved in a file at
@@ -141,10 +142,12 @@ compare <store-dir-name> <local-dir-name>
stored format, which is encrypted and compressed.
<
-> usage
+> usage [-m]
Show space used on the server for this account.
+ -m -- display the output in machine-readable form
+
Used: Total amount of space used on the server.
Old files: Space used by old files
Deleted files: Space used by deleted files
@@ -158,6 +161,21 @@ compare <store-dir-name> <local-dir-name>
files is near zero.
<
+> undelete <directory-name>
+undelete -i <object-id>
+
+ Removes the deleted flag from the specified directory name (in the
+ current directory) or hex object ID. Be careful not to use this
+ command where a directory already exists with the same name which is
+ not marked as deleted.
+<
+
+> delete <file-name>
+
+ Sets the deleted flag on the specified file name (in the current
+ directory, or with a relative path).
+<
+
> quit
End session and exit.
diff --git a/bin/bbstoreaccounts/bbstoreaccounts.cpp b/bin/bbstoreaccounts/bbstoreaccounts.cpp
index 6f079d30..71114ef4 100644
--- a/bin/bbstoreaccounts/bbstoreaccounts.cpp
+++ b/bin/bbstoreaccounts/bbstoreaccounts.cpp
@@ -9,12 +9,17 @@
#include "Box.h"
-#include <unistd.h>
+#include <limits.h>
#include <stdio.h>
+#include <unistd.h>
+
#include <sys/types.h>
-#include <limits.h>
-#include <vector>
+
#include <algorithm>
+#include <cstring>
+#include <iostream>
+#include <ostream>
+#include <vector>
#include "BoxPortsAndFiles.h"
#include "BackupStoreConfigVerify.h"
@@ -27,12 +32,15 @@
#include "NamedLock.h"
#include "UnixUser.h"
#include "BackupStoreCheck.h"
+#include "Utils.h"
#include "MemLeakFindOn.h"
// max size of soft limit as percent of hard limit
#define MAX_SOFT_LIMIT_SIZE 97
+bool sMachineReadableOutput = false;
+
void CheckSoftHardLimits(int64_t SoftLimit, int64_t HardLimit)
{
if(SoftLimit >= HardLimit)
@@ -62,22 +70,11 @@ int BlockSizeOfDiscSet(int DiscSet)
return controller.GetDiscSet(DiscSet).GetBlockSize();
}
-const char *BlockSizeToString(int64_t Blocks, int DiscSet)
+std::string BlockSizeToString(int64_t Blocks, int64_t MaxBlocks, int DiscSet)
{
- // Not reentrant, nor can be used in the same function call twice, etc.
- static char string[256];
-
- // Work out size in Mb.
- double mb = (Blocks * BlockSizeOfDiscSet(DiscSet)) / (1024.0*1024.0);
-
- // Format string
-#ifdef WIN32
- sprintf(string, "%I64d (%.2fMb)", Blocks, mb);
-#else
- sprintf(string, "%lld (%.2fMb)", Blocks, mb);
-#endif
-
- return string;
+ return FormatUsageBar(Blocks, Blocks * BlockSizeOfDiscSet(DiscSet),
+ MaxBlocks * BlockSizeOfDiscSet(DiscSet),
+ sMachineReadableOutput);
}
int64_t SizeStringToBlocks(const char *string, int DiscSet)
@@ -118,7 +115,7 @@ int64_t SizeStringToBlocks(const char *string, int DiscSet)
default:
BOX_FATAL(string << " has an invalid units specifier "
- "(use B for blocks, M for Mb, G for Gb, eg 2Gb)");
+ "(use B for blocks, M for MB, G for GB, eg 2GB)");
exit(1);
break;
}
@@ -211,7 +208,9 @@ int SetLimit(Configuration &rConfig, const std::string &rUsername, int32_t ID, c
int AccountInfo(Configuration &rConfig, int32_t ID)
{
// Load in the account database
- std::auto_ptr<BackupStoreAccountDatabase> db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str()));
+ std::auto_ptr<BackupStoreAccountDatabase> db(
+ BackupStoreAccountDatabase::Read(
+ rConfig.GetKeyValue("AccountDatabase").c_str()));
// Exists?
if(!db->EntryExists(ID))
@@ -226,18 +225,34 @@ int AccountInfo(Configuration &rConfig, int32_t ID)
std::string rootDir;
int discSet;
acc.GetAccountRoot(ID, rootDir, discSet);
- std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, rootDir, discSet, true /* ReadOnly */));
+ std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID,
+ rootDir, discSet, true /* ReadOnly */));
// Then print out lots of info
- printf(" Account ID: %08x\n", ID);
- printf(" Last object ID: %lld\n", info->GetLastObjectIDUsed());
- printf(" Blocks used: %s\n", BlockSizeToString(info->GetBlocksUsed(), discSet));
- printf(" Blocks used by old files: %s\n", BlockSizeToString(info->GetBlocksInOldFiles(), discSet));
- printf("Blocks used by deleted files: %s\n", BlockSizeToString(info->GetBlocksInDeletedFiles(), discSet));
- printf(" Blocks used by directories: %s\n", BlockSizeToString(info->GetBlocksInDirectories(), discSet));
- printf(" Block soft limit: %s\n", BlockSizeToString(info->GetBlocksSoftLimit(), discSet));
- printf(" Block hard limit: %s\n", BlockSizeToString(info->GetBlocksHardLimit(), discSet));
- printf(" Client store marker: %lld\n", info->GetClientStoreMarker());
+ std::cout << FormatUsageLineStart("Account ID", sMachineReadableOutput) <<
+ BOX_FORMAT_ACCOUNT(ID) << std::endl;
+ std::cout << FormatUsageLineStart("Last object ID", sMachineReadableOutput) <<
+ BOX_FORMAT_OBJECTID(info->GetLastObjectIDUsed()) << std::endl;
+ std::cout << FormatUsageLineStart("Used", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksUsed(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Old files", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksInOldFiles(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Deleted files", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksInDeletedFiles(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Directories", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksInDirectories(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Soft limit", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksSoftLimit(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Hard limit", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksHardLimit(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Client store marker", sMachineReadableOutput) <<
+ info->GetLastObjectIDUsed() << std::endl;
return 0;
}
@@ -409,8 +424,32 @@ int CreateAccount(Configuration &rConfig, const std::string &rUsername, int32_t
void PrintUsageAndExit()
{
- printf("Usage: bbstoreaccounts [-c config_file] action account_id [args]\nAccount ID is integer specified in hex\n");
- exit(1);
+ printf(
+"Usage: bbstoreaccounts [-c config_file] action account_id [args]\n"
+"Account ID is integer specified in hex\n"
+"\n"
+"Commands (and arguments):\n"
+" create <account> <discnum> <softlimit> <hardlimit>\n"
+" Creates the specified account number (in hex with no 0x) on the\n"
+" specified raidfile disc set number (see raidfile.conf for valid\n"
+" set numbers) with the specified soft and hard limits (in blocks\n"
+" if suffixed with B, MB with M, GB with G)\n"
+" info [-m] <account>\n"
+" Prints information about the specified account including number\n"
+" of blocks used. The -m option enable machine-readable output.\n"
+" setlimit <accounts> <softlimit> <hardlimit>\n"
+" Changes the limits of the account as specified. Numbers are\n"
+" interpreted as for the 'create' command (suffixed with B, M or G)\n"
+" delete <account> [yes]\n"
+" Deletes the specified account. Prompts for confirmation unless\n"
+" the optional 'yes' parameter is provided.\n"
+" check <account> [fix] [quiet]\n"
+" Checks the specified account for errors. If the 'fix' option is\n"
+" provided, any errors discovered that can be fixed automatically\n"
+" will be fixed. If the 'quiet' option is provided, less output is\n"
+" produced.\n"
+ );
+ exit(2);
}
int main(int argc, const char *argv[])
@@ -420,6 +459,8 @@ int main(int argc, const char *argv[])
MAINHELPER_START
+ Logging::SetProgramName("bbstoreaccounts");
+
// Filename for configuration file?
std::string configFilename;
@@ -431,7 +472,7 @@ int main(int argc, const char *argv[])
// See if there's another entry on the command line
int c;
- while((c = getopt(argc, (char * const *)argv, "c:")) != -1)
+ while((c = getopt(argc, (char * const *)argv, "c:m")) != -1)
{
switch(c)
{
@@ -440,6 +481,11 @@ int main(int argc, const char *argv[])
configFilename = optarg;
break;
+ case 'm':
+ // enable machine readable output
+ sMachineReadableOutput = true;
+ break;
+
case '?':
default:
PrintUsageAndExit();
diff --git a/bin/bbstored/BBStoreDHousekeeping.cpp b/bin/bbstored/BBStoreDHousekeeping.cpp
index 16a1432a..1c1767ca 100644
--- a/bin/bbstored/BBStoreDHousekeeping.cpp
+++ b/bin/bbstored/BBStoreDHousekeeping.cpp
@@ -46,6 +46,12 @@ void BackupStoreDaemon::HousekeepingProcess()
{
RunHousekeepingIfNeeded();
+ // Stop early?
+ if(StopRun())
+ {
+ break;
+ }
+
// Calculate how long should wait before doing the next
// housekeeping run
int64_t timeNow = GetCurrentBoxTime();
@@ -155,7 +161,11 @@ void BackupStoreDaemon::RunHousekeepingIfNeeded()
void BackupStoreDaemon::OnIdle()
{
- #ifdef WIN32
+ if (!IsSingleProcess())
+ {
+ return;
+ }
+
if (!mHousekeepingInited)
{
HousekeepingInit();
@@ -163,7 +173,6 @@ void BackupStoreDaemon::OnIdle()
}
RunHousekeepingIfNeeded();
- #endif
}
// --------------------------------------------------------------------------
@@ -193,7 +202,8 @@ bool BackupStoreDaemon::CheckForInterProcessMsg(int AccountNum, int MaximumWaitT
std::string line;
if(mInterProcessComms.GetLine(line, false /* no pre-processing */, MaximumWaitTime))
{
- TRACE1("Housekeeping received command '%s' over interprocess comms\n", line.c_str());
+ BOX_TRACE("Housekeeping received command '" << line <<
+ "' over interprocess comms");
int account = 0;
diff --git a/bin/bbstored/BackupCommands.cpp b/bin/bbstored/BackupCommands.cpp
index bca52c04..38cda234 100644
--- a/bin/bbstored/BackupCommands.cpp
+++ b/bin/bbstored/BackupCommands.cpp
@@ -13,25 +13,25 @@
#include <sstream>
#include "autogen_BackupProtocolServer.h"
+#include "autogen_RaidFileException.h"
#include "BackupConstants.h"
-#include "BackupContext.h"
-#include "CollectInBufferStream.h"
+#include "BackupStoreContext.h"
+#include "BackupStoreConstants.h"
#include "BackupStoreDirectory.h"
#include "BackupStoreException.h"
#include "BackupStoreFile.h"
-#include "StreamableMemBlock.h"
-#include "BackupStoreConstants.h"
-#include "RaidFileController.h"
#include "BackupStoreInfo.h"
-#include "RaidFileController.h"
+#include "BufferedStream.h"
+#include "CollectInBufferStream.h"
#include "FileStream.h"
#include "InvisibleTempFileStream.h"
-#include "BufferedStream.h"
+#include "RaidFileController.h"
+#include "StreamableMemBlock.h"
#include "MemLeakFindOn.h"
#define CHECK_PHASE(phase) \
- if(rContext.GetPhase() != BackupContext::phase) \
+ if(rContext.GetPhase() != BackupStoreContext::phase) \
{ \
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError( \
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_NotInRightProtocolPhase)); \
@@ -47,12 +47,12 @@
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerVersion::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerVersion::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Return the current version, or an error if the requested version isn't allowed
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerVersion::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerVersion::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Version)
@@ -64,7 +64,7 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerVersion::DoCommand(BackupProto
}
// Mark the next phase
- rContext.SetPhase(BackupContext::Phase_Login);
+ rContext.SetPhase(BackupStoreContext::Phase_Login);
// Return our version
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerVersion(BACKUP_STORE_SERVER_VERSION));
@@ -73,12 +73,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerVersion::DoCommand(BackupProto
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerLogin::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerLogin::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Return the current version, or an error if the requested version isn't allowed
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerLogin::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerLogin::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Login)
@@ -131,7 +131,7 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerLogin::DoCommand(BackupProtoco
int64_t clientStoreMarker = rContext.GetClientStoreMarker();
// Mark the next phase
- rContext.SetPhase(BackupContext::Phase_Commands);
+ rContext.SetPhase(BackupStoreContext::Phase_Commands);
// Log login
BOX_NOTICE("Login from Client ID " <<
@@ -151,12 +151,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerLogin::DoCommand(BackupProtoco
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerFinished::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerFinished::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Marks end of conversation (Protocol framework handles this)
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerFinished::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerFinished::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
BOX_NOTICE("Session finished for Client ID " <<
BOX_FORMAT_ACCOUNT(rContext.GetClientID()));
@@ -172,47 +172,72 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerFinished::DoCommand(BackupProt
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerListDirectory::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerListDirectory::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Command to list a directory
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerListDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerListDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
- // Ask the context for a directory
- const BackupStoreDirectory &rdir(rContext.GetDirectory(mObjectID));
-
// Store the listing to a stream
std::auto_ptr<CollectInBufferStream> stream(new CollectInBufferStream);
- rdir.WriteToStream(*stream, mFlagsMustBeSet, mFlagsNotToBeSet, mSendAttributes,
- false /* never send dependency info to the client */);
+
+ try
+ {
+ // Ask the context for a directory
+ const BackupStoreDirectory &rdir(
+ rContext.GetDirectory(mObjectID));
+ rdir.WriteToStream(*stream, mFlagsMustBeSet,
+ mFlagsNotToBeSet, mSendAttributes,
+ false /* never send dependency info to the client */);
+ }
+ catch (RaidFileException &e)
+ {
+ if (e.GetSubType() == RaidFileException::RaidFileDoesntExist)
+ {
+ return std::auto_ptr<ProtocolObject>(
+ new BackupProtocolServerError(
+ BackupProtocolServerError::ErrorType,
+ BackupProtocolServerError::Err_DoesNotExist));
+ }
+ throw;
+ }
+
stream->SetForReading();
// Get the protocol to send the stream
rProtocol.SendStreamAfterCommand(stream.release());
- return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mObjectID));
+ return std::auto_ptr<ProtocolObject>(
+ new BackupProtocolServerSuccess(mObjectID));
}
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerStoreFile::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerStoreFile::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Command to store a file on the server
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerStoreFile::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerStoreFile::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
+
+ std::auto_ptr<ProtocolObject> hookResult =
+ rContext.StartCommandHook(*this);
+ if(hookResult.get())
+ {
+ return hookResult;
+ }
// Check that the diff from file actually exists, if it's specified
if(mDiffFromFileID != 0)
{
- if(!rContext.ObjectExists(mDiffFromFileID, BackupContext::ObjectExists_File))
+ if(!rContext.ObjectExists(mDiffFromFileID, BackupStoreContext::ObjectExists_File))
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DiffFromFileDoesNotExist));
@@ -257,12 +282,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerStoreFile::DoCommand(BackupPro
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetObject::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerGetObject::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Command to get an arbitary object from the server
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetObject::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetObject::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -285,13 +310,13 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetObject::DoCommand(BackupPro
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetFile::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerGetFile::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Command to get an file object from the server -- may have to do a bit of
// work to get the object.
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetFile::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetFile::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -457,12 +482,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetFile::DoCommand(BackupProto
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerCreateDirectory::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerCreateDirectory::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Create directory command
// Created: 2003/09/04
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerCreateDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerCreateDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -500,12 +525,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerCreateDirectory::DoCommand(Bac
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerChangeDirAttributes::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerChangeDirAttributes::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Change attributes on directory
// Created: 2003/09/06
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerChangeDirAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerChangeDirAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -528,12 +553,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerChangeDirAttributes::DoCommand
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerSetReplacementFileAttributes::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerSetReplacementFileAttributes::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Change attributes on directory
// Created: 2003/09/06
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerSetReplacementFileAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerSetReplacementFileAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -563,12 +588,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerSetReplacementFileAttributes::
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Delete a file
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -585,12 +610,36 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteFile::DoCommand(BackupPr
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerUndeleteFile::DoCommand(
+// BackupProtocolServer &, BackupStoreContext &)
+// Purpose: Undelete a file
+// Created: 2008-09-12
+//
+// --------------------------------------------------------------------------
+std::auto_ptr<ProtocolObject> BackupProtocolServerUndeleteFile::DoCommand(
+ BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
+{
+ CHECK_PHASE(Phase_Commands)
+ CHECK_WRITEABLE_SESSION
+
+ // Context handles this
+ bool result = rContext.UndeleteFile(mObjectID, mInDirectory);
+
+ // return the object ID or zero for not found
+ return std::auto_ptr<ProtocolObject>(
+ new BackupProtocolServerSuccess(result ? mObjectID : 0));
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Delete a directory
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -613,12 +662,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteDirectory::DoCommand(Bac
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Undelete a directory
// Created: 23/11/03
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -640,12 +689,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerUndeleteDirectory::DoCommand(B
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Command to set the client's store marker
// Created: 2003/10/29
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -661,12 +710,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerSetClientStoreMarker::DoComman
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Command to move an object from one directory to another
// Created: 2003/11/12
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -704,12 +753,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerMoveObject::DoCommand(BackupPr
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Command to find the name of an object
// Created: 12/11/03
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -730,7 +779,7 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetObjectName::DoCommand(Backu
do
{
// Check the directory really exists
- if(!rContext.ObjectExists(dirID, BackupContext::ObjectExists_Directory))
+ if(!rContext.ObjectExists(dirID, BackupStoreContext::ObjectExists_Directory))
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerObjectName(BackupProtocolServerObjectName::NumNameElements_ObjectDoesntExist, 0, 0, 0));
}
@@ -795,12 +844,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetObjectName::DoCommand(Backu
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Get the block index from a file, by ID
// Created: 19/1/04
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -821,12 +870,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByID::DoCommand(B
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Get the block index from a file, by name within a directory
// Created: 19/1/04
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -873,12 +922,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByName::DoCommand
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Return the amount of disc space used
// Created: 19/4/04
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -904,12 +953,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetAccountUsage::DoCommand(Bac
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Return the amount of disc space used
// Created: 19/4/04
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
diff --git a/bin/bbstored/BackupConstants.h b/bin/bbstored/BackupConstants.h
index 515b3bcd..19d06a15 100644
--- a/bin/bbstored/BackupConstants.h
+++ b/bin/bbstored/BackupConstants.h
@@ -10,8 +10,6 @@
#ifndef BACKUPCONSTANTS__H
#define BACKUPCONSTANTS__H
-#define BACKUP_STORE_DEFAULT_ACCOUNT_DATABASE_FILE "/etc/box/backupstoreaccounts"
-
// 15 minutes to timeout (milliseconds)
#define BACKUP_STORE_TIMEOUT (15*60*1000)
diff --git a/bin/bbstored/BackupContext.cpp b/bin/bbstored/BackupStoreContext.cpp
index 16388099..990be05d 100644
--- a/bin/bbstored/BackupContext.cpp
+++ b/bin/bbstored/BackupStoreContext.cpp
@@ -1,7 +1,7 @@
// --------------------------------------------------------------------------
//
// File
-// Name: BackupContext.cpp
+// Name: BackupStoreContext.cpp
// Purpose: Context for backup store server
// Created: 2003/08/20
//
@@ -11,7 +11,7 @@
#include <stdio.h>
-#include "BackupContext.h"
+#include "BackupStoreContext.h"
#include "RaidFileWrite.h"
#include "RaidFileRead.h"
#include "BackupStoreDirectory.h"
@@ -33,7 +33,7 @@
// Maximum number of directories to keep in the cache
// When the cache is bigger than this, everything gets
// deleted.
-#ifdef NDEBUG
+#ifdef BOX_RELEASE_BUILD
#define MAX_CACHE_SIZE 32
#else
#define MAX_CACHE_SIZE 2
@@ -48,31 +48,33 @@
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::BackupContext()
+// Name: BackupStoreContext::BackupStoreContext()
// Purpose: Constructor
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-BackupContext::BackupContext(int32_t ClientID, BackupStoreDaemon &rDaemon)
+BackupStoreContext::BackupStoreContext(int32_t ClientID,
+ HousekeepingInterface &rDaemon)
: mClientID(ClientID),
mrDaemon(rDaemon),
mProtocolPhase(Phase_START),
mClientHasAccount(false),
mStoreDiscSet(-1),
mReadOnly(true),
- mSaveStoreInfoDelay(STORE_INFO_SAVE_DELAY)
+ mSaveStoreInfoDelay(STORE_INFO_SAVE_DELAY),
+ mpTestHook(NULL)
{
}
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::~BackupContext()
+// Name: BackupStoreContext::~BackupStoreContext()
// Purpose: Destructor
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-BackupContext::~BackupContext()
+BackupStoreContext::~BackupStoreContext()
{
// Delete the objects in the cache
for(std::map<int64_t, BackupStoreDirectory*>::iterator i(mDirectoryCache.begin()); i != mDirectoryCache.end(); ++i)
@@ -85,12 +87,12 @@ BackupContext::~BackupContext()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::CleanUp()
+// Name: BackupStoreContext::CleanUp()
// Purpose: Clean up after a connection
// Created: 16/12/03
//
// --------------------------------------------------------------------------
-void BackupContext::CleanUp()
+void BackupStoreContext::CleanUp()
{
// Make sure the store info is saved, if it has been loaded, isn't read only and has been modified
if(mpStoreInfo.get() && !(mpStoreInfo->IsReadOnly()) && mpStoreInfo->IsModified())
@@ -102,12 +104,12 @@ void BackupContext::CleanUp()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::ReceivedFinishCommand()
+// Name: BackupStoreContext::ReceivedFinishCommand()
// Purpose: Called when the finish command is received by the protocol
// Created: 16/12/03
//
// --------------------------------------------------------------------------
-void BackupContext::ReceivedFinishCommand()
+void BackupStoreContext::ReceivedFinishCommand()
{
if(!mReadOnly && mpStoreInfo.get())
{
@@ -120,12 +122,12 @@ void BackupContext::ReceivedFinishCommand()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::AttemptToGetWriteLock()
+// Name: BackupStoreContext::AttemptToGetWriteLock()
// Purpose: Attempt to get a write lock for the store, and if so, unset the read only flags
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
-bool BackupContext::AttemptToGetWriteLock()
+bool BackupStoreContext::AttemptToGetWriteLock()
{
// Make the filename of the write lock file
std::string writeLockFile;
@@ -166,12 +168,12 @@ bool BackupContext::AttemptToGetWriteLock()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::LoadStoreInfo()
+// Name: BackupStoreContext::LoadStoreInfo()
// Purpose: Load the store info from disc
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-void BackupContext::LoadStoreInfo()
+void BackupStoreContext::LoadStoreInfo()
{
if(mpStoreInfo.get() != 0)
{
@@ -195,12 +197,12 @@ void BackupContext::LoadStoreInfo()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::SaveStoreInfo(bool)
+// Name: BackupStoreContext::SaveStoreInfo(bool)
// Purpose: Potentially delayed saving of the store info
// Created: 16/12/03
//
// --------------------------------------------------------------------------
-void BackupContext::SaveStoreInfo(bool AllowDelay)
+void BackupStoreContext::SaveStoreInfo(bool AllowDelay)
{
if(mpStoreInfo.get() == 0)
{
@@ -233,13 +235,13 @@ void BackupContext::SaveStoreInfo(bool AllowDelay)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::MakeObjectFilename(int64_t, std::string &, bool)
+// Name: BackupStoreContext::MakeObjectFilename(int64_t, std::string &, bool)
// Purpose: Create the filename of an object in the store, optionally creating the
// containing directory if it doesn't already exist.
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
-void BackupContext::MakeObjectFilename(int64_t ObjectID, std::string &rOutput, bool EnsureDirectoryExists)
+void BackupStoreContext::MakeObjectFilename(int64_t ObjectID, std::string &rOutput, bool EnsureDirectoryExists)
{
// Delegate to utility function
StoreStructure::MakeObjectFilename(ObjectID, mStoreRoot, mStoreDiscSet, rOutput, EnsureDirectoryExists);
@@ -249,7 +251,7 @@ void BackupContext::MakeObjectFilename(int64_t ObjectID, std::string &rOutput, b
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::GetDirectoryInternal(int64_t)
+// Name: BackupStoreContext::GetDirectoryInternal(int64_t)
// Purpose: Return a reference to a directory. Valid only until the
// next time a function which affects directories is called.
// Mainly this funciton, and creation of files.
@@ -257,7 +259,7 @@ void BackupContext::MakeObjectFilename(int64_t ObjectID, std::string &rOutput, b
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
-BackupStoreDirectory &BackupContext::GetDirectoryInternal(int64_t ObjectID)
+BackupStoreDirectory &BackupStoreContext::GetDirectoryInternal(int64_t ObjectID)
{
// Get the filename
std::string filename;
@@ -277,9 +279,17 @@ BackupStoreDirectory &BackupContext::GetDirectoryInternal(int64_t ObjectID)
if(revID == item->second->GetRevisionID())
{
// Looks good... return the cached object
+ BOX_TRACE("Returning object " <<
+ BOX_FORMAT_OBJECTID(ObjectID) <<
+ " from cache, modtime = " << revID);
return *(item->second);
}
+ BOX_TRACE("Refreshing object " <<
+ BOX_FORMAT_OBJECTID(ObjectID) <<
+ " in cache, modtime changed from " <<
+ item->second->GetRevisionID() << " to " << revID);
+
// Delete this cached object
delete item->second;
mDirectoryCache.erase(item);
@@ -335,12 +345,12 @@ BackupStoreDirectory &BackupContext::GetDirectoryInternal(int64_t ObjectID)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::AllocateObjectID()
+// Name: BackupStoreContext::AllocateObjectID()
// Purpose: Allocate a new object ID, tolerant of failures to save store info
// Created: 16/12/03
//
// --------------------------------------------------------------------------
-int64_t BackupContext::AllocateObjectID()
+int64_t BackupStoreContext::AllocateObjectID()
{
if(mpStoreInfo.get() == 0)
{
@@ -374,7 +384,8 @@ int64_t BackupContext::AllocateObjectID()
// Mark that the store info should be saved as soon as possible
mSaveStoreInfoDelay = 0;
- TRACE1("When allocating object ID, found that %lld is already in use\n", id);
+ BOX_WARNING("When allocating object ID, found that " <<
+ BOX_FORMAT_OBJECTID(id) << " is already in use");
}
THROW_EXCEPTION(BackupStoreException, CouldNotFindUnusedIDDuringAllocation)
@@ -384,13 +395,13 @@ int64_t BackupContext::AllocateObjectID()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::AddFile(IOStream &, int64_t, int64_t, int64_t, const BackupStoreFilename &, bool)
+// Name: BackupStoreContext::AddFile(IOStream &, int64_t, int64_t, int64_t, const BackupStoreFilename &, bool)
// Purpose: Add a file to the store, from a given stream, into a specified directory.
// Returns object ID of the new file.
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-int64_t BackupContext::AddFile(IOStream &rFile, int64_t InDirectory, int64_t ModificationTime,
+int64_t BackupStoreContext::AddFile(IOStream &rFile, int64_t InDirectory, int64_t ModificationTime,
int64_t AttributesHash, int64_t DiffFromFileID, const BackupStoreFilename &rFilename,
bool MarkFileWithSameNameAsOldVersions)
{
@@ -672,18 +683,19 @@ int64_t BackupContext::AddFile(IOStream &rFile, int64_t InDirectory, int64_t Mod
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &)
+// Name: BackupStoreContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &)
// Purpose: Deletes a file, returning true if the file existed. Object ID returned too, set to zero if not found.
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
-bool BackupContext::DeleteFile(const BackupStoreFilename &rFilename, int64_t InDirectory, int64_t &rObjectIDOut)
+bool BackupStoreContext::DeleteFile(const BackupStoreFilename &rFilename, int64_t InDirectory, int64_t &rObjectIDOut)
{
// Essential checks!
if(mpStoreInfo.get() == 0)
{
THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
}
+
if(mReadOnly)
{
THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
@@ -748,23 +760,102 @@ bool BackupContext::DeleteFile(const BackupStoreFilename &rFilename, int64_t InD
RemoveDirectoryFromCache(InDirectory);
throw;
}
-
return fileExisted;
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BackupStoreContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &)
+// Purpose: Deletes a file, returning true if the file existed. Object ID returned too, set to zero if not found.
+// Created: 2003/10/21
+//
+// --------------------------------------------------------------------------
+bool BackupStoreContext::UndeleteFile(int64_t ObjectID, int64_t InDirectory)
+{
+ // Essential checks!
+ if(mpStoreInfo.get() == 0)
+ {
+ THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
+ }
+
+ if(mReadOnly)
+ {
+ THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
+ }
+
+ // Find the directory the file is in (will exception if it fails)
+ BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory));
+
+ // Setup flags
+ bool fileExisted = false;
+ bool madeChanges = false;
+
+ // Count of deleted blocks
+ int64_t blocksDel = 0;
+
+ try
+ {
+ // Iterate through directory, only looking at files which have been deleted
+ BackupStoreDirectory::Iterator i(dir);
+ BackupStoreDirectory::Entry *e = 0;
+ while((e = i.Next(BackupStoreDirectory::Entry::Flags_File |
+ BackupStoreDirectory::Entry::Flags_Deleted, 0)) != 0)
+ {
+ // Compare name
+ if(e->GetObjectID() == ObjectID)
+ {
+ // Check that it's definitely already deleted
+ ASSERT((e->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) != 0);
+ // Clear deleted flag
+ e->RemoveFlags(BackupStoreDirectory::Entry::Flags_Deleted);
+ // Mark as made a change
+ madeChanges = true;
+ blocksDel -= e->GetSizeInBlocks();
+
+ // Is this the last version?
+ if((e->GetFlags() & BackupStoreDirectory::Entry::Flags_OldVersion) == 0)
+ {
+ // Yes. It's been found.
+ fileExisted = true;
+ }
+ }
+ }
+
+ // Save changes?
+ if(madeChanges)
+ {
+ // Save the directory back
+ SaveDirectory(dir, InDirectory);
+
+ // Modify the store info, and write
+ mpStoreInfo->ChangeBlocksInDeletedFiles(blocksDel);
+
+ // Maybe postponed save of store info
+ SaveStoreInfo();
+ }
+ }
+ catch(...)
+ {
+ RemoveDirectoryFromCache(InDirectory);
+ throw;
+ }
+
+ return fileExisted;
+}
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::RemoveDirectoryFromCache(int64_t)
+// Name: BackupStoreContext::RemoveDirectoryFromCache(int64_t)
// Purpose: Remove directory from cache
// Created: 2003/09/04
//
// --------------------------------------------------------------------------
-void BackupContext::RemoveDirectoryFromCache(int64_t ObjectID)
+void BackupStoreContext::RemoveDirectoryFromCache(int64_t ObjectID)
{
std::map<int64_t, BackupStoreDirectory*>::iterator item(mDirectoryCache.find(ObjectID));
if(item != mDirectoryCache.end())
@@ -780,12 +871,12 @@ void BackupContext::RemoveDirectoryFromCache(int64_t ObjectID)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::SaveDirectory(BackupStoreDirectory &, int64_t)
+// Name: BackupStoreContext::SaveDirectory(BackupStoreDirectory &, int64_t)
// Purpose: Save directory back to disc, update time in cache
// Created: 2003/09/04
//
// --------------------------------------------------------------------------
-void BackupContext::SaveDirectory(BackupStoreDirectory &rDir, int64_t ObjectID)
+void BackupStoreContext::SaveDirectory(BackupStoreDirectory &rDir, int64_t ObjectID)
{
if(mpStoreInfo.get() == 0)
{
@@ -842,12 +933,12 @@ void BackupContext::SaveDirectory(BackupStoreDirectory &rDir, int64_t ObjectID)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::AddDirectory(int64_t, const BackupStoreFilename &, bool &)
+// Name: BackupStoreContext::AddDirectory(int64_t, const BackupStoreFilename &, bool &)
// Purpose: Creates a directory (or just returns the ID of an existing one). rAlreadyExists set appropraitely.
// Created: 2003/09/04
//
// --------------------------------------------------------------------------
-int64_t BackupContext::AddDirectory(int64_t InDirectory, const BackupStoreFilename &rFilename, const StreamableMemBlock &Attributes, int64_t AttributesModTime, bool &rAlreadyExists)
+int64_t BackupStoreContext::AddDirectory(int64_t InDirectory, const BackupStoreFilename &rFilename, const StreamableMemBlock &Attributes, int64_t AttributesModTime, bool &rAlreadyExists)
{
if(mpStoreInfo.get() == 0)
{
@@ -936,12 +1027,12 @@ int64_t BackupContext::AddDirectory(int64_t InDirectory, const BackupStoreFilena
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &, bool)
+// Name: BackupStoreContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &, bool)
// Purpose: Recusively deletes a directory (or undeletes if Undelete = true)
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
-void BackupContext::DeleteDirectory(int64_t ObjectID, bool Undelete)
+void BackupStoreContext::DeleteDirectory(int64_t ObjectID, bool Undelete)
{
// Essential checks!
if(mpStoreInfo.get() == 0)
@@ -1018,12 +1109,12 @@ void BackupContext::DeleteDirectory(int64_t ObjectID, bool Undelete)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::DeleteDirectoryRecurse(BackupStoreDirectory &, int64_t)
+// Name: BackupStoreContext::DeleteDirectoryRecurse(BackupStoreDirectory &, int64_t)
// Purpose: Private. Deletes a directory depth-first recusively.
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
-void BackupContext::DeleteDirectoryRecurse(int64_t ObjectID, int64_t &rBlocksDeletedOut, bool Undelete)
+void BackupStoreContext::DeleteDirectoryRecurse(int64_t ObjectID, int64_t &rBlocksDeletedOut, bool Undelete)
{
try
{
@@ -1120,12 +1211,12 @@ void BackupContext::DeleteDirectoryRecurse(int64_t ObjectID, int64_t &rBlocksDel
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::ChangeDirAttributes(int64_t, const StreamableMemBlock &, int64_t)
+// Name: BackupStoreContext::ChangeDirAttributes(int64_t, const StreamableMemBlock &, int64_t)
// Purpose: Change the attributes of a directory
// Created: 2003/09/06
//
// --------------------------------------------------------------------------
-void BackupContext::ChangeDirAttributes(int64_t Directory, const StreamableMemBlock &Attributes, int64_t AttributesModTime)
+void BackupStoreContext::ChangeDirAttributes(int64_t Directory, const StreamableMemBlock &Attributes, int64_t AttributesModTime)
{
if(mpStoreInfo.get() == 0)
{
@@ -1157,12 +1248,12 @@ void BackupContext::ChangeDirAttributes(int64_t Directory, const StreamableMemBl
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::ChangeFileAttributes(int64_t, int64_t, const StreamableMemBlock &, int64_t)
+// Name: BackupStoreContext::ChangeFileAttributes(int64_t, int64_t, const StreamableMemBlock &, int64_t)
// Purpose: Sets the attributes on a directory entry. Returns true if the object existed, false if it didn't.
// Created: 2003/09/06
//
// --------------------------------------------------------------------------
-bool BackupContext::ChangeFileAttributes(const BackupStoreFilename &rFilename, int64_t InDirectory, const StreamableMemBlock &Attributes, int64_t AttributesHash, int64_t &rObjectIDOut)
+bool BackupStoreContext::ChangeFileAttributes(const BackupStoreFilename &rFilename, int64_t InDirectory, const StreamableMemBlock &Attributes, int64_t AttributesHash, int64_t &rObjectIDOut)
{
if(mpStoreInfo.get() == 0)
{
@@ -1222,12 +1313,12 @@ bool BackupContext::ChangeFileAttributes(const BackupStoreFilename &rFilename, i
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::ObjectExists(int64_t)
+// Name: BackupStoreContext::ObjectExists(int64_t)
// Purpose: Test to see if an object of this ID exists in the store
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-bool BackupContext::ObjectExists(int64_t ObjectID, int MustBe)
+bool BackupStoreContext::ObjectExists(int64_t ObjectID, int MustBe)
{
if(mpStoreInfo.get() == 0)
{
@@ -1293,12 +1384,12 @@ bool BackupContext::ObjectExists(int64_t ObjectID, int MustBe)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::OpenObject(int64_t)
+// Name: BackupStoreContext::OpenObject(int64_t)
// Purpose: Opens an object
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-std::auto_ptr<IOStream> BackupContext::OpenObject(int64_t ObjectID)
+std::auto_ptr<IOStream> BackupStoreContext::OpenObject(int64_t ObjectID)
{
if(mpStoreInfo.get() == 0)
{
@@ -1315,12 +1406,12 @@ std::auto_ptr<IOStream> BackupContext::OpenObject(int64_t ObjectID)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::GetClientStoreMarker()
+// Name: BackupStoreContext::GetClientStoreMarker()
// Purpose: Retrieve the client store marker
// Created: 2003/10/29
//
// --------------------------------------------------------------------------
-int64_t BackupContext::GetClientStoreMarker()
+int64_t BackupStoreContext::GetClientStoreMarker()
{
if(mpStoreInfo.get() == 0)
{
@@ -1334,12 +1425,12 @@ int64_t BackupContext::GetClientStoreMarker()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::GetStoreDiscUsageInfo(int64_t &, int64_t &, int64_t &)
+// Name: BackupStoreContext::GetStoreDiscUsageInfo(int64_t &, int64_t &, int64_t &)
// Purpose: Get disc usage info from store info
// Created: 1/1/04
//
// --------------------------------------------------------------------------
-void BackupContext::GetStoreDiscUsageInfo(int64_t &rBlocksUsed, int64_t &rBlocksSoftLimit, int64_t &rBlocksHardLimit)
+void BackupStoreContext::GetStoreDiscUsageInfo(int64_t &rBlocksUsed, int64_t &rBlocksSoftLimit, int64_t &rBlocksHardLimit)
{
if(mpStoreInfo.get() == 0)
{
@@ -1355,12 +1446,12 @@ void BackupContext::GetStoreDiscUsageInfo(int64_t &rBlocksUsed, int64_t &rBlocks
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::HardLimitExceeded()
+// Name: BackupStoreContext::HardLimitExceeded()
// Purpose: Returns true if the hard limit has been exceeded
// Created: 1/1/04
//
// --------------------------------------------------------------------------
-bool BackupContext::HardLimitExceeded()
+bool BackupStoreContext::HardLimitExceeded()
{
if(mpStoreInfo.get() == 0)
{
@@ -1374,12 +1465,12 @@ bool BackupContext::HardLimitExceeded()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::SetClientStoreMarker(int64_t)
+// Name: BackupStoreContext::SetClientStoreMarker(int64_t)
// Purpose: Sets the client store marker, and commits it to disc
// Created: 2003/10/29
//
// --------------------------------------------------------------------------
-void BackupContext::SetClientStoreMarker(int64_t ClientStoreMarker)
+void BackupStoreContext::SetClientStoreMarker(int64_t ClientStoreMarker)
{
if(mpStoreInfo.get() == 0)
{
@@ -1398,12 +1489,12 @@ void BackupContext::SetClientStoreMarker(int64_t ClientStoreMarker)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::MoveObject(int64_t, int64_t, int64_t, const BackupStoreFilename &, bool)
+// Name: BackupStoreContext::MoveObject(int64_t, int64_t, int64_t, const BackupStoreFilename &, bool)
// Purpose: Move an object (and all objects with the same name) from one directory to another
// Created: 12/11/03
//
// --------------------------------------------------------------------------
-void BackupContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int64_t MoveToDirectory, const BackupStoreFilename &rNewFilename, bool MoveAllWithSameName, bool AllowMoveOverDeletedObject)
+void BackupStoreContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int64_t MoveToDirectory, const BackupStoreFilename &rNewFilename, bool MoveAllWithSameName, bool AllowMoveOverDeletedObject)
{
if(mReadOnly)
{
@@ -1643,12 +1734,12 @@ void BackupContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int6
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::GetBackupStoreInfo()
+// Name: BackupStoreContext::GetBackupStoreInfo()
// Purpose: Return the backup store info object, exception if it isn't loaded
// Created: 19/4/04
//
// --------------------------------------------------------------------------
-const BackupStoreInfo &BackupContext::GetBackupStoreInfo() const
+const BackupStoreInfo &BackupStoreContext::GetBackupStoreInfo() const
{
if(mpStoreInfo.get() == 0)
{
diff --git a/bin/bbstored/BackupContext.h b/bin/bbstored/BackupStoreContext.h
index 18f2f25c..4cfdb601 100644
--- a/bin/bbstored/BackupContext.h
+++ b/bin/bbstored/BackupStoreContext.h
@@ -1,7 +1,7 @@
// --------------------------------------------------------------------------
//
// File
-// Name: BackupContext.h
+// Name: BackupStoreContext.h
// Purpose: Context for backup store server
// Created: 2003/08/20
//
@@ -15,6 +15,7 @@
#include <memory>
#include "NamedLock.h"
+#include "ProtocolObject.h"
#include "Utils.h"
class BackupStoreDirectory;
@@ -22,23 +23,31 @@ class BackupStoreFilename;
class BackupStoreDaemon;
class BackupStoreInfo;
class IOStream;
+class BackupProtocolObject;
class StreamableMemBlock;
+class HousekeepingInterface
+{
+ public:
+ virtual ~HousekeepingInterface() { }
+ virtual void SendMessageToHousekeepingProcess(const void *Msg, int MsgLen) = 0;
+};
+
// --------------------------------------------------------------------------
//
// Class
-// Name: BackupContext
+// Name: BackupStoreContext
// Purpose: Context for backup store server
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-class BackupContext
+class BackupStoreContext
{
public:
- BackupContext(int32_t ClientID, BackupStoreDaemon &rDaemon);
- ~BackupContext();
+ BackupStoreContext(int32_t ClientID, HousekeepingInterface &rDaemon);
+ ~BackupStoreContext();
private:
- BackupContext(const BackupContext &rToCopy);
+ BackupStoreContext(const BackupStoreContext &rToCopy);
public:
void ReceivedFinishCommand();
@@ -83,7 +92,7 @@ public:
// --------------------------------------------------------------------------
//
// Function
- // Name: BackupContext::GetDirectory(int64_t)
+ // Name: BackupStoreContext::GetDirectory(int64_t)
// Purpose: Return a reference to a directory. Valid only until the
// next time a function which affects directories is called.
// Mainly this funciton, and creation of files.
@@ -103,6 +112,7 @@ public:
void ChangeDirAttributes(int64_t Directory, const StreamableMemBlock &Attributes, int64_t AttributesModTime);
bool ChangeFileAttributes(const BackupStoreFilename &rFilename, int64_t InDirectory, const StreamableMemBlock &Attributes, int64_t AttributesHash, int64_t &rObjectIDOut);
bool DeleteFile(const BackupStoreFilename &rFilename, int64_t InDirectory, int64_t &rObjectIDOut);
+ bool UndeleteFile(int64_t ObjectID, int64_t InDirectory);
void DeleteDirectory(int64_t ObjectID, bool Undelete = false);
void MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int64_t MoveToDirectory, const BackupStoreFilename &rNewFilename, bool MoveAllWithSameName, bool AllowMoveOverDeletedObject);
@@ -129,7 +139,7 @@ private:
private:
int32_t mClientID;
- BackupStoreDaemon &mrDaemon;
+ HousekeepingInterface &mrDaemon;
int mProtocolPhase;
bool mClientHasAccount;
std::string mStoreRoot; // has final directory separator
@@ -143,6 +153,30 @@ private:
// Directory cache
std::map<int64_t, BackupStoreDirectory*> mDirectoryCache;
+
+public:
+ class TestHook
+ {
+ public:
+ virtual std::auto_ptr<ProtocolObject> StartCommand(BackupProtocolObject&
+ rCommand) = 0;
+ virtual ~TestHook() { }
+ };
+ void SetTestHook(TestHook& rTestHook)
+ {
+ mpTestHook = &rTestHook;
+ }
+ std::auto_ptr<ProtocolObject> StartCommandHook(BackupProtocolObject& rCommand)
+ {
+ if(mpTestHook)
+ {
+ return mpTestHook->StartCommand(rCommand);
+ }
+ return std::auto_ptr<ProtocolObject>();
+ }
+
+private:
+ TestHook* mpTestHook;
};
#endif // BACKUPCONTEXT__H
diff --git a/bin/bbstored/BackupStoreDaemon.cpp b/bin/bbstored/BackupStoreDaemon.cpp
index 28e28176..4de0a078 100644
--- a/bin/bbstored/BackupStoreDaemon.cpp
+++ b/bin/bbstored/BackupStoreDaemon.cpp
@@ -17,7 +17,7 @@
#include <syslog.h>
#endif
-#include "BackupContext.h"
+#include "BackupStoreContext.h"
#include "BackupStoreDaemon.h"
#include "BackupStoreConfigVerify.h"
#include "autogen_BackupProtocolServer.h"
@@ -43,7 +43,8 @@ BackupStoreDaemon::BackupStoreDaemon()
mHaveForkedHousekeeping(false),
mIsHousekeepingProcess(false),
mHousekeepingInited(false),
- mInterProcessComms(mInterProcessCommsSocket)
+ mInterProcessComms(mInterProcessCommsSocket),
+ mpTestHook(NULL)
{
}
@@ -171,11 +172,12 @@ void BackupStoreDaemon::Run()
const Configuration &config(GetConfiguration());
mExtendedLogging = config.GetKeyValueBool("ExtendedLogging");
-#ifdef WIN32
- // Housekeeping runs synchronously on Win32
-#else
- // Fork off housekeeping daemon -- must only do this the first time Run() is called
- if(!mHaveForkedHousekeeping)
+ // Fork off housekeeping daemon -- must only do this the first
+ // time Run() is called. Housekeeping runs synchronously on Win32
+ // because IsSingleProcess() is always true
+
+#ifndef WIN32
+ if(!IsSingleProcess() && !mHaveForkedHousekeeping)
{
// Open a socket pair for communication
int sv[2] = {-1,-1};
@@ -205,7 +207,9 @@ void BackupStoreDaemon::Run()
// Log that housekeeping started
BOX_INFO("Housekeeping process started");
// Ignore term and hup
- // Parent will handle these and alert the child via the socket, don't want to randomly die
+ // Parent will handle these and alert the
+ // child via the socket, don't want to
+ // randomly die!
::signal(SIGHUP, SIG_IGN);
::signal(SIGTERM, SIG_IGN);
}
@@ -317,10 +321,18 @@ void BackupStoreDaemon::Connection2(SocketStreamTLS &rStream)
}
// Make ps listings clearer
- SetProcessTitle("client %08x", id);
+ std::ostringstream tag;
+ tag << "client=" << BOX_FORMAT_ACCOUNT(id);
+ SetProcessTitle(tag.str().c_str());
+ Logging::Tagger tagWithClientID(tag.str());
// Create a context, using this ID
- BackupContext context(id, *this);
+ BackupStoreContext context(id, *this);
+
+ if (mpTestHook)
+ {
+ context.SetTestHook(*mpTestHook);
+ }
// See if the client has an account?
if(mpAccounts && mpAccounts->AccountExists(id))
@@ -352,7 +364,7 @@ void BackupStoreDaemon::LogConnectionStats(const char *commonName,
const SocketStreamTLS &s)
{
// Log the amount of data transferred
- BOX_INFO("Connection statistics for " << commonName << ":"
+ BOX_NOTICE("Connection statistics for " << commonName << ":"
" IN=" << s.GetBytesRead() <<
" OUT=" << s.GetBytesWritten() <<
" TOTAL=" << (s.GetBytesRead() + s.GetBytesWritten()));
diff --git a/bin/bbstored/BackupStoreDaemon.h b/bin/bbstored/BackupStoreDaemon.h
index 387a1d5b..a5d216f4 100644
--- a/bin/bbstored/BackupStoreDaemon.h
+++ b/bin/bbstored/BackupStoreDaemon.h
@@ -13,6 +13,7 @@
#include "ServerTLS.h"
#include "BoxPortsAndFiles.h"
#include "BackupConstants.h"
+#include "BackupStoreContext.h"
#include "IOStreamGetLine.h"
class BackupStoreAccounts;
@@ -27,7 +28,8 @@ class HousekeepStoreAccount;
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-class BackupStoreDaemon : public ServerTLS<BOX_PORT_BBSTORED>
+class BackupStoreDaemon : public ServerTLS<BOX_PORT_BBSTORED>,
+ HousekeepingInterface
{
friend class HousekeepStoreAccount;
@@ -38,7 +40,7 @@ private:
BackupStoreDaemon(const BackupStoreDaemon &rToCopy);
public:
- // For BackupContext to communicate with housekeeping process
+ // For BackupStoreContext to communicate with housekeeping process
void SendMessageToHousekeepingProcess(const void *Msg, int MsgLen)
{
#ifndef WIN32
@@ -81,6 +83,15 @@ private:
void HousekeepingInit();
void RunHousekeepingIfNeeded();
int64_t mLastHousekeepingRun;
+
+public:
+ void SetTestHook(BackupStoreContext::TestHook& rTestHook)
+ {
+ mpTestHook = &rTestHook;
+ }
+
+private:
+ BackupStoreContext::TestHook* mpTestHook;
};
diff --git a/bin/bbstored/HousekeepStoreAccount.cpp b/bin/bbstored/HousekeepStoreAccount.cpp
index 9f4239e7..dbb9b544 100644
--- a/bin/bbstored/HousekeepStoreAccount.cpp
+++ b/bin/bbstored/HousekeepStoreAccount.cpp
@@ -85,16 +85,19 @@ void HousekeepStoreAccount::DoHousekeeping()
{
// Attempt to lock the account
std::string writeLockFilename;
- StoreStructure::MakeWriteLockFilename(mStoreRoot, mStoreDiscSet, writeLockFilename);
+ StoreStructure::MakeWriteLockFilename(mStoreRoot, mStoreDiscSet,
+ writeLockFilename);
NamedLock writeLock;
- if(!writeLock.TryAndGetLock(writeLockFilename.c_str(), 0600 /* restrictive file permissions */))
+ if(!writeLock.TryAndGetLock(writeLockFilename.c_str(),
+ 0600 /* restrictive file permissions */))
{
// Couldn't lock the account -- just stop now
return;
}
// Load the store info to find necessary info for the housekeeping
- std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(mAccountID, mStoreRoot, mStoreDiscSet, false /* Read/Write */));
+ std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(mAccountID,
+ mStoreRoot, mStoreDiscSet, false /* Read/Write */));
// Calculate how much should be deleted
mDeletionSizeTarget = info->GetBlocksUsed() - info->GetBlocksSoftLimit();
@@ -104,14 +107,18 @@ void HousekeepStoreAccount::DoHousekeeping()
}
// Scan the directory for potential things to delete
- // This will also remove elegiable items marked with RemoveASAP
+ // This will also remove eligible items marked with RemoveASAP
bool continueHousekeeping = ScanDirectory(BACKUPSTORE_ROOT_DIRECTORY_ID);
- // If scan directory stopped for some reason, probably parent instructed to teminate, stop now.
+ // If scan directory stopped for some reason, probably parent
+ // instructed to terminate, stop now.
if(!continueHousekeeping)
{
- // If any files were marked "delete now", then update the size of the store.
- if(mBlocksUsedDelta != 0 || mBlocksInOldFilesDelta != 0 || mBlocksInDeletedFilesDelta != 0)
+ // If any files were marked "delete now", then update
+ // the size of the store.
+ if(mBlocksUsedDelta != 0 ||
+ mBlocksInOldFilesDelta != 0 ||
+ mBlocksInDeletedFilesDelta != 0)
{
info->ChangeBlocksUsed(mBlocksUsedDelta);
info->ChangeBlocksInOldFiles(mBlocksInOldFilesDelta);
@@ -124,8 +131,8 @@ void HousekeepStoreAccount::DoHousekeeping()
return;
}
- // Log any difference in opinion between the values recorded in the store info, and
- // the values just calculated for space usage.
+ // Log any difference in opinion between the values recorded in
+ // the store info, and the values just calculated for space usage.
// BLOCK
{
int64_t used = info->GetBlocksUsed();
@@ -133,9 +140,12 @@ void HousekeepStoreAccount::DoHousekeeping()
int64_t usedDeleted = info->GetBlocksInDeletedFiles();
int64_t usedDirectories = info->GetBlocksInDirectories();
- // If the counts were wrong, taking into account RemoveASAP items deleted, log a message
- if((used + mBlocksUsedDelta) != mBlocksUsed || (usedOld + mBlocksInOldFilesDelta) != mBlocksInOldFiles
- || (usedDeleted + mBlocksInDeletedFilesDelta) != mBlocksInDeletedFiles || usedDirectories != mBlocksInDirectories)
+ // If the counts were wrong, taking into account RemoveASAP
+ // items deleted, log a message
+ if((used + mBlocksUsedDelta) != mBlocksUsed
+ || (usedOld + mBlocksInOldFilesDelta) != mBlocksInOldFiles
+ || (usedDeleted + mBlocksInDeletedFilesDelta) != mBlocksInDeletedFiles
+ || usedDirectories != mBlocksInDirectories)
{
// Log this
BOX_ERROR("Housekeeping on account " <<
@@ -153,18 +163,25 @@ void HousekeepStoreAccount::DoHousekeeping()
}
// If the current values don't match, store them
- if(used != mBlocksUsed || usedOld != mBlocksInOldFiles
- || usedDeleted != mBlocksInDeletedFiles || usedDirectories != (mBlocksInDirectories + mBlocksInDirectoriesDelta))
+ if(used != mBlocksUsed
+ || usedOld != mBlocksInOldFiles
+ || usedDeleted != mBlocksInDeletedFiles
+ || usedDirectories != (mBlocksInDirectories + mBlocksInDirectoriesDelta))
{
// Set corrected values in store info
- info->CorrectAllUsedValues(mBlocksUsed, mBlocksInOldFiles, mBlocksInDeletedFiles, mBlocksInDirectories + mBlocksInDirectoriesDelta);
+ info->CorrectAllUsedValues(mBlocksUsed,
+ mBlocksInOldFiles, mBlocksInDeletedFiles,
+ mBlocksInDirectories + mBlocksInDirectoriesDelta);
info->Save();
}
}
- // Reset the delta counts for files, as they will include RemoveASAP flagged files deleted
- // during the initial scan.
- int64_t removeASAPBlocksUsedDelta = mBlocksUsedDelta; // keep for reporting
+ // Reset the delta counts for files, as they will include
+ // RemoveASAP flagged files deleted during the initial scan.
+
+ // keep for reporting
+ int64_t removeASAPBlocksUsedDelta = mBlocksUsedDelta;
+
mBlocksUsedDelta = 0;
mBlocksInOldFilesDelta = 0;
mBlocksInDeletedFilesDelta = 0;
@@ -172,7 +189,8 @@ void HousekeepStoreAccount::DoHousekeeping()
// Go and delete items from the accounts
bool deleteInterrupted = DeleteFiles();
- // If that wasn't interrupted, remove any empty directories which are also marked as deleted in their containing directory
+ // If that wasn't interrupted, remove any empty directories which
+ // are also marked as deleted in their containing directory
if(!deleteInterrupted)
{
deleteInterrupted = DeleteEmptyDirectories();
@@ -190,8 +208,9 @@ void HousekeepStoreAccount::DoHousekeeping()
(deleteInterrupted?" and was interrupted":""));
}
- // Make sure the delta's won't cause problems if the counts are really wrong, and
- // it wasn't fixed because the store was updated during the scan.
+ // Make sure the delta's won't cause problems if the counts are
+ // really wrong, and it wasn't fixed because the store was
+ // updated during the scan.
if(mBlocksUsedDelta < (0 - info->GetBlocksUsed()))
{
mBlocksUsedDelta = (0 - info->GetBlocksUsed());
@@ -218,7 +237,8 @@ void HousekeepStoreAccount::DoHousekeeping()
// Save the store info back
info->Save();
- // Explicity release the lock (would happen automatically on going out of scope, included for code clarity)
+ // Explicity release the lock (would happen automatically on
+ // going out of scope, included for code clarity)
writeLock.ReleaseLock();
}
@@ -243,8 +263,9 @@ void HousekeepStoreAccount::MakeObjectFilename(int64_t ObjectID, std::string &rF
//
// Function
// Name: HousekeepStoreAccount::ScanDirectory(int64_t)
-// Purpose: Private. Scan a directory for potenitally deleteable items, and
-// add them to the list. Returns true if the scan should continue.
+// Purpose: Private. Scan a directory for potentially deleteable
+// items, and add them to the list. Returns true if the
+// scan should continue.
// Created: 11/12/03
//
// --------------------------------------------------------------------------
@@ -253,9 +274,12 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
#ifndef WIN32
if((--mCountUntilNextInterprocessMsgCheck) <= 0)
{
- mCountUntilNextInterprocessMsgCheck = POLL_INTERPROCESS_MSG_CHECK_FREQUENCY;
+ mCountUntilNextInterprocessMsgCheck =
+ POLL_INTERPROCESS_MSG_CHECK_FREQUENCY;
+
// Check for having to stop
- if(mrDaemon.CheckForInterProcessMsg(mAccountID)) // include account ID here as the specified account is locked
+ // Include account ID here as the specified account is locked
+ if(mrDaemon.CheckForInterProcessMsg(mAccountID))
{
// Need to abort now
return false;
@@ -268,7 +292,8 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
MakeObjectFilename(ObjectID, objectFilename);
// Open it.
- std::auto_ptr<RaidFileRead> dirStream(RaidFileRead::Open(mStoreDiscSet, objectFilename));
+ std::auto_ptr<RaidFileRead> dirStream(RaidFileRead::Open(mStoreDiscSet,
+ objectFilename));
// Add the size of the directory on disc to the size being calculated
int64_t originalDirSizeInBlocks = dirStream->GetDiscUsageInBlocks();
@@ -290,8 +315,8 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
// BLOCK
{
- // Remove any files which are marked for removal as soon as they become old
- // or deleted.
+ // Remove any files which are marked for removal as soon
+ // as they become old or deleted.
bool deletedSomething = false;
do
{
@@ -324,7 +349,8 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
// Add files to the list of potential deletions
// map to count the distance from the mark
- std::map<std::pair<BackupStoreFilename, int32_t>, int32_t> markVersionAges;
+ typedef std::pair<std::string, int32_t> version_t;
+ std::map<version_t, int32_t> markVersionAges;
// map of pair (filename, mark number) -> version age
// NOTE: use a reverse iterator to allow the distance from mark stuff to work
@@ -342,7 +368,10 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
// Work out ages of this version from the last mark
int32_t enVersionAge = 0;
- std::map<std::pair<BackupStoreFilename, int32_t>, int32_t>::iterator enVersionAgeI(markVersionAges.find(std::pair<BackupStoreFilename, int32_t>(en->GetName(), en->GetMarkNumber())));
+ std::map<version_t, int32_t>::iterator enVersionAgeI(
+ markVersionAges.find(
+ version_t(en->GetName().GetEncodedFilename(),
+ en->GetMarkNumber())));
if(enVersionAgeI != markVersionAges.end())
{
enVersionAge = enVersionAgeI->second + 1;
@@ -350,7 +379,7 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
}
else
{
- markVersionAges[std::pair<BackupStoreFilename, int32_t>(en->GetName(), en->GetMarkNumber())] = enVersionAge;
+ markVersionAges[version_t(en->GetName().GetEncodedFilename(), en->GetMarkNumber())] = enVersionAge;
}
// enVersionAge is now the age of this version.
@@ -364,6 +393,9 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
d.mSizeInBlocks = en->GetSizeInBlocks();
d.mMarkNumber = en->GetMarkNumber();
d.mVersionAgeWithinMark = enVersionAge;
+ d.mIsFlagDeleted = (enFlags &
+ BackupStoreDirectory::Entry::Flags_Deleted)
+ ? true : false;
// Add it to the list
mPotentialDeletions.insert(d);
@@ -541,6 +573,10 @@ bool HousekeepStoreAccount::DeleteFiles()
// Delete the file
DeleteFile(i->mInDirectory, i->mObjectID, dir, dirFilename, dirSizeInBlocksOrig);
+ BOX_INFO("Housekeeping removed " <<
+ (i->mIsFlagDeleted ? "deleted" : "old") <<
+ " file " << BOX_FORMAT_OBJECTID(i->mObjectID) <<
+ " from dir " << BOX_FORMAT_OBJECTID(i->mInDirectory));
// Stop if the deletion target has been matched or exceeded
// (checking here rather than at the beginning will tend to reduce the
@@ -786,99 +822,115 @@ bool HousekeepStoreAccount::DeleteEmptyDirectories()
continue;
}
- // Load up the directory to potentially delete
- std::string dirFilename;
- BackupStoreDirectory dir;
- int64_t dirSizeInBlocks = 0;
- {
- MakeObjectFilename(*i, dirFilename);
- // Check it actually exists (just in case it gets added twice to the list)
- if(!RaidFileRead::FileExists(mStoreDiscSet, dirFilename))
- {
- // doesn't exist, next!
- continue;
- }
- // load
- std::auto_ptr<RaidFileRead> dirStream(RaidFileRead::Open(mStoreDiscSet, dirFilename));
- dirSizeInBlocks = dirStream->GetDiscUsageInBlocks();
- dir.ReadFromStream(*dirStream, IOStream::TimeOutInfinite);
- }
+ DeleteEmptyDirectory(*i, toExamine);
+ }
- // Make sure this directory is actually empty
- if(dir.GetNumberOfEntries() != 0)
- {
- // Not actually empty, try next one
- continue;
- }
+ // Remove contents of empty directories
+ mEmptyDirectories.clear();
+ // Swap in new, so it's examined next time round
+ mEmptyDirectories.swap(toExamine);
+ }
+
+ // Not interrupted
+ return false;
+}
- // Candiate for deletion... open containing directory
- std::string containingDirFilename;
- BackupStoreDirectory containingDir;
- int64_t containingDirSizeInBlocksOrig = 0;
- {
- MakeObjectFilename(dir.GetContainerID(), containingDirFilename);
- std::auto_ptr<RaidFileRead> containingDirStream(RaidFileRead::Open(mStoreDiscSet, containingDirFilename));
- containingDirSizeInBlocksOrig = containingDirStream->GetDiscUsageInBlocks();
- containingDir.ReadFromStream(*containingDirStream, IOStream::TimeOutInfinite);
- }
+void HousekeepStoreAccount::DeleteEmptyDirectory(int64_t dirId,
+ std::vector<int64_t>& rToExamine)
+{
+ // Load up the directory to potentially delete
+ std::string dirFilename;
+ BackupStoreDirectory dir;
+ int64_t dirSizeInBlocks = 0;
- // Find the entry
- BackupStoreDirectory::Entry *pdirentry = containingDir.FindEntryByID(dir.GetObjectID());
- if((pdirentry != 0) && ((pdirentry->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) != 0))
- {
- // Should be deleted
- containingDir.DeleteEntry(dir.GetObjectID());
+ // BLOCK
+ {
+ MakeObjectFilename(dirId, dirFilename);
+ // Check it actually exists (just in case it gets
+ // added twice to the list)
+ if(!RaidFileRead::FileExists(mStoreDiscSet, dirFilename))
+ {
+ // doesn't exist, next!
+ return;
+ }
+ // load
+ std::auto_ptr<RaidFileRead> dirStream(
+ RaidFileRead::Open(mStoreDiscSet, dirFilename));
+ dirSizeInBlocks = dirStream->GetDiscUsageInBlocks();
+ dir.ReadFromStream(*dirStream, IOStream::TimeOutInfinite);
+ }
- // Is the containing dir now a candidate for deletion?
- if(containingDir.GetNumberOfEntries() == 0)
- {
- toExamine.push_back(containingDir.GetObjectID());
- }
+ // Make sure this directory is actually empty
+ if(dir.GetNumberOfEntries() != 0)
+ {
+ // Not actually empty, try next one
+ return;
+ }
- // Write revised parent directory
- RaidFileWrite writeDir(mStoreDiscSet, containingDirFilename);
- writeDir.Open(true /* allow overwriting */);
- containingDir.WriteToStream(writeDir);
+ // Candidate for deletion... open containing directory
+ std::string containingDirFilename;
+ BackupStoreDirectory containingDir;
+ int64_t containingDirSizeInBlocksOrig = 0;
+ {
+ MakeObjectFilename(dir.GetContainerID(), containingDirFilename);
+ std::auto_ptr<RaidFileRead> containingDirStream(
+ RaidFileRead::Open(mStoreDiscSet,
+ containingDirFilename));
+ containingDirSizeInBlocksOrig =
+ containingDirStream->GetDiscUsageInBlocks();
+ containingDir.ReadFromStream(*containingDirStream,
+ IOStream::TimeOutInfinite);
+ }
- // get the disc usage (must do this before commiting it)
- int64_t dirSize = writeDir.GetDiscUsageInBlocks();
+ // Find the entry
+ BackupStoreDirectory::Entry *pdirentry =
+ containingDir.FindEntryByID(dir.GetObjectID());
+ if((pdirentry != 0) && ((pdirentry->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) != 0))
+ {
+ // Should be deleted
+ containingDir.DeleteEntry(dir.GetObjectID());
- // Commit directory
- writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
+ // Is the containing dir now a candidate for deletion?
+ if(containingDir.GetNumberOfEntries() == 0)
+ {
+ rToExamine.push_back(containingDir.GetObjectID());
+ }
- // adjust usage counts for this directory
- if(dirSize > 0)
- {
- int64_t adjust = dirSize - containingDirSizeInBlocksOrig;
- mBlocksUsedDelta += adjust;
- mBlocksInDirectoriesDelta += adjust;
- }
+ // Write revised parent directory
+ RaidFileWrite writeDir(mStoreDiscSet, containingDirFilename);
+ writeDir.Open(true /* allow overwriting */);
+ containingDir.WriteToStream(writeDir);
- // Delete the directory itself
- {
- RaidFileWrite del(mStoreDiscSet, dirFilename);
- del.Delete();
- }
+ // get the disc usage (must do this before commiting it)
+ int64_t dirSize = writeDir.GetDiscUsageInBlocks();
- // And adjust usage counts for the directory that's just been deleted
- mBlocksUsedDelta -= dirSizeInBlocks;
- mBlocksInDirectoriesDelta -= dirSizeInBlocks;
+ // Commit directory
+ writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
- // Update count
- ++mEmptyDirectoriesDeleted;
- }
+ // adjust usage counts for this directory
+ if(dirSize > 0)
+ {
+ int64_t adjust = dirSize - containingDirSizeInBlocksOrig;
+ mBlocksUsedDelta += adjust;
+ mBlocksInDirectoriesDelta += adjust;
}
- // Remove contents of empty directories
- mEmptyDirectories.clear();
- // Swap in new, so it's examined next time round
- mEmptyDirectories.swap(toExamine);
- }
-
- // Not interrupted
- return false;
-}
+ // Delete the directory itself
+ {
+ RaidFileWrite del(mStoreDiscSet, dirFilename);
+ del.Delete();
+ }
+ BOX_INFO("Housekeeping removed empty deleted dir " <<
+ BOX_FORMAT_OBJECTID(dirId));
+ // And adjust usage counts for the directory that's
+ // just been deleted
+ mBlocksUsedDelta -= dirSizeInBlocks;
+ mBlocksInDirectoriesDelta -= dirSizeInBlocks;
+ // Update count
+ ++mEmptyDirectoriesDeleted;
+ }
+}
diff --git a/bin/bbstored/HousekeepStoreAccount.h b/bin/bbstored/HousekeepStoreAccount.h
index 6c8f251d..5c2a9885 100644
--- a/bin/bbstored/HousekeepStoreAccount.h
+++ b/bin/bbstored/HousekeepStoreAccount.h
@@ -42,6 +42,8 @@ private:
bool ScanDirectory(int64_t ObjectID);
bool DeleteFiles();
bool DeleteEmptyDirectories();
+ void DeleteEmptyDirectory(int64_t dirId,
+ std::vector<int64_t>& rToExamine);
void DeleteFile(int64_t InDirectory, int64_t ObjectID, BackupStoreDirectory &rDirectory, const std::string &rDirectoryFilename, int64_t OriginalDirSizeInBlocks);
private:
@@ -52,6 +54,7 @@ private:
int64_t mSizeInBlocks;
int32_t mMarkNumber;
int32_t mVersionAgeWithinMark; // 0 == current, 1 latest old version, etc
+ bool mIsFlagDeleted; // false for files flagged "Old"
} DelEn;
struct DelEnCompare
diff --git a/bin/bbstored/backupprotocol.txt b/bin/bbstored/backupprotocol.txt
index 62d837ff..3eca514a 100644
--- a/bin/bbstored/backupprotocol.txt
+++ b/bin/bbstored/backupprotocol.txt
@@ -4,7 +4,7 @@
Name Backup
IdentString Box-Backup:v=C
-ServerContextClass BackupContext BackupContext.h
+ServerContextClass BackupStoreContext BackupStoreContext.h
ClientType Filename BackupStoreFilenameClear BackupStoreFilenameClear.h
ServerType Filename BackupStoreFilename BackupStoreFilename.h
@@ -204,6 +204,12 @@ GetBlockIndexByName 35 Command(Success)
# stream of the block index follows the reply if found ID != 0
+UndeleteFile 36 Command(Success)
+ int64 InDirectory
+ int64 ObjectID
+ # will return 0 if the object couldn't be found in the specified directory
+
+
# -------------------------------------------------------------------------------------
# Information commands
# -------------------------------------------------------------------------------------
diff --git a/bin/bbstored/bbstored-config.in b/bin/bbstored/bbstored-config.in
index 33cfb39a..5ad96d50 100755
--- a/bin/bbstored/bbstored-config.in
+++ b/bin/bbstored/bbstored-config.in
@@ -196,7 +196,7 @@ TimeBetweenHousekeeping = 900
Server
{
- PidFile = @localstatedir_expanded@/bbstored.pid
+ PidFile = @localstatedir_expanded@/run/bbstored.pid
User = $username
ListenAddresses = inet:$server
CertificateFile = $certificate
@@ -234,7 +234,7 @@ What you need to do now...
4) Create accounts with bbstoreaccounts
5) Start the backup store daemon with the command
- @bindir_expanded@/bbstored$daemon_args
+ @sbindir_expanded@/bbstored$daemon_args
in /etc/rc.local, or your local equivalent.
===================================================================
diff --git a/bin/bbstored/bbstored.cpp b/bin/bbstored/bbstored.cpp
index 54858dd4..21a9e5f1 100644
--- a/bin/bbstored/bbstored.cpp
+++ b/bin/bbstored/bbstored.cpp
@@ -18,7 +18,7 @@ int main(int argc, const char *argv[])
{
MAINHELPER_START
- Logging::SetProgramName("Box Backup (bbstored)");
+ Logging::SetProgramName("bbstored");
Logging::ToConsole(true);
Logging::ToSyslog (true);
diff --git a/cleanupforcvs.pl b/cleanupforcvs.pl
index 270ea525..3379a7ad 100755
--- a/cleanupforcvs.pl
+++ b/cleanupforcvs.pl
@@ -10,7 +10,7 @@ my $cleaned = 1;
my $dist_archives_exist = 0;
my @bad_h;
-open EVERYTHING,'find . |' or die "Can't open find for file listing";
+open EVERYTHING,'find . -type d \( -name docs \) -prune -o -type f |' or die "Can't open find for file listing";
my %exclude_from_memtest_checks = ('PollEmulator.cpp'=>1,'DebugMemLeakFinder.cpp'=>1,'MemLeakFinder.h'=>1,'MemLeakFindOn.h'=>1,'MemLeakFindOff.h'=>1,'Box.h'=>1);
@@ -178,7 +178,7 @@ sub ask_about_delete
{
print $_,"\n";
}
- print "Delete these ",$#$del_r + 1, " $name?";
+ print "Delete these ",$#$del_r + 1, " $name? ";
my $in = <STDIN>;
chomp $in;
if($in eq 'yes')
diff --git a/configure.ac b/configure.ac
index 60caf9d5..a6e8c812 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,14 +2,10 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
-AC_INIT([Box Backup], 0.11, [boxbackup@fluffy.co.uk])
+AC_INIT([Box Backup], 0.11, [boxbackup@boxbackup.org])
AC_CONFIG_SRCDIR([lib/common/Box.h])
AC_CONFIG_HEADERS([lib/common/BoxConfig.h])
-# override default sysconfdir, for backwards compatibility
-AC_SUBST([sysconfdir], ['/etc'])dnl
-AC_SUBST([localstatedir], ['/var/run'])dnl
-
touch install-sh
AC_CANONICAL_SYSTEM
test -s install-sh || rm install-sh
@@ -24,11 +20,16 @@ if test "x$ac_cv_cxx_exceptions" != "xyes" || \
test "x$ac_cv_cxx_namespaces" != "xyes"; then
AC_MSG_ERROR([[basic compile checks failed, the C++ compiler is broken]])
fi
+
if test "x$GXX" = "xyes"; then
# Use -Wall if we have gcc. This gives better warnings
AC_SUBST([CXXFLAGS_STRICT], ['-Wall -Wundef'])
- # Use -rdynamic if we have gcc. This is needed for backtrace
- AC_SUBST([LDADD_RDYNAMIC], ['-rdynamic'])
+
+ # Use -rdynamic if we have gcc, but not mingw. This is needed for backtrace
+ case $target_os in
+ mingw*) ;;
+ *) AC_SUBST([LDADD_RDYNAMIC], ['-rdynamic']) ;;
+ esac
fi
AC_PATH_PROG([PERL], [perl], [AC_MSG_ERROR([[perl executable was not found]])])
@@ -46,11 +47,18 @@ AC_SUBST([TARGET_PERL])
AC_DEFINE_UNQUOTED([PERL_EXECUTABLE], ["$TARGET_PERL"],
[Location of the perl executable])
-AC_CHECK_PROGS([AR], [ar],
+AC_CHECK_TOOL([AR], [ar],
[AC_MSG_ERROR([[cannot find ar executable]])])
-AC_CHECK_PROGS([RANLIB], [ranlib],
+AC_CHECK_TOOL([RANLIB], [ranlib],
[AC_MSG_ERROR([[cannot find ranlib executable]])])
+case $target_os in
+mingw*)
+ AC_CHECK_TOOL([WINDRES], [windres],
+ [AC_MSG_ERROR([[cannot find windres executable]])])
+ ;;
+esac
+
### Checks for libraries.
case $target_os in
@@ -62,6 +70,7 @@ winnt) ;;
;;
esac
+AC_CHECK_HEADER([zlib.h],, [AC_MSG_ERROR([[cannot find zlib.h]])])
AC_CHECK_LIB([z], [zlibVersion],, [AC_MSG_ERROR([[cannot find zlib]])])
VL_LIB_READLINE([have_libreadline=yes], [have_libreadline=no])
@@ -112,7 +121,7 @@ esac
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS([dlfcn.h execinfo.h getopt.h process.h pwd.h signal.h])
-AC_CHECK_HEADERS([syslog.h time.h])
+AC_CHECK_HEADERS([syslog.h time.h cxxabi.h])
AC_CHECK_HEADERS([netinet/in.h])
AC_CHECK_HEADERS([sys/param.h sys/socket.h sys/time.h sys/types.h sys/wait.h])
AC_CHECK_HEADERS([sys/uio.h sys/xattr.h])
@@ -126,6 +135,7 @@ else
fi
if test "$have_pcreposix_h" = "yes"; then
+ AC_DEFINE([PCRE_STATIC], [1], [Box Backup always uses static PCRE])
AC_SEARCH_LIBS([regcomp], ["pcreposix -lpcre"],,[have_pcreposix_h=no_regcomp])
fi
@@ -150,7 +160,6 @@ AC_CHECK_TYPES([uint8_t, uint16_t, uint32_t, uint64_t])
AC_HEADER_STDBOOL
AC_C_CONST
AC_C_BIGENDIAN
-AX_CHECK_NONALIGNED_ACCESS
AC_TYPE_UID_T
AC_TYPE_MODE_T
AC_TYPE_OFF_T
@@ -159,6 +168,8 @@ AC_TYPE_SIZE_T
AC_CHECK_MEMBERS([struct stat.st_flags])
AC_CHECK_MEMBERS([struct stat.st_mtimespec])
+AC_CHECK_MEMBERS([struct stat.st_atim.tv_nsec])
+AC_CHECK_MEMBERS([struct stat.st_atimensec])
AC_CHECK_MEMBERS([struct sockaddr_in.sin_len],,, [[
#include <sys/types.h>
#include <netinet/in.h>
@@ -170,6 +181,10 @@ AC_CHECK_DECLS([INFTIM],,, [[#include <poll.h>]])
AC_CHECK_DECLS([SO_PEERCRED],,, [[#include <sys/socket.h>]])
AC_CHECK_DECLS([O_BINARY],,,)
+# Solaris provides getpeerucred() instead of getpeereid() or SO_PEERCRED
+AC_CHECK_HEADERS([ucred.h])
+AC_CHECK_FUNCS([getpeerucred])
+
AC_CHECK_DECLS([optreset],,, [[#include <getopt.h>]])
AC_CHECK_DECLS([dirfd],,,
[[
@@ -207,13 +222,15 @@ AC_FUNC_CLOSEDIR_VOID
AC_FUNC_ERROR_AT_LINE
AC_TYPE_SIGNAL
AC_FUNC_STAT
-AC_CHECK_FUNCS([getpeereid lchown setproctitle getpid gettimeofday])
+AC_CHECK_FUNCS([getpeereid lchown setproctitle getpid gettimeofday waitpid])
+
# NetBSD implements kqueue too differently for us to get it fixed by 0.10
# TODO: Remove this when NetBSD kqueue implementation is working
netbsd_hack=`echo $target_os | sed 's/netbsd.*/netbsd/'`
if test "$netbsd_hack" != "netbsd"; then
AC_CHECK_FUNCS([kqueue])
fi
+
AX_FUNC_SYSCALL
AX_CHECK_SYSCALL_LSEEK
AC_CHECK_FUNCS([listxattr llistxattr getxattr lgetxattr setxattr lsetxattr])
@@ -224,14 +241,16 @@ AC_CHECK_DECLS([XATTR_NOFOLLOW],,, [[#include <sys/xattr.h>]])
## Check for large file support active. AC_SYS_LARGEFILE has already worked
## out how to enable it if necessary, we just use this to report to the user
-AC_CACHE_CHECK([if we have large file support enabled], [have_large_file_support],
- [AC_RUN_IFELSE([AC_LANG_PROGRAM([[$ac_includes_default]], [[
+AC_CACHE_CHECK([if we have large file support enabled], [box_cv_have_large_file_support],
+ [AC_TRY_RUN([AC_LANG_PROGRAM([[$ac_includes_default]], [[
return sizeof(off_t)==4;
]])],
- [have_large_file_support=yes], [have_large_file_support=no]
+ [box_cv_have_large_file_support=yes],
+ [box_cv_have_large_file_support=no],
+ [box_cv_have_large_file_support=no # safe for cross-compile]
)])
-if test "x$have_large_file_support" = "xyes"; then
+if test "x$box_cv_have_large_file_support" = "xyes"; then
AC_DEFINE([HAVE_LARGE_FILE_SUPPORT], [1],
[Define to 1 if large files are supported])
fi
@@ -271,23 +290,56 @@ if test "x$enable_static_bin" = "xyes"; then
LIBS="-Wl,-Bstatic $LIBS -Wl,-Bdynamic"
fi
+# override default sysconfdir, for backwards compatibility
+test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc
+test "$localstatedir" = '${prefix}/var' && localstatedir=/var
+
## Kludge to allow makeparcels.pl to use bindir. This is not a good long term
## solution because it prevents use of "make exec_prefix=/some/dir"
saved_prefix=$prefix
saved_exec_prefix=$exec_prefix
test "x$prefix" = xNONE && prefix=$ac_default_prefix
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
-bindir_expanded=` eval "echo $bindir"`
-bindir_expanded=` eval "echo $bindir_expanded"`
-sysconfdir_expanded=` eval "echo $sysconfdir"`
-localstatedir_expanded=`eval "echo $localstatedir"`
+eval bindir_expanded=` eval "echo $bindir"`
+eval sbindir_expanded=` eval "echo $sbindir"`
+eval sysconfdir_expanded=` eval "echo $sysconfdir"`
+eval localstatedir_expanded=`eval "echo $localstatedir"`
prefix=$saved_prefix
exec_prefix=$saved_exec_prefix
-AC_SUBST([bindir_expanded sysconfdir_expanded localstatedir_expanded])
+AC_SUBST([bindir_expanded])
+AC_SUBST([sbindir_expanded])
+AC_SUBST([sysconfdir_expanded])
+AC_SUBST([localstatedir_expanded])
+
+## Figure out the client parcel directory and substitute it
+build_dir=`dirname $0`
+build_dir=`cd $build_dir && pwd`
+client_parcel_dir=`$PERL infrastructure/parcelpath.pl backup-client $target_os`
+
+if test "$build_os" = "cygwin"; then
+ client_parcel_dir=`cygpath -wa $client_parcel_dir | sed -e 's|\\\|/|g'`
+ build_dir=` cygpath -wa $build_dir | sed -e 's|\\\|/|g'`
+fi
+
+AC_SUBST([client_parcel_dir])
+AC_SUBST([build_dir])
+## Figure out version and substitute it in
+box_version=`$PERL infrastructure/printversion.pl`
+AC_SUBST([box_version])
### Output files
-AC_CONFIG_FILES([infrastructure/BoxPlatform.pm])
+AC_CONFIG_FILES([infrastructure/BoxPlatform.pm
+ contrib/mac_osx/org.boxbackup.bbackupd.plist
+ contrib/mac_osx/org.boxbackup.bbstored.plist
+ contrib/solaris/bbackupd-manifest.xml
+ contrib/solaris/bbstored-manifest.xml
+ lib/common/BoxPortsAndFiles.h
+ test/bbackupd/testfiles/bbackupd.conf
+ test/bbackupd/testfiles/bbackupd-exclude.conf
+ test/bbackupd/testfiles/bbackupd-snapshot.conf
+ test/bbackupd/testfiles/bbackupd-symlink.conf
+ ])
AX_CONFIG_SCRIPTS([bin/bbackupd/bbackupd-config
bin/bbackupquery/makedocumentation.pl
bin/bbstored/bbstored-certs
@@ -298,20 +350,17 @@ AX_CONFIG_SCRIPTS([bin/bbackupd/bbackupd-config
contrib/redhat/bbstored
contrib/suse/bbackupd
contrib/suse/bbstored
- contrib/solaris/bbackupd-manifest.xml
- contrib/solaris/bbstored-manifest.xml
contrib/solaris/bbackupd-smf-method
contrib/solaris/bbstored-smf-method
+ contrib/windows/installer/boxbackup.mpi
infrastructure/makebuildenv.pl
infrastructure/makeparcels.pl
infrastructure/makedistribution.pl
- lib/common/BoxPortsAndFiles.h
lib/common/makeexception.pl
lib/raidfile/raidfile-config
lib/server/makeprotocol.pl
runtest.pl
test/backupstorefix/testfiles/testbackupstorefix.pl
- test/bbackupd/testfiles/bbackupd.conf
test/bbackupd/testfiles/extcheck1.pl
test/bbackupd/testfiles/extcheck2.pl
test/bbackupd/testfiles/notifyscript.pl
@@ -334,7 +383,7 @@ without these features, but will work better where they are present. Refer
to the documentation for more information on each feature.
Regular expressions: $have_regex_support
-Large files: $have_large_file_support
+Large files: $box_cv_have_large_file_support
Berkeley DB: $ax_path_bdb_ok
Readline: $have_libreadline
Extended attributes: $ac_cv_header_sys_xattr_h
@@ -343,7 +392,7 @@ EOC
### Warnings at end for visibility
-if test "x$gcc_3_plus" != "xyes" && test "x$malloc_workaround" != "xyes"; then
+if test "x$box_cv_gcc_3_plus" != "xyes" && test "x$box_cv_malloc_workaround" != "xyes"; then
echo
AC_MSG_WARN([[the implementation of the C++ STL on this platform may
have a flaw which causes it to apparently leak memory, and this flaw cannot be
diff --git a/contrib/bbadmin/accounts.cgi b/contrib/bbadmin/accounts.cgi
new file mode 100755
index 00000000..d68b82c6
--- /dev/null
+++ b/contrib/bbadmin/accounts.cgi
@@ -0,0 +1,580 @@
+#!/usr/bin/perl
+
+# Box Backup web management interface (c) Chris Wilson, 2008
+#
+# LICENSE: The Box Backup license applies to this code, with the following
+# additional conditions:
+#
+# If you make any changes to this code, except for changes to existing
+# variables in the Configuration section below, you must publish the changes
+# under the same license, whether or not you distribute copies of the
+# changed version.
+#
+# If you use any of this code in a derivative work, you must publish the
+# source code of the derivative work under the same or compatible license,
+# whether or not you distribute copies of the derivative work.
+#
+# The terms of the Box Backup license may be viewed here:
+# https://www.boxbackup.org/license.html
+#
+# If you require access to the code under a different license, this may
+# be negotiated with the copyright holder.
+
+use strict;
+use warnings;
+
+# Variables which you may need to change to match your installation
+# Changes to existing variables are NOT required to be published.
+
+my $box_dir = "/etc/box";
+my $bbstored_dir = "$box_dir/bbstored";
+my $ca_dir = "/mnt/backup/boxbackup/ca";
+
+# You should not need to change these unless you have a non-standard installation
+
+my $bbstored_conf_file = "$box_dir/bbstored.conf";
+my $bbstoreaccounts = "/usr/local/sbin/bbstoreaccounts";
+my $accounts_db_file = undef;
+# my $accounts_db_file = "/etc/box/bbstored/accounts.txt";
+my $raidfile_conf_file = undef;
+# my $raidfile_conf_file = "/etc/box/raidfile.conf";
+my $sign_period = '5000';
+
+# install Perl module with:
+# perl -MCPAN -e 'install Config::Scoped'
+# perl -MCPAN -e 'force install P/PT/PTHOMSEN/BoxBackup/BBConfig-0.03.tar.gz'
+# perl -MCPAN -e 'install Convert::ASN1'
+# download http://search.cpan.org/CPAN/authors/id/L/LE/LEO/Convert-X509-0.1.tar.gz,
+# unpack, and move the Convert folder to /usr/lib/perl5/site_perl/X.X.X
+
+# Check that SSL is being used.
+# DO NOT DISABLE THIS unless you really know what you're doing!
+die "This script requires an SSL web server" unless $ENV{HTTPS};
+
+# Check that the script is protected by basic authentication.
+# DO NOT DISABLE THIS unless you really know what you're doing!
+die "This script requires HTTP Authentication" unless $ENV{REMOTE_USER};
+
+# You should not need to change anything below this line.
+# If you do, you must publish your changes to comply with the license.
+
+use BoxBackup::Config::Accounts;
+use BoxBackup::Config::DiskSets;
+use CGI::Carp qw(fatalsToBrowser);
+use CGI::Pretty;
+use Config::Scoped;
+use Convert::X509::Request;
+use English;
+use Fcntl;
+use File::Temp;
+use URI;
+use URI::QueryParam;
+
+sub check_access($$)
+{
+ my ($file,$desc) = @_;
+ unless (-r $file)
+ {
+ open FILE, "< $file" and die "should have failed";
+ die "Failed to access $desc ($file): $!";
+ }
+}
+
+sub check_executable($$)
+{
+ my ($file,$desc) = @_;
+ unless (-x $file)
+ {
+ open FILE, "< $file" and die "should have failed";
+ die "$desc is not executable ($file): $!";
+ }
+}
+
+
+my $cgi = new CGI;
+
+if (my $download = $cgi->param("download"))
+{
+ my ($filename, $acct_no);
+
+ if ($download eq "cert")
+ {
+ $acct_no = $cgi->param("account");
+ $acct_no =~ tr/0-9a-fA-F//cd;
+ $filename = "$acct_no-cert.pem";
+ }
+ elsif ($download eq "cacert")
+ {
+ $filename = "serverCA.pem";
+ }
+ else
+ {
+ die "No such download method $download";
+ }
+
+ print $cgi->header(-type => "text/plain",
+ -"content-disposition" => "attachment; filename=$filename");
+
+ my $send_file;
+
+ if ($download eq "cert")
+ {
+ $send_file = "$ca_dir/clients/$filename";
+ }
+ elsif ($download eq "cacert")
+ {
+ $send_file = "$ca_dir/roots/serverCA.pem";
+ }
+
+ die "File does not exist: $send_file"
+ unless -f $send_file;
+ die "File is not readable by user " . getpwuid($UID) .
+ ": $send_file" unless -r $send_file;
+
+ open SENDFILE, "< $send_file" or die "Failed to open file " .
+ "$send_file: $!";
+ while (my $line = <SENDFILE>)
+ {
+ print $line;
+ }
+ close SENDFILE;
+ exit 0;
+}
+
+print $cgi->header(), $cgi->start_html(-title=>"Box Backup Certificates",
+ -style=>'bb.css');
+print $cgi->h1("Box Backup Certificates");
+
+check_access($bbstored_conf_file, "BBStoreD configuration file");
+
+my $bbstored_conf = Config::Scoped->new(file => $bbstored_conf_file)->parse();
+
+$accounts_db_file ||= $bbstored_conf->{'Server'}{'AccountDatabase'};
+die "Missing AccountDatabase in $bbstored_conf_file" unless $accounts_db_file;
+check_access($accounts_db_file, "Accounts Database");
+
+$raidfile_conf_file ||= $bbstored_conf->{'Server'}{'RaidFileConf'};
+die "Missing RaidFileConf in $bbstored_conf_file" unless $raidfile_conf_file;
+check_access($raidfile_conf_file, "RaidFile configuration file");
+
+my $accounts_db = BoxBackup::Config::Accounts->new($accounts_db_file);
+
+check_executable($bbstoreaccounts, "bbstoreaccounts program");
+
+sub error($)
+{
+ my ($message) = @_;
+ unless ($message =~ /^</)
+ {
+ $message = $cgi->p($message);
+ }
+ print $cgi->div({-class=>"error"}, $message);
+ return 0;
+}
+
+sub url
+{
+ my $cgi = shift @_;
+ my %params = @_;
+ my $uri = URI->new($cgi->url(-absolute=>1));
+ foreach my $param (keys %params)
+ {
+ $uri->query_param($param, $params{$param});
+ }
+ return $uri;
+}
+
+sub create_account($)
+{
+ my ($cgi) = @_;
+
+ my $upload = $cgi->upload('req');
+ unless ($upload)
+ {
+ return error("Please attach a certificate request file.");
+ }
+
+ my $tempfile = File::Temp->new("bbaccount-certreq-XXXXXX.pem");
+ my $csr_data = "";
+
+ while (my $line = <$upload>)
+ {
+ print $tempfile $line;
+ $csr_data .= $line;
+ }
+
+ my @accounts = $accounts_db->getAccountIDs();
+ my $new_acc_no = $cgi->param('account');
+ if (not $new_acc_no)
+ {
+ return error("Please enter an account number.");
+ }
+
+ foreach my $account_no (@accounts)
+ {
+ if ($account_no == $new_acc_no)
+ {
+ return error("The account number $new_acc_no " .
+ "already exists, please use one which " .
+ "does not.");
+ }
+ }
+
+ my $req = Convert::X509::Request->new($csr_data);
+ my $cn;
+ foreach my $part ($req->subject)
+ {
+ if ($part =~ /^cn=(.*)/i)
+ {
+ $cn = $1;
+ last;
+ }
+ }
+
+ unless ($cn)
+ {
+ return error("The certificate request does not include a " .
+ "common name, which should be BACKUP-$new_acc_no.");
+ }
+
+ unless ($cn eq "BACKUP-$new_acc_no")
+ {
+ return error("The certificate request includes the wrong " .
+ "common name. Expected " .
+ "<tt>BACKUP-$new_acc_no</tt> but found " .
+ "<tt>$cn</tt>.");
+ }
+
+ my $out_cert_dir = "$ca_dir/clients";
+ unless (-w $out_cert_dir)
+ {
+ return error("Cannot write to certificate directory " .
+ "<tt>$out_cert_dir</tt> as user " .
+ "<tt>" . getpwuid($UID) . "</tt>.");
+ }
+
+ my $out_cert = "$out_cert_dir/$new_acc_no-cert.pem";
+ if (-f $out_cert and not -w $out_cert)
+ {
+ return error("The certificate file <tt>$out_cert</tt> " .
+ "exists and is not writable as user " .
+ "<tt>$out_cert_dir</tt> as user " .
+ "<tt>" . getpwuid($UID) . "</tt>.");
+ }
+
+ my $client_ca_cert_file = "$ca_dir/roots/clientCA.pem";
+ unless (-r $client_ca_cert_file)
+ {
+ return error("The client CA certificate file " .
+ "<tt>$client_ca_cert_file</tt> " .
+ "is not readable by user " .
+ "<tt>" . getpwuid($UID) . "</tt>.");
+ }
+
+ my $client_ca_key_file = "$ca_dir/keys/clientRootKey.pem";
+ unless (-r $client_ca_key_file)
+ {
+ return error("The client CA key file " .
+ "<tt>$client_ca_key_file</tt> " .
+ "is not readable by user " .
+ "<tt>" . getpwuid($UID) . "</tt>.");
+ }
+
+ my $serial_file = "$ca_dir/roots/clientCA.srl";
+ unless (-w $serial_file)
+ {
+ return error("The certificate serial number file " .
+ "<tt>$serial_file</tt> " .
+ "is not writable by user " .
+ "<tt>" . getpwuid($UID) . "</tt>.");
+ }
+
+ my $outputfile = File::Temp->new("bbaccounts-openssl-output-XXXXXX");
+
+ if (system("openssl x509 -req -in $tempfile -sha1 " .
+ "-extensions usr_crt " .
+ "-CA $client_ca_cert_file " .
+ "-CAkey $client_ca_key_file " .
+ "-out $out_cert -days $sign_period " .
+ ">$outputfile 2>&1") != 0)
+ {
+ open ERR, "< $outputfile" or die "$outputfile: $!";
+ my $errors = join("", <ERR>);
+ close ERR;
+ return error($cgi->p("Failed to sign certificate:") .
+ $cgi->pre($errors));
+ }
+
+ my $cert_uri = url($cgi, download => "cert", account => $new_acc_no);
+ my $ca_uri = url($cgi, download => "cacert");
+
+ print $cgi->div({-class=>"success"},
+ $cgi->p("Account created. Please download the following " .
+ "files:") .
+ $cgi->ul(
+ $cgi->li($cgi->a({href=>$cert_uri},
+ "Client Certificate")),
+ $cgi->li($cgi->a({href=>$ca_uri},
+ "CA Certificate"))
+ )
+ );
+
+ return 1;
+}
+
+if ($cgi->param("create"))
+{
+ print $cgi->h2("Account Creation");
+ create_account($cgi);
+}
+
+print $cgi->h2("Accounts");
+print $cgi->start_table({-border=>0, -class=>"numbers"});
+
+print $cgi->Tr(
+ $cgi->th("Account"),
+ $cgi->th('Used'), $cgi->th('%'),
+ $cgi->th('Old files'), $cgi->th('%'),
+ $cgi->th('Deleted files'), $cgi->th('%'),
+ $cgi->th('Directories'), $cgi->th('%'),
+ $cgi->th('Soft limit'), $cgi->th('%'),
+ $cgi->th('Hard limit'),
+ $cgi->th('Actions')
+ );
+
+sub human_format($)
+{
+ my ($kb) = @_;
+ die "bad format in value: expected number followed by kB, " .
+ "found '$kb'" unless $kb =~ /^(\d+) (kB)$/;
+
+ my $value = $1;
+ my $units = $2;
+
+ if ($value > 1024)
+ {
+ $value /= 1024;
+ $units = "MB";
+ }
+
+ if ($value > 1024)
+ {
+ $value /= 1024;
+ $units = "GB";
+ }
+
+ $value = sprintf("%.1f", $value);
+ return "$value $units";
+}
+
+sub bbstoreaccounts_format($)
+{
+ my ($kb) = @_;
+ die unless $kb =~ /^(\d+) (kB)$/;
+
+ my $value = $1;
+ my $units = "K";
+
+ unless ($value % 1024)
+ {
+ $value /= 1024;
+ $units = "M";
+ }
+
+ unless ($value % 1024)
+ {
+ $value /= 1024;
+ $units = "G";
+ }
+
+ return "$value$units";
+}
+
+sub get_account_info($)
+{
+ my ($account) = @_;
+
+ open BBSA, "$bbstoreaccounts -c $bbstored_conf_file -m info $account |"
+ or die "Failed to get account info for $account: $!";
+
+ my $account_info = {};
+
+ while (my $line = <BBSA>)
+ {
+ unless ($line =~ m/([^:]*): (.*)/)
+ {
+ die "Bad format in bbstoreaccounts info output " .
+ "for account $account: '$line'";
+ }
+
+ my $name = $1;
+ my $value = $2;
+
+ if ($value =~ /(.*), (.*)/)
+ {
+ $account_info->{$name} = [$1, $2];
+ }
+ else
+ {
+ $account_info->{$name} = $value;
+ }
+ }
+
+ return $account_info;
+}
+
+sub format_account_info($)
+{
+ my ($values) = @_;
+ my $kb = $values->[0];
+ my $pc = $values->[1];
+ return $cgi->td(human_format($kb)), $cgi->td($values->[1]);
+}
+
+my %account_numbers;
+
+my @accounts = $accounts_db->getAccountIDs();
+foreach my $i (@accounts)
+{
+ die "Duplicate account number $i" if $account_numbers{hex($i)};
+ $account_numbers{hex($i)} = 1;
+
+ # Find out what account is on what diskset.
+ my $disk = $accounts_db->getDisk($i);
+
+ # store limits
+ my $account_info = get_account_info($i);
+
+ print $cgi->Tr(
+ $cgi->td($i),
+ format_account_info($account_info->{'Used'}),
+ format_account_info($account_info->{'Old files'}),
+ format_account_info($account_info->{'Deleted files'}),
+ format_account_info($account_info->{'Directories'}),
+ format_account_info($account_info->{'Soft limit'}),
+ $cgi->td(human_format($account_info->{'Hard limit'}[0])),
+ $cgi->td($cgi->a({-href=>url($cgi, account=>$i)},
+ "Edit"))
+ );
+}
+
+print $cgi->end_table();
+
+my $account_no = $cgi->param("account");
+$account_no =~ tr/0-9a-fA-F//cd;
+
+if (not $cgi->param("showcreate"))
+{
+ print $cgi->start_form,
+ $cgi->submit(-name=>"showcreate",
+ -value=>"Create Account"),
+ $cgi->end_form();
+}
+
+if ($account_no)
+{
+ print $cgi->h2("Edit Account");
+ my $account_info = get_account_info($account_no);
+ $cgi->param("account", $account_no);
+ $cgi->param("soft_limit",
+ bbstoreaccounts_format($account_info->{'Soft limit'}[0]));
+ $cgi->param("hard_limit",
+ bbstoreaccounts_format($account_info->{'Hard limit'}[0]));
+}
+elsif ($cgi->param("showcreate"))
+{
+ print $cgi->h2("Create Account");
+}
+
+if ($account_no or $cgi->param("showcreate"))
+{
+ my $new_account_no = 1;
+ while ($account_numbers{$new_account_no})
+ {
+ $new_account_no++;
+ }
+
+ my $disksets_conf = BoxBackup::Config::DiskSets->new($raidfile_conf_file);
+ my @disk_names = $disksets_conf->getListofDisks();
+ my @disk_numbers;
+ my %disk_labels;
+
+ foreach my $name (@disk_names)
+ {
+ my $num = $disksets_conf->getParamVal($name, "SetNumber");
+ push @disk_numbers, $num;
+ $disk_labels{$num} = $name;
+ }
+
+ print $cgi->start_multipart_form(),
+ $cgi->start_table();
+
+ if ($account_no)
+ {
+ print $cgi->Tr(
+ $cgi->th("Account Number"),
+ $cgi->td($account_no .
+ $cgi->hidden("account", $account_no))
+ );
+ }
+ else
+ {
+ print $cgi->Tr(
+ $cgi->th("Account Number"),
+ $account_no ? $account_no :
+ $cgi->td($cgi->textfield(-name => "account",
+ -default => sprintf("%x", $new_account_no))),
+ );
+ }
+
+ if (not $account_no)
+ {
+ print $cgi->Tr(
+ $cgi->th("Disk Set"),
+ $cgi->td($cgi->popup_menu(-name => "disk_set",
+ -values => \@disk_numbers,
+ -labels => \%disk_labels))
+ );
+ }
+
+ print $cgi->Tr(
+ $cgi->th("Soft Limit"),
+ $cgi->td($cgi->textfield(-name => "soft_limit",
+ -default => "10G"))
+ ),
+ $cgi->Tr(
+ $cgi->th("Hard Limit"),
+ $cgi->td($cgi->textfield(-name => "hard_limit",
+ -default => "20G"))
+ ),
+ $cgi->Tr(
+ $cgi->th("Certificate Request"),
+ $cgi->td($cgi->filefield({
+ -name => "req",
+ -default => "*.crt"
+ }))
+ );
+
+ if ($account_no)
+ {
+ print $cgi->Tr(
+ $cgi->th(),
+ $cgi->td($cgi->submit(-name => "update",
+ -value => "Update Account"))
+ );
+ }
+ else
+ {
+ print $cgi->Tr(
+ $cgi->th(),
+ $cgi->td($cgi->submit(-name => "create",
+ -value => "Create Account"))
+ );
+ }
+
+ print $cgi->end_table(), $cgi->end_form();
+}
+
+print $cgi->end_html;
+
+exit 0;
diff --git a/contrib/bbadmin/apache.conf b/contrib/bbadmin/apache.conf
new file mode 100644
index 00000000..e22668ab
--- /dev/null
+++ b/contrib/bbadmin/apache.conf
@@ -0,0 +1,14 @@
+Alias /bbadmin /var/www/localhost/bbadmin
+
+<Directory /var/www/localhost/bbadmin>
+ AuthType basic
+ AuthName "Box Backup Web Management Interface"
+ AuthUserFile /etc/apache2/bbadmin.cgi.htpasswd
+ Require valid-user
+
+ Allow from all
+
+ Options ExecCGI
+ AddHandler cgi-script .cgi
+ DirectoryIndex accounts.cgi
+</Directory>
diff --git a/contrib/bbadmin/bb.css b/contrib/bbadmin/bb.css
new file mode 100644
index 00000000..76d48e93
--- /dev/null
+++ b/contrib/bbadmin/bb.css
@@ -0,0 +1,70 @@
+body
+{
+ background: #edeef3;
+}
+
+table
+{
+ border-spacing: 0px;
+}
+
+h1, th
+{
+ background: #e4e6ed;
+}
+
+h1
+{
+ border-top: 1px solid #c4c4d5;
+ border-bottom: 1px solid #fff;
+}
+
+td, th
+{
+ border-top: 1px solid #fff;
+ border-bottom: 1px solid #c4c4d5;
+ margin: 0px;
+ padding: 0.2em 0.5em;
+}
+
+th
+{
+ text-align: left;
+}
+
+table.numbers td
+{
+ text-align: right;
+}
+
+div.error, div.success
+{
+ margin: 1em;
+}
+
+div.error>*, div.success>*
+{
+ margin: 0.5em;
+}
+
+div.error
+{
+ background: #fdd;
+ border: 2px solid #c00;
+}
+
+div.success
+{
+ background: #dfd;
+ border: 2px solid #0c0;
+}
+
+h2, table, p
+{
+ margin: 0.5em;
+}
+
+form
+{
+ margin: 0.5em 0;
+}
diff --git a/contrib/bbreporter/LICENSE b/contrib/bbreporter/LICENSE
new file mode 100644
index 00000000..94a9ed02
--- /dev/null
+++ b/contrib/bbreporter/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/contrib/bbreporter/bbreporter.py b/contrib/bbreporter/bbreporter.py
new file mode 100755
index 00000000..9c8253f1
--- /dev/null
+++ b/contrib/bbreporter/bbreporter.py
@@ -0,0 +1,538 @@
+#!/usr/bin/env python
+# BoxBackupReporter - Simple script to report on backups that have been
+# performed using BoxBackup.
+#
+# Copyright: (C) 2007 Three A IT Limited
+# Author: Kenny Millington <kenny.millington@3ait.co.uk>
+#
+# Credit: This script is based on the ideas of BoxReport.pl by Matt Brown of
+# Three A IT Support Limited.
+#
+################################################################################
+# !! Important !!
+# To make use of this script you need to run the boxbackup client with the -v
+# commandline option and set LogAllFileAccess = yes in your bbackupd.conf file.
+#
+# Notes on lazy mode:
+# If reporting on lazy mode backups you absolutely must ensure that
+# logrotate (or similar) rotates the log files at the same rate at
+# which you run this reporting script or you will report on the same
+# backup sessions on each execution.
+#
+# Notes on --rotate and log rotation in general:
+# The use-case for --rotate that I imagine is that you'll add a line like the
+# following into your syslog.conf file:-
+#
+# local6.* -/var/log/box
+#
+# Then specifying --rotate to this script will make it rotate the logs
+# each time you report on the backup so that you don't risk a backup session
+# being spread across two log files (e.g. syslog and syslog.0).
+#
+# NB: To do this you'll need to prevent logrotate/syslog from rotating your
+# /var/log/box file. On Debian based distros you'll need to edit two files.
+#
+# First: /etc/cron.daily/sysklogd, find the following line and make the
+# the required change:
+# Change: for LOG in `syslogd-listfiles`
+# To: for LOG in `syslogd-listfiles -s box`
+#
+# Second: /etc/cron.weekly/sysklogd, find the following line and make the
+# the required change:
+# Change: for LOG in `syslogd-listfiles --weekly`
+# To: for LOG in `syslogd-listfiles --weekly -s box`
+#
+# Alternatively, if suitable just ensure the backups stop before the
+# /etc/cron.daily/sysklogd file runs (usually 6:25am) and report on it
+# before the files get rotated. (If going for this option I'd just use
+# the main syslog file instead of creating a separate log file for box
+# backup since you know for a fact the syslog will get rotated daily.)
+#
+################################################################################
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# If sendmail is not in one of these paths, add the path.
+SENDMAIL_PATHS = ["/usr/sbin/", "/usr/bin/", "/bin/" , "/sbin/"]
+
+# The name of the sendmail binary, you probably won't need to change this.
+SENDMAIL_BIN = "sendmail"
+
+# Number of files to rotate around
+ROTATE_COUNT = 7
+
+# Import the required libraries
+import sys, os, re, getopt, shutil, gzip
+
+class BoxBackupReporter:
+ class BoxBackupReporterError(Exception):
+ pass
+
+ def __init__(self, config_file="/etc/box/bbackupd.conf",
+ log_file="/var/log/syslog", email_to=None,
+ email_from="report@boxbackup", rotate=False,
+ verbose=False, stats=False, sort=False, debug=False):
+
+ # Config options
+ self.config_file = config_file
+ self.log_file = log_file
+ self.email_to = email_to
+ self.email_from = email_from
+ self.rotate_log_file = rotate
+ self.verbose_report = verbose
+ self.usage_stats = stats
+ self.sort_files = sort
+ self.debug = debug
+
+ # Regex's
+ self.re_automatic_backup = re.compile(" *AutomaticBackup *= *no", re.I)
+ self.re_syslog = re.compile("(\S+) +(\S+) +([\d:]+) +(\S+) +([^:]+): +"+
+ "(?:[A-Z]+:)? *([^:]+): *(.*)")
+
+ # Initialise report
+ self.reset()
+
+ def _debug(self, msg):
+ if self.debug:
+ sys.stderr.write("[bbreporter.py Debug]: %s\n" % msg)
+
+ def reset(self):
+ # Reset report data to default values
+ self.hostname = ""
+ self.patched_files = []
+ self.synced_files = []
+ self.uploaded_files = []
+ self.warnings = []
+ self.errors = []
+ self.stats = None
+ self.start_datetime = "Unknown"
+ self.end_datetime = "Unfinished"
+ self.report = "No report generated"
+
+ def run(self):
+ try:
+ self._determine_operating_mode()
+
+ if self.lazy_mode:
+ self._debug("Operating in LAZY MODE.")
+ else:
+ self._debug("Operating in SNAPSHOT MODE.")
+
+ except IOError:
+ raise BoxBackupReporter.BoxBackupReporterError("Error: "+\
+ "Config file \"%s\" could not be read." % self.config_file)
+
+ try:
+ self._parse_syslog()
+ except IOError:
+ raise BoxBackupReporter.BoxBackupReporterError("Error: "+\
+ "Log file \"%s\" could not be read." % self.log_file)
+
+ self._parse_stats()
+ self._generate_report()
+
+ def deliver(self):
+ # If we're not e-mailing the report then just dump it to stdout
+ # and return.
+ if self.email_to is None:
+ print self.report
+ # Now that we've delivered the report it's time to rotate the logs
+ # if we're requested to do so.
+ self._rotate_log()
+ return
+
+ # Locate the sendmail binary
+ sendmail = self._locate_sendmail()
+ if(sendmail is None):
+ raise BoxBackupReporter.BoxBackupReporterError("Error: "+\
+ "Could not find sendmail binary - Unable to send e-mail!")
+
+
+ # Set the subject based on whether we think we failed or not.
+ # (suffice it to say I consider getting an error and backing up
+ # no files a failure or indeed not finding a start time in the logs).
+ subject = "BoxBackup Reporter (%s) - " % self.hostname
+ if self.start_datetime == "Unknown" or\
+ (len(self.patched_files) == 0 and len(self.synced_files) == 0 and\
+ len(self.uploaded_files) == 0):
+ subject = subject + "FAILED"
+ else:
+ subject = subject + "SUCCESS"
+
+ if len(self.errors) > 0:
+ subject = subject + " (with errors)"
+
+ # Prepare the e-mail message.
+ mail = []
+ mail.append("To: " + self.email_to)
+ mail.append("From: " + self.email_from)
+ mail.append("Subject: " + subject)
+ mail.append("")
+ mail.append(self.report)
+
+ # Send the mail.
+ p = os.popen(sendmail + " -t", "w")
+ p.write("\r\n".join(mail))
+ p.close()
+
+ # Now that we've delivered the report it's time to rotate the logs
+ # if we're requested to do so.
+ self._rotate_log()
+
+ def _determine_operating_mode(self):
+ # Scan the config file and determine if we're running in lazy or
+ # snapshot mode.
+ cfh = open(self.config_file)
+
+ for line in cfh:
+ if not line.startswith("#"):
+ if self.re_automatic_backup.match(line):
+ self.lazy_mode = False
+ cfh.close()
+ return
+
+ self.lazy_mode = True
+ cfh.close()
+
+ def _parse_syslog(self):
+ lfh = open(self.log_file)
+
+ patched_files = {}
+ uploaded_files = {}
+ synced_files = {}
+
+ for line in lfh:
+ # Only run the regex if we find a box backup entry.
+ if line.find("Box Backup") > -1 or line.find("bbackupd") > -1:
+ raw_data = self.re_syslog.findall(line)
+ try:
+ data = raw_data[0]
+ except IndexError:
+ # If the regex didn't match it's not a message that we're
+ # interested in so move to the next line.
+ continue
+
+ # Set the hostname, it shouldn't change in a log file
+ self.hostname = data[3]
+
+ # If we find the backup-start event then set the start_datetime.
+ if data[6].find("backup-start") > -1:
+ # If we're not in lazy mode or the start_datetime hasn't
+ # been set then reset the data and set it.
+ #
+ # If we're in lazy mode and encounter a second backup-start
+ # we don't want to change the start_datetime likewise if
+ # we're not in lazy mode we do want to and we want to reset
+ # so we only capture the most recent session.
+ if not self.lazy_mode or self.start_datetime == "Unknown":
+ self._debug("Reset start dtime with old time: %s." %
+ self.start_datetime)
+
+ # Reset ourselves
+ self.reset()
+
+ # Reset our temporary variables which we store
+ # the files in.
+ patched_files = {}
+ uploaded_files = {}
+ synced_files = {}
+
+ self.start_datetime = data[1]+" "+data[0]+ " "+data[2]
+ self._debug("Reset start dtime with new time %s." %
+ self.start_datetime)
+
+ # If we find the backup-finish event then set the end_datetime.
+ elif data[6].find("backup-finish") > -1:
+ self.end_datetime = data[1] + " " + data[0] + " " + data[2]
+ self._debug("Set end dtime: %s" % self.end_datetime)
+
+ # Only log the events if we have our start time.
+ elif self.start_datetime != "Unknown":
+ # We found a patch event, add the file to the patched_files.
+ if data[5] == "Uploading patch to file":
+ patched_files[data[6]] = ""
+
+ # We found an upload event, add to uploaded files.
+ elif data[5] == "Uploading complete file":
+ uploaded_files[data[6]] = ""
+
+ # We found another upload event.
+ elif data[5] == "Uploaded file":
+ uploaded_files[data[6]] = ""
+
+ # We found a sync event, add the file to the synced_files.
+ elif data[5] == "Synchronised file":
+ synced_files[data[6]] = ""
+
+ # We found a warning, add the warning to the warnings.
+ elif data[5] == "WARNING":
+ self.warnings.append(data[6])
+
+ # We found an error, add the error to the errors.
+ elif data[5] == "ERROR":
+ self.errors.append(data[6])
+
+
+ self.patched_files = patched_files.keys()
+ self.uploaded_files = uploaded_files.keys()
+ self.synced_files = synced_files.keys()
+
+ # There's no point running the sort functions if we're not going
+ # to display the resultant lists.
+ if self.sort_files and self.verbose_report:
+ self.patched_files.sort()
+ self.uploaded_files.sort()
+
+
+ lfh.close()
+
+ def _parse_stats(self):
+ if(not self.usage_stats):
+ return
+
+ # Grab the stats from bbackupquery
+ sfh = os.popen("bbackupquery usage quit", "r")
+ raw_stats = sfh.read()
+ sfh.close()
+
+ # Parse the stats
+ stats_re = re.compile("commands.[\n ]*\n(.*)\n+", re.S)
+ stats = stats_re.findall(raw_stats)
+
+ try:
+ self.stats = stats[0]
+ except IndexError:
+ self.stats = "Unable to retrieve usage information."
+
+ def _generate_report(self):
+ if self.start_datetime == "Unknown":
+ self.report = "No report data has been found."
+ return
+
+ total_files = len(self.patched_files) + len(self.uploaded_files)
+
+ report = []
+ report.append("--------------------------------------------------")
+ report.append("Report Title : Box Backup - Backup Statistics")
+ report.append("Report Period : %s - %s" % (self.start_datetime,
+ self.end_datetime))
+ report.append("--------------------------------------------------")
+ report.append("")
+ report.append("This is your box backup report, in summary:")
+ report.append("")
+ report.append("%d file(s) have been backed up." % total_files)
+ report.append("%d file(s) were uploaded." % len(self.uploaded_files))
+ report.append("%d file(s) were patched." % len(self.patched_files))
+ report.append("%d file(s) were synchronised." % len(self.synced_files))
+
+ report.append("")
+ report.append("%d warning(s) occurred." % len(self.warnings))
+ report.append("%d error(s) occurred." % len(self.errors))
+ report.append("")
+ report.append("")
+
+ # If we asked for the backup stats and they're available
+ # show them.
+ if(self.stats is not None and self.stats != ""):
+ report.append("Your backup usage information follows:")
+ report.append("")
+ report.append(self.stats)
+ report.append("")
+ report.append("")
+
+ # List the files if we've been asked for a verbose report.
+ if(self.verbose_report):
+ if len(self.uploaded_files) > 0:
+ report.append("Uploaded Files (%d)" % len(self.uploaded_files))
+ report.append("---------------------")
+ for file in self.uploaded_files:
+ report.append(file)
+ report.append("")
+ report.append("")
+
+ if len(self.patched_files) > 0:
+ report.append("Patched Files (%d)" % len(self.patched_files))
+ report.append("---------------------")
+ for file in self.patched_files:
+ report.append(file)
+ report.append("")
+ report.append("")
+
+ # Always output the warnings/errors.
+ if len(self.warnings) > 0:
+ report.append("Warnings (%d)" % len(self.warnings))
+ report.append("---------------------")
+ for warning in self.warnings:
+ report.append(warning)
+ report.append("")
+ report.append("")
+
+ if len(self.errors) > 0:
+ report.append("Errors (%d)" % len(self.errors))
+ report.append("---------------------")
+ for error in self.errors:
+ report.append(error)
+ report.append("")
+ report.append("")
+
+ self.report = "\r\n".join(report)
+
+ def _locate_sendmail(self):
+ for path in SENDMAIL_PATHS:
+ sendmail = os.path.join(path, SENDMAIL_BIN)
+ if os.path.isfile(sendmail):
+ return sendmail
+
+ return None
+
+ def _rotate_log(self):
+ # If we're not configured to rotate then abort.
+ if(not self.rotate_log_file):
+ return
+
+ # So we have these files to possibly account for while we process the
+ # rotation:-
+ # self.log_file, self.log_file.0, self.log_file.1.gz, self.log_file.2.gz
+ # self.log_file.3.gz....self.log_file.(ROTATE_COUNT-1).gz
+ #
+ # Algorithm:-
+ # * Delete last file.
+ # * Work backwards moving 5->6, 4->5, 3->4, etc... but stop at .0
+ # * For .0 move it to .1 then gzip it.
+ # * Move self.log_file to .0
+ # * Done.
+
+ # If it exists, remove the oldest file.
+ if(os.path.isfile(self.log_file + ".%d.gz" % (ROTATE_COUNT - 1))):
+ os.unlink(self.log_file + ".%d.gz" % (ROTATE_COUNT - 1))
+
+ # Copy through the other gzipped log files.
+ for i in range(ROTATE_COUNT - 1, 1, -1):
+ src_file = self.log_file + ".%d.gz" % (i - 1)
+ dst_file = self.log_file + ".%d.gz" % i
+
+ # If the source file exists move/rename it.
+ if(os.path.isfile(src_file)):
+ shutil.move(src_file, dst_file)
+
+ # Now we need to handle the .0 -> .1.gz case.
+ if(os.path.isfile(self.log_file + ".0")):
+ # Move .0 to .1
+ shutil.move(self.log_file + ".0", self.log_file + ".1")
+
+ # gzip the file.
+ fh = open(self.log_file + ".1", "r")
+ zfh = gzip.GzipFile(self.log_file + ".1.gz", "w")
+ zfh.write(fh.read())
+ zfh.flush()
+ zfh.close()
+ fh.close()
+
+ # If gzip worked remove the original .1 file.
+ if(os.path.isfile(self.log_file + ".1.gz")):
+ os.unlink(self.log_file + ".1")
+
+ # Finally move the current logfile to .0
+ shutil.move(self.log_file, self.log_file + ".0")
+
+
+def stderr(text):
+ sys.stderr.write("%s\n" % text)
+
+def usage():
+ stderr("Usage: %s [OPTIONS]\n" % sys.argv[0])
+ stderr("Valid Options:-")
+ stderr(" --logfile=LOGFILE\t\t\tSpecify the logfile to process,\n"+\
+ "\t\t\t\t\tdefault: /var/log/syslog\n")
+
+ stderr(" --configfile=CONFIGFILE\t\tSpecify the bbackupd config file,\n "+\
+ "\t\t\t\t\tdefault: /etc/box/bbackupd.conf\n")
+
+ stderr(" --email-to=user@example.com\t\tSpecify the e-mail address(es)\n"+\
+ "\t\t\t\t\tto send the report to, default is to\n"+\
+ "\t\t\t\t\tdisplay the report on the console.\n")
+
+ stderr(" --email-from=user@example.com\t\tSpecify the e-mail address(es)"+\
+ "\n\t\t\t\t\tto set the From: address to,\n "+\
+ "\t\t\t\t\tdefault: report@boxbackup\n")
+
+ stderr(" --stats\t\t\t\tIncludes the usage stats retrieved from \n"+\
+ "\t\t\t\t\t'bbackupquery usage' in the report.\n")
+
+ stderr(" --sort\t\t\t\tSorts the file lists in verbose mode.\n")
+
+ stderr(" --debug\t\t\t\tEnables debug output.\n")
+
+ stderr(" --verbose\t\t\t\tList every file that was backed up to\n"+\
+ "\t\t\t\t\tthe server, default is to just display\n"+\
+ "\t\t\t\t\tthe summary.\n")
+
+ stderr(" --rotate\t\t\t\tRotates the log files like logrotate\n"+\
+ "\t\t\t\t\twould, see the comments for a use-case.\n")
+
+def main():
+ # The defaults
+ logfile = "/var/log/syslog"
+ configfile = "/etc/box/bbackupd.conf"
+ email_to = None
+ email_from = "report@boxbackup"
+ rotate = False
+ verbose = False
+ stats = False
+ sort = False
+ debug = False
+ # Parse the options
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "dosrvhl:c:t:f:",
+ ["help", "logfile=", "configfile=","email-to=",
+ "email-from=","rotate","verbose","stats","sort",
+ "debug"])
+ except getopt.GetoptError:
+ usage()
+ return
+
+ for opt, arg in opts:
+ if(opt in ("--logfile","-l")):
+ logfile = arg
+ elif(opt in ("--configfile", "-c")):
+ configfile = arg
+ elif(opt in ("--email-to", "-t")):
+ email_to = arg
+ elif(opt in ("--email-from", "-f")):
+ email_from = arg
+ elif(opt in ("--rotate", "-r")):
+ rotate = True
+ elif(opt in ("--verbose", "-v")):
+ verbose = True
+ elif(opt in ("--stats", "-s")):
+ stats = True
+ elif(opt in ("--sort", "-o")):
+ sort = True
+ elif(opt in ("--debug", "-d")):
+ debug = True
+ elif(opt in ("--help", "-h")):
+ usage()
+ return
+
+ # Run the reporter
+ bbr = BoxBackupReporter(configfile, logfile, email_to, email_from,
+ rotate, verbose, stats, sort, debug)
+ try:
+ bbr.run()
+ bbr.deliver()
+ except BoxBackupReporter.BoxBackupReporterError, error_msg:
+ print error_msg
+
+if __name__ == "__main__":
+ main()
diff --git a/contrib/debian/bbackupd.in b/contrib/debian/bbackupd.in
index 78d4f4ee..bb7536b5 100644
--- a/contrib/debian/bbackupd.in
+++ b/contrib/debian/bbackupd.in
@@ -1,46 +1,59 @@
#! /bin/sh
# Start and stop the Box Backup client daemon.
+# Originally by James Stark, modified by Chris Wilson and James O'Gorman
+# For support, visit http://www.boxbackup.org/trac/wiki/MailingLists
-BBACKUPD=@bindir_expanded@/bbackupd
-CONFIG=@sysconfdir_expanded@/box/bbackupd.conf
-PIDFILE=@localstatedir_expanded@/bbackupd.pid
+NAME=bbackupd
+LONGNAME="Box Backup Client daemon"
+BINARY=@sbindir_expanded@/$NAME
+CONFIG=@sysconfdir_expanded@/box/$NAME.conf
+PIDFILE=@localstatedir_expanded@/bbackupd/$NAME.pid
-test -x $BBACKUPD || exit 0
+test -x $BINARY || exit 0
test -f $CONFIG || exit 0
+start_stop() {
+ start-stop-daemon --quiet --exec $BINARY --pidfile $PIDFILE "$@"
+}
+
+start_stop_verbose() {
+ if start_stop "$@"; then
+ echo "."
+ else
+ echo " failed!"
+ exit 1
+ fi
+}
+
case $1 in
start)
- echo -n "Starting Box Backup Client daemon: bbackupd"
- start-stop-daemon --start --quiet --exec $BBACKUPD > /dev/null
- echo "."
+ echo -n "Starting $LONGNAME: $NAME"
+ start_stop_verbose --start
;;
-
+
stop)
- echo -n "Stopping Box Backup Client daemon: bbackupd"
- start-stop-daemon --stop --quiet \
- --pidfile $PIDFILE --exec $BBACKUPD
- echo "."
+ echo -n "Stopping $LONGNAME: $NAME"
+ start_stop_verbose --stop
;;
reload|force-reload)
- echo -n "Reloading Box Backup Client configuration"
- start-stop-daemon --stop --signal 1 --quiet --oknodo \
- --pidfile $PIDFILE --exec $BBACKUPD
- echo "."
+ echo -n "Reloading $LONGNAME configuration"
+ start_stop_verbose --stop --signal 1
;;
-
+
restart)
- echo -n "Restarting Box Backup Client daemon: bbackupd"
- start-stop-daemon --stop --quiet --pidfile $PIDFILE \
- --exec $BBACKUPD
- start-stop-daemon --start --quiet \
- --exec $BBACKUPD > /dev/null
- echo "."
+ echo -n "Restarting $LONGNAME: $NAME"
+ if start_stop --stop --retry 5 && start_stop --start; then
+ echo "."
+ else
+ echo " failed!"
+ exit 1
+ fi
;;
+
*)
- echo "Usage: /etc/init.d/bbackupd {start|stop|reload|force-reload|restart}"
- exit 1
+ echo "Usage: $0 {start|stop|reload|force-reload|restart}"
esac
exit 0
diff --git a/contrib/debian/bbstored.in b/contrib/debian/bbstored.in
index da6a50a0..48fc47f1 100644
--- a/contrib/debian/bbstored.in
+++ b/contrib/debian/bbstored.in
@@ -1,46 +1,59 @@
#! /bin/sh
# Start and stop the Box Backup server daemon.
+# Originally by James Stark, modified by Chris Wilson and James O'Gorman
+# For support, visit http://www.boxbackup.org/trac/wiki/MailingLists
-BBSTORED=@bindir_expanded@/bbstored
-CONFIG=@sysconfdir_expanded@/box/bbstored.conf
-PIDFILE=@localstatedir_expanded@/bbstored.pid
+NAME=bbstored
+LONGNAME="Box Backup Server daemon"
+BINARY=@sbindir_expanded@/$NAME
+CONFIG=@sysconfdir_expanded@/box/$NAME.conf
+PIDFILE=@localstatedir_expanded@/run/$NAME.pid
-test -x $BBACKUPD || exit 0
+test -x $BINARY || exit 0
test -f $CONFIG || exit 0
+start_stop() {
+ start-stop-daemon --quiet --exec $BINARY --pidfile $PIDFILE "$@"
+}
+
+start_stop_verbose() {
+ if start_stop "$@"; then
+ echo "."
+ else
+ echo " failed!"
+ exit 1
+ fi
+}
+
case $1 in
start)
- echo -n "Starting Box Backup Server daemon: bbstored"
- start-stop-daemon --start --quiet --exec $BBSTORED > /dev/null
- echo "."
+ echo -n "Starting $LONGNAME: $NAME"
+ start_stop_verbose --start
;;
stop)
- echo -n "Stopping Box Backup Server daemon: bbstored"
- start-stop-daemon --stop --quiet \
- --pidfile $PIDFILE --exec $BBSTORED
- echo "."
+ echo -n "Stopping $LONGNAME: $NAME"
+ start_stop_verbose --stop
;;
reload|force-reload)
- echo -n "Reloading Box Backup Server configuration"
- start-stop-daemon --stop --signal 1 --quiet --oknodo \
- --pidfile $PIDFILE --exec $BBSTORED
- echo "."
+ echo -n "Reloading $LONGNAME configuration"
+ start_stop_verbose --stop --signal 1
;;
restart)
- echo -n "Restarting Box Backup Server daemon: bbstored"
- start-stop-daemon --stop --quiet --pidfile $PIDFILE \
- --exec $BBSTORED
- start-stop-daemon --start --quiet \
- --exec $BBSTORED > /dev/null
- echo "."
+ echo -n "Restarting $LONGNAME: $NAME"
+ if start_stop --stop --retry 5 && start_stop --start; then
+ echo "."
+ else
+ echo " failed!"
+ exit 1
+ fi
;;
*)
- echo "Usage: /etc/init.d/bbstored {start|stop|reload|force-reload|restart}"
+ echo "Usage: $0 {start|stop|reload|force-reload|restart}"
esac
exit 0
diff --git a/contrib/mac_osx/org.boxbackup.bbackupd.plist.in b/contrib/mac_osx/org.boxbackup.bbackupd.plist.in
new file mode 100644
index 00000000..803deece
--- /dev/null
+++ b/contrib/mac_osx/org.boxbackup.bbackupd.plist.in
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>org.boxbackup.bbackupd</string>
+ <key>RunAtLoad</key>
+ <true/>
+ <key>ProgramArguments</key>
+ <array>
+ <string>@prefix@/sbin/bbackupd</string>
+ <string>-F</string>
+ <string>@prefix@/etc/boxbackup/bbackupd.conf</string>
+ </array>
+ <key>LowPriorityIO</key>
+ <true/>
+ <key>Nice</key>
+ <integer>1</integer>
+</dict>
+</plist>
diff --git a/contrib/mac_osx/org.boxbackup.bbstored.plist.in b/contrib/mac_osx/org.boxbackup.bbstored.plist.in
new file mode 100644
index 00000000..bfe8c88c
--- /dev/null
+++ b/contrib/mac_osx/org.boxbackup.bbstored.plist.in
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>org.boxbackup.bbstored</string>
+ <key>RunAtLoad</key>
+ <true/>
+ <key>ProgramArguments</key>
+ <array>
+ <string>@prefix@/sbin/bbstored</string>
+ <string>-F</string>
+ <string>@prefix@/etc/boxbackup/bbackupd.conf</string>
+ </array>
+ </array>
+ <key>LowPriorityIO</key>
+ <true/>
+ <key>Nice</key>
+ <integer>1</integer>
+</dict>
+</plist>
diff --git a/contrib/redhat/bbackupd.in b/contrib/redhat/bbackupd.in
index e8ecdc68..2f51137a 100644
--- a/contrib/redhat/bbackupd.in
+++ b/contrib/redhat/bbackupd.in
@@ -23,7 +23,7 @@ prog="bbackupd"
start() {
echo -n $"Starting $prog: "
- daemon $prog
+ daemon @sbindir_expanded@/$prog
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
@@ -32,7 +32,7 @@ start() {
stop() {
echo -n $"Stopping $prog: "
- killproc $prog
+ killproc @sbindir_expanded@/$prog
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
@@ -40,7 +40,7 @@ stop() {
}
rhstatus() {
- status $prog
+ status @sbindir_expanded@/$prog
}
restart() {
@@ -50,7 +50,7 @@ restart() {
reload() {
echo -n $"Reloading $prog configuration: "
- killproc $prog -HUP
+ killproc @sbindir_expanded@/$prog -HUP
retval=$?
echo
return $RETVAL
diff --git a/contrib/redhat/bbstored.in b/contrib/redhat/bbstored.in
index c7675df5..755a8569 100644
--- a/contrib/redhat/bbstored.in
+++ b/contrib/redhat/bbstored.in
@@ -23,7 +23,7 @@ prog="bbstored"
start() {
echo -n $"Starting $prog: "
- daemon $prog
+ daemon @sbindir_expanded@/$prog
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
@@ -32,7 +32,7 @@ start() {
stop() {
echo -n $"Stopping $prog: "
- killproc $prog
+ killproc @sbindir_expanded@/$prog
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
@@ -40,7 +40,7 @@ stop() {
}
rhstatus() {
- status $prog
+ status @sbindir_expanded@/$prog
}
restart() {
@@ -50,7 +50,7 @@ restart() {
reload() {
echo -n $"Reloading $prog configuration: "
- killproc $prog -HUP
+ killproc @sbindir_expanded@/$prog -HUP
retval=$?
echo
return $RETVAL
diff --git a/contrib/rpm/boxbackup.spec b/contrib/rpm/boxbackup.spec
index a5b287ff..9c494159 100644
--- a/contrib/rpm/boxbackup.spec
+++ b/contrib/rpm/boxbackup.spec
@@ -17,7 +17,7 @@
# Detect distribution. So far we only special-case SUSE. If you need to make
# any distro specific changes to get the package building on your system
-# please email them to boxbackup-dev@fluffy.co.uk
+# please email them to boxbackup-dev@boxbackup.org
#%define is_fc %(test -e %{_sysconfdir}/fedora-release && echo 1 || echo 0)
#%define is_mdk %(test -e %{_sysconfdir}/mandrake-release && echo 1 || echo 0)
#%define is_rh %(test -e %{_sysconfdir}/redhat-release && echo 1 || echo 0)
@@ -39,8 +39,8 @@ Version: ###DISTRIBUTION-VERSION-NUMBER###
Release: 1
License: BSD
Group: Applications/Archiving
-Packager: Martin Ebourne <boxbackup-dev@fluffy.co.uk>
-URL: http://www.fluffy.co.uk/boxbackup/
+Packager: boxbackup-dev@boxbackup.org
+URL: http://www.boxbackup.org/
Source0: %{ident}.tgz
Requires: openssl >= 0.9.7a
BuildRoot: %{_tmppath}/%{ident}-%{release}-root
@@ -96,6 +96,7 @@ make
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT%{_docdir}/%{ident}
+mkdir -p $RPM_BUILD_ROOT%{_docdir}/%{ident}/bbreporter
mkdir -p $RPM_BUILD_ROOT%{_bindir}
mkdir -p $RPM_BUILD_ROOT%{_sbindir}
mkdir -p $RPM_BUILD_ROOT%{init_dir}
@@ -121,6 +122,11 @@ install -m 644 %{distribution_dir}LINUX.txt \
install -m 644 %{distribution_dir}THANKS.txt \
$RPM_BUILD_ROOT%{_docdir}/%{ident}
+install -m 644 contrib/bbreporter/LICENSE \
+ $RPM_BUILD_ROOT%{_docdir}/%{ident}/bbreporter
+install -m 755 contrib/bbreporter/bbreporter.py \
+ $RPM_BUILD_ROOT%{_docdir}/%{ident}/bbreporter
+
# Client
touch $RPM_BUILD_ROOT%{_sysconfdir}/box/bbackupd.conf
install -m 755 contrib/%{dist}/bbackupd $RPM_BUILD_ROOT%{init_dir}
@@ -143,7 +149,7 @@ ln -s ../../%{init_dir}/bbstored $RPM_BUILD_ROOT%{_sbindir}/rcbbstored
%define server_dir parcels/%{ident}-backup-server-linux-gnu
install %{server_dir}/bbstored $RPM_BUILD_ROOT%{_sbindir}
install %{server_dir}/bbstoreaccounts $RPM_BUILD_ROOT%{_sbindir}
-install %{server_dir}/bbstored-certs $RPM_BUILD_ROOT%{_bindir}
+install %{server_dir}/bbstored-certs $RPM_BUILD_ROOT%{_sbindir}
install %{server_dir}/bbstored-config $RPM_BUILD_ROOT%{_sbindir}
install %{server_dir}/raidfile-config $RPM_BUILD_ROOT%{_sbindir}
@@ -213,11 +219,15 @@ rm -rf $RPM_BUILD_ROOT
%config %ghost %{_sysconfdir}/box/raidfile.conf
%{_sbindir}/bbstored
%{_sbindir}/bbstoreaccounts
-%{_bindir}/bbstored-certs
+%{_sbindir}/bbstored-certs
%{_sbindir}/bbstored-config
%{_sbindir}/raidfile-config
+%doc %{_docdir}/%{ident}/bbreporter
%changelog
+* Thu May 29 2008 Martin Ebourne <martin@zepler.org>
+- Fix paths to bbreporter files
+
* Sat Jan 13 2006 Chris Wilson <chris+box@qwirx.com>
- Support building from an unofficial tarball (from svn) by changing
%{distribution_dir} at the top.
diff --git a/contrib/solaris/bbackupd-smf-method.in b/contrib/solaris/bbackupd-smf-method.in
index 2c839961..0ff610bf 100755
--- a/contrib/solaris/bbackupd-smf-method.in
+++ b/contrib/solaris/bbackupd-smf-method.in
@@ -5,7 +5,7 @@ case $1 in
# SMF arguments (start and restart [really "refresh"])
'start')
- @bindir_expanded@/bbackupd
+ @sbindir_expanded@/bbackupd
;;
'restart')
diff --git a/contrib/solaris/bbstored-smf-method.in b/contrib/solaris/bbstored-smf-method.in
index 0ea25e40..dbdb3e69 100755
--- a/contrib/solaris/bbstored-smf-method.in
+++ b/contrib/solaris/bbstored-smf-method.in
@@ -4,7 +4,7 @@ case $1 in
# SMF arguments (start and restart [really "refresh"])
'start')
- @bindir_expanded@/bbstored
+ @sbindir_expanded@/bbstored
;;
'restart')
diff --git a/contrib/suse/bbackupd.in b/contrib/suse/bbackupd.in
index d3a5659e..77fd40ba 100644
--- a/contrib/suse/bbackupd.in
+++ b/contrib/suse/bbackupd.in
@@ -28,7 +28,7 @@
### END INIT INFO
# Check for missing binaries (stale symlinks should not happen)
-BBACKUPD_BIN=@bindir_expanded@/bbackupd
+BBACKUPD_BIN=@sbindir_expanded@/bbackupd
if [ ! -x $BBACKUPD_BIN ] ; then
echo "$BBACKUPD_BIN not installed"
exit 5
@@ -91,7 +91,7 @@ case "$1" in
probe)
test @sysconfdir_expanded@/box/bbackupd.conf \
- -nt @localstatedir_expanded@/bbackupd.pid \
+ -nt @localstatedir_expanded@/bbackupd/bbackupd.pid \
&& echo reload
;;
diff --git a/contrib/suse/bbstored.in b/contrib/suse/bbstored.in
index e8c74278..e84edfd1 100644
--- a/contrib/suse/bbstored.in
+++ b/contrib/suse/bbstored.in
@@ -29,7 +29,7 @@
#
# Check for missing binaries (stale symlinks should not happen)
-BBSTORED_BIN=@bindir_expanded@/bbstored
+BBSTORED_BIN=@sbindir_expanded@/bbstored
if [ ! -x $BBSTORED_BIN ] ; then
echo "$BBSTORED_BIN not installed"
exit 5
@@ -92,7 +92,7 @@ case "$1" in
probe)
test @sysconfdir_expanded@/box/bbstored.conf \
- -nt @localstatedir_expanded@/bbstored.pid && echo reload
+ -nt @localstatedir_expanded@/run/bbstored.pid && echo reload
;;
*)
diff --git a/contrib/windows/installer/boxbackup.mpi.in b/contrib/windows/installer/boxbackup.mpi.in
new file mode 100755
index 00000000..12f6d62d
--- /dev/null
+++ b/contrib/windows/installer/boxbackup.mpi.in
@@ -0,0 +1,3392 @@
+array set info {
+AccountNo
+10005005
+
+AllowLanguageSelection
+No
+
+AppName
+<%BrandName%>
+
+ApplicationID
+E10C6FD9-E524-28BD-B0AB3588F16C
+
+ApplicationURL
+http://www.boxbackup.org/
+
+AutoFileGroups
+No
+
+AutoRefreshFiles
+Yes
+
+BBVersionNo
+@box_version@
+
+BrandName
+{Box Backup}
+
+BuildFailureAction
+{Fail (recommended)}
+
+CancelledInstallAction
+{Rollback and Stop}
+
+CleanupCancelledInstall
+Yes
+
+CommandLineFailureAction
+{Fail (recommended)}
+
+Company
+{Tebuco, Inc. and Ben Summers and Contributors}
+
+CompressionLevel
+6
+
+CompressionMethod
+zlib
+
+ConfigFileName
+{<%InstallDir%>\bbackupd.conf}
+
+ConfigFileTemplate
+{<%InstallDir%>\templates\template.conf}
+
+Copyright
+{2003-2008 Tebuco, Inc. and Ben Summers and Contributors}
+
+CreateDesktopShortcut
+No
+
+CreateQuickLaunchShortcut
+No
+
+DefaultDirectoryLocation
+{}
+
+DefaultLanguage
+English
+
+EncryptedKeyFilePassword
+Enter_EncryptedKeys_Password_Here
+
+Ext
+.exe
+
+ExtractSolidArchivesOnStartup
+No
+
+Icon
+{}
+
+Image
+@build_dir@/docs/html/images/bblogo.png
+
+IncludeDebugging
+Yes
+
+InstallDirSuffix
+<%ShortAppName%>
+
+InstallPassword
+{}
+
+InstallVersion
+@box_version@
+
+Language,de
+No
+
+Language,en
+Yes
+
+Language,es
+No
+
+Language,fr
+No
+
+Language,hu
+Yes
+
+Language,it
+Yes
+
+Language,nl
+Yes
+
+Language,pl
+No
+
+Language,pt_br
+No
+
+Language,ru
+Yes
+
+LaunchApplication
+No
+
+PackageDescription
+{<%BrandName%> Backup Service}
+
+PackageLicense
+{}
+
+PackageMaintainer
+{Tebuco, Inc. and Ben Summers and Contributors}
+
+PackageName
+<%ShortAppName%>
+
+PackagePackager
+{Tebuco, Inc. and Ben Summers and Contributors}
+
+PackageRelease
+<%PatchVersion%>
+
+PackageSummary
+{}
+
+PackageVersion
+<%MajorVersion%>.<%MinorVersion%>
+
+PreserveFileAttributes
+Yes
+
+PreserveFilePermissions
+Yes
+
+ProjectID
+140B9882-3327-FEA8-13415A62FBB2
+
+ProjectVersion
+1.2.9.0
+
+SaveOnlyToplevelDirs
+No
+
+ScriptExt
+.bat
+
+ServiceExeName
+bbackupd.exe
+
+ServiceName
+<%BrandName%>
+
+ShortAppName
+<%BrandName%>
+
+SkipUnusedFileGroups
+Yes
+
+SystemLanguage
+en_us
+
+Theme
+Modern_Wizard
+
+ThemeDir
+Modern_Wizard
+
+ThemeVersion
+1
+
+UpgradeApplicationID
+{}
+
+UserInfoAcctNo
+<%AccountNo%>
+
+UserInfoCompany
+{}
+
+UserInfoEmail
+{}
+
+UserInfoName
+{}
+
+UserInfoPhone
+{}
+
+Version
+@box_version@
+
+ViewReadme
+No
+
+WizardHeight
+365
+
+WizardWidth
+500
+
+}
+
+array set ::InstallJammer::InstallCommandLineOptions {
+D
+{{} Prefix No No {} {set the value of an option in the installer}}
+
+S
+{InstallMode Switch No No Silent {run the installer in silent mode}}
+
+T
+{Testing Switch Yes No {} {run installer without installing any files}}
+
+Y
+{InstallMode Switch No No Default {accept all defaults and run the installer}}
+
+debug
+{Debugging Switch Yes No {} {run installer in debug mode}}
+
+debugconsole
+{ShowConsole Switch Yes No {} {run installer with a debug console open}}
+
+mode
+{InstallMode Choice No No {Console Default Silent Standard} {set the mode to run the installer in}}
+
+prefix
+{InstallDir String No No {} {set the installation directory}}
+
+test
+{Testing Switch Yes No {} {run installer without installing any files}}
+
+}
+array set ::InstallJammer::UninstallCommandLineOptions {
+S
+{InstallMode Switch No No Silent {run the uninstaller in silent mode}}
+
+Y
+{InstallMode Switch No No Default {accept all defaults and run the uninstaller}}
+
+debugconsole
+{ShowConsole Switch Yes No {} {run uninstaller with a debug console open}}
+
+mode
+{UninstallMode Choice No No {Console Silent Standard} {set the mode to run the uninstaller in}}
+
+test
+{Testing Switch Yes No {} {run uninstaller without uninstalling any files}}
+
+}
+FileGroup ::481451CC-F49C-D389-8645076F595B -setup Install -active Yes -platforms {Windows MacOS-X} -name {Program Files} -parent FileGroups
+File ::B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 -filemethod {Update files with more recent dates} -type dir -directory <%InstallDir%> -name /home/petjal/doc/teb/cli/bu/installer/win/2.2 -location @client_parcel_dir@ -parent 481451CC-F49C-D389-8645076F595B
+File ::CDDED10B-2747-DD07-5F9D-42A7FD7BB7E6 -name LICENSE.txt -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::D6E262BC-8A84-B6DB-794B-8FDC8AECB079 -name mgwz.dll -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::E56A0360-7D7F-D99E-E9A4-3C20BC4C2B99 -name mingwm10.dll -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::47602DF7-8463-AB89-E13F-11983610CAA2 -type dir -name tools -location @build_dir@/contrib/windows/installer/tools -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::F7F61231-C340-5CD5-686B-01F521994B0C -name InstallService.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::68DAE474-165D-81FE-1396-FDD2E6081B41 -name KillBackupProcess.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::2436C940-3332-13AA-7613-8EE67C35CE9B -name ReloadConfig.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::336DDAC3-F3BA-1117-73D4-11DFEF9E98AB -name RemoveService.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::0C15AE46-0FF3-3B7F-FC55-D91EF279DBD3 -name RestartService.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::58D97EDE-58F2-15D7-7113-BEE3047F0782 -name StartService.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::BE7CDB16-D3FE-30FA-2153-7C0509CD5E78 -name StopService.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::73BD5859-FB38-71F8-24BD-BDCF871F9FD3 -name Sync.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::67B3838F-4EF7-2C1C-2E86-78DB8ADD6682 -name ShowUsage.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::2646A97C-C0D9-A29C-E145-C5C371F44938 -name QueryOutputAll.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::2F41E3D2-DA4D-2FCB-B3D5-F04032D17A63 -name QueryOutputCurrent.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::C6430BDD-8A07-B80E-FC0C-426C16EB4187 -name RemoteControl.exe -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::5EADAB59-F559-44CB-A3EE-9A56D4EF17C8 -type dir -name .svn -active 0 -parent 47602DF7-8463-AB89-E13F-11983610CAA2
+File ::31429CC4-525E-4E30-9328-4774AFA9F619 -name entries -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8
+File ::A27BEFB6-1421-4030-8F11-F04316BCE57C -name format -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8
+File ::C99700CE-1035-498C-9A96-B60835652077 -type dir -name prop-base -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8
+File ::6FF9DFDE-4BB7-4319-AC85-BF1E88731C1C -name InstallService.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::6AAE8600-5CFC-4240-A47F-5CFCF4E571C6 -name KillBackupProcess.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::834C33D3-4B53-4B2D-9380-A05420AEE75F -name QueryOutputAll.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::854AD23A-5DDE-44C4-900C-34F5845E6747 -name QueryOutputCurrent.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::9A587DE7-B17C-4CDF-B92C-0D267E30E361 -name ReloadConfig.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::197AAEFA-C62E-4E79-890F-C2E4C8440239 -name RemoteControl.exe.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::912E0F50-53DD-4483-A4F4-CA69944A5959 -name RemoveService.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::BDD695BF-9C7E-4F06-BBCE-EC89536DCF27 -name RestartService.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::61615EE4-BF66-40E7-B89A-E6A50B92AF93 -name ShowUsage.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::A6F7C6B7-9759-4B86-9388-4A42E6F7C5C3 -name StartService.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::45D3E7F5-277B-4E52-81BA-ED6D2BB441D7 -name StopService.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::58966972-8387-4D14-A06E-15AA176633A3 -name Sync.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077
+File ::2A77AF5B-4761-45B5-A543-6328A7F0F39B -type dir -name props -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8
+File ::BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 -type dir -name text-base -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8
+File ::D972D6B2-40E5-40B3-BC06-66B8B7F51B04 -name InstallService.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::2F14E4F3-5331-4AC5-93F7-C4748970C7F4 -name KillBackupProcess.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::9F3663B2-8BAA-4EAE-B606-53D5C922E703 -name QueryOutputAll.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::BE9BD4FB-DF44-4F4B-BB55-15285A8566BA -name QueryOutputCurrent.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::3B4E0BF4-7FDD-4903-8D43-76C43F38C6C4 -name ReloadConfig.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::038CEEA0-3F21-48F6-B109-BBE1EF7D3E96 -name RemoteControl.exe.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::0C177E04-DF2D-43AB-8A42-9E7A389AB1D1 -name RemoveService.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::9BE16F40-9D1D-4C84-843D-955A44069040 -name RestartService.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::4E503A3C-EB42-4870-9849-D508A3D9BAB7 -name ShowUsage.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::131B7D8B-1BEB-456C-8F05-386C2EAFBEAE -name StartService.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::D3D0AFC1-CB6C-42D4-8C05-21898505DA40 -name StopService.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::C89F78B2-25A7-432B-9D92-DC2AB636CFB5 -name Sync.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7
+File ::90695C82-0000-4F6A-8FE7-0ABDEAA17CAE -type dir -name tmp -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8
+File ::7DFF0EE4-7298-4C8C-A5BC-56BBDD81CFC8 -type dir -name prop-base -active 0 -parent 90695C82-0000-4F6A-8FE7-0ABDEAA17CAE
+File ::4C60E473-119E-4B0B-9B01-56240F24D9D5 -type dir -name props -active 0 -parent 90695C82-0000-4F6A-8FE7-0ABDEAA17CAE
+File ::E1E25ACC-487B-4191-B8CF-9E7C8C88EA09 -type dir -name text-base -active 0 -parent 90695C82-0000-4F6A-8FE7-0ABDEAA17CAE
+File ::E7258732-3D21-4E89-AA41-24AA8B8EBF29 -name all-wcprops -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8
+File ::9CEBA2A0-C68B-48BA-944E-2E8EE9A35D97 -name bbackupctl.exe -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::497483A6-7264-4361-86F2-F2703F719908 -name bbackupd-config -active 0 -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::204358FC-610F-47DF-8928-1D0E39921700 -targetfilename templates/original.conf -name bbackupd.conf -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::98C376E2-A6C3-4B6C-BBD4-F70CAC2E6A7B -name bbackupd.exe -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::5C3EAB34-7CD4-4DF3-9DEB-0FC23A6F5812 -name bbackupquery.exe -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::7633DBC3-EACA-4F9B-9A87-AD3AF0EC298E -name installer.iss -active 0 -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::D3CF86E1-CAFF-4342-8730-463F96EACC39 -targetfilename templates/NotifySysAdmin.original.vbs -name NotifySysAdmin.vbs -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+File ::A702625F-29C7-4CA0-A8F8-E50DBF5C541B -name uninstall.exe -active 0 -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2
+Component ::4A9C852B-647E-EED5-5482FFBCC2AF -setup Install -active Yes -platforms {Windows MacOS-X} -name {Default Component} -parent Components
+SetupType ::8202CECC-54A0-9B6C-D24D111BA52E -setup Install -active Yes -platforms {Windows MacOS-X} -name Typical -parent SetupTypes
+
+InstallComponent AE3BD5B4-35DE-4240-B79914D43E56 -setup Install -type pane -title {Welcome Screen} -component Welcome -active No -parent StandardInstall
+InstallComponent 2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8 -setup Install -type pane -conditions 4EE35849-FAD7-170B-0E45-FA30636467B1 -title {Install Password} -component InstallPassword -command insert -active No -parent StandardInstall
+Condition 4EE35849-FAD7-170B-0E45-FA30636467B1 -active Yes -parent 2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8 -title {Password Test Condition} -component PasswordTestCondition -TreeObject::id 4EE35849-FAD7-170B-0E45-FA30636467B1
+InstallComponent B3B99E2D-C368-A921-B7BC-A71EBDE3AD4D -setup Install -type action -title {Set Install Password} -component SetInstallPassword -active Yes -parent 2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8
+InstallComponent 1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E -setup Install -type pane -title {User Information} -component UserInformation -active Yes -parent StandardInstall
+InstallComponent 9013E862-8E81-5290-64F9-D8BCD13EC7E5 -setup Install -type pane -title {User Information Phone Email} -component UserInformation -active Yes -parent StandardInstall
+InstallComponent F8FD4BD6-F1DF-3F8D-B857-98310E4B1143 -setup Install -type pane -title {User Information Account No} -component UserInformation -active Yes -parent StandardInstall
+InstallComponent 58E1119F-639E-17C9-5D3898F385AA -setup Install -type pane -conditions 84DA7F05-9FB7-CC36-9EC98F8A6826 -title {Select Destination} -component SelectDestination -command insert -active Yes -parent StandardInstall
+Condition 84DA7F05-9FB7-CC36-9EC98F8A6826 -active Yes -parent 58E1119F-639E-17C9-5D3898F385AA -title {File Permission Condition} -component FilePermissionCondition -TreeObject::id 84DA7F05-9FB7-CC36-9EC98F8A6826
+InstallComponent 0FDBA082-90AB-808C-478A-A13E7C525336 -setup Install -type action -title BackupLocationNumber -component ExecuteScript -active Yes -parent 58E1119F-639E-17C9-5D3898F385AA
+InstallComponent 0047FF40-0139-2A59-AAC0-A44D46D6F5CC -setup Install -type action -title BackupLocationName -component ExecuteScript -active No -parent 58E1119F-639E-17C9-5D3898F385AA
+InstallComponent 2BB06B72-DE53-2319-B1B8-351CDCBA2008 -setup Install -type action -title AddBackupLocation -component ExecuteScript -active Yes -parent 58E1119F-639E-17C9-5D3898F385AA
+InstallComponent B506E7DA-E7C4-4D42-8C03-FD27BA16D078 -setup Install -type pane -title {License Agreement} -component License -active Yes -parent StandardInstall
+InstallComponent B93D2216-1DDB-484C-A9AC-D6C18ED7DE23 -setup Install -type action -conditions {6D9D1ABC-7146-443F-9EE9-205D5CA6C830 79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C} -title {Modify Widget} -component ModifyWidget -command insert -active Yes -parent B506E7DA-E7C4-4D42-8C03-FD27BA16D078
+Condition 6D9D1ABC-7146-443F-9EE9-205D5CA6C830 -active Yes -parent B93D2216-1DDB-484C-A9AC-D6C18ED7DE23 -title {String Is Condition} -component StringIsCondition -TreeObject::id 6D9D1ABC-7146-443F-9EE9-205D5CA6C830
+Condition 79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C -active Yes -parent B93D2216-1DDB-484C-A9AC-D6C18ED7DE23 -title {String Is Condition} -component StringIsCondition -TreeObject::id 79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C
+InstallComponent 37E627F2-E04B-AEF2-D566C017A4D6 -setup Install -type pane -title {Copying Files} -component CopyFiles -active Yes -parent StandardInstall
+InstallComponent 3CFFF099-6122-46DD-9CE4-F5819434AC53 -setup Install -type action -title {Stop running service} -component ExecuteExternalProgram -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent FB697A88-2842-468E-9776-85E84B009340 -setup Install -type action -title {Remove installed service} -component ExecuteExternalProgram -active No -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent 41CDE776-2667-5CEB-312A-FC4C33A83E7F -setup Install -type action -title {Backup File} -component BackupFile -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent 0D93323D-779D-44A8-1E0614E5285D -setup Install -type action -title {Disable Buttons} -component ModifyWidget -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent 5CA3EA16-E37C-AABE-E576C4636EB0 -setup Install -type action -title {Execute Action} -component ExecuteAction -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent F5F21749-8B3A-49C6-9138-9C4D6D703D26 -setup Install -type action -title {Unpack Keys} -component ExecuteExternalProgram -active No -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent FDF68FD6-BEA8-4A74-867D-5139F4D9E793 -setup Install -type action -title Wait -component Wait -active No -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent E56ADFF4-C15E-AEDB-A599-C468AF72C4BB -setup Install -type action -title {Copy File NotifySysAdmin} -component CopyFile -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent D9F88AC1-3D2D-F6DB-871E-3A0E016770B1 -setup Install -type action -title {Copy File config} -component CopyFile -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent 5F2C1F1C-B9F7-1642-59D9-A18318C1D70B -setup Install -type action -title {Replace Text In File} -component ReplaceTextInFile -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent 2EC82FBD-8294-A3E4-7F39-1CBA0582FA64 -setup Install -type action -title {Write Text To File} -component WriteTextToFile -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent 28E76C8B-2605-4739-9FFE-9C2880C17E59 -setup Install -type action -title {Edit config file} -component ExecuteExternalProgram -active No -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent 52F0A238-57E1-A578-2CE4DA177B32 -setup Install -type action -title {Move Forward} -component MoveForward -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6
+InstallComponent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 -setup Install -type pane -title SetBackupLocations -component CustomBlankPane2 -command reorder -active Yes -parent StandardInstall
+InstallComponent 614C45B2-7515-780C-E444-7F165CF02DD7 -setup Install -type action -title {Execute Script} -component ExecuteScript -active No -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent A5B32DA1-B2FE-C1FA-6057-FBC3059EF076 -setup Install -type action -title {Execute Script} -component ExecuteScript -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent F9E38720-6ABA-8B99-2471-496902E4CBC2 -setup Install -type action -title {Execute Script} -component ExecuteScript -active No -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent 362B6D6A-11BC-83CE-AFF6-410D8FBCF54D -setup Install -type action -title {Execute Script} -component ExecuteScript -active No -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent 2E2963BD-DDBD-738D-A910-B7F3F04946F9 -setup Install -type action -title ShowAddAnotherValue -component AddWidget -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent 93AA298C-B64E-5683-14D2-7B86F7DEFD2C -setup Install -type action -title BackupLocationName -component ExecuteScript -active No -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent 3FDB57ED-598D-8A4E-CEF7-D90833305558 -setup Install -type action -title {Backup Directory} -component AddWidget -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A -setup Install -type action -title BackupLocationShortName -component AddWidget -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent 855DE408-060E-3D35-08B5-1D9AB05C2865 -setup Install -type action -title Exclusions -component AddWidget -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent 9892B25C-689B-5B8F-F0C9-B14FF6ACC40C -setup Install -type action -title {Execute Script} -component ExecuteScript -active No -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent 8419AAAD-5860-F73E-8D11-4D1BDA4D7D37 -setup Install -type action -title AddAnother -component AddWidget -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent C7762473-273F-E3CA-17E3-65789B14CDB0 -setup Install -type action -title {Write Text To File} -component WriteTextToFile -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent D7FBBEBB-2186-5674-BA87-BB7151859D4E -setup Install -type action -title BackupLocationNumber -component ExecuteScript -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+InstallComponent 49E80443-62DB-1C10-392D-1091AEA5ED88 -setup Install -type action -conditions EB532611-5F30-3C24-66EB-F3826D9054FD -title {Move to Pane} -component MoveToPane -command insert -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+Condition EB532611-5F30-3C24-66EB-F3826D9054FD -active Yes -parent 49E80443-62DB-1C10-392D-1091AEA5ED88 -title {String Is Condition} -component StringIsCondition -TreeObject::id EB532611-5F30-3C24-66EB-F3826D9054FD
+InstallComponent 9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266 -setup Install -type pane -title {Click Next to Continue} -component CustomBlankPane2 -active Yes -parent StandardInstall
+InstallComponent DDBBD8A9-13D7-9509-9202-419E989F60A9 -setup Install -type action -title {Add Widget} -component AddWidget -active No -parent 9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266
+InstallComponent 8E095096-F018-A880-429D-A2177A9B70EA -setup Install -type action -title {Add Widget} -component AddWidget -active No -parent 9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266
+InstallComponent 88A50FD5-480F-19A5-DA74-C915EB0A9765 -setup Install -type action -conditions 5EE78EF7-37CA-D440-3DB5-09136CD566B3 -title {Move to Pane} -component MoveToPane -command insert -active No -parent 9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266
+Condition 5EE78EF7-37CA-D440-3DB5-09136CD566B3 -active Yes -parent 88A50FD5-480F-19A5-DA74-C915EB0A9765 -title {String Is Condition} -component StringIsCondition -TreeObject::id 5EE78EF7-37CA-D440-3DB5-09136CD566B3
+InstallComponent 908CE221-5A3D-0A78-24A1-E7C91EBE38D4 -setup Install -type pane -title {Next-Build Config} -component CustomBlankPane2 -active No -parent StandardInstall
+InstallComponent DA33B826-E633-A845-4646-76DFA78B907B -setup Install -type pane -title {Custom Blank Pane 2} -component CustomBlankPane2 -active Yes -parent StandardInstall
+InstallComponent 6FEE2889-0338-1D49-60BF-1471F465AB26 -setup Install -type action -title {Write Text To File} -component WriteTextToFile -active Yes -parent DA33B826-E633-A845-4646-76DFA78B907B
+InstallComponent 73DD4D07-B1DC-BA38-2B12-07EB24A7F0C8 -setup Install -type action -title {Copy File} -component CopyFile -active Yes -parent DA33B826-E633-A845-4646-76DFA78B907B
+InstallComponent D23DD94C-E517-7F34-FD59-802CB18AB887 -setup Install -type action -title {Adjust Line Feeds} -component AdjustLineFeeds -active Yes -parent DA33B826-E633-A845-4646-76DFA78B907B
+InstallComponent 7D8E1902-2BC4-80D8-2C18771E7C22 -setup Install -type action -title {Installing service} -component ExecuteExternalProgram -active Yes -parent DA33B826-E633-A845-4646-76DFA78B907B
+InstallComponent 1C14291C-0971-4283-92E9-3808401303F5 -setup Install -type action -title {Starting service} -component ExecuteExternalProgram -active No -parent DA33B826-E633-A845-4646-76DFA78B907B
+InstallComponent 6C323815-B9AB-FA94-4F5D152EBC51 -setup Install -type pane -title {Setup Complete} -component SetupComplete -active Yes -parent StandardInstall
+InstallComponent 574198A7-7322-2F5E-02EF185D965C -setup Install -type pane -title {Copying Files} -component CopyFiles -active Yes -parent DefaultInstall
+InstallComponent 8A761DBD-0640-D98C-9B3AD7672A8F -setup Install -type action -title {Disable Buttons} -component ModifyWidget -active Yes -parent 574198A7-7322-2F5E-02EF185D965C
+InstallComponent 6E70FB1F-6A43-6C23-3242E965A0D0 -setup Install -type action -title {Execute Action} -component ExecuteAction -active Yes -parent 574198A7-7322-2F5E-02EF185D965C
+InstallComponent 8E1A5944-5AF5-5906-16D395E386D8 -setup Install -type action -title {Move Forward} -component MoveForward -active Yes -parent 574198A7-7322-2F5E-02EF185D965C
+InstallComponent 1F0926EE-6884-1330-B4A1DB11C1BF -setup Install -type pane -title {Setup Complete} -component SetupComplete -active Yes -parent DefaultInstall
+InstallComponent 3B6E2E7C-1A26-27F1-D578E383B128 -setup Install -type action -conditions {13BD88FE-CD71-5AC7-E99C10B6CB28 E02368C5-95B5-03A7-3282740037B0} -title {View Readme Checkbutton} -component AddWidget -command insert -active Yes -parent 1F0926EE-6884-1330-B4A1DB11C1BF
+Condition 13BD88FE-CD71-5AC7-E99C10B6CB28 -active Yes -parent 3B6E2E7C-1A26-27F1-D578E383B128 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 13BD88FE-CD71-5AC7-E99C10B6CB28
+Condition E02368C5-95B5-03A7-3282740037B0 -active Yes -parent 3B6E2E7C-1A26-27F1-D578E383B128 -title {String Is Condition} -component StringIsCondition -TreeObject::id E02368C5-95B5-03A7-3282740037B0
+InstallComponent CFFA27AF-A641-E41C-B4A0E3BB3CBB -setup Install -type action -conditions {592F46AE-8CEE-01F3-0BA7EBDCA4F4 793D8178-0F51-7F07-BC5886586D3C} -title {Launch Application Checkbutton} -component AddWidget -command insert -active Yes -parent 1F0926EE-6884-1330-B4A1DB11C1BF
+Condition 592F46AE-8CEE-01F3-0BA7EBDCA4F4 -active Yes -parent CFFA27AF-A641-E41C-B4A0E3BB3CBB -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 592F46AE-8CEE-01F3-0BA7EBDCA4F4
+Condition 793D8178-0F51-7F07-BC5886586D3C -active Yes -parent CFFA27AF-A641-E41C-B4A0E3BB3CBB -title {String Is Condition} -component StringIsCondition -TreeObject::id 793D8178-0F51-7F07-BC5886586D3C
+InstallComponent 16D53E40-546B-54C3-088B1B5E3BBB -setup Install -type action -conditions {4E643D8A-CA31-018D-57D7053C2CE8 B39C0455-D1B6-7DDC-E2717F83463E} -title {Desktop Shortcut Checkbutton} -component AddWidget -command insert -active Yes -parent 1F0926EE-6884-1330-B4A1DB11C1BF
+Condition 4E643D8A-CA31-018D-57D7053C2CE8 -active Yes -parent 16D53E40-546B-54C3-088B1B5E3BBB -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 4E643D8A-CA31-018D-57D7053C2CE8
+Condition B39C0455-D1B6-7DDC-E2717F83463E -active Yes -parent 16D53E40-546B-54C3-088B1B5E3BBB -title {String Is Condition} -component StringIsCondition -TreeObject::id B39C0455-D1B6-7DDC-E2717F83463E
+InstallComponent 937C3FDD-FB28-98BD-3DAB276E59ED -setup Install -type action -conditions {6B966959-05D9-DB32-8D9C4AD2A3DF 748D673B-DFE6-5F74-329903ACE4DB 3379F80B-36D6-73DC-6FC1D6223A26} -title {Quick Launch Shortcut Checkbutton} -component AddWidget -command insert -active Yes -parent 1F0926EE-6884-1330-B4A1DB11C1BF
+Condition 6B966959-05D9-DB32-8D9C4AD2A3DF -active Yes -parent 937C3FDD-FB28-98BD-3DAB276E59ED -title {Platform Condition} -component PlatformCondition -TreeObject::id 6B966959-05D9-DB32-8D9C4AD2A3DF
+Condition 748D673B-DFE6-5F74-329903ACE4DB -active Yes -parent 937C3FDD-FB28-98BD-3DAB276E59ED -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 748D673B-DFE6-5F74-329903ACE4DB
+Condition 3379F80B-36D6-73DC-6FC1D6223A26 -active Yes -parent 937C3FDD-FB28-98BD-3DAB276E59ED -title {String Is Condition} -component StringIsCondition -TreeObject::id 3379F80B-36D6-73DC-6FC1D6223A26
+InstallComponent 3FE82C17-A3E2-4A57-A563-F80818B00B81 -setup Install -type action -title {Console Ask Yes Or No} -component ConsoleAskYesOrNo -active Yes -parent ConsoleInstall
+InstallComponent 56EE5149-6AA2-4E0C-8841-F66A2EF9276E -setup Install -type action -conditions 241BBFCE-4EB1-432F-94DD-69D444DDB6C0 -title Exit -component Exit -command insert -active Yes -parent ConsoleInstall
+Condition 241BBFCE-4EB1-432F-94DD-69D444DDB6C0 -active Yes -parent 56EE5149-6AA2-4E0C-8841-F66A2EF9276E -title {String Is Condition} -component StringIsCondition -TreeObject::id 241BBFCE-4EB1-432F-94DD-69D444DDB6C0
+InstallComponent 0C12D2D3-AEBC-42FE-A73A-0815EFB10DA5 -setup Install -type action -conditions BC4EA5FD-50BD-4D6E-953F-5E3EDB957360 -title {Console Get User Input} -component ConsoleGetUserInput -command insert -active Yes -parent ConsoleInstall
+Condition BC4EA5FD-50BD-4D6E-953F-5E3EDB957360 -active Yes -parent 0C12D2D3-AEBC-42FE-A73A-0815EFB10DA5 -title {File Permission Condition} -component FilePermissionCondition -TreeObject::id BC4EA5FD-50BD-4D6E-953F-5E3EDB957360
+InstallComponent B002A311-F8E7-41DE-B039-521391924E5B -setup Install -type action -title {Console Message} -component ConsoleMessage -active Yes -parent ConsoleInstall
+InstallComponent D4FC6EB5-DDEE-4E4A-B8E1-D4B588A7928B -setup Install -type action -title {Execute Action} -component ExecuteAction -active Yes -parent ConsoleInstall
+InstallComponent 2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6 -setup Install -type action -title {Console Message} -component ConsoleMessage -active Yes -parent ConsoleInstall
+InstallComponent 6B4CB3C2-4799-4C9F-BA8E-1EE47C4606E1 -setup Install -type action -title Exit -component Exit -active Yes -parent ConsoleInstall
+InstallComponent D8F0AA0F-AD79-C566-15CC508F503B -setup Install -type action -title {Execute Action} -component ExecuteAction -active Yes -parent SilentInstall
+InstallComponent 175CBE81-9EBE-1E21-A91479BEEFAE -setup Install -type action -title Exit -component Exit -active Yes -parent SilentInstall
+InstallComponent A1DD1DC2-85D7-9BC6-998AC3D4A3A9 -setup Install -type actiongroup -title {Startup Actions} -active Yes -parent ActionGroupsInstall
+InstallComponent 1F9E8CB8-02C1-0416-1F7445B4147F -setup Install -type action -conditions {3D0D1898-4C65-3E66-F82F56581E87 32F5B0AF-EB83-7A03-D8FAE1ECE473} -title Exit -component Exit -command insert -active Yes -parent A1DD1DC2-85D7-9BC6-998AC3D4A3A9
+Condition 3D0D1898-4C65-3E66-F82F56581E87 -active Yes -parent 1F9E8CB8-02C1-0416-1F7445B4147F -title {String Is Condition} -component StringIsCondition -TreeObject::id 3D0D1898-4C65-3E66-F82F56581E87
+Condition 32F5B0AF-EB83-7A03-D8FAE1ECE473 -active Yes -parent 1F9E8CB8-02C1-0416-1F7445B4147F -title {Ask Yes or No} -component AskYesOrNo -TreeObject::id 32F5B0AF-EB83-7A03-D8FAE1ECE473
+InstallComponent 32DC8FB1-A04B-71AA-EC18496D4BD0 -setup Install -type action -title {Create Install Panes} -component CreateInstallPanes -active Yes -parent A1DD1DC2-85D7-9BC6-998AC3D4A3A9
+InstallComponent 198905FB-9FAC-23DE-7422D25B8ECA -setup Install -type actiongroup -title {Install Actions} -active Yes -parent ActionGroupsInstall
+InstallComponent 4D4A7BF0-7CCE-46E6-BDE5222F82D7 -setup Install -type action -title {Install Selected Files} -component InstallSelectedFiles -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent 53588803-6B41-D9FC-A385906A5106 -setup Install -type action -title {Install Uninstaller} -component InstallUninstaller -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent 73EA65C1-3BE3-B190-55C3E99F6269 -setup Install -type action -conditions 4EF787E3-0643-DE46-15E64BAF0816 -title {Windows Uninstall Registry} -component AddWindowsUninstallEntry -command insert -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+Condition 4EF787E3-0643-DE46-15E64BAF0816 -active Yes -parent 73EA65C1-3BE3-B190-55C3E99F6269 -title {Platform Condition} -component PlatformCondition -TreeObject::id 4EF787E3-0643-DE46-15E64BAF0816
+InstallComponent 39B2B666-78D8-75E6-6EA071594D34 -setup Install -type action -conditions 18C00430-D6B1-151F-307762B3A045 -title {Uninstall Shortcut} -component InstallWindowsShortcut -command insert -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+Condition 18C00430-D6B1-151F-307762B3A045 -active Yes -parent 39B2B666-78D8-75E6-6EA071594D34 -title {Platform Condition} -component PlatformCondition -TreeObject::id 18C00430-D6B1-151F-307762B3A045
+InstallComponent 6652193C-5D4B-44B6-ABC6-D6E96D89E5DC -setup Install -type action -title {Install Program Folder Shortcut} -component InstallProgramFolderShortcut -active No -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent 9D101299-B80C-441B-8685-6E3AC61808E8 -setup Install -type action -title {RemoteControl Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E -setup Install -type action -title {stopservice Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7 -setup Install -type action -title {Install Backup Service} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent 25AA533E-02FC-47D9-9273-25266B8FA1F9 -setup Install -type action -title {Remove Backup Service} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent CDD84DE3-C970-458F-9162-1A3CE0AA716B -setup Install -type action -title {startservice Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent B5DFEC63-92A9-4686-909E-0CE78A7069D6 -setup Install -type action -title {restartservice Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent C0452595-F3EB-43AD-BCA2-661437584636 -setup Install -type action -title {editconfig Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent 1AF5CD58-65C0-49CB-9A9D-994816CF414E -setup Install -type action -title {QueryUpload Shortcut} -component InstallProgramFolderShortcut -active No -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent 1681CF85-A5D2-4D73-A3FC-52B2A6A1847D -setup Install -type action -title {killbackupprocess Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968 -setup Install -type action -title {reloadconfig Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent 6F61CDA8-30C9-454F-82A3-9987E1203079 -setup Install -type action -title {sync Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent 9A663209-495B-ED16-09BE-457B61148022 -setup Install -type action -title QueryCurrent -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent C0AF7C05-A31A-8376-BCB9-BA8B3A666252 -setup Install -type action -title SafeQueryAll -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent 32B08FB1-99DF-234E-8BAF-333E80AAC9F5 -setup Install -type action -title Usage -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA
+InstallComponent FEFD090D-C133-BC95-B3564F693CD3 -setup Install -type actiongroup -title {Finish Actions} -active Yes -parent ActionGroupsInstall
+InstallComponent DECC120D-6904-7F17-45A49184A5A3 -setup Install -type action -conditions {E44CFF46-6302-C518-B9C30D2E43F7 B0AA6839-AAB6-A602-C0E4ECA2E4FF} -title {Install Desktop Shortcut} -component InstallDesktopShortcut -command insert -active No -parent FEFD090D-C133-BC95-B3564F693CD3
+Condition E44CFF46-6302-C518-B9C30D2E43F7 -active Yes -parent DECC120D-6904-7F17-45A49184A5A3 -title {String Is Condition} -component StringIsCondition -TreeObject::id E44CFF46-6302-C518-B9C30D2E43F7
+Condition B0AA6839-AAB6-A602-C0E4ECA2E4FF -active Yes -parent DECC120D-6904-7F17-45A49184A5A3 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id B0AA6839-AAB6-A602-C0E4ECA2E4FF
+InstallComponent 7B770A07-A785-5215-956FA82CF14E -setup Install -type action -conditions {6F94698F-0839-3ABF-0CF2DF05A4C8 738DD098-7E3B-BC89-875CDB93CBE2 8C866252-8760-9B08-FE569C25B60D} -title {Install Quick Launch Shortcut} -component InstallWindowsShortcut -command insert -active No -parent FEFD090D-C133-BC95-B3564F693CD3
+Condition 6F94698F-0839-3ABF-0CF2DF05A4C8 -active Yes -parent 7B770A07-A785-5215-956FA82CF14E -title {String Is Condition} -component StringIsCondition -TreeObject::id 6F94698F-0839-3ABF-0CF2DF05A4C8
+Condition 738DD098-7E3B-BC89-875CDB93CBE2 -active Yes -parent 7B770A07-A785-5215-956FA82CF14E -title {Platform Condition} -component PlatformCondition -TreeObject::id 738DD098-7E3B-BC89-875CDB93CBE2
+Condition 8C866252-8760-9B08-FE569C25B60D -active Yes -parent 7B770A07-A785-5215-956FA82CF14E -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 8C866252-8760-9B08-FE569C25B60D
+InstallComponent C105AAAE-7C16-2C9E-769FE4535B60 -setup Install -type action -conditions {2583A547-11DE-1C27-B6D04B023CC0 A6E1B027-A1B4-5848-4F868D028D00 0357FAE9-FCFD-26D8-6541D810CD61} -title {View Readme Window} -component TextWindow -command insert -active No -parent FEFD090D-C133-BC95-B3564F693CD3
+Condition 2583A547-11DE-1C27-B6D04B023CC0 -active Yes -parent C105AAAE-7C16-2C9E-769FE4535B60 -title {String Is Condition} -component StringIsCondition -TreeObject::id 2583A547-11DE-1C27-B6D04B023CC0
+Condition A6E1B027-A1B4-5848-4F868D028D00 -active Yes -parent C105AAAE-7C16-2C9E-769FE4535B60 -title {String Is Condition} -component StringIsCondition -TreeObject::id A6E1B027-A1B4-5848-4F868D028D00
+Condition 0357FAE9-FCFD-26D8-6541D810CD61 -active Yes -parent C105AAAE-7C16-2C9E-769FE4535B60 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 0357FAE9-FCFD-26D8-6541D810CD61
+InstallComponent C33D74B2-26FA-16F5-433A10C6A747 -setup Install -type action -conditions {CC4337CC-F3B5-757C-DFCF5D1D365A 795EE61F-6C0D-4A8B-93E02AA3894A 1528F4F0-145C-A48D-A8526DBB6289} -title {Launch Application} -component ExecuteExternalProgram -command insert -active No -parent FEFD090D-C133-BC95-B3564F693CD3
+Condition CC4337CC-F3B5-757C-DFCF5D1D365A -active Yes -parent C33D74B2-26FA-16F5-433A10C6A747 -title {String Is Condition} -component StringIsCondition -TreeObject::id CC4337CC-F3B5-757C-DFCF5D1D365A
+Condition 795EE61F-6C0D-4A8B-93E02AA3894A -active Yes -parent C33D74B2-26FA-16F5-433A10C6A747 -title {String Is Condition} -component StringIsCondition -TreeObject::id 795EE61F-6C0D-4A8B-93E02AA3894A
+Condition 1528F4F0-145C-A48D-A8526DBB6289 -active Yes -parent C33D74B2-26FA-16F5-433A10C6A747 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 1528F4F0-145C-A48D-A8526DBB6289
+InstallComponent E23AC50D-7CFB-800E-A99C6F4068F8 -setup Install -type actiongroup -title {Cancel Actions} -active Yes -parent ActionGroupsInstall
+InstallComponent 3B8CDC8E-1239-D2E9-DF4CA6B1756D -setup Uninstall -type pane -title Uninstall -component Uninstall -active Yes -parent StandardUninstall
+InstallComponent 19ADBDDB-1690-4A57-913E32A026C4 -setup Uninstall -type action -title {Modify Widget} -component ModifyWidget -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D
+InstallComponent 7A983CD8-302C-4942-BE59-525C5B5FA2F2 -setup Uninstall -type action -title {Stop Backup Process} -component ExecuteExternalProgram -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D
+InstallComponent E4DEA723-FC78-45D7-BAB1-A3E4C4C96EA1 -setup Uninstall -type action -title {Stop Service} -component ExecuteExternalProgram -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D
+InstallComponent B4D31D1E-ADB1-DE8F-18EB7294DDA8 -setup Uninstall -type action -title {Remove Service} -component ExecuteExternalProgram -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D
+InstallComponent D55BA4AF-E73B-60D1-E26F79175227 -setup Uninstall -type action -title {Execute Action} -component ExecuteAction -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D
+InstallComponent 69FD7409-5E2A-143B-DABD1C3B1E67 -setup Uninstall -type action -conditions {96A68CAC-9ED7-806C-086B104720FD E161F216-E597-B340-C1A71C476E2C} -title {Uninstall Leftover Files} -component UninstallLeftoverFiles -command insert -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D
+Condition 96A68CAC-9ED7-806C-086B104720FD -active Yes -parent 69FD7409-5E2A-143B-DABD1C3B1E67 -title {String Is Condition} -component StringIsCondition -TreeObject::id 96A68CAC-9ED7-806C-086B104720FD
+Condition E161F216-E597-B340-C1A71C476E2C -active Yes -parent 69FD7409-5E2A-143B-DABD1C3B1E67 -title {Ask Yes or No} -component AskYesOrNo -TreeObject::id E161F216-E597-B340-C1A71C476E2C
+InstallComponent 05060263-E852-87AB-8D0F2954CAA6 -setup Uninstall -type action -title {Move Forward} -component MoveForward -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D
+InstallComponent 41D3E165-C263-5F80-0FEEC0AEE47A -setup Uninstall -type pane -conditions EB2B31A1-C111-3582-0C8A5656692A -title {Uninstall Details} -component UninstallDetails -command insert -active Yes -parent StandardUninstall
+Condition EB2B31A1-C111-3582-0C8A5656692A -active Yes -parent 41D3E165-C263-5F80-0FEEC0AEE47A -title {String Is Condition} -component StringIsCondition -TreeObject::id EB2B31A1-C111-3582-0C8A5656692A
+InstallComponent 3D33AA8C-0037-204B-39A339FD38BD -setup Uninstall -type pane -title {Uninstall Complete} -component UninstallComplete -active Yes -parent StandardUninstall
+InstallComponent 49E59F91-27F7-46D1-A1C1-19865C2392D3 -setup Uninstall -type action -title {Console Ask Yes Or No} -component ConsoleAskYesOrNo -active Yes -parent ConsoleUninstall
+InstallComponent ADA6EB2F-8820-4366-BBEF-ED1335B7F828 -setup Uninstall -type action -conditions 87DE6D78-81E1-495B-A214-B3FF3E7E5614 -title Exit -component Exit -command insert -active Yes -parent ConsoleUninstall
+Condition 87DE6D78-81E1-495B-A214-B3FF3E7E5614 -active Yes -parent ADA6EB2F-8820-4366-BBEF-ED1335B7F828 -title {String Is Condition} -component StringIsCondition -TreeObject::id 87DE6D78-81E1-495B-A214-B3FF3E7E5614
+InstallComponent B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174 -setup Uninstall -type action -title {Console Message} -component ConsoleMessage -active Yes -parent ConsoleUninstall
+InstallComponent 3C7130B3-3206-403D-B09E-59D4A758FBAD -setup Uninstall -type action -title {Execute Action} -component ExecuteAction -active Yes -parent ConsoleUninstall
+InstallComponent 20CBDBEA-2217-457B-8D98-D692C4F591E9 -setup Uninstall -type action -title {Console Message} -component ConsoleMessage -active Yes -parent ConsoleUninstall
+InstallComponent 7F85263E-CAE2-46BA-AAC0-6B89D20FD2DE -setup Uninstall -type action -title Exit -component Exit -active Yes -parent ConsoleUninstall
+InstallComponent 17D8BA8E-5992-AA5C-F5ECB73A3433 -setup Uninstall -type action -title {Execute Action} -component ExecuteAction -active Yes -parent SilentUninstall
+InstallComponent D3D73C76-D9D3-07DA-63D4163A44BE -setup Uninstall -type action -title Exit -component Exit -active Yes -parent SilentUninstall
+InstallComponent 848844B5-6103-9343-8B731B0BE4E0 -setup Uninstall -type actiongroup -title {Startup Actions} -active Yes -parent ActionGroupsUninstall
+InstallComponent 97ACF525-C075-8635-E019202A83D8 -setup Uninstall -type action -conditions {DFFF91A9-2CA5-6ABE-8474D814AF88 4ACB0B47-42B3-2B3A-BFE9AA4EC707} -title Exit -component Exit -command insert -active Yes -parent 848844B5-6103-9343-8B731B0BE4E0
+Condition DFFF91A9-2CA5-6ABE-8474D814AF88 -active Yes -parent 97ACF525-C075-8635-E019202A83D8 -title {String Is Condition} -component StringIsCondition -TreeObject::id DFFF91A9-2CA5-6ABE-8474D814AF88
+Condition 4ACB0B47-42B3-2B3A-BFE9AA4EC707 -active Yes -parent 97ACF525-C075-8635-E019202A83D8 -title {Ask Yes or No} -component AskYesOrNo -TreeObject::id 4ACB0B47-42B3-2B3A-BFE9AA4EC707
+InstallComponent F4024A3E-9A6D-2726-5E0CFFA93054 -setup Uninstall -type actiongroup -title {Uninstall Actions} -active Yes -parent ActionGroupsUninstall
+InstallComponent 39D7394E-04E9-CA70-0034DB830BFE -setup Uninstall -type action -title {Uninstall Selected Files} -component UninstallSelectedFiles -active Yes -parent F4024A3E-9A6D-2726-5E0CFFA93054
+InstallComponent 39270FD8-932E-6132-7EF795ED9B93 -setup Uninstall -type actiongroup -title {Finish Actions} -active Yes -parent ActionGroupsUninstall
+InstallComponent 905DA2E9-988C-2F27-BB1F5F274AC9 -setup Uninstall -type actiongroup -title {Cancel Actions} -active Yes -parent ActionGroupsUninstall
+
+array set Properties {
+0047FF40-0139-2A59-AAC0-A44D46D6F5CC,Active
+No
+
+0047FF40-0139-2A59-AAC0-A44D46D6F5CC,Comment
+{set BackupLocationName "BackupLocation_${BackupLocationNumber}"}
+
+0047FF40-0139-2A59-AAC0-A44D46D6F5CC,Conditions
+{0 conditions}
+
+0047FF40-0139-2A59-AAC0-A44D46D6F5CC,ExecuteAction
+{Before Next Pane is Displayed}
+
+0047FF40-0139-2A59-AAC0-A44D46D6F5CC,ResultVirtualText
+BackupLocationName
+
+0047FF40-0139-2A59-AAC0-A44D46D6F5CC,TclScript
+{set BackupLocationName "BackupLocation_${BackupLocationNumber}"}
+
+0357FAE9-FCFD-26D8-6541D810CD61,CheckCondition
+{Before Action is Executed}
+
+0357FAE9-FCFD-26D8-6541D810CD61,Filename
+<%ProgramReadme%>
+
+05060263-E852-87AB-8D0F2954CAA6,Conditions
+{0 conditions}
+
+0C12D2D3-AEBC-42FE-A73A-0815EFB10DA5,Prompt
+<%ConsoleSelectDestinationText%>
+
+0C12D2D3-AEBC-42FE-A73A-0815EFB10DA5,VirtualText
+InstallDir
+
+0D93323D-779D-44A8-1E0614E5285D,Conditions
+{0 conditions}
+
+0D93323D-779D-44A8-1E0614E5285D,State
+disabled
+
+0D93323D-779D-44A8-1E0614E5285D,Widget
+{Back Button;Next Button}
+
+0FDBA082-90AB-808C-478A-A13E7C525336,Conditions
+{0 conditions}
+
+0FDBA082-90AB-808C-478A-A13E7C525336,ExecuteAction
+{Before Next Pane is Displayed}
+
+0FDBA082-90AB-808C-478A-A13E7C525336,ResultVirtualText
+BackupLocationNumber
+
+0FDBA082-90AB-808C-478A-A13E7C525336,TclScript
+{set BackupLocationNumber 1}
+
+13BD88FE-CD71-5AC7-E99C10B6CB28,CheckCondition
+{Before Action is Executed}
+
+13BD88FE-CD71-5AC7-E99C10B6CB28,Filename
+<%ProgramReadme%>
+
+1528F4F0-145C-A48D-A8526DBB6289,CheckCondition
+{Before Action is Executed}
+
+1528F4F0-145C-A48D-A8526DBB6289,Filename
+<%ProgramExecutable%>
+
+1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,Alias
+{Stop Backup Windows Process}
+
+1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,Conditions
+{0 conditions}
+
+1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,FileName
+<%ShortAppName%>-program-killbackupprocess
+
+1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,ShortcutName
+{Stop backup process}
+
+1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,TargetFileName
+<%InstallDir%>/tools/KillBackupProcess.bat
+
+1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,WorkingDirectory
+<%InstallDir%>
+
+16D53E40-546B-54C3-088B1B5E3BBB,Background
+white
+
+16D53E40-546B-54C3-088B1B5E3BBB,Conditions
+{2 conditions}
+
+16D53E40-546B-54C3-088B1B5E3BBB,Text,subst
+1
+
+16D53E40-546B-54C3-088B1B5E3BBB,Type
+checkbutton
+
+16D53E40-546B-54C3-088B1B5E3BBB,VirtualText
+CreateDesktopShortcut
+
+16D53E40-546B-54C3-088B1B5E3BBB,X
+185
+
+16D53E40-546B-54C3-088B1B5E3BBB,Y
+180
+
+175CBE81-9EBE-1E21-A91479BEEFAE,ExitType
+Finish
+
+17D8BA8E-5992-AA5C-F5ECB73A3433,Action
+{Uninstall Actions}
+
+17D8BA8E-5992-AA5C-F5ECB73A3433,Conditions
+{0 conditions}
+
+18C00430-D6B1-151F-307762B3A045,CheckCondition
+{Before Action is Executed}
+
+18C00430-D6B1-151F-307762B3A045,Platform
+Windows
+
+198905FB-9FAC-23DE-7422D25B8ECA,Alias
+{Install Actions}
+
+198905FB-9FAC-23DE-7422D25B8ECA,Conditions
+{0 conditions}
+
+19ADBDDB-1690-4A57-913E32A026C4,Conditions
+{0 conditions}
+
+19ADBDDB-1690-4A57-913E32A026C4,State
+disabled
+
+19ADBDDB-1690-4A57-913E32A026C4,Widget
+{NextButton; CancelButton}
+
+1AF5CD58-65C0-49CB-9A9D-994816CF414E,Active
+No
+
+1AF5CD58-65C0-49CB-9A9D-994816CF414E,Alias
+{Upload File Listing}
+
+1AF5CD58-65C0-49CB-9A9D-994816CF414E,Comment
+{Upload list of backed up files for Tech Support purposes. May be blocked by firewalls.}
+
+1AF5CD58-65C0-49CB-9A9D-994816CF414E,Conditions
+{0 conditions}
+
+1AF5CD58-65C0-49CB-9A9D-994816CF414E,FileName
+<%ShortAppName%>-program-TebucoSafeQuerypload
+
+1AF5CD58-65C0-49CB-9A9D-994816CF414E,ShortcutName
+{Upload Filelisting to TebucoSafe for review}
+
+1AF5CD58-65C0-49CB-9A9D-994816CF414E,TargetFileName
+<%InstallDir%>/tools/TebucoSafeQueryUpload.bat
+
+1AF5CD58-65C0-49CB-9A9D-994816CF414E,WorkingDirectory
+<%InstallDir%>
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Active
+Yes
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,BackButton,subst
+1
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,CancelButton,subst
+1
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Caption,subst
+1
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,CompanyLabel,subst
+0
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Conditions
+{0 conditions}
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Message,subst
+1
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,NextButton,subst
+1
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Subtitle,subst
+1
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Title,subst
+1
+
+1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,UserNameLabel,subst
+0
+
+1C14291C-0971-4283-92E9-3808401303F5,Active
+No
+
+1C14291C-0971-4283-92E9-3808401303F5,Comment
+{Don't start it yet, need to install keys by hand.}
+
+1C14291C-0971-4283-92E9-3808401303F5,Conditions
+{0 conditions}
+
+1C14291C-0971-4283-92E9-3808401303F5,ProgramCommandLine
+{net start <%ServiceName%>}
+
+1C14291C-0971-4283-92E9-3808401303F5,WorkingDirectory
+<%InstallDir%>
+
+1F0926EE-6884-1330-B4A1DB11C1BF,BackButton,subst
+1
+
+1F0926EE-6884-1330-B4A1DB11C1BF,CancelButton,subst
+1
+
+1F0926EE-6884-1330-B4A1DB11C1BF,Caption,subst
+1
+
+1F0926EE-6884-1330-B4A1DB11C1BF,Message,subst
+1
+
+1F0926EE-6884-1330-B4A1DB11C1BF,NextButton,subst
+1
+
+1F9E8CB8-02C1-0416-1F7445B4147F,Comment
+{Ask the user if they want to proceed with the install.}
+
+1F9E8CB8-02C1-0416-1F7445B4147F,Conditions
+{2 conditions}
+
+20CBDBEA-2217-457B-8D98-D692C4F591E9,Message,subst
+1
+
+241BBFCE-4EB1-432F-94DD-69D444DDB6C0,CheckCondition
+{Before Action is Executed}
+
+241BBFCE-4EB1-432F-94DD-69D444DDB6C0,Operator
+false
+
+241BBFCE-4EB1-432F-94DD-69D444DDB6C0,String
+<%Answer%>
+
+2583A547-11DE-1C27-B6D04B023CC0,CheckCondition
+{Before Action is Executed}
+
+2583A547-11DE-1C27-B6D04B023CC0,Operator
+false
+
+2583A547-11DE-1C27-B6D04B023CC0,String
+<%SilentMode%>
+
+25AA533E-02FC-47D9-9273-25266B8FA1F9,Alias
+{Remove Backup Service}
+
+25AA533E-02FC-47D9-9273-25266B8FA1F9,Comment
+{Remove the Backup Windows Service}
+
+25AA533E-02FC-47D9-9273-25266B8FA1F9,Conditions
+{0 conditions}
+
+25AA533E-02FC-47D9-9273-25266B8FA1F9,FileName
+<%ShortAppName%>-program-removeService
+
+25AA533E-02FC-47D9-9273-25266B8FA1F9,ShortcutName
+{Remove Service}
+
+25AA533E-02FC-47D9-9273-25266B8FA1F9,TargetFileName
+<%InstallDir%>/tools/RemoveService.bat
+
+25AA533E-02FC-47D9-9273-25266B8FA1F9,WorkingDirectory
+<%InstallDir%>
+
+28E76C8B-2605-4739-9FFE-9C2880C17E59,Active
+No
+
+28E76C8B-2605-4739-9FFE-9C2880C17E59,Conditions
+{0 conditions}
+
+28E76C8B-2605-4739-9FFE-9C2880C17E59,ProgramCommandLine
+{notepad <%ConfigFileName%>}
+
+28E76C8B-2605-4739-9FFE-9C2880C17E59,WorkingDirectory
+<%InstallDir%>
+
+2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,BackButton,subst
+1
+
+2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,CancelButton,subst
+1
+
+2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,Caption,subst
+1
+
+2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,Conditions
+{1 condition}
+
+2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,Message,subst
+1
+
+2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,NextButton,subst
+1
+
+2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,Subtitle,subst
+1
+
+2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,Title,subst
+1
+
+2BB06B72-DE53-2319-B1B8-351CDCBA2008,Conditions
+{0 conditions}
+
+2BB06B72-DE53-2319-B1B8-351CDCBA2008,ExecuteAction
+{Before Next Pane is Displayed}
+
+2BB06B72-DE53-2319-B1B8-351CDCBA2008,ResultVirtualText
+AddBackupLocation
+
+2BB06B72-DE53-2319-B1B8-351CDCBA2008,TclScript
+{set AddBackupLocation no}
+
+2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6,Message,subst
+1
+
+2E2963BD-DDBD-738D-A910-B7F3F04946F9,Conditions
+{0 conditions}
+
+2E2963BD-DDBD-738D-A910-B7F3F04946F9,Text,subst
+1
+
+2E2963BD-DDBD-738D-A910-B7F3F04946F9,Value
+<%AddBackupLocation%>
+
+2E2963BD-DDBD-738D-A910-B7F3F04946F9,X
+400
+
+2E2963BD-DDBD-738D-A910-B7F3F04946F9,Y
+70
+
+2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,AppendNewline
+No
+
+2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,Comment
+{.conf doesn't exist yet}
+
+2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,Conditions
+{0 conditions}
+
+2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,FileOpenAction
+{Append to file}
+
+2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,Files
+<%ConfigFileTemplate%>
+
+2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,TextToWrite,subst
+1
+
+32B08FB1-99DF-234E-8BAF-333E80AAC9F5,Conditions
+{0 conditions}
+
+32B08FB1-99DF-234E-8BAF-333E80AAC9F5,FileName
+<%ShortAppName%>-program-Usage
+
+32B08FB1-99DF-234E-8BAF-333E80AAC9F5,ShortcutName
+Usage
+
+32B08FB1-99DF-234E-8BAF-333E80AAC9F5,TargetFileName
+<%InstallDir%>/tools/ShowUsage.bat
+
+32B08FB1-99DF-234E-8BAF-333E80AAC9F5,WorkingDirectory
+<%InstallDir%>
+
+32DC8FB1-A04B-71AA-EC18496D4BD0,Conditions
+{0 conditions}
+
+32F5B0AF-EB83-7A03-D8FAE1ECE473,CheckCondition
+{Before Action is Executed}
+
+32F5B0AF-EB83-7A03-D8FAE1ECE473,Message,subst
+1
+
+32F5B0AF-EB83-7A03-D8FAE1ECE473,Title,subst
+1
+
+32F5B0AF-EB83-7A03-D8FAE1ECE473,TrueValue
+No
+
+3379F80B-36D6-73DC-6FC1D6223A26,CheckCondition
+{Before Action is Executed}
+
+3379F80B-36D6-73DC-6FC1D6223A26,Operator
+false
+
+3379F80B-36D6-73DC-6FC1D6223A26,String
+<%InstallStopped%>
+
+362B6D6A-11BC-83CE-AFF6-410D8FBCF54D,Active
+No
+
+362B6D6A-11BC-83CE-AFF6-410D8FBCF54D,Conditions
+{0 conditions}
+
+362B6D6A-11BC-83CE-AFF6-410D8FBCF54D,ResultVirtualText
+BackupLocationExclusions
+
+362B6D6A-11BC-83CE-AFF6-410D8FBCF54D,TclScript
+{set BackupLocationExclusions ""}
+
+37E627F2-E04B-AEF2-D566C017A4D6,BackButton,subst
+1
+
+37E627F2-E04B-AEF2-D566C017A4D6,CancelButton,subst
+1
+
+37E627F2-E04B-AEF2-D566C017A4D6,Caption,subst
+1
+
+37E627F2-E04B-AEF2-D566C017A4D6,Conditions
+{0 conditions}
+
+37E627F2-E04B-AEF2-D566C017A4D6,FileLabel,subst
+1
+
+37E627F2-E04B-AEF2-D566C017A4D6,Message,subst
+1
+
+37E627F2-E04B-AEF2-D566C017A4D6,NextButton,subst
+1
+
+37E627F2-E04B-AEF2-D566C017A4D6,ProgressValue,subst
+1
+
+37E627F2-E04B-AEF2-D566C017A4D6,Subtitle,subst
+1
+
+37E627F2-E04B-AEF2-D566C017A4D6,Title,subst
+1
+
+39270FD8-932E-6132-7EF795ED9B93,Alias
+{Finish Actions}
+
+39270FD8-932E-6132-7EF795ED9B93,Conditions
+{0 conditions}
+
+39B2B666-78D8-75E6-6EA071594D34,Conditions
+{1 condition}
+
+39B2B666-78D8-75E6-6EA071594D34,ShortcutName
+{Uninstall <%BrandName%>}
+
+39B2B666-78D8-75E6-6EA071594D34,TargetFileName
+<%Uninstaller%>
+
+39B2B666-78D8-75E6-6EA071594D34,WorkingDirectory
+<%InstallDir%>
+
+39D7394E-04E9-CA70-0034DB830BFE,Conditions
+{0 conditions}
+
+3B6E2E7C-1A26-27F1-D578E383B128,Background
+white
+
+3B6E2E7C-1A26-27F1-D578E383B128,Conditions
+{2 conditions}
+
+3B6E2E7C-1A26-27F1-D578E383B128,Text,subst
+1
+
+3B6E2E7C-1A26-27F1-D578E383B128,Type
+checkbutton
+
+3B6E2E7C-1A26-27F1-D578E383B128,VirtualText
+ViewReadme
+
+3B6E2E7C-1A26-27F1-D578E383B128,X
+185
+
+3B6E2E7C-1A26-27F1-D578E383B128,Y
+140
+
+3B8CDC8E-1239-D2E9-DF4CA6B1756D,BackButton,subst
+1
+
+3B8CDC8E-1239-D2E9-DF4CA6B1756D,CancelButton,subst
+1
+
+3B8CDC8E-1239-D2E9-DF4CA6B1756D,Caption,subst
+1
+
+3B8CDC8E-1239-D2E9-DF4CA6B1756D,Conditions
+{0 conditions}
+
+3B8CDC8E-1239-D2E9-DF4CA6B1756D,FileValue,subst
+1
+
+3B8CDC8E-1239-D2E9-DF4CA6B1756D,Message,subst
+1
+
+3B8CDC8E-1239-D2E9-DF4CA6B1756D,NextButton,subst
+1
+
+3B8CDC8E-1239-D2E9-DF4CA6B1756D,ProgressValue,subst
+1
+
+3B8CDC8E-1239-D2E9-DF4CA6B1756D,Subtitle,subst
+1
+
+3B8CDC8E-1239-D2E9-DF4CA6B1756D,Title,subst
+1
+
+3C7130B3-3206-403D-B09E-59D4A758FBAD,Action
+{Uninstall Actions}
+
+3CFFF099-6122-46DD-9CE4-F5819434AC53,Conditions
+{0 conditions}
+
+3CFFF099-6122-46DD-9CE4-F5819434AC53,IgnoreErrors
+Yes
+
+3CFFF099-6122-46DD-9CE4-F5819434AC53,ProgramCommandLine
+{net stop <%ServiceName%>}
+
+3CFFF099-6122-46DD-9CE4-F5819434AC53,ProgressiveOutputWidget
+Message
+
+3CFFF099-6122-46DD-9CE4-F5819434AC53,WorkingDirectory
+<%Temp%>
+
+3D0D1898-4C65-3E66-F82F56581E87,CheckCondition
+{Before Action is Executed}
+
+3D0D1898-4C65-3E66-F82F56581E87,Operator
+false
+
+3D0D1898-4C65-3E66-F82F56581E87,String
+<%SilentMode%>
+
+3D33AA8C-0037-204B-39A339FD38BD,BackButton,subst
+1
+
+3D33AA8C-0037-204B-39A339FD38BD,CancelButton,subst
+1
+
+3D33AA8C-0037-204B-39A339FD38BD,Caption,subst
+1
+
+3D33AA8C-0037-204B-39A339FD38BD,Conditions
+{0 conditions}
+
+3D33AA8C-0037-204B-39A339FD38BD,Message,subst
+1
+
+3D33AA8C-0037-204B-39A339FD38BD,NextButton,subst
+1
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Active
+Yes
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Alias
+SetBackupLocations
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,BackButton,subst
+1
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,CancelButton,subst
+1
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Caption,subst
+1
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Conditions
+{0 conditions}
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Message,subst
+1
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,NextButton,subst
+1
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Subtitle,subst
+1
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Title,subst
+1
+
+3FDB57ED-598D-8A4E-CEF7-D90833305558,Conditions
+{0 conditions}
+
+3FDB57ED-598D-8A4E-CEF7-D90833305558,LabelSide
+left
+
+3FDB57ED-598D-8A4E-CEF7-D90833305558,Text,subst
+1
+
+3FDB57ED-598D-8A4E-CEF7-D90833305558,Type
+{browse entry}
+
+3FDB57ED-598D-8A4E-CEF7-D90833305558,VirtualText
+BackupLocationPath
+
+3FDB57ED-598D-8A4E-CEF7-D90833305558,Y
+70
+
+3FE82C17-A3E2-4A57-A563-F80818B00B81,Default
+Yes
+
+3FE82C17-A3E2-4A57-A563-F80818B00B81,Prompt
+<%InstallStartupText%>
+
+41CDE776-2667-5CEB-312A-FC4C33A83E7F,Conditions
+{0 conditions}
+
+41CDE776-2667-5CEB-312A-FC4C33A83E7F,Files
+{*/*.conf;*/*.txt;*/*.pem;*/*.raw;*/*.exe;*/*.bat;*/*.dll}
+
+41CDE776-2667-5CEB-312A-FC4C33A83E7F,RenameFiles
+Yes
+
+41D3E165-C263-5F80-0FEEC0AEE47A,BackButton,subst
+1
+
+41D3E165-C263-5F80-0FEEC0AEE47A,CancelButton,subst
+1
+
+41D3E165-C263-5F80-0FEEC0AEE47A,Caption,subst
+1
+
+41D3E165-C263-5F80-0FEEC0AEE47A,Conditions
+{1 condition}
+
+41D3E165-C263-5F80-0FEEC0AEE47A,Message,subst
+1
+
+41D3E165-C263-5F80-0FEEC0AEE47A,NextButton,subst
+1
+
+41D3E165-C263-5F80-0FEEC0AEE47A,Subtitle,subst
+1
+
+41D3E165-C263-5F80-0FEEC0AEE47A,Text,subst
+1
+
+41D3E165-C263-5F80-0FEEC0AEE47A,Title,subst
+1
+
+481451CC-F49C-D389-8645076F595B,Destination
+<%InstallDir%>
+
+481451CC-F49C-D389-8645076F595B,FileSize
+15288767
+
+481451CC-F49C-D389-8645076F595B,Name
+{Program Files}
+
+49E59F91-27F7-46D1-A1C1-19865C2392D3,Default
+Yes
+
+49E59F91-27F7-46D1-A1C1-19865C2392D3,Prompt
+<%UninstallStartupText%>
+
+49E80443-62DB-1C10-392D-1091AEA5ED88,Conditions
+{1 condition}
+
+49E80443-62DB-1C10-392D-1091AEA5ED88,ExecuteAction
+{Before Next Pane is Displayed}
+
+49E80443-62DB-1C10-392D-1091AEA5ED88,Pane
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+
+4A9C852B-647E-EED5-5482FFBCC2AF,Description,subst
+1
+
+4A9C852B-647E-EED5-5482FFBCC2AF,DisplayName,subst
+1
+
+4A9C852B-647E-EED5-5482FFBCC2AF,FileGroups
+481451CC-F49C-D389-8645076F595B
+
+4A9C852B-647E-EED5-5482FFBCC2AF,Name
+{Default Component}
+
+4A9C852B-647E-EED5-5482FFBCC2AF,RequiredComponent
+Yes
+
+4ACB0B47-42B3-2B3A-BFE9AA4EC707,CheckCondition
+{Before Action is Executed}
+
+4ACB0B47-42B3-2B3A-BFE9AA4EC707,Message,subst
+1
+
+4ACB0B47-42B3-2B3A-BFE9AA4EC707,Title,subst
+1
+
+4ACB0B47-42B3-2B3A-BFE9AA4EC707,TrueValue
+No
+
+4D4A7BF0-7CCE-46E6-BDE5222F82D7,Conditions
+{0 conditions}
+
+4D4A7BF0-7CCE-46E6-BDE5222F82D7,UpdateFilePercentage
+Yes
+
+4D4A7BF0-7CCE-46E6-BDE5222F82D7,UpdateFileText
+Yes
+
+4E643D8A-CA31-018D-57D7053C2CE8,CheckCondition
+{Before Action is Executed}
+
+4E643D8A-CA31-018D-57D7053C2CE8,Filename
+<%ProgramExecutable%>
+
+4EE35849-FAD7-170B-0E45-FA30636467B1,CheckCondition
+{Before Next Pane is Displayed}
+
+4EE35849-FAD7-170B-0E45-FA30636467B1,EncryptedPassword
+<%InstallPasswordEncrypted%>
+
+4EE35849-FAD7-170B-0E45-FA30636467B1,FailureFocus
+{Password Entry}
+
+4EE35849-FAD7-170B-0E45-FA30636467B1,FailureMessage
+<%PasswordIncorrectText%>
+
+4EE35849-FAD7-170B-0E45-FA30636467B1,UnencryptedPassword
+<%InstallPassword%>
+
+4EF787E3-0643-DE46-15E64BAF0816,CheckCondition
+{Before Action is Executed}
+
+4EF787E3-0643-DE46-15E64BAF0816,Platform
+Windows
+
+52F0A238-57E1-A578-2CE4DA177B32,Conditions
+{0 conditions}
+
+53588803-6B41-D9FC-A385906A5106,Conditions
+{0 conditions}
+
+574198A7-7322-2F5E-02EF185D965C,BackButton,subst
+1
+
+574198A7-7322-2F5E-02EF185D965C,CancelButton,subst
+1
+
+574198A7-7322-2F5E-02EF185D965C,Caption,subst
+1
+
+574198A7-7322-2F5E-02EF185D965C,Conditions
+{0 conditions}
+
+574198A7-7322-2F5E-02EF185D965C,FileLabel,subst
+1
+
+574198A7-7322-2F5E-02EF185D965C,Message,subst
+1
+
+574198A7-7322-2F5E-02EF185D965C,NextButton,subst
+1
+
+574198A7-7322-2F5E-02EF185D965C,ProgressValue,subst
+1
+
+574198A7-7322-2F5E-02EF185D965C,Subtitle,subst
+1
+
+574198A7-7322-2F5E-02EF185D965C,Title,subst
+1
+
+58E1119F-639E-17C9-5D3898F385AA,BackButton,subst
+1
+
+58E1119F-639E-17C9-5D3898F385AA,BrowseButton,subst
+1
+
+58E1119F-639E-17C9-5D3898F385AA,BrowseText,subst
+1
+
+58E1119F-639E-17C9-5D3898F385AA,CancelButton,subst
+1
+
+58E1119F-639E-17C9-5D3898F385AA,Caption,subst
+1
+
+58E1119F-639E-17C9-5D3898F385AA,Conditions
+{1 condition}
+
+58E1119F-639E-17C9-5D3898F385AA,DestinationLabel,subst
+0
+
+58E1119F-639E-17C9-5D3898F385AA,Message,subst
+1
+
+58E1119F-639E-17C9-5D3898F385AA,NextButton,subst
+1
+
+58E1119F-639E-17C9-5D3898F385AA,Subtitle,subst
+1
+
+58E1119F-639E-17C9-5D3898F385AA,Title,subst
+1
+
+592F46AE-8CEE-01F3-0BA7EBDCA4F4,CheckCondition
+{Before Action is Executed}
+
+592F46AE-8CEE-01F3-0BA7EBDCA4F4,Filename
+<%ProgramExecutable%>
+
+5CA3EA16-E37C-AABE-E576C4636EB0,Action
+{Install Actions}
+
+5CA3EA16-E37C-AABE-E576C4636EB0,Conditions
+{0 conditions}
+
+5EE78EF7-37CA-D440-3DB5-09136CD566B3,CheckCondition
+{Before Action is Executed}
+
+5EE78EF7-37CA-D440-3DB5-09136CD566B3,String
+<%AddBackupLocation%>
+
+5F2C1F1C-B9F7-1642-59D9-A18318C1D70B,Conditions
+{0 conditions}
+
+5F2C1F1C-B9F7-1642-59D9-A18318C1D70B,Files
+{<%ConfigFileTemplate%>;*/*.bat;<%InstallDir%>/*.vbs}
+
+5F2C1F1C-B9F7-1642-59D9-A18318C1D70B,LineFeed
+Windows
+
+5F2C1F1C-B9F7-1642-59D9-A18318C1D70B,StringMap
+{"@@CUSTOMERCOMPANY@@" <%UserInfoCompany%>
+"@@BRANDNAME@@" <%BrandName%>
+"@@SERVICENAME@@" <%ServiceName%>
+"@@CUSTOMERUSERNAME@@" <%UserInfoName%>
+"@@CUSTOMERUSERPHONE@@" <%UserInfoPhone%>
+"@@CUSTOMERUSEREMAIL@@" <%UserInfoEmail%>
+"@@ACCTNO@@" <%UserInfoAcctNo%>
+"@@INSTALLDIR@@" <%InstallDir%>}
+
+614C45B2-7515-780C-E444-7F165CF02DD7,Active
+No
+
+614C45B2-7515-780C-E444-7F165CF02DD7,Conditions
+{0 conditions}
+
+614C45B2-7515-780C-E444-7F165CF02DD7,ResultVirtualText
+BackupLocationShortName
+
+614C45B2-7515-780C-E444-7F165CF02DD7,TclScript
+{set BackupLocationShortName ""}
+
+6652193C-5D4B-44B6-ABC6-D6E96D89E5DC,Active
+No
+
+6652193C-5D4B-44B6-ABC6-D6E96D89E5DC,Comment
+{PJ removed. Is this the one at the top leve?}
+
+6652193C-5D4B-44B6-ABC6-D6E96D89E5DC,Conditions
+{0 conditions}
+
+69FD7409-5E2A-143B-DABD1C3B1E67,Conditions
+{2 conditions}
+
+6B4CB3C2-4799-4C9F-BA8E-1EE47C4606E1,ExitType
+Finish
+
+6B966959-05D9-DB32-8D9C4AD2A3DF,CheckCondition
+{Before Action is Executed}
+
+6B966959-05D9-DB32-8D9C4AD2A3DF,Platform
+Windows
+
+6C323815-B9AB-FA94-4F5D152EBC51,BackButton,subst
+1
+
+6C323815-B9AB-FA94-4F5D152EBC51,CancelButton,subst
+1
+
+6C323815-B9AB-FA94-4F5D152EBC51,Caption,subst
+1
+
+6C323815-B9AB-FA94-4F5D152EBC51,Conditions
+{0 conditions}
+
+6C323815-B9AB-FA94-4F5D152EBC51,Message,subst
+1
+
+6C323815-B9AB-FA94-4F5D152EBC51,NextButton,subst
+1
+
+6D9D1ABC-7146-443F-9EE9-205D5CA6C830,CheckCondition
+{Before Action is Executed}
+
+6D9D1ABC-7146-443F-9EE9-205D5CA6C830,String
+{<%Property <%CurrentPane%> UserMustAcceptLicense%>}
+
+6E70FB1F-6A43-6C23-3242E965A0D0,Action
+{Install Actions}
+
+6E70FB1F-6A43-6C23-3242E965A0D0,Conditions
+{0 conditions}
+
+6F61CDA8-30C9-454F-82A3-9987E1203079,Alias
+{Start a sync now.}
+
+6F61CDA8-30C9-454F-82A3-9987E1203079,Conditions
+{0 conditions}
+
+6F61CDA8-30C9-454F-82A3-9987E1203079,FileName
+<%ShortAppName%>-program-sync
+
+6F61CDA8-30C9-454F-82A3-9987E1203079,ShortcutName
+{Sync now}
+
+6F61CDA8-30C9-454F-82A3-9987E1203079,TargetFileName
+<%InstallDir%>/tools/Sync.bat
+
+6F61CDA8-30C9-454F-82A3-9987E1203079,WorkingDirectory
+<%InstallDir%>
+
+6F94698F-0839-3ABF-0CF2DF05A4C8,CheckCondition
+{Before Action is Executed}
+
+6F94698F-0839-3ABF-0CF2DF05A4C8,String
+<%CreateQuickLaunchShortcut%>
+
+6FEE2889-0338-1D49-60BF-1471F465AB26,AppendNewline
+No
+
+6FEE2889-0338-1D49-60BF-1471F465AB26,Comment
+{Closing final BackupLocations bracket}
+
+6FEE2889-0338-1D49-60BF-1471F465AB26,Conditions
+{0 conditions}
+
+6FEE2889-0338-1D49-60BF-1471F465AB26,FileOpenAction
+{Append to file}
+
+6FEE2889-0338-1D49-60BF-1471F465AB26,Files
+<%ConfigFileTemplate%>
+
+6FEE2889-0338-1D49-60BF-1471F465AB26,LineFeed
+Windows
+
+6FEE2889-0338-1D49-60BF-1471F465AB26,TextToWrite,subst
+1
+
+738DD098-7E3B-BC89-875CDB93CBE2,CheckCondition
+{Before Action is Executed}
+
+738DD098-7E3B-BC89-875CDB93CBE2,Platform
+Windows
+
+73DD4D07-B1DC-BA38-2B12-07EB24A7F0C8,Conditions
+{0 conditions}
+
+73DD4D07-B1DC-BA38-2B12-07EB24A7F0C8,Destination
+<%ConfigFileName%>
+
+73DD4D07-B1DC-BA38-2B12-07EB24A7F0C8,Source
+<%ConfigFileTemplate%>
+
+73EA65C1-3BE3-B190-55C3E99F6269,Conditions
+{1 condition}
+
+748D673B-DFE6-5F74-329903ACE4DB,CheckCondition
+{Before Action is Executed}
+
+748D673B-DFE6-5F74-329903ACE4DB,Filename
+<%ProgramExecutable%>
+
+793D8178-0F51-7F07-BC5886586D3C,CheckCondition
+{Before Action is Executed}
+
+793D8178-0F51-7F07-BC5886586D3C,Operator
+false
+
+793D8178-0F51-7F07-BC5886586D3C,String
+<%InstallStopped%>
+
+795EE61F-6C0D-4A8B-93E02AA3894A,CheckCondition
+{Before Action is Executed}
+
+795EE61F-6C0D-4A8B-93E02AA3894A,String
+<%LaunchApplication%>
+
+79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C,CheckCondition
+{Before Action is Executed}
+
+79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C,Operator
+false
+
+79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C,String
+<%LicenseAccepted%>
+
+7A983CD8-302C-4942-BE59-525C5B5FA2F2,Conditions
+{0 conditions}
+
+7A983CD8-302C-4942-BE59-525C5B5FA2F2,ProgramCommandLine
+{ServiceControl terminate}
+
+7A983CD8-302C-4942-BE59-525C5B5FA2F2,WorkingDirectory
+<%InstallDir%>
+
+7B770A07-A785-5215-956FA82CF14E,Active
+No
+
+7B770A07-A785-5215-956FA82CF14E,Conditions
+{3 conditions}
+
+7B770A07-A785-5215-956FA82CF14E,ShortcutDirectory
+<%QUICK_LAUNCH%>
+
+7B770A07-A785-5215-956FA82CF14E,ShortcutName
+<%BrandName%>
+
+7B770A07-A785-5215-956FA82CF14E,TargetFileName
+<%ProgramExecutable%>
+
+7B770A07-A785-5215-956FA82CF14E,WorkingDirectory
+<%InstallDir%>
+
+7D8E1902-2BC4-80D8-2C18771E7C22,Conditions
+{0 conditions}
+
+7D8E1902-2BC4-80D8-2C18771E7C22,ProgramCommandLine
+{<%ServiceExeName%> -i -S <%ServiceName%> -c "<%ConfigFileName%>"}
+
+7D8E1902-2BC4-80D8-2C18771E7C22,ProgressiveOutputWidget
+Message
+
+7D8E1902-2BC4-80D8-2C18771E7C22,ShowProgressiveOutput
+Yes
+
+7D8E1902-2BC4-80D8-2C18771E7C22,WorkingDirectory
+<%InstallDir%>
+
+7F85263E-CAE2-46BA-AAC0-6B89D20FD2DE,ExitType
+Finish
+
+8202CECC-54A0-9B6C-D24D111BA52E,Components
+4A9C852B-647E-EED5-5482FFBCC2AF
+
+8202CECC-54A0-9B6C-D24D111BA52E,Description,subst
+1
+
+8202CECC-54A0-9B6C-D24D111BA52E,DisplayName,subst
+1
+
+8202CECC-54A0-9B6C-D24D111BA52E,Name
+Typical
+
+8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Checked
+No
+
+8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Conditions
+{0 conditions}
+
+8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Text,subst
+1
+
+8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Type
+checkbutton
+
+8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Value
+Yes
+
+8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,VirtualText
+AddBackupLocation
+
+8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Y
+250
+
+848844B5-6103-9343-8B731B0BE4E0,Alias
+{Startup Actions}
+
+848844B5-6103-9343-8B731B0BE4E0,Conditions
+{0 conditions}
+
+84DA7F05-9FB7-CC36-9EC98F8A6826,CheckCondition
+{Before Next Pane is Displayed}
+
+84DA7F05-9FB7-CC36-9EC98F8A6826,FailureMessage
+<%DirectoryPermissionText%>
+
+84DA7F05-9FB7-CC36-9EC98F8A6826,Filename
+<%InstallDir%>
+
+84DA7F05-9FB7-CC36-9EC98F8A6826,Permission
+{can create}
+
+855DE408-060E-3D35-08B5-1D9AB05C2865,Conditions
+{0 conditions}
+
+855DE408-060E-3D35-08B5-1D9AB05C2865,Height
+100
+
+855DE408-060E-3D35-08B5-1D9AB05C2865,Text,subst
+1
+
+855DE408-060E-3D35-08B5-1D9AB05C2865,Type
+text
+
+855DE408-060E-3D35-08B5-1D9AB05C2865,VirtualText
+BackupLocationExclusions
+
+855DE408-060E-3D35-08B5-1D9AB05C2865,Y
+130
+
+87DE6D78-81E1-495B-A214-B3FF3E7E5614,CheckCondition
+{Before Action is Executed}
+
+87DE6D78-81E1-495B-A214-B3FF3E7E5614,Operator
+false
+
+87DE6D78-81E1-495B-A214-B3FF3E7E5614,String
+<%Answer%>
+
+88A50FD5-480F-19A5-DA74-C915EB0A9765,Active
+No
+
+88A50FD5-480F-19A5-DA74-C915EB0A9765,Conditions
+{1 condition}
+
+88A50FD5-480F-19A5-DA74-C915EB0A9765,ExecuteAction
+{After Pane is Finished}
+
+88A50FD5-480F-19A5-DA74-C915EB0A9765,Pane
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7
+
+8A761DBD-0640-D98C-9B3AD7672A8F,Conditions
+{0 conditions}
+
+8A761DBD-0640-D98C-9B3AD7672A8F,State
+disabled
+
+8A761DBD-0640-D98C-9B3AD7672A8F,Widget
+{Back Button;Next Button}
+
+8C866252-8760-9B08-FE569C25B60D,CheckCondition
+{Before Action is Executed}
+
+8C866252-8760-9B08-FE569C25B60D,Filename
+<%ProgramExecutable%>
+
+8E095096-F018-A880-429D-A2177A9B70EA,Active
+No
+
+8E095096-F018-A880-429D-A2177A9B70EA,Conditions
+{0 conditions}
+
+8E095096-F018-A880-429D-A2177A9B70EA,Text,subst
+1
+
+8E095096-F018-A880-429D-A2177A9B70EA,X
+50
+
+8E095096-F018-A880-429D-A2177A9B70EA,Y
+150
+
+8E1A5944-5AF5-5906-16D395E386D8,Conditions
+{0 conditions}
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,Active
+Yes
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,BackButton,subst
+1
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,CancelButton,subst
+1
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,Caption,subst
+1
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,CompanyLabel,subst
+1
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,Conditions
+{0 conditions}
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,Message,subst
+1
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,NextButton,subst
+1
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,Subtitle,subst
+1
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,Title,subst
+1
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,UserNameLabel,subst
+1
+
+905DA2E9-988C-2F27-BB1F5F274AC9,Alias
+{Cancel Actions}
+
+905DA2E9-988C-2F27-BB1F5F274AC9,Conditions
+{0 conditions}
+
+908CE221-5A3D-0A78-24A1-E7C91EBE38D4,BackButton,subst
+1
+
+908CE221-5A3D-0A78-24A1-E7C91EBE38D4,CancelButton,subst
+1
+
+908CE221-5A3D-0A78-24A1-E7C91EBE38D4,Caption,subst
+1
+
+908CE221-5A3D-0A78-24A1-E7C91EBE38D4,Conditions
+{0 conditions}
+
+908CE221-5A3D-0A78-24A1-E7C91EBE38D4,Message,subst
+1
+
+908CE221-5A3D-0A78-24A1-E7C91EBE38D4,NextButton,subst
+1
+
+908CE221-5A3D-0A78-24A1-E7C91EBE38D4,Subtitle,subst
+1
+
+908CE221-5A3D-0A78-24A1-E7C91EBE38D4,Title,subst
+1
+
+937C3FDD-FB28-98BD-3DAB276E59ED,Background
+white
+
+937C3FDD-FB28-98BD-3DAB276E59ED,Conditions
+{3 conditions}
+
+937C3FDD-FB28-98BD-3DAB276E59ED,Text,subst
+1
+
+937C3FDD-FB28-98BD-3DAB276E59ED,Type
+checkbutton
+
+937C3FDD-FB28-98BD-3DAB276E59ED,VirtualText
+CreateQuickLaunchShortcut
+
+937C3FDD-FB28-98BD-3DAB276E59ED,X
+185
+
+937C3FDD-FB28-98BD-3DAB276E59ED,Y
+200
+
+93AA298C-B64E-5683-14D2-7B86F7DEFD2C,Active
+No
+
+93AA298C-B64E-5683-14D2-7B86F7DEFD2C,Comment
+{set BackupLocationName "BackupLocation_${BackupLocationNumber}"}
+
+93AA298C-B64E-5683-14D2-7B86F7DEFD2C,Conditions
+{0 conditions}
+
+93AA298C-B64E-5683-14D2-7B86F7DEFD2C,ResultVirtualText
+BackupLocationName
+
+93AA298C-B64E-5683-14D2-7B86F7DEFD2C,TclScript
+{set BackupLocationName "BackupLocation_${BackupLocationNumber}"}
+
+96A68CAC-9ED7-806C-086B104720FD,CheckCondition
+{Before Action is Executed}
+
+96A68CAC-9ED7-806C-086B104720FD,String
+<%ErrorsOccurred%>
+
+97ACF525-C075-8635-E019202A83D8,Comment
+{Ask the user if they want to proceed with the uninstall.}
+
+9892B25C-689B-5B8F-F0C9-B14FF6ACC40C,Active
+No
+
+9892B25C-689B-5B8F-F0C9-B14FF6ACC40C,Conditions
+{0 conditions}
+
+9892B25C-689B-5B8F-F0C9-B14FF6ACC40C,ResultVirtualText
+AddBackupLocation
+
+9892B25C-689B-5B8F-F0C9-B14FF6ACC40C,TclScript
+{set AddBackupLocation no}
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Active
+Yes
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,BackButton,subst
+1
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,CancelButton,subst
+1
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Caption,subst
+1
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Conditions
+{0 conditions}
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Message,subst
+1
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,NextButton,subst
+1
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Subtitle,subst
+1
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Title,subst
+1
+
+9A663209-495B-ED16-09BE-457B61148022,Conditions
+{0 conditions}
+
+9A663209-495B-ED16-09BE-457B61148022,FileName
+<%ShortAppName%>-program-QueryCurrent
+
+9A663209-495B-ED16-09BE-457B61148022,ShortcutName
+{Query Current Only}
+
+9A663209-495B-ED16-09BE-457B61148022,TargetFileName
+<%InstallDir%>/tools/QueryOutputCurrent.bat
+
+9A663209-495B-ED16-09BE-457B61148022,WorkingDirectory
+<%InstallDir%>
+
+9D101299-B80C-441B-8685-6E3AC61808E8,Alias
+{Remote Control}
+
+9D101299-B80C-441B-8685-6E3AC61808E8,Comment
+{Get tech support via remote control}
+
+9D101299-B80C-441B-8685-6E3AC61808E8,Conditions
+{0 conditions}
+
+9D101299-B80C-441B-8685-6E3AC61808E8,FileName
+<%ShortAppName%>-program-RemoteControl
+
+9D101299-B80C-441B-8685-6E3AC61808E8,ShortcutName
+RemoteControl
+
+9D101299-B80C-441B-8685-6E3AC61808E8,TargetFileName
+<%InstallDir%>/tools/RemoteControl.exe
+
+9D101299-B80C-441B-8685-6E3AC61808E8,WorkingDirectory
+<%InstallDir%>
+
+A1DD1DC2-85D7-9BC6-998AC3D4A3A9,Alias
+{Startup Actions}
+
+A1DD1DC2-85D7-9BC6-998AC3D4A3A9,Conditions
+{0 conditions}
+
+A5B32DA1-B2FE-C1FA-6057-FBC3059EF076,Conditions
+{0 conditions}
+
+A5B32DA1-B2FE-C1FA-6057-FBC3059EF076,ResultVirtualText
+AddBackupLocation
+
+A5B32DA1-B2FE-C1FA-6057-FBC3059EF076,TclScript
+{set AddBackupLocation no }
+
+A6E1B027-A1B4-5848-4F868D028D00,CheckCondition
+{Before Action is Executed}
+
+A6E1B027-A1B4-5848-4F868D028D00,String
+<%ViewReadme%>
+
+ADA6EB2F-8820-4366-BBEF-ED1335B7F828,Conditions
+{1 condition}
+
+AE3BD5B4-35DE-4240-B79914D43E56,Active
+No
+
+AE3BD5B4-35DE-4240-B79914D43E56,BackButton,subst
+1
+
+AE3BD5B4-35DE-4240-B79914D43E56,CancelButton,subst
+1
+
+AE3BD5B4-35DE-4240-B79914D43E56,Caption,subst
+1
+
+AE3BD5B4-35DE-4240-B79914D43E56,Conditions
+{0 conditions}
+
+AE3BD5B4-35DE-4240-B79914D43E56,Message,subst
+1
+
+AE3BD5B4-35DE-4240-B79914D43E56,NextButton,subst
+1
+
+AIX-ppc,Active
+No
+
+AIX-ppc,DefaultDirectoryPermission
+0755
+
+AIX-ppc,DefaultFilePermission
+0755
+
+AIX-ppc,Executable
+<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%>
+
+AIX-ppc,FallBackToConsole
+Yes
+
+AIX-ppc,InstallDir
+<%Home%>/<%ShortAppName%>
+
+AIX-ppc,InstallMode
+Standard
+
+AIX-ppc,InstallType
+Typical
+
+AIX-ppc,ProgramExecutable
+{}
+
+AIX-ppc,ProgramFolderAllUsers
+No
+
+AIX-ppc,ProgramFolderName
+<%AppName%>
+
+AIX-ppc,ProgramLicense
+<%InstallDir%>/LICENSE.txt
+
+AIX-ppc,ProgramName
+{}
+
+AIX-ppc,ProgramReadme
+<%InstallDir%>/README.txt
+
+AIX-ppc,PromptForRoot
+Yes
+
+AIX-ppc,RequireRoot
+No
+
+AIX-ppc,RootInstallDir
+/usr/local/<%ShortAppName%>
+
+B002A311-F8E7-41DE-B039-521391924E5B,Message,subst
+1
+
+B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,Alias
+{Stop Backup Service}
+
+B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,Comment
+{Stop the Backup Windows Service}
+
+B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,Conditions
+{0 conditions}
+
+B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,FileName
+<%ShortAppName%>-program-stopservice
+
+B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,ShortcutName
+{Stop Service}
+
+B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,TargetFileName
+<%InstallDir%>/tools/StopService.bat
+
+B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,WorkingDirectory
+<%InstallDir%>
+
+B0AA6839-AAB6-A602-C0E4ECA2E4FF,CheckCondition
+{Before Action is Executed}
+
+B0AA6839-AAB6-A602-C0E4ECA2E4FF,Filename
+<%ProgramExecutable%>
+
+B39C0455-D1B6-7DDC-E2717F83463E,CheckCondition
+{Before Action is Executed}
+
+B39C0455-D1B6-7DDC-E2717F83463E,Operator
+false
+
+B39C0455-D1B6-7DDC-E2717F83463E,String
+<%InstallStopped%>
+
+B3B99E2D-C368-A921-B7BC-A71EBDE3AD4D,Conditions
+{0 conditions}
+
+B3B99E2D-C368-A921-B7BC-A71EBDE3AD4D,ExecuteAction
+{Before Next Pane is Displayed}
+
+B3B99E2D-C368-A921-B7BC-A71EBDE3AD4D,Password
+<%InstallPassword%>
+
+B4D31D1E-ADB1-DE8F-18EB7294DDA8,Conditions
+{0 conditions}
+
+B4D31D1E-ADB1-DE8F-18EB7294DDA8,ProgramCommandLine
+{<%ServiceExeName%> -r -S <%ServiceName%>}
+
+B4D31D1E-ADB1-DE8F-18EB7294DDA8,WorkingDirectory
+<%InstallDir%>
+
+B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174,Message,subst
+1
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,AcceptRadiobutton,subst
+0
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Active
+Yes
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,BackButton,subst
+1
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,CancelButton,subst
+1
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Caption,subst
+1
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Conditions
+{0 conditions}
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,DeclineRadiobutton,subst
+0
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Message,subst
+1
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,NextButton,subst
+1
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Subtitle,subst
+1
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Text,subst
+1
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Title,subst
+1
+
+B5DFEC63-92A9-4686-909E-0CE78A7069D6,Alias
+{Restart Backup Service}
+
+B5DFEC63-92A9-4686-909E-0CE78A7069D6,Comment
+{Stop and restart the Backup Windows Service}
+
+B5DFEC63-92A9-4686-909E-0CE78A7069D6,Conditions
+{0 conditions}
+
+B5DFEC63-92A9-4686-909E-0CE78A7069D6,FileName
+<%ShortAppName%>-program-restartservice
+
+B5DFEC63-92A9-4686-909E-0CE78A7069D6,ShortcutName
+{Restart Service}
+
+B5DFEC63-92A9-4686-909E-0CE78A7069D6,TargetFileName
+{<%InstallDir%>\tools\RestartService.bat}
+
+B5DFEC63-92A9-4686-909E-0CE78A7069D6,WorkingDirectory
+<%InstallDir%>
+
+B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,Conditions
+{0 conditions}
+
+B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,LabelSide
+left
+
+B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,Text,subst
+1
+
+B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,Type
+entry
+
+B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,VirtualText
+BackupLocationShortName
+
+B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,Y
+100
+
+B93D2216-1DDB-484C-A9AC-D6C18ED7DE23,Conditions
+{2 conditions}
+
+B93D2216-1DDB-484C-A9AC-D6C18ED7DE23,State
+disabled
+
+B93D2216-1DDB-484C-A9AC-D6C18ED7DE23,Widget
+NextButton
+
+BC4EA5FD-50BD-4D6E-953F-5E3EDB957360,CheckCondition
+{Before Next Action is Executed}
+
+BC4EA5FD-50BD-4D6E-953F-5E3EDB957360,FailureMessage
+<%DirectoryPermissionText%>
+
+BC4EA5FD-50BD-4D6E-953F-5E3EDB957360,Filename
+<%InstallDir%>
+
+BC4EA5FD-50BD-4D6E-953F-5E3EDB957360,Permission
+{can create}
+
+C0452595-F3EB-43AD-BCA2-661437584636,Alias
+{Modify Backup Configuration}
+
+C0452595-F3EB-43AD-BCA2-661437584636,Comment
+{Modify your Backup Configuration}
+
+C0452595-F3EB-43AD-BCA2-661437584636,Conditions
+{0 conditions}
+
+C0452595-F3EB-43AD-BCA2-661437584636,FileName
+<%ShortAppName%>-program-editconfig
+
+C0452595-F3EB-43AD-BCA2-661437584636,ShortcutName
+{Edit Config File}
+
+C0452595-F3EB-43AD-BCA2-661437584636,TargetFileName
+<%InstallDir%>/tools/EditConfig.bat
+
+C0452595-F3EB-43AD-BCA2-661437584636,WorkingDirectory
+<%InstallDir%>
+
+C0AF7C05-A31A-8376-BCB9-BA8B3A666252,Conditions
+{0 conditions}
+
+C0AF7C05-A31A-8376-BCB9-BA8B3A666252,FileName
+<%ShortAppName%>-program-QueryAll
+
+C0AF7C05-A31A-8376-BCB9-BA8B3A666252,ShortcutName
+{Query All}
+
+C0AF7C05-A31A-8376-BCB9-BA8B3A666252,TargetFileName
+<%InstallDir%>/tools/QueryOutputAll.bat
+
+C0AF7C05-A31A-8376-BCB9-BA8B3A666252,WorkingDirectory
+<%InstallDir%>
+
+C105AAAE-7C16-2C9E-769FE4535B60,Active
+No
+
+C105AAAE-7C16-2C9E-769FE4535B60,Caption,subst
+1
+
+C105AAAE-7C16-2C9E-769FE4535B60,CloseButton,subst
+1
+
+C105AAAE-7C16-2C9E-769FE4535B60,Conditions
+{3 conditions}
+
+C105AAAE-7C16-2C9E-769FE4535B60,Message,subst
+1
+
+C105AAAE-7C16-2C9E-769FE4535B60,TextFile
+<%ProgramReadme%>
+
+C105AAAE-7C16-2C9E-769FE4535B60,Title,subst
+1
+
+C33D74B2-26FA-16F5-433A10C6A747,Active
+No
+
+C33D74B2-26FA-16F5-433A10C6A747,Conditions
+{3 conditions}
+
+C33D74B2-26FA-16F5-433A10C6A747,ProgramCommandLine
+<%ProgramExecutable%>
+
+C33D74B2-26FA-16F5-433A10C6A747,WaitForProgram
+No
+
+C33D74B2-26FA-16F5-433A10C6A747,WorkingDirectory
+<%InstallDir%>
+
+C7762473-273F-E3CA-17E3-65789B14CDB0,Conditions
+{0 conditions}
+
+C7762473-273F-E3CA-17E3-65789B14CDB0,ExecuteAction
+{Before Next Pane is Displayed}
+
+C7762473-273F-E3CA-17E3-65789B14CDB0,FileOpenAction
+{Append to file}
+
+C7762473-273F-E3CA-17E3-65789B14CDB0,Files
+<%ConfigFileTemplate%>
+
+C7762473-273F-E3CA-17E3-65789B14CDB0,TextToWrite,subst
+1
+
+CC4337CC-F3B5-757C-DFCF5D1D365A,CheckCondition
+{Before Action is Executed}
+
+CC4337CC-F3B5-757C-DFCF5D1D365A,Operator
+false
+
+CC4337CC-F3B5-757C-DFCF5D1D365A,String
+<%SilentMode%>
+
+CDD84DE3-C970-458F-9162-1A3CE0AA716B,Alias
+{Start Backup Service}
+
+CDD84DE3-C970-458F-9162-1A3CE0AA716B,Comment
+{Start Backup Windows Service}
+
+CDD84DE3-C970-458F-9162-1A3CE0AA716B,Conditions
+{0 conditions}
+
+CDD84DE3-C970-458F-9162-1A3CE0AA716B,FileName
+<%ShortAppName%>-program-startservice
+
+CDD84DE3-C970-458F-9162-1A3CE0AA716B,ShortcutName
+{Start Service}
+
+CDD84DE3-C970-458F-9162-1A3CE0AA716B,TargetFileName
+{<%InstallDir%>\tools\StartService.bat}
+
+CDD84DE3-C970-458F-9162-1A3CE0AA716B,WorkingDirectory
+<%InstallDir%>
+
+CFFA27AF-A641-E41C-B4A0E3BB3CBB,Background
+white
+
+CFFA27AF-A641-E41C-B4A0E3BB3CBB,Conditions
+{2 conditions}
+
+CFFA27AF-A641-E41C-B4A0E3BB3CBB,Text,subst
+1
+
+CFFA27AF-A641-E41C-B4A0E3BB3CBB,Type
+checkbutton
+
+CFFA27AF-A641-E41C-B4A0E3BB3CBB,VirtualText
+LaunchApplication
+
+CFFA27AF-A641-E41C-B4A0E3BB3CBB,X
+185
+
+CFFA27AF-A641-E41C-B4A0E3BB3CBB,Y
+160
+
+D23DD94C-E517-7F34-FD59-802CB18AB887,Comment
+{Need to do before starting anything else.}
+
+D23DD94C-E517-7F34-FD59-802CB18AB887,Conditions
+{0 conditions}
+
+D23DD94C-E517-7F34-FD59-802CB18AB887,Files
+{*/*.conf;*/*.txt;*/*.bat}
+
+D23DD94C-E517-7F34-FD59-802CB18AB887,LineFeed
+Windows
+
+D3D73C76-D9D3-07DA-63D4163A44BE,Conditions
+{0 conditions}
+
+D3D73C76-D9D3-07DA-63D4163A44BE,ExitType
+Finish
+
+D4FC6EB5-DDEE-4E4A-B8E1-D4B588A7928B,Action
+{Install Actions}
+
+D55BA4AF-E73B-60D1-E26F79175227,Action
+{Uninstall Actions}
+
+D55BA4AF-E73B-60D1-E26F79175227,Conditions
+{0 conditions}
+
+D7FBBEBB-2186-5674-BA87-BB7151859D4E,Conditions
+{0 conditions}
+
+D7FBBEBB-2186-5674-BA87-BB7151859D4E,ResultVirtualText
+BackupLocationNumber
+
+D7FBBEBB-2186-5674-BA87-BB7151859D4E,TclScript
+{incr BackupLocationNumber}
+
+D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,Alias
+{Reload Configuration File, after editing it.}
+
+D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,Conditions
+{0 conditions}
+
+D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,FileName
+<%ShortAppName%>-program-reloadconfig
+
+D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,ShortcutName
+{Reload configuration file}
+
+D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,TargetFileName
+<%InstallDir%>/tools/ReloadConfig.bat
+
+D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,WorkingDirectory
+<%InstallDir%>
+
+D8F0AA0F-AD79-C566-15CC508F503B,Action
+{Install Actions}
+
+D8F0AA0F-AD79-C566-15CC508F503B,Conditions
+{0 conditions}
+
+D9F88AC1-3D2D-F6DB-871E-3A0E016770B1,Conditions
+{0 conditions}
+
+D9F88AC1-3D2D-F6DB-871E-3A0E016770B1,Destination
+<%ConfigFileTemplate%>
+
+D9F88AC1-3D2D-F6DB-871E-3A0E016770B1,Source
+<%InstallDir%>/templates/original.conf
+
+DA33B826-E633-A845-4646-76DFA78B907B,Active
+Yes
+
+DA33B826-E633-A845-4646-76DFA78B907B,BackButton,subst
+1
+
+DA33B826-E633-A845-4646-76DFA78B907B,CancelButton,subst
+1
+
+DA33B826-E633-A845-4646-76DFA78B907B,Caption,subst
+1
+
+DA33B826-E633-A845-4646-76DFA78B907B,Conditions
+{0 conditions}
+
+DA33B826-E633-A845-4646-76DFA78B907B,Message,subst
+1
+
+DA33B826-E633-A845-4646-76DFA78B907B,NextButton,subst
+1
+
+DA33B826-E633-A845-4646-76DFA78B907B,Subtitle,subst
+1
+
+DA33B826-E633-A845-4646-76DFA78B907B,Title,subst
+1
+
+DDBBD8A9-13D7-9509-9202-419E989F60A9,Active
+No
+
+DDBBD8A9-13D7-9509-9202-419E989F60A9,Checked
+No
+
+DDBBD8A9-13D7-9509-9202-419E989F60A9,Conditions
+{0 conditions}
+
+DDBBD8A9-13D7-9509-9202-419E989F60A9,Text,subst
+1
+
+DDBBD8A9-13D7-9509-9202-419E989F60A9,Type
+checkbutton
+
+DDBBD8A9-13D7-9509-9202-419E989F60A9,VirtualText
+AddBackupLocation
+
+DDBBD8A9-13D7-9509-9202-419E989F60A9,X
+50
+
+DDBBD8A9-13D7-9509-9202-419E989F60A9,Y
+200
+
+DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,Alias
+{Install Backup Service}
+
+DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,Comment
+{Install the Backup Windows Service}
+
+DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,Conditions
+{0 conditions}
+
+DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,FileName
+<%ShortAppName%>-program-installService
+
+DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,ShortcutName
+{Install Service}
+
+DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,TargetFileName
+<%InstallDir%>/tools/InstallService.bat
+
+DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,WorkingDirectory
+<%InstallDir%>
+
+DECC120D-6904-7F17-45A49184A5A3,Active
+No
+
+DECC120D-6904-7F17-45A49184A5A3,Conditions
+{2 conditions}
+
+DECC120D-6904-7F17-45A49184A5A3,ShortcutName
+<%AppName%>
+
+DECC120D-6904-7F17-45A49184A5A3,TargetFileName
+<%ProgramExecutable%>
+
+DECC120D-6904-7F17-45A49184A5A3,WorkingDirectory
+<%InstallDir%>
+
+DFFF91A9-2CA5-6ABE-8474D814AF88,CheckCondition
+{Before Action is Executed}
+
+DFFF91A9-2CA5-6ABE-8474D814AF88,Operator
+false
+
+DFFF91A9-2CA5-6ABE-8474D814AF88,String
+<%SilentMode%>
+
+E02368C5-95B5-03A7-3282740037B0,CheckCondition
+{Before Action is Executed}
+
+E02368C5-95B5-03A7-3282740037B0,Operator
+false
+
+E02368C5-95B5-03A7-3282740037B0,String
+<%InstallStopped%>
+
+E161F216-E597-B340-C1A71C476E2C,CheckCondition
+{Before Action is Executed}
+
+E161F216-E597-B340-C1A71C476E2C,Message,subst
+1
+
+E161F216-E597-B340-C1A71C476E2C,Title,subst
+1
+
+E23AC50D-7CFB-800E-A99C6F4068F8,Alias
+{Cancel Actions}
+
+E23AC50D-7CFB-800E-A99C6F4068F8,Conditions
+{0 conditions}
+
+E44CFF46-6302-C518-B9C30D2E43F7,CheckCondition
+{Before Action is Executed}
+
+E44CFF46-6302-C518-B9C30D2E43F7,String
+<%CreateDesktopShortcut%>
+
+E4DEA723-FC78-45D7-BAB1-A3E4C4C96EA1,Conditions
+{0 conditions}
+
+E4DEA723-FC78-45D7-BAB1-A3E4C4C96EA1,ProgramCommandLine
+{net stop <%ServiceName%>}
+
+E56ADFF4-C15E-AEDB-A599-C468AF72C4BB,Conditions
+{0 conditions}
+
+E56ADFF4-C15E-AEDB-A599-C468AF72C4BB,Destination
+{<%InstallDir%>\templates\NotifySysAdmin.original.vbs}
+
+E56ADFF4-C15E-AEDB-A599-C468AF72C4BB,Source
+{<%InstallDir%>\templates\NotifySysAdmin.template.vbs}
+
+EB2B31A1-C111-3582-0C8A5656692A,String
+<%ErrorsOccurred%>
+
+EB532611-5F30-3C24-66EB-F3826D9054FD,CheckCondition
+{Before Action is Executed}
+
+EB532611-5F30-3C24-66EB-F3826D9054FD,String
+<%AddBackupLocation%>
+
+F4024A3E-9A6D-2726-5E0CFFA93054,Alias
+{Uninstall Actions}
+
+F4024A3E-9A6D-2726-5E0CFFA93054,Conditions
+{0 conditions}
+
+F5F21749-8B3A-49C6-9138-9C4D6D703D26,Active
+No
+
+F5F21749-8B3A-49C6-9138-9C4D6D703D26,Conditions
+{0 conditions}
+
+F5F21749-8B3A-49C6-9138-9C4D6D703D26,ProgramCommandLine
+{cmd /k tools/7za.exe encrypted_keys.exe -p<%InstallPassword%>}
+
+F5F21749-8B3A-49C6-9138-9C4D6D703D26,ProgressiveOutputWidget
+Message
+
+F5F21749-8B3A-49C6-9138-9C4D6D703D26,ShowProgressiveOutput
+Yes
+
+F5F21749-8B3A-49C6-9138-9C4D6D703D26,WorkingDirectory
+<%InstallDir%>
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Active
+Yes
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,BackButton,subst
+1
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,CancelButton,subst
+1
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Caption,subst
+1
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,CompanyLabel,subst
+0
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Conditions
+{0 conditions}
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Message,subst
+1
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,NextButton,subst
+1
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Subtitle,subst
+1
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Title,subst
+1
+
+F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,UserNameLabel,subst
+1
+
+F9E38720-6ABA-8B99-2471-496902E4CBC2,Active
+No
+
+F9E38720-6ABA-8B99-2471-496902E4CBC2,Conditions
+{0 conditions}
+
+F9E38720-6ABA-8B99-2471-496902E4CBC2,ResultVirtualText
+BackupLocationPath
+
+F9E38720-6ABA-8B99-2471-496902E4CBC2,TclScript
+{set BackupLocationPath "" }
+
+FB697A88-2842-468E-9776-85E84B009340,Active
+No
+
+FB697A88-2842-468E-9776-85E84B009340,Conditions
+{0 conditions}
+
+FB697A88-2842-468E-9776-85E84B009340,IgnoreErrors
+Yes
+
+FB697A88-2842-468E-9776-85E84B009340,ProgramCommandLine
+{"<%InstallDir%>\<%ServiceExeName%> -r -S <%ServiceName%>}
+
+FB697A88-2842-468E-9776-85E84B009340,WorkingDirectory
+<%Temp%>
+
+FDF68FD6-BEA8-4A74-867D-5139F4D9E793,Active
+No
+
+FDF68FD6-BEA8-4A74-867D-5139F4D9E793,Conditions
+{0 conditions}
+
+FDF68FD6-BEA8-4A74-867D-5139F4D9E793,WaitTime
+2000
+
+FEFD090D-C133-BC95-B3564F693CD3,Alias
+{Finish Actions}
+
+FEFD090D-C133-BC95-B3564F693CD3,Conditions
+{0 conditions}
+
+FreeBSD-4-x86,Active
+No
+
+FreeBSD-4-x86,DefaultDirectoryPermission
+0755
+
+FreeBSD-4-x86,DefaultFilePermission
+0755
+
+FreeBSD-4-x86,Executable
+<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%>
+
+FreeBSD-4-x86,FallBackToConsole
+Yes
+
+FreeBSD-4-x86,InstallDir
+<%Home%>/<%ShortAppName%>
+
+FreeBSD-4-x86,InstallMode
+Standard
+
+FreeBSD-4-x86,InstallType
+Typical
+
+FreeBSD-4-x86,ProgramExecutable
+{}
+
+FreeBSD-4-x86,ProgramFolderAllUsers
+No
+
+FreeBSD-4-x86,ProgramFolderName
+<%AppName%>
+
+FreeBSD-4-x86,ProgramLicense
+<%InstallDir%>/LICENSE.txt
+
+FreeBSD-4-x86,ProgramName
+{}
+
+FreeBSD-4-x86,ProgramReadme
+<%InstallDir%>/README.txt
+
+FreeBSD-4-x86,PromptForRoot
+Yes
+
+FreeBSD-4-x86,RequireRoot
+No
+
+FreeBSD-4-x86,RootInstallDir
+/usr/local/<%ShortAppName%>
+
+FreeBSD-x86,Active
+No
+
+FreeBSD-x86,DefaultDirectoryPermission
+0755
+
+FreeBSD-x86,DefaultFilePermission
+0755
+
+FreeBSD-x86,Executable
+<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%>
+
+FreeBSD-x86,FallBackToConsole
+Yes
+
+FreeBSD-x86,InstallDir
+<%Home%>/<%ShortAppName%>
+
+FreeBSD-x86,InstallMode
+Standard
+
+FreeBSD-x86,InstallType
+Typical
+
+FreeBSD-x86,ProgramExecutable
+{}
+
+FreeBSD-x86,ProgramFolderAllUsers
+No
+
+FreeBSD-x86,ProgramFolderName
+<%AppName%>
+
+FreeBSD-x86,ProgramLicense
+<%InstallDir%>/LICENSE.txt
+
+FreeBSD-x86,ProgramName
+{}
+
+FreeBSD-x86,ProgramReadme
+<%InstallDir%>/README.txt
+
+FreeBSD-x86,PromptForRoot
+Yes
+
+FreeBSD-x86,RequireRoot
+No
+
+FreeBSD-x86,RootInstallDir
+/usr/local/<%ShortAppName%>
+
+HPUX-hppa,Active
+No
+
+HPUX-hppa,DefaultDirectoryPermission
+0755
+
+HPUX-hppa,DefaultFilePermission
+0755
+
+HPUX-hppa,Executable
+<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%>
+
+HPUX-hppa,FallBackToConsole
+Yes
+
+HPUX-hppa,InstallDir
+<%Home%>/<%ShortAppName%>
+
+HPUX-hppa,InstallMode
+Standard
+
+HPUX-hppa,InstallType
+Typical
+
+HPUX-hppa,ProgramExecutable
+{}
+
+HPUX-hppa,ProgramFolderAllUsers
+No
+
+HPUX-hppa,ProgramFolderName
+<%AppName%>
+
+HPUX-hppa,ProgramLicense
+<%InstallDir%>/LICENSE.txt
+
+HPUX-hppa,ProgramName
+{}
+
+HPUX-hppa,ProgramReadme
+<%InstallDir%>/README.txt
+
+HPUX-hppa,PromptForRoot
+Yes
+
+HPUX-hppa,RequireRoot
+No
+
+HPUX-hppa,RootInstallDir
+/usr/local/<%ShortAppName%>
+
+Linux-x86,Active
+No
+
+Linux-x86,BuildType
+dynamic
+
+Linux-x86,DefaultDirectoryPermission
+00755
+
+Linux-x86,DefaultFilePermission
+00755
+
+Linux-x86,Executable
+<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%>
+
+Linux-x86,FallBackToConsole
+Yes
+
+Linux-x86,InstallDir
+<%Home%>/<%ShortAppName%>
+
+Linux-x86,InstallMode
+Standard
+
+Linux-x86,InstallType
+Typical
+
+Linux-x86,ProgramExecutable
+<%InstallDir%>/TebucoSafe
+
+Linux-x86,ProgramFolderAllUsers
+No
+
+Linux-x86,ProgramFolderName
+<%AppName%>
+
+Linux-x86,ProgramLicense
+<%InstallDir%>/LICENSE.txt
+
+Linux-x86,ProgramName
+{}
+
+Linux-x86,ProgramReadme
+<%InstallDir%>/README.txt
+
+Linux-x86,PromptForRoot
+Yes
+
+Linux-x86,RequireRoot
+No
+
+Linux-x86,RootInstallDir
+/usr/local/<%ShortAppName%>
+
+Solaris-sparc,Active
+No
+
+Solaris-sparc,DefaultDirectoryPermission
+0755
+
+Solaris-sparc,DefaultFilePermission
+0755
+
+Solaris-sparc,Executable
+<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%>
+
+Solaris-sparc,FallBackToConsole
+Yes
+
+Solaris-sparc,InstallDir
+<%Home%>/<%ShortAppName%>
+
+Solaris-sparc,InstallMode
+Standard
+
+Solaris-sparc,InstallType
+Typical
+
+Solaris-sparc,ProgramExecutable
+{}
+
+Solaris-sparc,ProgramFolderAllUsers
+No
+
+Solaris-sparc,ProgramFolderName
+<%AppName%>
+
+Solaris-sparc,ProgramLicense
+<%InstallDir%>/LICENSE.txt
+
+Solaris-sparc,ProgramName
+{}
+
+Solaris-sparc,ProgramReadme
+<%InstallDir%>/README.txt
+
+Solaris-sparc,PromptForRoot
+Yes
+
+Solaris-sparc,RequireRoot
+No
+
+Solaris-sparc,RootInstallDir
+/usr/local/<%ShortAppName%>
+
+TarArchive,Active
+No
+
+TarArchive,CompressionLevel
+6
+
+TarArchive,DefaultDirectoryPermission
+0755
+
+TarArchive,DefaultFilePermission
+0755
+
+TarArchive,Executable
+<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%>
+
+TarArchive,FallBackToConsole
+Yes
+
+TarArchive,InstallDir
+<%Home%>/<%ShortAppName%>
+
+TarArchive,InstallMode
+Standard
+
+TarArchive,InstallType
+Typical
+
+TarArchive,OutputFileName
+<%ShortAppName%>-<%Version%>.tar.gz
+
+TarArchive,ProgramExecutable
+{}
+
+TarArchive,ProgramFolderAllUsers
+No
+
+TarArchive,ProgramFolderName
+<%AppName%>
+
+TarArchive,ProgramLicense
+<%InstallDir%>/LICENSE.txt
+
+TarArchive,ProgramName
+{}
+
+TarArchive,ProgramReadme
+<%InstallDir%>/README.txt
+
+TarArchive,PromptForRoot
+Yes
+
+TarArchive,RequireRoot
+No
+
+TarArchive,RootInstallDir
+/usr/local/<%ShortAppName%>
+
+TarArchive,VirtualTextMap
+{<%InstallDir%> <%ShortAppName%>}
+
+Windows,Active
+Yes
+
+Windows,BuildType
+{}
+
+Windows,Executable
+installer.exe
+
+Windows,IncludeTWAPI
+No
+
+Windows,InstallDir
+{C:\Program Files\<%BrandName%>}
+
+Windows,InstallMode
+Standard
+
+Windows,InstallType
+Typical
+
+Windows,ProgramExecutable
+{}
+
+Windows,ProgramFolderAllUsers
+No
+
+Windows,ProgramFolderName
+<%BrandName%>
+
+Windows,ProgramLicense
+{<%InstallDir%>\LICENSE.txt}
+
+Windows,ProgramName
+{}
+
+Windows,ProgramReadme
+{}
+
+Windows,WindowsIcon
+{}
+
+ZipArchive,Active
+No
+
+ZipArchive,CompressionLevel
+6
+
+ZipArchive,DefaultDirectoryPermission
+0755
+
+ZipArchive,DefaultFilePermission
+0755
+
+ZipArchive,Executable
+<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%>
+
+ZipArchive,FallBackToConsole
+Yes
+
+ZipArchive,InstallDir
+<%Home%>/<%ShortAppName%>
+
+ZipArchive,InstallMode
+Standard
+
+ZipArchive,InstallType
+Typical
+
+ZipArchive,OutputFileName
+<%ShortAppName%>-<%Version%>.zip
+
+ZipArchive,ProgramExecutable
+{}
+
+ZipArchive,ProgramFolderAllUsers
+No
+
+ZipArchive,ProgramFolderName
+<%AppName%>
+
+ZipArchive,ProgramLicense
+<%InstallDir%>/LICENSE.txt
+
+ZipArchive,ProgramName
+{}
+
+ZipArchive,ProgramReadme
+<%InstallDir%>/README.txt
+
+ZipArchive,PromptForRoot
+Yes
+
+ZipArchive,RequireRoot
+No
+
+ZipArchive,RootInstallDir
+/usr/local/<%ShortAppName%>
+
+ZipArchive,VirtualTextMap
+{<%InstallDir%> <%ShortAppName%>}
+
+}
+
+::msgcat::mcmset de {
+20CBDBEA-2217-457B-8D98-D692C4F591E9,Message
+<%UninstallCompleteText%>
+
+2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6,Message
+<%InstallationCompleteText%>
+
+B002A311-F8E7-41DE-B039-521391924E5B,Message
+<%InstallingApplicationText%>
+
+B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174,Message
+<%UninstallingApplicationText%>
+
+}
+::msgcat::mcmset en {
+16D53E40-546B-54C3-088B1B5E3BBB,Text
+<%CreateDesktopShortcutText%>
+
+20CBDBEA-2217-457B-8D98-D692C4F591E9,Message
+<%UninstallCompleteText%>
+
+2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6,Message
+<%InstallationCompleteText%>
+
+2E2963BD-DDBD-738D-A910-B7F3F04946F9,Text
+{No:<%BackupLocationNumber%> }
+
+2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,TextToWrite
+BackupLocations\n\{\n
+
+32F5B0AF-EB83-7A03-D8FAE1ECE473,Message
+<%InstallStartupText%>
+
+32F5B0AF-EB83-7A03-D8FAE1ECE473,Title
+<%InstallApplicationText%>
+
+36FF8915-8148-0F1F-27D7239CBFA1,Text
+<%ViewReadmeText%>
+
+3B6E2E7C-1A26-27F1-D578E383B128,Text
+<%ViewReadmeText%>
+
+3D33AA8C-0037-204B-39A339FD38BD,Message
+{<%BrandName%> has been removed from your system. Thank you for using the <%BrandName%> Backup Service. If you need further assistance, please contact us at http://<%BrandName%>.com or support@<%BrandName%>.com.}
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Caption
+{}
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Message
+{}
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Subtitle
+{Add a directory to backup, nickname it, and add some exclusions.}
+
+3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Title
+{Backup Locations}
+
+3FDB57ED-598D-8A4E-CEF7-D90833305558,Text
+{Backup Directory}
+
+4A9C852B-647E-EED5-5482FFBCC2AF,Description
+<%ProgramFilesDescription%>
+
+4ACB0B47-42B3-2B3A-BFE9AA4EC707,Message
+<%UninstallStartupText%>
+
+4ACB0B47-42B3-2B3A-BFE9AA4EC707,Title
+<%UninstallApplicationText%>
+
+58E1119F-639E-17C9-5D3898F385AA,Caption
+{Setup will install <%ShortAppName%> in the following folder.
+
+To install to this folder, click Next. To install to a different folder, click Browse and select another folder.}
+
+58E1119F-639E-17C9-5D3898F385AA,Subtitle
+{Where should <%ShortAppName%> be installed?}
+
+59395A3B-6116-F93A-84E1-5E079C2CD44B,Caption
+{Backup Exclusions}
+
+59395A3B-6116-F93A-84E1-5E079C2CD44B,Message
+{}
+
+59395A3B-6116-F93A-84E1-5E079C2CD44B,Subtitle
+{Enter your backup exclusions for this Backup Location <%BackupLocationName_ShortName%> here.}
+
+59395A3B-6116-F93A-84E1-5E079C2CD44B,Text
+{}
+
+59395A3B-6116-F93A-84E1-5E079C2CD44B,Title
+{Backup Exclusions}
+
+640DA2B2-6CF3-0873-D7AE-ABCDDE39EFCF,Text
+{Put your custom text here.
+<%AddBackupLocation%>
+<%BackupLocationNumber%>
+<%BackupLocationName%>}
+
+6C323815-B9AB-FA94-4F5D152EBC51,Caption
+{Installation Wizard Complete}
+
+6C323815-B9AB-FA94-4F5D152EBC51,Message
+{The Installation Wizard has successfully installed the <%BrandName%> Backup Service. Click Finish to exit the wizard.}
+
+6CFBBE13-6B70-4B7C-B5EF-0677752D95A8,Caption
+{Installing encryption keys...}
+
+6CFBBE13-6B70-4B7C-B5EF-0677752D95A8,Message
+{Click next to install encrypted keys and configuration file (bbackupd.conf)...
+
+You will be presented with the current configuration file so that you may make any last minute changes...}
+
+6FEE2889-0338-1D49-60BF-1471F465AB26,TextToWrite
+\}\n
+
+8202CECC-54A0-9B6C-D24D111BA52E,Description
+<%TypicalInstallDescription%>
+
+8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Text
+{Add another backup location?}
+
+855DE408-060E-3D35-08B5-1D9AB05C2865,Text
+Exclusions
+
+8E095096-F018-A880-429D-A2177A9B70EA,Text
+{AddAnother: <%AddBackupLocation%>}
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,Caption
+{Please enter your phone number and email address.}
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,CompanyLabel
+Email:
+
+9013E862-8E81-5290-64F9-D8BCD13EC7E5,UserNameLabel
+Phone:
+
+937C3FDD-FB28-98BD-3DAB276E59ED,Text
+<%CreateQuickLaunchShortcutText%>
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Caption
+{Click Next to continue...}
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Message
+{Building configuration file...}
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Subtitle
+{}
+
+9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Title
+Continue
+
+9BAB328D-414B-D351-CA8D-824DF94B9DCA,Text
+{Add another backup location after this one?}
+
+A18C2977-1409-C1FB-892415711F72,Text
+<%LaunchApplicationText%>
+
+AAF2142A-9FC9-4664-DFF2-13B9EB7BA0E1,CompanyLabel
+Company:
+
+AE3BD5B4-35DE-4240-B79914D43E56,Caption
+{Welcome to the Installation Wizard for <%BrandName%> Backup Service!}
+
+AE3BD5B4-35DE-4240-B79914D43E56,Message
+{Thank you for installing the <%BrandName%>(SM) Backup Service.
+
+If you need any assistance, please contact us at http://<%BrandName%>.com or support@<%BrandName%>.com.
+
+This will install <%BrandName%> version <%Version%> on your computer.
+
+It is recommended that you close all other applications before continuing.
+
+Click Next to continue or Cancel to exit Setup.
+}
+
+B002A311-F8E7-41DE-B039-521391924E5B,Message
+<%InstallingApplicationText%>
+
+B4404713-AF4F-4F4B-670F3115517F,Description
+<%CustomInstallDescription%>
+
+B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174,Message
+<%UninstallingApplicationText%>
+
+B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Text
+{Box Backup, http://www.fluffy.co.uk/boxbackup
+Copyright (c) 2003-2007 Ben Summers and contributors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. All use of this software and associated advertising materials must display the following acknowledgement:
+ This product includes software developed by Ben Summers and contributors.
+
+4. The names of the Authors may not be used to endorse or promote products derived from this software without specific prior written permission.
+
+[Where legally impermissible the Authors do not disclaim liability for direct physical injury or death caused solely by defects in the software unless it is modified by a third party.]
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE}
+
+B57F8C91-2439-CFD3-7EB5-57D4EA48D3C6,Caption
+{<%BrandName%> will backup the following folder.
+
+To backup this folder, click Next. To backup a different folder, click Browse and select another folder.}
+
+B57F8C91-2439-CFD3-7EB5-57D4EA48D3C6,DestinationLabel
+{Backup This Folder}
+
+B57F8C91-2439-CFD3-7EB5-57D4EA48D3C6,Message
+{}
+
+B57F8C91-2439-CFD3-7EB5-57D4EA48D3C6,Subtitle
+{What directory should <%BrandName%> backup?}
+
+B57F8C91-2439-CFD3-7EB5-57D4EA48D3C6,Title
+{Choose Backup Location}
+
+B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,Text
+{Short Name}
+
+B9B85EF1-1D76-4BF5-ABB9-092A8DB35851,Caption
+{Please enter the agreed-upon password for your encrypted key file...}
+
+B9B85EF1-1D76-4BF5-ABB9-092A8DB35851,CompanyLabel
+Password:
+
+B9B85EF1-1D76-4BF5-ABB9-092A8DB35851,Subtitle
+{Please enter your encrypted key file password.}
+
+C1