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%>