From 757294d769a7defe6a531a09fba9674cc6b388f7 Mon Sep 17 00:00:00 2001 From: Reinhard Tartler Date: Thu, 27 Oct 2011 19:53:48 -0400 Subject: Import boxbackup_0.11.1~r2837.orig.tar.gz [dgit import orig boxbackup_0.11.1~r2837.orig.tar.gz] --- .hgignore | 37 + .svnrevision | 1 + BUGS.txt | 15 + COPYING.txt | 491 ++ LICENSE-DUAL.txt | 59 + LICENSE-GPL.txt | 41 + VERSION.txt | 2 + bin/bbackupctl/bbackupctl.cpp | 366 ++ bin/bbackupd/BackupClientContext.cpp | 578 ++ bin/bbackupd/BackupClientContext.h | 237 + bin/bbackupd/BackupClientDeleteList.cpp | 229 + bin/bbackupd/BackupClientDeleteList.h | 75 + bin/bbackupd/BackupClientDirectoryRecord.cpp | 1876 ++++++ bin/bbackupd/BackupClientDirectoryRecord.h | 171 + bin/bbackupd/BackupClientInodeToIDMap.cpp | 327 ++ bin/bbackupd/BackupClientInodeToIDMap.h | 73 + bin/bbackupd/BackupDaemon.cpp | 2883 ++++++++++ bin/bbackupd/BackupDaemon.h | 525 ++ bin/bbackupd/BackupDaemonInterface.h | 167 + bin/bbackupd/ClientException.txt | 11 + bin/bbackupd/Makefile.extra | 7 + bin/bbackupd/Win32BackupService.cpp | 48 + bin/bbackupd/Win32BackupService.h | 21 + bin/bbackupd/Win32ServiceFunctions.cpp | 384 ++ bin/bbackupd/Win32ServiceFunctions.h | 19 + bin/bbackupd/bbackupd-config.in | 601 ++ bin/bbackupd/bbackupd.cpp | 56 + bin/bbackupd/win32/NotifySysAdmin.vbs | 113 + bin/bbackupd/win32/bbackupd.conf | 238 + bin/bbackupd/win32/installer.iss | 51 + bin/bbackupobjdump/bbackupobjdump.cpp | 83 + bin/bbackupquery/BackupQueries.cpp | 2340 ++++++++ bin/bbackupquery/BackupQueries.h | 346 ++ bin/bbackupquery/BoxBackupCompareParams.h | 107 + bin/bbackupquery/Makefile.extra | 6 + bin/bbackupquery/bbackupquery.cpp | 441 ++ bin/bbackupquery/documentation.txt | 187 + bin/bbackupquery/makedocumentation.pl.in | 75 + bin/bbstoreaccounts/bbstoreaccounts.cpp | 626 ++ bin/bbstored/BBStoreDHousekeeping.cpp | 240 + bin/bbstored/BackupCommands.cpp | 970 ++++ bin/bbstored/BackupConstants.h | 21 + bin/bbstored/BackupStoreContext.cpp | 1785 ++++++ bin/bbstored/BackupStoreContext.h | 186 + bin/bbstored/BackupStoreDaemon.cpp | 371 ++ bin/bbstored/BackupStoreDaemon.h | 100 + bin/bbstored/HousekeepStoreAccount.cpp | 1067 ++++ bin/bbstored/HousekeepStoreAccount.h | 111 + bin/bbstored/Makefile.extra | 9 + bin/bbstored/backupprotocol.txt | 234 + bin/bbstored/bbstored-certs.in | 319 ++ bin/bbstored/bbstored-config.in | 245 + bin/bbstored/bbstored.cpp | 37 + bin/s3simulator/s3simulator.cpp | 32 + bootstrap | 5 + cleanupforcvs.pl | 196 + config.guess | 1456 +++++ config.sub | 1549 +++++ configure.ac | 420 ++ contrib/bbadmin/accounts.cgi | 580 ++ contrib/bbadmin/apache.conf | 14 + contrib/bbadmin/bb.css | 70 + contrib/bbreporter/LICENSE | 674 +++ contrib/bbreporter/bbreporter.py | 538 ++ contrib/debian/README.txt | 9 + contrib/debian/bbackupd.in | 59 + contrib/debian/bbstored.in | 59 + contrib/mac_osx/org.boxbackup.bbackupd.plist.in | 20 + contrib/mac_osx/org.boxbackup.bbstored.plist.in | 21 + contrib/redhat/README.txt | 7 + contrib/redhat/bbackupd.in | 83 + contrib/redhat/bbstored.in | 83 + contrib/rpm/README.txt | 16 + contrib/rpm/boxbackup.spec | 244 + contrib/solaris/bbackupd-manifest.xml.in | 59 + contrib/solaris/bbackupd-smf-method.in | 24 + contrib/solaris/bbstored-manifest.xml.in | 60 + contrib/solaris/bbstored-smf-method.in | 23 + contrib/suse/README.txt | 5 + contrib/suse/bbackupd.in | 103 + contrib/suse/bbstored.in | 104 + contrib/windows/installer/boxbackup.mpi.in | 3392 +++++++++++ contrib/windows/installer/tools/InstallService.bat | 3 + .../windows/installer/tools/KillBackupProcess.bat | 3 + contrib/windows/installer/tools/QueryOutputAll.bat | 5 + .../windows/installer/tools/QueryOutputCurrent.bat | 5 + contrib/windows/installer/tools/ReloadConfig.bat | 3 + contrib/windows/installer/tools/RemoteControl.exe | Bin 0 -> 156536 bytes contrib/windows/installer/tools/RemoveService.bat | 3 + contrib/windows/installer/tools/RestartService.bat | 5 + contrib/windows/installer/tools/ShowUsage.bat | 3 + contrib/windows/installer/tools/StartService.bat | 3 + contrib/windows/installer/tools/StopService.bat | 3 + contrib/windows/installer/tools/Sync.bat | 3 + distribution/COMMON-MANIFEST.txt | 47 + distribution/boxbackup/CONTACT.txt | 6 + distribution/boxbackup/DISTRIBUTION-MANIFEST.txt | 95 + distribution/boxbackup/DOCUMENTATION.txt | 6 + distribution/boxbackup/LINUX.txt | 29 + distribution/boxbackup/NETBSD.txt | 8 + distribution/boxbackup/THANKS.txt | 69 + distribution/boxbackup/VERSION.txt | 2 + docs/Makefile | 183 + docs/api-notes/INDEX.txt | 61 + docs/api-notes/Win32_Clients.txt | 13 + docs/api-notes/backup_encryption.txt | 109 + docs/api-notes/bin_bbackupd.txt | 88 + docs/api-notes/bin_bbstored.txt | 54 + docs/api-notes/common/lib_common.txt | 52 + docs/api-notes/common/lib_common/BoxTime.txt | 7 + .../common/lib_common/CollectInBufferStream.txt | 26 + docs/api-notes/common/lib_common/Configuration.txt | 102 + docs/api-notes/common/lib_common/Conversion.txt | 14 + docs/api-notes/common/lib_common/ExcludeList.txt | 21 + docs/api-notes/common/lib_common/FdGetLine.txt | 11 + docs/api-notes/common/lib_common/Guards.txt | 5 + docs/api-notes/common/lib_common/IOStream.txt | 89 + .../common/lib_common/IOStreamGetLine.txt | 29 + docs/api-notes/common/lib_common/MainHelper.txt | 4 + docs/api-notes/common/lib_common/WaitForEvent.txt | 16 + docs/api-notes/common/lib_common/xStream.txt | 40 + docs/api-notes/common/lib_compress.txt | 8 + .../common/lib_compress/CompressStream.txt | 27 + docs/api-notes/common/lib_crypto.txt | 28 + docs/api-notes/common/lib_crypto/CipherContext.txt | 28 + .../common/lib_crypto/RollingChecksum.txt | 36 + docs/api-notes/common/lib_server.txt | 9 + docs/api-notes/common/lib_server/Daemon.txt | 96 + docs/api-notes/common/lib_server/Protocol.txt | 120 + docs/api-notes/common/lib_server/ServerStream.txt | 29 + docs/api-notes/common/lib_server/ServerTLS.txt | 6 + docs/api-notes/common/lib_server/SocketStream.txt | 8 + .../common/lib_server/SocketStreamTLS.txt | 11 + docs/api-notes/common/lib_server/TLSContext.txt | 16 + docs/api-notes/common/memory_leaks.txt | 44 + docs/api-notes/encrypt_rsync.txt | 66 + docs/api-notes/lib_backupclient.txt | 46 + docs/api-notes/lib_backupstore.txt | 30 + docs/api-notes/raidfile/RaidFileRead.txt | 14 + docs/api-notes/raidfile/RaidFileWrite.txt | 36 + docs/api-notes/raidfile/lib_raidfile.txt | 73 + .../win32_build_on_cygwin_using_mingw.txt | 113 + .../api-notes/win32_build_on_linux_using_mingw.txt | 108 + docs/api-notes/windows_porting.txt | 100 + docs/docbook/adminguide.xml | 1981 +++++++ docs/docbook/bb-book.xsl | 17 + docs/docbook/bb-man.xsl | 9 + docs/docbook/bb-nochunk-book.xsl | 17 + docs/docbook/bbackupctl.xml | 205 + docs/docbook/bbackupd-config.xml | 153 + docs/docbook/bbackupd.conf.xml | 479 ++ docs/docbook/bbackupd.xml | 209 + docs/docbook/bbackupquery.xml | 506 ++ docs/docbook/bbstoreaccounts.xml | 386 ++ docs/docbook/bbstored-certs.xml | 180 + docs/docbook/bbstored-config.xml | 148 + docs/docbook/bbstored.conf.xml | 211 + docs/docbook/bbstored.xml | 90 + docs/docbook/html/bbdoc-man.css | 104 + docs/docbook/html/bbdoc.css | 112 + docs/docbook/html/favicon.ico | Bin 0 -> 67646 bytes docs/docbook/html/images/arrow.png | Bin 0 -> 197 bytes docs/docbook/html/images/bblogo.png | Bin 0 -> 5882 bytes docs/docbook/html/images/stepahead.png | Bin 0 -> 298 bytes docs/docbook/instguide.xml | 766 +++ docs/docbook/raidfile-config.xml | 198 + docs/docbook/raidfile.conf.xml | 143 + docs/images/bblogo-alpha.xcf | Bin 0 -> 93980 bytes docs/tools/generate_except_xml.pl | 74 + docs/xsl-generic/VERSION | 113 + docs/xsl-generic/common/af.xml | 1223 ++++ docs/xsl-generic/common/am.xml | 1223 ++++ docs/xsl-generic/common/ar.xml | 1223 ++++ docs/xsl-generic/common/autoidx-kimber.xsl | 43 + docs/xsl-generic/common/autoidx-kosek.xsl | 150 + docs/xsl-generic/common/az.xml | 666 +++ docs/xsl-generic/common/bg.xml | 718 +++ docs/xsl-generic/common/bn.xml | 1223 ++++ docs/xsl-generic/common/bs.xml | 656 +++ docs/xsl-generic/common/ca.xml | 1223 ++++ docs/xsl-generic/common/charmap.xml | 185 + docs/xsl-generic/common/charmap.xsl | 221 + docs/xsl-generic/common/common.xml | 622 ++ docs/xsl-generic/common/common.xsl | 1981 +++++++ docs/xsl-generic/common/cs.xml | 694 +++ docs/xsl-generic/common/cy.xml | 1239 ++++ docs/xsl-generic/common/da.xml | 658 +++ docs/xsl-generic/common/de.xml | 660 +++ docs/xsl-generic/common/el.xml | 1223 ++++ docs/xsl-generic/common/en.xml | 1223 ++++ docs/xsl-generic/common/entities.ent | 47 + docs/xsl-generic/common/eo.xml | 1223 ++++ docs/xsl-generic/common/es.xml | 670 +++ docs/xsl-generic/common/et.xml | 1223 ++++ docs/xsl-generic/common/eu.xml | 1223 ++++ docs/xsl-generic/common/fa.xml | 1223 ++++ docs/xsl-generic/common/fi.xml | 664 +++ docs/xsl-generic/common/fr.xml | 684 +++ docs/xsl-generic/common/ga.xml | 1223 ++++ docs/xsl-generic/common/gentext.xsl | 831 +++ docs/xsl-generic/common/gu.xml | 1223 ++++ docs/xsl-generic/common/he.xml | 1223 ++++ docs/xsl-generic/common/hi.xml | 1223 ++++ docs/xsl-generic/common/hr.xml | 1223 ++++ docs/xsl-generic/common/hu.xml | 1223 ++++ docs/xsl-generic/common/id.xml | 1223 ++++ docs/xsl-generic/common/insertfile.xsl | 111 + docs/xsl-generic/common/it.xml | 1223 ++++ docs/xsl-generic/common/ja.xml | 1223 ++++ docs/xsl-generic/common/kn.xml | 1223 ++++ docs/xsl-generic/common/ko.xml | 1223 ++++ docs/xsl-generic/common/l10n.dtd | 63 + docs/xsl-generic/common/l10n.xml | 127 + docs/xsl-generic/common/l10n.xsl | 441 ++ docs/xsl-generic/common/la.xml | 1223 ++++ docs/xsl-generic/common/labels.xsl | 869 +++ docs/xsl-generic/common/lt.xml | 672 +++ docs/xsl-generic/common/lv.xml | 1223 ++++ docs/xsl-generic/common/mn.xml | 724 +++ docs/xsl-generic/common/nl.xml | 1223 ++++ docs/xsl-generic/common/nn.xml | 1223 ++++ docs/xsl-generic/common/no.xml | 1223 ++++ docs/xsl-generic/common/olink.xsl | 1149 ++++ docs/xsl-generic/common/or.xml | 1223 ++++ docs/xsl-generic/common/pa.xml | 1223 ++++ docs/xsl-generic/common/pi.xsl | 346 ++ docs/xsl-generic/common/pl.xml | 1223 ++++ docs/xsl-generic/common/pt.xml | 1223 ++++ docs/xsl-generic/common/pt_br.xml | 1223 ++++ docs/xsl-generic/common/refentry.xml | 781 +++ docs/xsl-generic/common/refentry.xsl | 1277 +++++ docs/xsl-generic/common/ro.xml | 1223 ++++ docs/xsl-generic/common/ru.xml | 720 +++ docs/xsl-generic/common/sk.xml | 1223 ++++ docs/xsl-generic/common/sl.xml | 1223 ++++ docs/xsl-generic/common/sq.xml | 1223 ++++ docs/xsl-generic/common/sr.xml | 714 +++ docs/xsl-generic/common/sr_Latn.xml | 673 +++ docs/xsl-generic/common/stripns.xsl | 342 ++ docs/xsl-generic/common/subtitles.xsl | 155 + docs/xsl-generic/common/sv.xml | 658 +++ docs/xsl-generic/common/ta.xml | 1223 ++++ docs/xsl-generic/common/table.xsl | 514 ++ docs/xsl-generic/common/targetdatabase.dtd | 49 + docs/xsl-generic/common/targets.xsl | 272 + docs/xsl-generic/common/th.xml | 1223 ++++ docs/xsl-generic/common/titles.xsl | 740 +++ docs/xsl-generic/common/tl.xml | 1223 ++++ docs/xsl-generic/common/tr.xml | 660 +++ docs/xsl-generic/common/uk.xml | 1223 ++++ docs/xsl-generic/common/utility.xml | 259 + docs/xsl-generic/common/utility.xsl | 290 + docs/xsl-generic/common/vi.xml | 1223 ++++ docs/xsl-generic/common/xh.xml | 1223 ++++ docs/xsl-generic/common/zh_cn.xml | 654 +++ docs/xsl-generic/common/zh_tw.xml | 1223 ++++ docs/xsl-generic/highlighting/c-hl.xml | 105 + docs/xsl-generic/highlighting/common.xsl | 62 + docs/xsl-generic/highlighting/delphi-hl.xml | 174 + docs/xsl-generic/highlighting/ini-hl.xml | 43 + docs/xsl-generic/highlighting/java-hl.xml | 98 + docs/xsl-generic/highlighting/m2-hl.xml | 86 + docs/xsl-generic/highlighting/myxml-hl.xml | 131 + docs/xsl-generic/highlighting/php-hl.xml | 127 + docs/xsl-generic/highlighting/xslthl-config.xml | 11 + docs/xsl-generic/html/admon.xsl | 132 + docs/xsl-generic/html/annotations.xsl | 169 + docs/xsl-generic/html/autoidx-kimber.xsl | 168 + docs/xsl-generic/html/autoidx-kosek.xsl | 125 + docs/xsl-generic/html/autoidx-ng.xsl | 20 + docs/xsl-generic/html/autoidx.xsl | 645 +++ docs/xsl-generic/html/autotoc.xsl | 675 +++ docs/xsl-generic/html/biblio-iso690.xsl | 1300 +++++ docs/xsl-generic/html/biblio.xsl | 1228 ++++ docs/xsl-generic/html/block.xsl | 434 ++ docs/xsl-generic/html/callout.xsl | 201 + docs/xsl-generic/html/changebars.xsl | 100 + docs/xsl-generic/html/chunk-code.xsl | 670 +++ docs/xsl-generic/html/chunk-common.xsl | 1886 ++++++ docs/xsl-generic/html/chunk.xsl | 52 + docs/xsl-generic/html/chunker.xsl | 439 ++ docs/xsl-generic/html/chunkfast.xsl | 72 + docs/xsl-generic/html/chunktoc.xsl | 468 ++ docs/xsl-generic/html/component.xsl | 401 ++ docs/xsl-generic/html/division.xsl | 228 + docs/xsl-generic/html/docbook.xsl | 479 ++ docs/xsl-generic/html/ebnf.xsl | 329 ++ docs/xsl-generic/html/footnote.xsl | 299 + docs/xsl-generic/html/formal.xsl | 400 ++ docs/xsl-generic/html/glossary.xsl | 482 ++ docs/xsl-generic/html/graphics.xsl | 1489 +++++ docs/xsl-generic/html/highlight.xsl | 54 + docs/xsl-generic/html/html-rtf.xsl | 336 ++ docs/xsl-generic/html/html.xsl | 241 + docs/xsl-generic/html/htmltbl.xsl | 55 + docs/xsl-generic/html/index.xsl | 229 + docs/xsl-generic/html/info.xsl | 43 + docs/xsl-generic/html/inline.xsl | 1439 +++++ docs/xsl-generic/html/keywords.xsl | 35 + docs/xsl-generic/html/lists.xsl | 1103 ++++ docs/xsl-generic/html/maketoc.xsl | 86 + docs/xsl-generic/html/manifest.xsl | 22 + docs/xsl-generic/html/math.xsl | 270 + docs/xsl-generic/html/oldchunker.xsl | 202 + docs/xsl-generic/html/onechunk.xsl | 37 + docs/xsl-generic/html/param.xsl | 412 ++ docs/xsl-generic/html/pi.xsl | 1240 ++++ docs/xsl-generic/html/profile-chunk-code.xsl | 609 ++ docs/xsl-generic/html/profile-chunk.xsl | 52 + docs/xsl-generic/html/profile-docbook.xsl | 411 ++ docs/xsl-generic/html/profile-onechunk.xsl | 37 + docs/xsl-generic/html/qandaset.xsl | 389 ++ docs/xsl-generic/html/refentry.xsl | 309 + docs/xsl-generic/html/sections.xsl | 622 ++ docs/xsl-generic/html/synop.xsl | 1596 ++++++ docs/xsl-generic/html/table.xsl | 1120 ++++ docs/xsl-generic/html/task.xsl | 76 + docs/xsl-generic/html/titlepage.templates.xml | 662 +++ docs/xsl-generic/html/titlepage.templates.xsl | 3622 ++++++++++++ docs/xsl-generic/html/titlepage.xsl | 1031 ++++ docs/xsl-generic/html/toc.xsl | 173 + docs/xsl-generic/html/verbatim.xsl | 376 ++ docs/xsl-generic/html/xref.xsl | 1348 +++++ docs/xsl-generic/lib/lib.xsl | 480 ++ docs/xsl-generic/manpages/block.xsl | 296 + docs/xsl-generic/manpages/charmap.groff.xsl | 5985 ++++++++++++++++++++ docs/xsl-generic/manpages/docbook.xsl | 293 + docs/xsl-generic/manpages/endnotes.xsl | 535 ++ docs/xsl-generic/manpages/html-synop.xsl | 1605 ++++++ docs/xsl-generic/manpages/info.xsl | 630 +++ docs/xsl-generic/manpages/inline.xsl | 190 + docs/xsl-generic/manpages/lists.xsl | 368 ++ docs/xsl-generic/manpages/other.xsl | 674 +++ docs/xsl-generic/manpages/param.xsl | 167 + docs/xsl-generic/manpages/profile-docbook.xsl | 259 + docs/xsl-generic/manpages/refentry.xsl | 256 + docs/xsl-generic/manpages/synop.xsl | 305 + docs/xsl-generic/manpages/table.xsl | 633 +++ docs/xsl-generic/manpages/utility.xsl | 452 ++ infrastructure/BoxPlatform.pm.in | 132 + infrastructure/buildenv-testmain-template.cpp | 390 ++ infrastructure/m4/ac_cxx_exceptions.m4 | 24 + infrastructure/m4/ac_cxx_namespaces.m4 | 25 + infrastructure/m4/ax_bswap64.m4 | 52 + infrastructure/m4/ax_check_bdb_v1.m4 | 43 + infrastructure/m4/ax_check_define_pragma.m4 | 25 + infrastructure/m4/ax_check_dirent_d_type.m4 | 45 + infrastructure/m4/ax_check_llong_minmax.m4 | 76 + infrastructure/m4/ax_check_malloc_workaround.m4 | 38 + infrastructure/m4/ax_check_mount_point.m4 | 60 + infrastructure/m4/ax_check_nonaligned_access.m4 | 57 + infrastructure/m4/ax_check_ssl.m4 | 37 + infrastructure/m4/ax_check_syscall_lseek.m4 | 69 + infrastructure/m4/ax_compare_version.m4 | 162 + infrastructure/m4/ax_config_scripts.m4 | 16 + infrastructure/m4/ax_func_syscall.m4 | 50 + infrastructure/m4/ax_path_bdb.m4 | 615 ++ infrastructure/m4/ax_random_device.m4 | 31 + infrastructure/m4/ax_split_version.m4 | 19 + infrastructure/m4/vl_lib_readline.m4 | 135 + infrastructure/makebuildenv.pl.in | 973 ++++ infrastructure/makedistribution.pl.in | 363 ++ infrastructure/makeparcels.pl.in | 396 ++ infrastructure/mingw/configure.sh | 38 + infrastructure/msvc/2003/bbackupctl.vcproj | 159 + infrastructure/msvc/2003/bbackupd.vcproj | 219 + infrastructure/msvc/2003/boxbackup.sln | 57 + infrastructure/msvc/2003/boxquery.vcproj | 174 + infrastructure/msvc/2003/common.vcproj | 672 +++ infrastructure/msvc/2003/win32test.vcproj | 148 + infrastructure/msvc/2005/bbackupctl.vcproj | 222 + infrastructure/msvc/2005/bbackupd.vcproj | 299 + infrastructure/msvc/2005/boxbackup.sln | 55 + infrastructure/msvc/2005/boxbackup.suo | Bin 0 -> 58880 bytes infrastructure/msvc/2005/boxquery.vcproj | 246 + infrastructure/msvc/2005/common.vcproj | 896 +++ infrastructure/msvc/2005/win32test.vcproj | 218 + infrastructure/msvc/getversion.pl | 19 + infrastructure/parcelpath.pl | 17 + infrastructure/printversion.pl | 12 + infrastructure/setupexternal.pl | 55 + lib/backupclient/BackupClientCryptoKeys.cpp | 85 + lib/backupclient/BackupClientCryptoKeys.h | 55 + lib/backupclient/BackupClientFileAttributes.cpp | 1186 ++++ lib/backupclient/BackupClientFileAttributes.h | 78 + lib/backupclient/BackupClientMakeExcludeList.cpp | 75 + lib/backupclient/BackupClientMakeExcludeList.h | 48 + lib/backupclient/BackupClientRestore.cpp | 918 +++ lib/backupclient/BackupClientRestore.h | 36 + lib/backupclient/BackupDaemonConfigVerify.cpp | 132 + lib/backupclient/BackupDaemonConfigVerify.h | 18 + lib/backupclient/BackupStoreConstants.h | 44 + lib/backupclient/BackupStoreDirectory.cpp | 568 ++ lib/backupclient/BackupStoreDirectory.h | 268 + lib/backupclient/BackupStoreException.h | 17 + lib/backupclient/BackupStoreException.txt | 71 + lib/backupclient/BackupStoreFile.cpp | 1556 +++++ lib/backupclient/BackupStoreFile.h | 228 + lib/backupclient/BackupStoreFileCmbDiff.cpp | 326 ++ lib/backupclient/BackupStoreFileCmbIdx.cpp | 324 ++ lib/backupclient/BackupStoreFileCombine.cpp | 410 ++ lib/backupclient/BackupStoreFileCryptVar.cpp | 31 + lib/backupclient/BackupStoreFileCryptVar.h | 39 + lib/backupclient/BackupStoreFileDiff.cpp | 1046 ++++ lib/backupclient/BackupStoreFileEncodeStream.cpp | 715 +++ lib/backupclient/BackupStoreFileEncodeStream.h | 135 + lib/backupclient/BackupStoreFileRevDiff.cpp | 258 + lib/backupclient/BackupStoreFileWire.h | 74 + lib/backupclient/BackupStoreFilename.cpp | 281 + lib/backupclient/BackupStoreFilename.h | 107 + lib/backupclient/BackupStoreFilenameClear.cpp | 335 ++ lib/backupclient/BackupStoreFilenameClear.h | 60 + lib/backupclient/BackupStoreObjectDump.cpp | 227 + lib/backupclient/BackupStoreObjectMagic.h | 31 + lib/backupclient/Makefile.extra | 16 + lib/backupclient/RunStatusProvider.h | 29 + lib/backupstore/BackupStoreAccountDatabase.cpp | 373 ++ lib/backupstore/BackupStoreAccountDatabase.h | 75 + lib/backupstore/BackupStoreAccounts.cpp | 170 + lib/backupstore/BackupStoreAccounts.h | 52 + lib/backupstore/BackupStoreCheck.cpp | 776 +++ lib/backupstore/BackupStoreCheck.h | 199 + lib/backupstore/BackupStoreCheck2.cpp | 916 +++ lib/backupstore/BackupStoreCheckData.cpp | 208 + lib/backupstore/BackupStoreConfigVerify.cpp | 57 + lib/backupstore/BackupStoreConfigVerify.h | 18 + lib/backupstore/BackupStoreInfo.cpp | 593 ++ lib/backupstore/BackupStoreInfo.h | 111 + lib/backupstore/BackupStoreRefCountDatabase.cpp | 321 ++ lib/backupstore/BackupStoreRefCountDatabase.h | 128 + lib/backupstore/StoreStructure.cpp | 95 + lib/backupstore/StoreStructure.h | 32 + lib/common/Archive.h | 161 + lib/common/BannerText.h | 18 + lib/common/BeginStructPackForWire.h | 23 + lib/common/Box.h | 185 + lib/common/BoxConfig-MSVC.h | 402 ++ lib/common/BoxException.cpp | 21 + lib/common/BoxException.h | 38 + lib/common/BoxPlatform.h | 201 + lib/common/BoxPortsAndFiles.h.in | 44 + lib/common/BoxTime.cpp | 96 + lib/common/BoxTime.h | 46 + lib/common/BoxTimeToText.cpp | 76 + lib/common/BoxTimeToText.h | 19 + lib/common/BoxTimeToUnix.h | 34 + lib/common/BufferedStream.cpp | 207 + lib/common/BufferedStream.h | 43 + lib/common/BufferedWriteStream.cpp | 181 + lib/common/BufferedWriteStream.h | 44 + lib/common/CollectInBufferStream.cpp | 274 + lib/common/CollectInBufferStream.h | 60 + lib/common/CommonException.h | 17 + lib/common/CommonException.txt | 47 + lib/common/Configuration.cpp | 920 +++ lib/common/Configuration.h | 147 + lib/common/Conversion.h | 98 + lib/common/ConversionException.txt | 8 + lib/common/ConversionString.cpp | 129 + lib/common/DebugAssertFailed.cpp | 37 + lib/common/DebugMemLeakFinder.cpp | 552 ++ lib/common/DebugPrintf.cpp | 83 + lib/common/EndStructPackForWire.h | 23 + lib/common/EventWatchFilesystemObject.cpp | 112 + lib/common/EventWatchFilesystemObject.h | 48 + lib/common/ExcludeList.cpp | 481 ++ lib/common/ExcludeList.h | 76 + lib/common/FdGetLine.cpp | 228 + lib/common/FdGetLine.h | 65 + lib/common/FileModificationTime.cpp | 64 + lib/common/FileModificationTime.h | 22 + lib/common/FileStream.cpp | 447 ++ lib/common/FileStream.h | 66 + lib/common/Guards.h | 121 + lib/common/IOStream.cpp | 251 + lib/common/IOStream.h | 73 + lib/common/IOStreamGetLine.cpp | 227 + lib/common/IOStreamGetLine.h | 71 + lib/common/InvisibleTempFileStream.cpp | 39 + lib/common/InvisibleTempFileStream.h | 35 + lib/common/Logging.cpp | 518 ++ lib/common/Logging.h | 346 ++ lib/common/MainHelper.h | 43 + lib/common/Makefile.extra | 11 + lib/common/MemBlockStream.cpp | 235 + lib/common/MemBlockStream.h | 52 + lib/common/MemLeakFindOff.h | 27 + lib/common/MemLeakFindOn.h | 25 + lib/common/MemLeakFinder.h | 63 + lib/common/NamedLock.cpp | 170 + lib/common/NamedLock.h | 41 + lib/common/PartialReadStream.cpp | 138 + lib/common/PartialReadStream.h | 46 + lib/common/PathUtils.cpp | 34 + lib/common/PathUtils.h | 26 + lib/common/ReadGatherStream.cpp | 263 + lib/common/ReadGatherStream.h | 67 + lib/common/ReadLoggingStream.cpp | 203 + lib/common/ReadLoggingStream.h | 58 + lib/common/SelfFlushingStream.h | 71 + lib/common/StreamableMemBlock.cpp | 364 ++ lib/common/StreamableMemBlock.h | 71 + lib/common/TemporaryDirectory.h | 46 + lib/common/Test.cpp | 486 ++ lib/common/Test.h | 167 + lib/common/Timer.cpp | 626 ++ lib/common/Timer.h | 89 + lib/common/UnixUser.cpp | 123 + lib/common/UnixUser.h | 37 + lib/common/Utils.cpp | 315 ++ lib/common/Utils.h | 44 + lib/common/WaitForEvent.cpp | 197 + lib/common/WaitForEvent.h | 152 + lib/common/ZeroStream.cpp | 170 + lib/common/ZeroStream.h | 39 + lib/common/makeexception.pl.in | 283 + lib/compress/Compress.h | 197 + lib/compress/CompressException.h | 17 + lib/compress/CompressException.txt | 12 + lib/compress/CompressStream.cpp | 425 ++ lib/compress/CompressStream.h | 62 + lib/compress/Makefile.extra | 7 + lib/crypto/CipherAES.cpp | 163 + lib/crypto/CipherAES.h | 50 + lib/crypto/CipherBlowfish.cpp | 220 + lib/crypto/CipherBlowfish.h | 60 + lib/crypto/CipherContext.cpp | 620 ++ lib/crypto/CipherContext.h | 83 + lib/crypto/CipherDescription.cpp | 73 + lib/crypto/CipherDescription.h | 59 + lib/crypto/CipherException.h | 17 + lib/crypto/CipherException.txt | 18 + lib/crypto/MD5Digest.cpp | 82 + lib/crypto/MD5Digest.h | 57 + lib/crypto/Makefile.extra | 7 + lib/crypto/Random.cpp | 128 + lib/crypto/Random.h | 25 + lib/crypto/RollingChecksum.cpp | 62 + lib/crypto/RollingChecksum.h | 107 + lib/httpserver/HTTPException.txt | 16 + lib/httpserver/HTTPQueryDecoder.cpp | 159 + lib/httpserver/HTTPQueryDecoder.h | 47 + lib/httpserver/HTTPRequest.cpp | 780 +++ lib/httpserver/HTTPRequest.h | 189 + lib/httpserver/HTTPResponse.cpp | 648 +++ lib/httpserver/HTTPResponse.h | 175 + lib/httpserver/HTTPServer.cpp | 247 + lib/httpserver/HTTPServer.h | 81 + lib/httpserver/Makefile.extra | 7 + lib/httpserver/S3Client.cpp | 243 + lib/httpserver/S3Client.h | 72 + lib/httpserver/S3Simulator.cpp | 309 + lib/httpserver/S3Simulator.h | 40 + lib/httpserver/cdecode.cpp | 92 + lib/httpserver/cdecode.h | 28 + lib/httpserver/cencode.cpp | 113 + lib/httpserver/cencode.h | 32 + lib/httpserver/decode.h | 77 + lib/httpserver/encode.h | 87 + lib/intercept/intercept.cpp | 673 +++ lib/intercept/intercept.h | 54 + lib/raidfile/Makefile.extra | 7 + lib/raidfile/RaidFileController.cpp | 227 + lib/raidfile/RaidFileController.h | 108 + lib/raidfile/RaidFileException.h | 17 + lib/raidfile/RaidFileException.txt | 28 + lib/raidfile/RaidFileRead.cpp | 1724 ++++++ lib/raidfile/RaidFileRead.h | 73 + lib/raidfile/RaidFileUtil.cpp | 210 + lib/raidfile/RaidFileUtil.h | 97 + lib/raidfile/RaidFileWrite.cpp | 930 +++ lib/raidfile/RaidFileWrite.h | 68 + lib/raidfile/raidfile-config.in | 100 + lib/server/ConnectionException.txt | 27 + lib/server/Daemon.cpp | 1024 ++++ lib/server/Daemon.h | 112 + lib/server/LocalProcessStream.cpp | 180 + lib/server/LocalProcessStream.h | 20 + lib/server/Makefile.extra | 11 + lib/server/OverlappedIO.h | 42 + lib/server/Protocol.cpp | 1160 ++++ lib/server/Protocol.h | 201 + lib/server/ProtocolObject.cpp | 125 + lib/server/ProtocolObject.h | 41 + lib/server/ProtocolUncertainStream.cpp | 206 + lib/server/ProtocolUncertainStream.h | 47 + lib/server/ProtocolWire.h | 43 + lib/server/SSLLib.cpp | 111 + lib/server/SSLLib.h | 36 + lib/server/ServerControl.cpp | 228 + lib/server/ServerControl.h | 18 + lib/server/ServerException.h | 46 + lib/server/ServerException.txt | 39 + lib/server/ServerStream.h | 418 ++ lib/server/ServerTLS.h | 80 + lib/server/Socket.cpp | 184 + lib/server/Socket.h | 56 + lib/server/SocketListen.h | 312 + lib/server/SocketStream.cpp | 514 ++ lib/server/SocketStream.h | 75 + lib/server/SocketStreamTLS.cpp | 492 ++ lib/server/SocketStreamTLS.h | 61 + lib/server/TLSContext.cpp | 131 + lib/server/TLSContext.h | 41 + lib/server/WinNamedPipeListener.h | 232 + lib/server/WinNamedPipeStream.cpp | 620 ++ lib/server/WinNamedPipeStream.h | 67 + lib/server/makeprotocol.pl.in | 1093 ++++ lib/win32/MSG00001.bin | Bin 0 -> 32 bytes lib/win32/emu.cpp | 1843 ++++++ lib/win32/emu.h | 424 ++ lib/win32/getopt.h | 98 + lib/win32/getopt_long.cpp | 550 ++ lib/win32/messages.h | 57 + lib/win32/messages.mc | 22 + lib/win32/messages.rc | 2 + modules.txt | 50 + parcels.txt | 86 + runtest.pl.in | 144 + test/backupdiff/difftestfiles.cpp | 295 + test/backupdiff/testbackupdiff.cpp | 605 ++ test/backupdiff/testextra | 2 + test/backupstore/Makefile.extra | 1 + test/backupstore/testbackupstore.cpp | 2178 +++++++ test/backupstore/testextra | 4 + test/backupstore/testfiles/accounts.txt | 0 test/backupstore/testfiles/bbackupd.keys | Bin 0 -> 1024 bytes test/backupstore/testfiles/bbstored.conf | 17 + test/backupstore/testfiles/bbstored_multi.conf | 16 + test/backupstore/testfiles/clientCerts.pem | 11 + test/backupstore/testfiles/clientPrivKey.pem | 15 + test/backupstore/testfiles/clientReq.pem | 10 + test/backupstore/testfiles/clientTrustedCAs.pem | 11 + test/backupstore/testfiles/query.conf | 34 + test/backupstore/testfiles/raidfile.conf | 10 + test/backupstore/testfiles/root.pem | 26 + test/backupstore/testfiles/root.srl | 1 + test/backupstore/testfiles/rootcert.pem | 11 + test/backupstore/testfiles/rootkey.pem | 15 + test/backupstore/testfiles/rootreq.pem | 10 + test/backupstore/testfiles/serverCerts.pem | 11 + test/backupstore/testfiles/serverPrivKey.pem | 15 + test/backupstore/testfiles/serverReq.pem | 10 + test/backupstore/testfiles/serverTrustedCAs.pem | 11 + test/backupstorefix/testbackupstorefix.cpp | 614 ++ test/backupstorefix/testextra | 5 + .../testfiles/testbackupstorefix.pl.in | 221 + test/backupstorepatch/testbackupstorepatch.cpp | 670 +++ test/backupstorepatch/testextra | 6 + test/basicserver/Makefile.extra | 21 + test/basicserver/TestCommands.cpp | 101 + test/basicserver/TestContext.cpp | 16 + test/basicserver/TestContext.h | 7 + test/basicserver/testbasicserver.cpp | 772 +++ test/basicserver/testfiles/clientCerts.pem | 14 + test/basicserver/testfiles/clientPrivKey.pem | 15 + test/basicserver/testfiles/clientReq.pem | 11 + test/basicserver/testfiles/clientTrustedCAs.pem | 14 + test/basicserver/testfiles/key-creation.txt | 83 + test/basicserver/testfiles/root.pem | 29 + test/basicserver/testfiles/root.srl | 1 + test/basicserver/testfiles/rootcert.pem | 14 + test/basicserver/testfiles/rootkey.pem | 15 + test/basicserver/testfiles/rootreq.pem | 11 + test/basicserver/testfiles/serverCerts.pem | 14 + test/basicserver/testfiles/serverPrivKey.pem | 15 + test/basicserver/testfiles/serverReq.pem | 11 + test/basicserver/testfiles/serverTrustedCAs.pem | 14 + test/basicserver/testfiles/srv1.conf | 6 + test/basicserver/testfiles/srv1b.conf | 6 + test/basicserver/testfiles/srv2.conf | 6 + test/basicserver/testfiles/srv3.conf | 9 + test/basicserver/testfiles/srv4.conf | 6 + test/basicserver/testprotocol.txt | 42 + test/bbackupd/Makefile.extra | 14 + test/bbackupd/testbbackupd.cpp | 4077 +++++++++++++ test/bbackupd/testextra | 4 + test/bbackupd/testfiles/accounts.txt | 0 test/bbackupd/testfiles/bbackupd-exclude.conf.in | 47 + test/bbackupd/testfiles/bbackupd-snapshot.conf.in | 56 + test/bbackupd/testfiles/bbackupd-symlink.conf.in | 55 + test/bbackupd/testfiles/bbackupd-temploc.conf | 55 + test/bbackupd/testfiles/bbackupd.conf.in | 55 + test/bbackupd/testfiles/bbackupd.keys | Bin 0 -> 1024 bytes test/bbackupd/testfiles/bbstored.conf | 17 + test/bbackupd/testfiles/clientCerts.pem | 11 + test/bbackupd/testfiles/clientPrivKey.pem | 15 + test/bbackupd/testfiles/clientTrustedCAs.pem | 11 + test/bbackupd/testfiles/extcheck1.pl.in | 58 + test/bbackupd/testfiles/extcheck2.pl.in | 50 + test/bbackupd/testfiles/notifyscript.pl.in | 24 + test/bbackupd/testfiles/raidfile.conf | 10 + test/bbackupd/testfiles/serverCerts.pem | 11 + test/bbackupd/testfiles/serverPrivKey.pem | 15 + test/bbackupd/testfiles/serverTrustedCAs.pem | 11 + test/bbackupd/testfiles/spacetest1.tgz | Bin 0 -> 288 bytes test/bbackupd/testfiles/spacetest2.tgz | Bin 0 -> 203 bytes test/bbackupd/testfiles/syncallowscript.pl.in | 33 + test/bbackupd/testfiles/test2.tgz | Bin 0 -> 25190 bytes test/bbackupd/testfiles/test3.tgz | Bin 0 -> 44957 bytes test/bbackupd/testfiles/test_base.tgz | Bin 0 -> 14950 bytes test/bbackupd/testfiles/testexclude.tgz | Bin 0 -> 377 bytes test/common/testcommon.cpp | 882 +++ test/common/testfiles/config1.txt | 40 + test/common/testfiles/config10.txt | 37 + test/common/testfiles/config11.txt | 39 + test/common/testfiles/config12.txt | 33 + test/common/testfiles/config13.txt | 15 + test/common/testfiles/config14.txt | 41 + test/common/testfiles/config15.txt | 45 + test/common/testfiles/config16.txt | 42 + test/common/testfiles/config2.txt | 39 + test/common/testfiles/config3.txt | 39 + test/common/testfiles/config4.txt | 40 + test/common/testfiles/config5.txt | 37 + test/common/testfiles/config6.txt | 39 + test/common/testfiles/config7.txt | 39 + test/common/testfiles/config8.txt | 37 + test/common/testfiles/config9.txt | 38 + test/common/testfiles/config9b.txt | 38 + test/common/testfiles/config9c.txt | 38 + test/common/testfiles/config9d.txt | 38 + test/common/testfiles/fdgetlinetest.txt | 20 + test/compress/testcompress.cpp | 261 + test/crypto/testcrypto.cpp | 314 + test/httpserver/testfiles/httpserver.conf | 8 + test/httpserver/testfiles/photos/puppy.jpg | 1 + test/httpserver/testfiles/s3simulator.conf | 10 + test/httpserver/testfiles/testrequests.pl | 143 + test/httpserver/testhttpserver.cpp | 480 ++ test/raidfile/testextra | 7 + test/raidfile/testfiles/raidfile.conf | 30 + test/raidfile/testraidfile.cpp | 981 ++++ test/win32/Makefile | 5 + test/win32/testlibwin32.cpp | 345 ++ test/win32/timezone.cpp | 87 + win32.bat | 33 + 737 files changed, 230534 insertions(+) create mode 100644 .hgignore create mode 100644 .svnrevision create mode 100644 BUGS.txt create mode 100644 COPYING.txt create mode 100644 LICENSE-DUAL.txt create mode 100644 LICENSE-GPL.txt create mode 100644 VERSION.txt create mode 100644 bin/bbackupctl/bbackupctl.cpp create mode 100644 bin/bbackupd/BackupClientContext.cpp create mode 100644 bin/bbackupd/BackupClientContext.h create mode 100644 bin/bbackupd/BackupClientDeleteList.cpp create mode 100644 bin/bbackupd/BackupClientDeleteList.h create mode 100644 bin/bbackupd/BackupClientDirectoryRecord.cpp create mode 100644 bin/bbackupd/BackupClientDirectoryRecord.h create mode 100644 bin/bbackupd/BackupClientInodeToIDMap.cpp create mode 100644 bin/bbackupd/BackupClientInodeToIDMap.h create mode 100644 bin/bbackupd/BackupDaemon.cpp create mode 100644 bin/bbackupd/BackupDaemon.h create mode 100644 bin/bbackupd/BackupDaemonInterface.h create mode 100644 bin/bbackupd/ClientException.txt create mode 100644 bin/bbackupd/Makefile.extra create mode 100644 bin/bbackupd/Win32BackupService.cpp create mode 100644 bin/bbackupd/Win32BackupService.h create mode 100644 bin/bbackupd/Win32ServiceFunctions.cpp create mode 100644 bin/bbackupd/Win32ServiceFunctions.h create mode 100755 bin/bbackupd/bbackupd-config.in create mode 100644 bin/bbackupd/bbackupd.cpp create mode 100644 bin/bbackupd/win32/NotifySysAdmin.vbs create mode 100644 bin/bbackupd/win32/bbackupd.conf create mode 100644 bin/bbackupd/win32/installer.iss create mode 100644 bin/bbackupobjdump/bbackupobjdump.cpp create mode 100644 bin/bbackupquery/BackupQueries.cpp create mode 100644 bin/bbackupquery/BackupQueries.h create mode 100644 bin/bbackupquery/BoxBackupCompareParams.h create mode 100644 bin/bbackupquery/Makefile.extra create mode 100644 bin/bbackupquery/bbackupquery.cpp create mode 100644 bin/bbackupquery/documentation.txt create mode 100755 bin/bbackupquery/makedocumentation.pl.in create mode 100644 bin/bbstoreaccounts/bbstoreaccounts.cpp create mode 100644 bin/bbstored/BBStoreDHousekeeping.cpp create mode 100644 bin/bbstored/BackupCommands.cpp create mode 100644 bin/bbstored/BackupConstants.h create mode 100644 bin/bbstored/BackupStoreContext.cpp create mode 100644 bin/bbstored/BackupStoreContext.h create mode 100644 bin/bbstored/BackupStoreDaemon.cpp create mode 100644 bin/bbstored/BackupStoreDaemon.h create mode 100644 bin/bbstored/HousekeepStoreAccount.cpp create mode 100644 bin/bbstored/HousekeepStoreAccount.h create mode 100644 bin/bbstored/Makefile.extra create mode 100644 bin/bbstored/backupprotocol.txt create mode 100755 bin/bbstored/bbstored-certs.in create mode 100755 bin/bbstored/bbstored-config.in create mode 100644 bin/bbstored/bbstored.cpp create mode 100644 bin/s3simulator/s3simulator.cpp create mode 100755 bootstrap create mode 100755 cleanupforcvs.pl create mode 100755 config.guess create mode 100755 config.sub create mode 100644 configure.ac create mode 100755 contrib/bbadmin/accounts.cgi create mode 100644 contrib/bbadmin/apache.conf create mode 100644 contrib/bbadmin/bb.css create mode 100644 contrib/bbreporter/LICENSE create mode 100755 contrib/bbreporter/bbreporter.py create mode 100644 contrib/debian/README.txt create mode 100644 contrib/debian/bbackupd.in create mode 100644 contrib/debian/bbstored.in create mode 100644 contrib/mac_osx/org.boxbackup.bbackupd.plist.in create mode 100644 contrib/mac_osx/org.boxbackup.bbstored.plist.in create mode 100644 contrib/redhat/README.txt create mode 100644 contrib/redhat/bbackupd.in create mode 100644 contrib/redhat/bbstored.in create mode 100644 contrib/rpm/README.txt create mode 100644 contrib/rpm/boxbackup.spec create mode 100644 contrib/solaris/bbackupd-manifest.xml.in create mode 100755 contrib/solaris/bbackupd-smf-method.in create mode 100644 contrib/solaris/bbstored-manifest.xml.in create mode 100755 contrib/solaris/bbstored-smf-method.in create mode 100644 contrib/suse/README.txt create mode 100644 contrib/suse/bbackupd.in create mode 100644 contrib/suse/bbstored.in create mode 100755 contrib/windows/installer/boxbackup.mpi.in create mode 100755 contrib/windows/installer/tools/InstallService.bat create mode 100755 contrib/windows/installer/tools/KillBackupProcess.bat create mode 100755 contrib/windows/installer/tools/QueryOutputAll.bat create mode 100755 contrib/windows/installer/tools/QueryOutputCurrent.bat create mode 100755 contrib/windows/installer/tools/ReloadConfig.bat create mode 100755 contrib/windows/installer/tools/RemoteControl.exe create mode 100755 contrib/windows/installer/tools/RemoveService.bat create mode 100755 contrib/windows/installer/tools/RestartService.bat create mode 100755 contrib/windows/installer/tools/ShowUsage.bat create mode 100755 contrib/windows/installer/tools/StartService.bat create mode 100755 contrib/windows/installer/tools/StopService.bat create mode 100755 contrib/windows/installer/tools/Sync.bat create mode 100644 distribution/COMMON-MANIFEST.txt create mode 100644 distribution/boxbackup/CONTACT.txt create mode 100644 distribution/boxbackup/DISTRIBUTION-MANIFEST.txt create mode 100644 distribution/boxbackup/DOCUMENTATION.txt create mode 100644 distribution/boxbackup/LINUX.txt create mode 100644 distribution/boxbackup/NETBSD.txt create mode 100644 distribution/boxbackup/THANKS.txt create mode 100644 distribution/boxbackup/VERSION.txt create mode 100644 docs/Makefile create mode 100644 docs/api-notes/INDEX.txt create mode 100644 docs/api-notes/Win32_Clients.txt create mode 100644 docs/api-notes/backup_encryption.txt create mode 100644 docs/api-notes/bin_bbackupd.txt create mode 100644 docs/api-notes/bin_bbstored.txt create mode 100644 docs/api-notes/common/lib_common.txt create mode 100644 docs/api-notes/common/lib_common/BoxTime.txt create mode 100644 docs/api-notes/common/lib_common/CollectInBufferStream.txt create mode 100644 docs/api-notes/common/lib_common/Configuration.txt create mode 100644 docs/api-notes/common/lib_common/Conversion.txt create mode 100644 docs/api-notes/common/lib_common/ExcludeList.txt create mode 100644 docs/api-notes/common/lib_common/FdGetLine.txt create mode 100644 docs/api-notes/common/lib_common/Guards.txt create mode 100644 docs/api-notes/common/lib_common/IOStream.txt create mode 100644 docs/api-notes/common/lib_common/IOStreamGetLine.txt create mode 100644 docs/api-notes/common/lib_common/MainHelper.txt create mode 100644 docs/api-notes/common/lib_common/WaitForEvent.txt create mode 100644 docs/api-notes/common/lib_common/xStream.txt create mode 100644 docs/api-notes/common/lib_compress.txt create mode 100644 docs/api-notes/common/lib_compress/CompressStream.txt create mode 100644 docs/api-notes/common/lib_crypto.txt create mode 100644 docs/api-notes/common/lib_crypto/CipherContext.txt create mode 100644 docs/api-notes/common/lib_crypto/RollingChecksum.txt create mode 100644 docs/api-notes/common/lib_server.txt create mode 100644 docs/api-notes/common/lib_server/Daemon.txt create mode 100644 docs/api-notes/common/lib_server/Protocol.txt create mode 100644 docs/api-notes/common/lib_server/ServerStream.txt create mode 100644 docs/api-notes/common/lib_server/ServerTLS.txt create mode 100644 docs/api-notes/common/lib_server/SocketStream.txt create mode 100644 docs/api-notes/common/lib_server/SocketStreamTLS.txt create mode 100644 docs/api-notes/common/lib_server/TLSContext.txt create mode 100644 docs/api-notes/common/memory_leaks.txt create mode 100644 docs/api-notes/encrypt_rsync.txt create mode 100644 docs/api-notes/lib_backupclient.txt create mode 100644 docs/api-notes/lib_backupstore.txt create mode 100644 docs/api-notes/raidfile/RaidFileRead.txt create mode 100644 docs/api-notes/raidfile/RaidFileWrite.txt create mode 100644 docs/api-notes/raidfile/lib_raidfile.txt create mode 100644 docs/api-notes/win32_build_on_cygwin_using_mingw.txt create mode 100644 docs/api-notes/win32_build_on_linux_using_mingw.txt create mode 100644 docs/api-notes/windows_porting.txt create mode 100644 docs/docbook/adminguide.xml create mode 100644 docs/docbook/bb-book.xsl create mode 100644 docs/docbook/bb-man.xsl create mode 100644 docs/docbook/bb-nochunk-book.xsl create mode 100644 docs/docbook/bbackupctl.xml create mode 100644 docs/docbook/bbackupd-config.xml create mode 100644 docs/docbook/bbackupd.conf.xml create mode 100644 docs/docbook/bbackupd.xml create mode 100644 docs/docbook/bbackupquery.xml create mode 100644 docs/docbook/bbstoreaccounts.xml create mode 100644 docs/docbook/bbstored-certs.xml create mode 100644 docs/docbook/bbstored-config.xml create mode 100644 docs/docbook/bbstored.conf.xml create mode 100644 docs/docbook/bbstored.xml create mode 100644 docs/docbook/html/bbdoc-man.css create mode 100644 docs/docbook/html/bbdoc.css create mode 100644 docs/docbook/html/favicon.ico create mode 100644 docs/docbook/html/images/arrow.png create mode 100644 docs/docbook/html/images/bblogo.png create mode 100644 docs/docbook/html/images/stepahead.png create mode 100644 docs/docbook/instguide.xml create mode 100644 docs/docbook/raidfile-config.xml create mode 100644 docs/docbook/raidfile.conf.xml create mode 100644 docs/images/bblogo-alpha.xcf create mode 100644 docs/tools/generate_except_xml.pl create mode 100644 docs/xsl-generic/VERSION create mode 100644 docs/xsl-generic/common/af.xml create mode 100644 docs/xsl-generic/common/am.xml create mode 100644 docs/xsl-generic/common/ar.xml create mode 100644 docs/xsl-generic/common/autoidx-kimber.xsl create mode 100644 docs/xsl-generic/common/autoidx-kosek.xsl create mode 100644 docs/xsl-generic/common/az.xml create mode 100644 docs/xsl-generic/common/bg.xml create mode 100644 docs/xsl-generic/common/bn.xml create mode 100644 docs/xsl-generic/common/bs.xml create mode 100644 docs/xsl-generic/common/ca.xml create mode 100644 docs/xsl-generic/common/charmap.xml create mode 100644 docs/xsl-generic/common/charmap.xsl create mode 100644 docs/xsl-generic/common/common.xml create mode 100644 docs/xsl-generic/common/common.xsl create mode 100644 docs/xsl-generic/common/cs.xml create mode 100644 docs/xsl-generic/common/cy.xml create mode 100644 docs/xsl-generic/common/da.xml create mode 100644 docs/xsl-generic/common/de.xml create mode 100644 docs/xsl-generic/common/el.xml create mode 100644 docs/xsl-generic/common/en.xml create mode 100644 docs/xsl-generic/common/entities.ent create mode 100644 docs/xsl-generic/common/eo.xml create mode 100644 docs/xsl-generic/common/es.xml create mode 100644 docs/xsl-generic/common/et.xml create mode 100644 docs/xsl-generic/common/eu.xml create mode 100644 docs/xsl-generic/common/fa.xml create mode 100644 docs/xsl-generic/common/fi.xml create mode 100644 docs/xsl-generic/common/fr.xml create mode 100644 docs/xsl-generic/common/ga.xml create mode 100644 docs/xsl-generic/common/gentext.xsl create mode 100644 docs/xsl-generic/common/gu.xml create mode 100644 docs/xsl-generic/common/he.xml create mode 100644 docs/xsl-generic/common/hi.xml create mode 100644 docs/xsl-generic/common/hr.xml create mode 100644 docs/xsl-generic/common/hu.xml create mode 100644 docs/xsl-generic/common/id.xml create mode 100644 docs/xsl-generic/common/insertfile.xsl create mode 100644 docs/xsl-generic/common/it.xml create mode 100644 docs/xsl-generic/common/ja.xml create mode 100644 docs/xsl-generic/common/kn.xml create mode 100644 docs/xsl-generic/common/ko.xml create mode 100644 docs/xsl-generic/common/l10n.dtd create mode 100644 docs/xsl-generic/common/l10n.xml create mode 100644 docs/xsl-generic/common/l10n.xsl create mode 100644 docs/xsl-generic/common/la.xml create mode 100644 docs/xsl-generic/common/labels.xsl create mode 100644 docs/xsl-generic/common/lt.xml create mode 100644 docs/xsl-generic/common/lv.xml create mode 100644 docs/xsl-generic/common/mn.xml create mode 100644 docs/xsl-generic/common/nl.xml create mode 100644 docs/xsl-generic/common/nn.xml create mode 100644 docs/xsl-generic/common/no.xml create mode 100644 docs/xsl-generic/common/olink.xsl create mode 100644 docs/xsl-generic/common/or.xml create mode 100644 docs/xsl-generic/common/pa.xml create mode 100644 docs/xsl-generic/common/pi.xsl create mode 100644 docs/xsl-generic/common/pl.xml create mode 100644 docs/xsl-generic/common/pt.xml create mode 100644 docs/xsl-generic/common/pt_br.xml create mode 100644 docs/xsl-generic/common/refentry.xml create mode 100644 docs/xsl-generic/common/refentry.xsl create mode 100644 docs/xsl-generic/common/ro.xml create mode 100644 docs/xsl-generic/common/ru.xml create mode 100644 docs/xsl-generic/common/sk.xml create mode 100644 docs/xsl-generic/common/sl.xml create mode 100644 docs/xsl-generic/common/sq.xml create mode 100644 docs/xsl-generic/common/sr.xml create mode 100644 docs/xsl-generic/common/sr_Latn.xml create mode 100644 docs/xsl-generic/common/stripns.xsl create mode 100644 docs/xsl-generic/common/subtitles.xsl create mode 100644 docs/xsl-generic/common/sv.xml create mode 100644 docs/xsl-generic/common/ta.xml create mode 100644 docs/xsl-generic/common/table.xsl create mode 100644 docs/xsl-generic/common/targetdatabase.dtd create mode 100644 docs/xsl-generic/common/targets.xsl create mode 100644 docs/xsl-generic/common/th.xml create mode 100644 docs/xsl-generic/common/titles.xsl create mode 100644 docs/xsl-generic/common/tl.xml create mode 100644 docs/xsl-generic/common/tr.xml create mode 100644 docs/xsl-generic/common/uk.xml create mode 100644 docs/xsl-generic/common/utility.xml create mode 100644 docs/xsl-generic/common/utility.xsl create mode 100644 docs/xsl-generic/common/vi.xml create mode 100644 docs/xsl-generic/common/xh.xml create mode 100644 docs/xsl-generic/common/zh_cn.xml create mode 100644 docs/xsl-generic/common/zh_tw.xml create mode 100644 docs/xsl-generic/highlighting/c-hl.xml create mode 100644 docs/xsl-generic/highlighting/common.xsl create mode 100644 docs/xsl-generic/highlighting/delphi-hl.xml create mode 100644 docs/xsl-generic/highlighting/ini-hl.xml create mode 100644 docs/xsl-generic/highlighting/java-hl.xml create mode 100644 docs/xsl-generic/highlighting/m2-hl.xml create mode 100644 docs/xsl-generic/highlighting/myxml-hl.xml create mode 100644 docs/xsl-generic/highlighting/php-hl.xml create mode 100644 docs/xsl-generic/highlighting/xslthl-config.xml create mode 100644 docs/xsl-generic/html/admon.xsl create mode 100644 docs/xsl-generic/html/annotations.xsl create mode 100644 docs/xsl-generic/html/autoidx-kimber.xsl create mode 100644 docs/xsl-generic/html/autoidx-kosek.xsl create mode 100644 docs/xsl-generic/html/autoidx-ng.xsl create mode 100644 docs/xsl-generic/html/autoidx.xsl create mode 100644 docs/xsl-generic/html/autotoc.xsl create mode 100644 docs/xsl-generic/html/biblio-iso690.xsl create mode 100644 docs/xsl-generic/html/biblio.xsl create mode 100644 docs/xsl-generic/html/block.xsl create mode 100644 docs/xsl-generic/html/callout.xsl create mode 100644 docs/xsl-generic/html/changebars.xsl create mode 100644 docs/xsl-generic/html/chunk-code.xsl create mode 100644 docs/xsl-generic/html/chunk-common.xsl create mode 100644 docs/xsl-generic/html/chunk.xsl create mode 100644 docs/xsl-generic/html/chunker.xsl create mode 100644 docs/xsl-generic/html/chunkfast.xsl create mode 100644 docs/xsl-generic/html/chunktoc.xsl create mode 100644 docs/xsl-generic/html/component.xsl create mode 100644 docs/xsl-generic/html/division.xsl create mode 100644 docs/xsl-generic/html/docbook.xsl create mode 100644 docs/xsl-generic/html/ebnf.xsl create mode 100644 docs/xsl-generic/html/footnote.xsl create mode 100644 docs/xsl-generic/html/formal.xsl create mode 100644 docs/xsl-generic/html/glossary.xsl create mode 100644 docs/xsl-generic/html/graphics.xsl create mode 100644 docs/xsl-generic/html/highlight.xsl create mode 100644 docs/xsl-generic/html/html-rtf.xsl create mode 100644 docs/xsl-generic/html/html.xsl create mode 100644 docs/xsl-generic/html/htmltbl.xsl create mode 100644 docs/xsl-generic/html/index.xsl create mode 100644 docs/xsl-generic/html/info.xsl create mode 100644 docs/xsl-generic/html/inline.xsl create mode 100644 docs/xsl-generic/html/keywords.xsl create mode 100644 docs/xsl-generic/html/lists.xsl create mode 100644 docs/xsl-generic/html/maketoc.xsl create mode 100644 docs/xsl-generic/html/manifest.xsl create mode 100644 docs/xsl-generic/html/math.xsl create mode 100644 docs/xsl-generic/html/oldchunker.xsl create mode 100644 docs/xsl-generic/html/onechunk.xsl create mode 100644 docs/xsl-generic/html/param.xsl create mode 100644 docs/xsl-generic/html/pi.xsl create mode 100644 docs/xsl-generic/html/profile-chunk-code.xsl create mode 100644 docs/xsl-generic/html/profile-chunk.xsl create mode 100644 docs/xsl-generic/html/profile-docbook.xsl create mode 100644 docs/xsl-generic/html/profile-onechunk.xsl create mode 100644 docs/xsl-generic/html/qandaset.xsl create mode 100644 docs/xsl-generic/html/refentry.xsl create mode 100644 docs/xsl-generic/html/sections.xsl create mode 100644 docs/xsl-generic/html/synop.xsl create mode 100644 docs/xsl-generic/html/table.xsl create mode 100644 docs/xsl-generic/html/task.xsl create mode 100644 docs/xsl-generic/html/titlepage.templates.xml create mode 100644 docs/xsl-generic/html/titlepage.templates.xsl create mode 100644 docs/xsl-generic/html/titlepage.xsl create mode 100644 docs/xsl-generic/html/toc.xsl create mode 100644 docs/xsl-generic/html/verbatim.xsl create mode 100644 docs/xsl-generic/html/xref.xsl create mode 100644 docs/xsl-generic/lib/lib.xsl create mode 100644 docs/xsl-generic/manpages/block.xsl create mode 100644 docs/xsl-generic/manpages/charmap.groff.xsl create mode 100644 docs/xsl-generic/manpages/docbook.xsl create mode 100644 docs/xsl-generic/manpages/endnotes.xsl create mode 100644 docs/xsl-generic/manpages/html-synop.xsl create mode 100644 docs/xsl-generic/manpages/info.xsl create mode 100644 docs/xsl-generic/manpages/inline.xsl create mode 100644 docs/xsl-generic/manpages/lists.xsl create mode 100644 docs/xsl-generic/manpages/other.xsl create mode 100644 docs/xsl-generic/manpages/param.xsl create mode 100644 docs/xsl-generic/manpages/profile-docbook.xsl create mode 100644 docs/xsl-generic/manpages/refentry.xsl create mode 100644 docs/xsl-generic/manpages/synop.xsl create mode 100644 docs/xsl-generic/manpages/table.xsl create mode 100644 docs/xsl-generic/manpages/utility.xsl create mode 100644 infrastructure/BoxPlatform.pm.in create mode 100644 infrastructure/buildenv-testmain-template.cpp create mode 100644 infrastructure/m4/ac_cxx_exceptions.m4 create mode 100644 infrastructure/m4/ac_cxx_namespaces.m4 create mode 100644 infrastructure/m4/ax_bswap64.m4 create mode 100644 infrastructure/m4/ax_check_bdb_v1.m4 create mode 100644 infrastructure/m4/ax_check_define_pragma.m4 create mode 100644 infrastructure/m4/ax_check_dirent_d_type.m4 create mode 100644 infrastructure/m4/ax_check_llong_minmax.m4 create mode 100644 infrastructure/m4/ax_check_malloc_workaround.m4 create mode 100644 infrastructure/m4/ax_check_mount_point.m4 create mode 100644 infrastructure/m4/ax_check_nonaligned_access.m4 create mode 100644 infrastructure/m4/ax_check_ssl.m4 create mode 100644 infrastructure/m4/ax_check_syscall_lseek.m4 create mode 100644 infrastructure/m4/ax_compare_version.m4 create mode 100644 infrastructure/m4/ax_config_scripts.m4 create mode 100644 infrastructure/m4/ax_func_syscall.m4 create mode 100644 infrastructure/m4/ax_path_bdb.m4 create mode 100644 infrastructure/m4/ax_random_device.m4 create mode 100644 infrastructure/m4/ax_split_version.m4 create mode 100644 infrastructure/m4/vl_lib_readline.m4 create mode 100755 infrastructure/makebuildenv.pl.in create mode 100755 infrastructure/makedistribution.pl.in create mode 100755 infrastructure/makeparcels.pl.in create mode 100755 infrastructure/mingw/configure.sh create mode 100644 infrastructure/msvc/2003/bbackupctl.vcproj create mode 100644 infrastructure/msvc/2003/bbackupd.vcproj create mode 100644 infrastructure/msvc/2003/boxbackup.sln create mode 100644 infrastructure/msvc/2003/boxquery.vcproj create mode 100644 infrastructure/msvc/2003/common.vcproj create mode 100644 infrastructure/msvc/2003/win32test.vcproj create mode 100644 infrastructure/msvc/2005/bbackupctl.vcproj create mode 100644 infrastructure/msvc/2005/bbackupd.vcproj create mode 100644 infrastructure/msvc/2005/boxbackup.sln create mode 100644 infrastructure/msvc/2005/boxbackup.suo create mode 100644 infrastructure/msvc/2005/boxquery.vcproj create mode 100644 infrastructure/msvc/2005/common.vcproj create mode 100644 infrastructure/msvc/2005/win32test.vcproj create mode 100644 infrastructure/msvc/getversion.pl create mode 100644 infrastructure/parcelpath.pl create mode 100644 infrastructure/printversion.pl create mode 100755 infrastructure/setupexternal.pl create mode 100644 lib/backupclient/BackupClientCryptoKeys.cpp create mode 100644 lib/backupclient/BackupClientCryptoKeys.h create mode 100644 lib/backupclient/BackupClientFileAttributes.cpp create mode 100644 lib/backupclient/BackupClientFileAttributes.h create mode 100644 lib/backupclient/BackupClientMakeExcludeList.cpp create mode 100644 lib/backupclient/BackupClientMakeExcludeList.h create mode 100644 lib/backupclient/BackupClientRestore.cpp create mode 100644 lib/backupclient/BackupClientRestore.h create mode 100644 lib/backupclient/BackupDaemonConfigVerify.cpp create mode 100644 lib/backupclient/BackupDaemonConfigVerify.h create mode 100644 lib/backupclient/BackupStoreConstants.h create mode 100644 lib/backupclient/BackupStoreDirectory.cpp create mode 100644 lib/backupclient/BackupStoreDirectory.h create mode 100644 lib/backupclient/BackupStoreException.h create mode 100644 lib/backupclient/BackupStoreException.txt create mode 100644 lib/backupclient/BackupStoreFile.cpp create mode 100644 lib/backupclient/BackupStoreFile.h create mode 100644 lib/backupclient/BackupStoreFileCmbDiff.cpp create mode 100644 lib/backupclient/BackupStoreFileCmbIdx.cpp create mode 100644 lib/backupclient/BackupStoreFileCombine.cpp create mode 100644 lib/backupclient/BackupStoreFileCryptVar.cpp create mode 100644 lib/backupclient/BackupStoreFileCryptVar.h create mode 100644 lib/backupclient/BackupStoreFileDiff.cpp create mode 100644 lib/backupclient/BackupStoreFileEncodeStream.cpp create mode 100644 lib/backupclient/BackupStoreFileEncodeStream.h create mode 100644 lib/backupclient/BackupStoreFileRevDiff.cpp create mode 100644 lib/backupclient/BackupStoreFileWire.h create mode 100644 lib/backupclient/BackupStoreFilename.cpp create mode 100644 lib/backupclient/BackupStoreFilename.h create mode 100644 lib/backupclient/BackupStoreFilenameClear.cpp create mode 100644 lib/backupclient/BackupStoreFilenameClear.h create mode 100644 lib/backupclient/BackupStoreObjectDump.cpp create mode 100644 lib/backupclient/BackupStoreObjectMagic.h create mode 100644 lib/backupclient/Makefile.extra create mode 100644 lib/backupclient/RunStatusProvider.h create mode 100644 lib/backupstore/BackupStoreAccountDatabase.cpp create mode 100644 lib/backupstore/BackupStoreAccountDatabase.h create mode 100644 lib/backupstore/BackupStoreAccounts.cpp create mode 100644 lib/backupstore/BackupStoreAccounts.h create mode 100644 lib/backupstore/BackupStoreCheck.cpp create mode 100644 lib/backupstore/BackupStoreCheck.h create mode 100644 lib/backupstore/BackupStoreCheck2.cpp create mode 100644 lib/backupstore/BackupStoreCheckData.cpp create mode 100644 lib/backupstore/BackupStoreConfigVerify.cpp create mode 100644 lib/backupstore/BackupStoreConfigVerify.h create mode 100644 lib/backupstore/BackupStoreInfo.cpp create mode 100644 lib/backupstore/BackupStoreInfo.h create mode 100644 lib/backupstore/BackupStoreRefCountDatabase.cpp create mode 100644 lib/backupstore/BackupStoreRefCountDatabase.h create mode 100644 lib/backupstore/StoreStructure.cpp create mode 100644 lib/backupstore/StoreStructure.h create mode 100644 lib/common/Archive.h create mode 100644 lib/common/BannerText.h create mode 100644 lib/common/BeginStructPackForWire.h create mode 100644 lib/common/Box.h create mode 100644 lib/common/BoxConfig-MSVC.h create mode 100644 lib/common/BoxException.cpp create mode 100644 lib/common/BoxException.h create mode 100644 lib/common/BoxPlatform.h create mode 100644 lib/common/BoxPortsAndFiles.h.in create mode 100644 lib/common/BoxTime.cpp create mode 100644 lib/common/BoxTime.h create mode 100644 lib/common/BoxTimeToText.cpp create mode 100644 lib/common/BoxTimeToText.h create mode 100644 lib/common/BoxTimeToUnix.h create mode 100644 lib/common/BufferedStream.cpp create mode 100644 lib/common/BufferedStream.h create mode 100644 lib/common/BufferedWriteStream.cpp create mode 100644 lib/common/BufferedWriteStream.h create mode 100644 lib/common/CollectInBufferStream.cpp create mode 100644 lib/common/CollectInBufferStream.h create mode 100644 lib/common/CommonException.h create mode 100644 lib/common/CommonException.txt create mode 100644 lib/common/Configuration.cpp create mode 100644 lib/common/Configuration.h create mode 100644 lib/common/Conversion.h create mode 100644 lib/common/ConversionException.txt create mode 100644 lib/common/ConversionString.cpp create mode 100644 lib/common/DebugAssertFailed.cpp create mode 100644 lib/common/DebugMemLeakFinder.cpp create mode 100644 lib/common/DebugPrintf.cpp create mode 100644 lib/common/EndStructPackForWire.h create mode 100644 lib/common/EventWatchFilesystemObject.cpp create mode 100644 lib/common/EventWatchFilesystemObject.h create mode 100644 lib/common/ExcludeList.cpp create mode 100644 lib/common/ExcludeList.h create mode 100644 lib/common/FdGetLine.cpp create mode 100644 lib/common/FdGetLine.h create mode 100644 lib/common/FileModificationTime.cpp create mode 100644 lib/common/FileModificationTime.h create mode 100644 lib/common/FileStream.cpp create mode 100644 lib/common/FileStream.h create mode 100644 lib/common/Guards.h create mode 100644 lib/common/IOStream.cpp create mode 100644 lib/common/IOStream.h create mode 100644 lib/common/IOStreamGetLine.cpp create mode 100644 lib/common/IOStreamGetLine.h create mode 100644 lib/common/InvisibleTempFileStream.cpp create mode 100644 lib/common/InvisibleTempFileStream.h create mode 100644 lib/common/Logging.cpp create mode 100644 lib/common/Logging.h create mode 100644 lib/common/MainHelper.h create mode 100644 lib/common/Makefile.extra create mode 100644 lib/common/MemBlockStream.cpp create mode 100644 lib/common/MemBlockStream.h create mode 100644 lib/common/MemLeakFindOff.h create mode 100644 lib/common/MemLeakFindOn.h create mode 100644 lib/common/MemLeakFinder.h create mode 100644 lib/common/NamedLock.cpp create mode 100644 lib/common/NamedLock.h create mode 100644 lib/common/PartialReadStream.cpp create mode 100644 lib/common/PartialReadStream.h create mode 100644 lib/common/PathUtils.cpp create mode 100644 lib/common/PathUtils.h create mode 100644 lib/common/ReadGatherStream.cpp create mode 100644 lib/common/ReadGatherStream.h create mode 100644 lib/common/ReadLoggingStream.cpp create mode 100644 lib/common/ReadLoggingStream.h create mode 100644 lib/common/SelfFlushingStream.h create mode 100644 lib/common/StreamableMemBlock.cpp create mode 100644 lib/common/StreamableMemBlock.h create mode 100644 lib/common/TemporaryDirectory.h create mode 100644 lib/common/Test.cpp create mode 100644 lib/common/Test.h create mode 100644 lib/common/Timer.cpp create mode 100644 lib/common/Timer.h create mode 100644 lib/common/UnixUser.cpp create mode 100644 lib/common/UnixUser.h create mode 100644 lib/common/Utils.cpp create mode 100644 lib/common/Utils.h create mode 100644 lib/common/WaitForEvent.cpp create mode 100644 lib/common/WaitForEvent.h create mode 100644 lib/common/ZeroStream.cpp create mode 100644 lib/common/ZeroStream.h create mode 100755 lib/common/makeexception.pl.in create mode 100644 lib/compress/Compress.h create mode 100644 lib/compress/CompressException.h create mode 100644 lib/compress/CompressException.txt create mode 100644 lib/compress/CompressStream.cpp create mode 100644 lib/compress/CompressStream.h create mode 100644 lib/compress/Makefile.extra create mode 100644 lib/crypto/CipherAES.cpp create mode 100644 lib/crypto/CipherAES.h create mode 100644 lib/crypto/CipherBlowfish.cpp create mode 100644 lib/crypto/CipherBlowfish.h create mode 100644 lib/crypto/CipherContext.cpp create mode 100644 lib/crypto/CipherContext.h create mode 100644 lib/crypto/CipherDescription.cpp create mode 100644 lib/crypto/CipherDescription.h create mode 100644 lib/crypto/CipherException.h create mode 100644 lib/crypto/CipherException.txt create mode 100644 lib/crypto/MD5Digest.cpp create mode 100644 lib/crypto/MD5Digest.h create mode 100644 lib/crypto/Makefile.extra create mode 100644 lib/crypto/Random.cpp create mode 100644 lib/crypto/Random.h create mode 100644 lib/crypto/RollingChecksum.cpp create mode 100644 lib/crypto/RollingChecksum.h create mode 100644 lib/httpserver/HTTPException.txt create mode 100644 lib/httpserver/HTTPQueryDecoder.cpp create mode 100644 lib/httpserver/HTTPQueryDecoder.h create mode 100644 lib/httpserver/HTTPRequest.cpp create mode 100644 lib/httpserver/HTTPRequest.h create mode 100644 lib/httpserver/HTTPResponse.cpp create mode 100644 lib/httpserver/HTTPResponse.h create mode 100644 lib/httpserver/HTTPServer.cpp create mode 100644 lib/httpserver/HTTPServer.h create mode 100644 lib/httpserver/Makefile.extra create mode 100644 lib/httpserver/S3Client.cpp create mode 100644 lib/httpserver/S3Client.h create mode 100644 lib/httpserver/S3Simulator.cpp create mode 100644 lib/httpserver/S3Simulator.h create mode 100644 lib/httpserver/cdecode.cpp create mode 100644 lib/httpserver/cdecode.h create mode 100644 lib/httpserver/cencode.cpp create mode 100644 lib/httpserver/cencode.h create mode 100644 lib/httpserver/decode.h create mode 100644 lib/httpserver/encode.h create mode 100644 lib/intercept/intercept.cpp create mode 100644 lib/intercept/intercept.h create mode 100644 lib/raidfile/Makefile.extra create mode 100644 lib/raidfile/RaidFileController.cpp create mode 100644 lib/raidfile/RaidFileController.h create mode 100644 lib/raidfile/RaidFileException.h create mode 100644 lib/raidfile/RaidFileException.txt create mode 100644 lib/raidfile/RaidFileRead.cpp create mode 100644 lib/raidfile/RaidFileRead.h create mode 100644 lib/raidfile/RaidFileUtil.cpp create mode 100644 lib/raidfile/RaidFileUtil.h create mode 100644 lib/raidfile/RaidFileWrite.cpp create mode 100644 lib/raidfile/RaidFileWrite.h create mode 100755 lib/raidfile/raidfile-config.in create mode 100644 lib/server/ConnectionException.txt create mode 100644 lib/server/Daemon.cpp create mode 100644 lib/server/Daemon.h create mode 100644 lib/server/LocalProcessStream.cpp create mode 100644 lib/server/LocalProcessStream.h create mode 100644 lib/server/Makefile.extra create mode 100644 lib/server/OverlappedIO.h create mode 100644 lib/server/Protocol.cpp create mode 100644 lib/server/Protocol.h create mode 100644 lib/server/ProtocolObject.cpp create mode 100644 lib/server/ProtocolObject.h create mode 100644 lib/server/ProtocolUncertainStream.cpp create mode 100644 lib/server/ProtocolUncertainStream.h create mode 100644 lib/server/ProtocolWire.h create mode 100644 lib/server/SSLLib.cpp create mode 100644 lib/server/SSLLib.h create mode 100644 lib/server/ServerControl.cpp create mode 100644 lib/server/ServerControl.h create mode 100644 lib/server/ServerException.h create mode 100644 lib/server/ServerException.txt create mode 100644 lib/server/ServerStream.h create mode 100644 lib/server/ServerTLS.h create mode 100644 lib/server/Socket.cpp create mode 100644 lib/server/Socket.h create mode 100644 lib/server/SocketListen.h create mode 100644 lib/server/SocketStream.cpp create mode 100644 lib/server/SocketStream.h create mode 100644 lib/server/SocketStreamTLS.cpp create mode 100644 lib/server/SocketStreamTLS.h create mode 100644 lib/server/TLSContext.cpp create mode 100644 lib/server/TLSContext.h create mode 100644 lib/server/WinNamedPipeListener.h create mode 100644 lib/server/WinNamedPipeStream.cpp create mode 100644 lib/server/WinNamedPipeStream.h create mode 100755 lib/server/makeprotocol.pl.in create mode 100755 lib/win32/MSG00001.bin create mode 100644 lib/win32/emu.cpp create mode 100644 lib/win32/emu.h create mode 100755 lib/win32/getopt.h create mode 100755 lib/win32/getopt_long.cpp create mode 100755 lib/win32/messages.h create mode 100644 lib/win32/messages.mc create mode 100755 lib/win32/messages.rc create mode 100644 modules.txt create mode 100644 parcels.txt create mode 100755 runtest.pl.in create mode 100644 test/backupdiff/difftestfiles.cpp create mode 100644 test/backupdiff/testbackupdiff.cpp create mode 100644 test/backupdiff/testextra create mode 100644 test/backupstore/Makefile.extra create mode 100644 test/backupstore/testbackupstore.cpp create mode 100644 test/backupstore/testextra create mode 100644 test/backupstore/testfiles/accounts.txt create mode 100644 test/backupstore/testfiles/bbackupd.keys create mode 100644 test/backupstore/testfiles/bbstored.conf create mode 100644 test/backupstore/testfiles/bbstored_multi.conf create mode 100644 test/backupstore/testfiles/clientCerts.pem create mode 100644 test/backupstore/testfiles/clientPrivKey.pem create mode 100644 test/backupstore/testfiles/clientReq.pem create mode 100644 test/backupstore/testfiles/clientTrustedCAs.pem create mode 100644 test/backupstore/testfiles/query.conf create mode 100644 test/backupstore/testfiles/raidfile.conf create mode 100644 test/backupstore/testfiles/root.pem create mode 100644 test/backupstore/testfiles/root.srl create mode 100644 test/backupstore/testfiles/rootcert.pem create mode 100644 test/backupstore/testfiles/rootkey.pem create mode 100644 test/backupstore/testfiles/rootreq.pem create mode 100644 test/backupstore/testfiles/serverCerts.pem create mode 100644 test/backupstore/testfiles/serverPrivKey.pem create mode 100644 test/backupstore/testfiles/serverReq.pem create mode 100644 test/backupstore/testfiles/serverTrustedCAs.pem create mode 100644 test/backupstorefix/testbackupstorefix.cpp create mode 100644 test/backupstorefix/testextra create mode 100755 test/backupstorefix/testfiles/testbackupstorefix.pl.in create mode 100644 test/backupstorepatch/testbackupstorepatch.cpp create mode 100644 test/backupstorepatch/testextra create mode 100644 test/basicserver/Makefile.extra create mode 100644 test/basicserver/TestCommands.cpp create mode 100644 test/basicserver/TestContext.cpp create mode 100644 test/basicserver/TestContext.h create mode 100644 test/basicserver/testbasicserver.cpp create mode 100644 test/basicserver/testfiles/clientCerts.pem create mode 100644 test/basicserver/testfiles/clientPrivKey.pem create mode 100644 test/basicserver/testfiles/clientReq.pem create mode 100644 test/basicserver/testfiles/clientTrustedCAs.pem create mode 100644 test/basicserver/testfiles/key-creation.txt create mode 100644 test/basicserver/testfiles/root.pem create mode 100644 test/basicserver/testfiles/root.srl create mode 100644 test/basicserver/testfiles/rootcert.pem create mode 100644 test/basicserver/testfiles/rootkey.pem create mode 100644 test/basicserver/testfiles/rootreq.pem create mode 100644 test/basicserver/testfiles/serverCerts.pem create mode 100644 test/basicserver/testfiles/serverPrivKey.pem create mode 100644 test/basicserver/testfiles/serverReq.pem create mode 100644 test/basicserver/testfiles/serverTrustedCAs.pem create mode 100644 test/basicserver/testfiles/srv1.conf create mode 100644 test/basicserver/testfiles/srv1b.conf create mode 100644 test/basicserver/testfiles/srv2.conf create mode 100644 test/basicserver/testfiles/srv3.conf create mode 100644 test/basicserver/testfiles/srv4.conf create mode 100644 test/basicserver/testprotocol.txt create mode 100644 test/bbackupd/Makefile.extra create mode 100644 test/bbackupd/testbbackupd.cpp create mode 100644 test/bbackupd/testextra create mode 100644 test/bbackupd/testfiles/accounts.txt create mode 100644 test/bbackupd/testfiles/bbackupd-exclude.conf.in create mode 100644 test/bbackupd/testfiles/bbackupd-snapshot.conf.in create mode 100644 test/bbackupd/testfiles/bbackupd-symlink.conf.in create mode 100644 test/bbackupd/testfiles/bbackupd-temploc.conf create mode 100644 test/bbackupd/testfiles/bbackupd.conf.in create mode 100644 test/bbackupd/testfiles/bbackupd.keys create mode 100644 test/bbackupd/testfiles/bbstored.conf create mode 100644 test/bbackupd/testfiles/clientCerts.pem create mode 100644 test/bbackupd/testfiles/clientPrivKey.pem create mode 100644 test/bbackupd/testfiles/clientTrustedCAs.pem create mode 100755 test/bbackupd/testfiles/extcheck1.pl.in create mode 100755 test/bbackupd/testfiles/extcheck2.pl.in create mode 100755 test/bbackupd/testfiles/notifyscript.pl.in create mode 100644 test/bbackupd/testfiles/raidfile.conf create mode 100644 test/bbackupd/testfiles/serverCerts.pem create mode 100644 test/bbackupd/testfiles/serverPrivKey.pem create mode 100644 test/bbackupd/testfiles/serverTrustedCAs.pem create mode 100644 test/bbackupd/testfiles/spacetest1.tgz create mode 100644 test/bbackupd/testfiles/spacetest2.tgz create mode 100755 test/bbackupd/testfiles/syncallowscript.pl.in create mode 100644 test/bbackupd/testfiles/test2.tgz create mode 100644 test/bbackupd/testfiles/test3.tgz create mode 100644 test/bbackupd/testfiles/test_base.tgz create mode 100644 test/bbackupd/testfiles/testexclude.tgz create mode 100644 test/common/testcommon.cpp create mode 100644 test/common/testfiles/config1.txt create mode 100644 test/common/testfiles/config10.txt create mode 100644 test/common/testfiles/config11.txt create mode 100644 test/common/testfiles/config12.txt create mode 100644 test/common/testfiles/config13.txt create mode 100644 test/common/testfiles/config14.txt create mode 100644 test/common/testfiles/config15.txt create mode 100644 test/common/testfiles/config16.txt create mode 100644 test/common/testfiles/config2.txt create mode 100644 test/common/testfiles/config3.txt create mode 100644 test/common/testfiles/config4.txt create mode 100644 test/common/testfiles/config5.txt create mode 100644 test/common/testfiles/config6.txt create mode 100644 test/common/testfiles/config7.txt create mode 100644 test/common/testfiles/config8.txt create mode 100644 test/common/testfiles/config9.txt create mode 100644 test/common/testfiles/config9b.txt create mode 100644 test/common/testfiles/config9c.txt create mode 100644 test/common/testfiles/config9d.txt create mode 100644 test/common/testfiles/fdgetlinetest.txt create mode 100644 test/compress/testcompress.cpp create mode 100644 test/crypto/testcrypto.cpp create mode 100644 test/httpserver/testfiles/httpserver.conf create mode 100644 test/httpserver/testfiles/photos/puppy.jpg create mode 100644 test/httpserver/testfiles/s3simulator.conf create mode 100755 test/httpserver/testfiles/testrequests.pl create mode 100644 test/httpserver/testhttpserver.cpp create mode 100644 test/raidfile/testextra create mode 100644 test/raidfile/testfiles/raidfile.conf create mode 100644 test/raidfile/testraidfile.cpp create mode 100644 test/win32/Makefile create mode 100644 test/win32/testlibwin32.cpp create mode 100644 test/win32/timezone.cpp create mode 100644 win32.bat 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 new file mode 100644 index 00000000..f1293228 --- /dev/null +++ b/.svnrevision @@ -0,0 +1 @@ +2837 diff --git a/BUGS.txt b/BUGS.txt new file mode 100644 index 00000000..e0569113 --- /dev/null +++ b/BUGS.txt @@ -0,0 +1,15 @@ +================================================================================================================ +Bugs +================================================================================================================ + +* need a test to check that small files aren't tracked +* things like object ids don't have proper typedefs +* if a file changes while it's being streamed to the server, bad things will happen (exception in bbackupd, or corrupt file on server) +* if bbackupd gets an error then a signal, it may not wait it's full 100 seconds before retrying. And then won't stop the cycle... +* bbackupquery restore, if not root, then won't do file ownership properly, but won't alert the user to this fact +* empty (real) directories in the store aren't deleted when they're empty (and will never be used again) -- uses up disc space unnecessarily +* need unit tests for SSL keepalives and state saving (serialisation) +* make Archive derive from Protocol +* more automated tests for win32 +* change off_t to box_off_t in preparation for win32 large file support +* support large files on win32 by using native *i64 functions instead of posix diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 00000000..54d27b62 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,491 @@ +Box Backup, http://www.boxbackup.org/ + +Copyright (c) 2003-2010, Ben Summers and contributors. +All rights reserved. + +The license of the code was changed on 23-Jan-2010 in order to meet the +Fedora Project's definition of Free Software, and therefore allow inclusion +in Fedora, Red Hat Linux and CentOS. This also solves a long-standing +incompatibility with the GNU Readline library that prevented us from +distributing Box Backup binaries compiled against that library. You can +review our discussions of the change in the mailing list archives at: +http://lists.boxbackup.org/pipermail/boxbackup/2010-January/000005.html + +Note that this project uses mixed licensing. Different parts of the project +may be used and distributed under different licenses, as described below. +The two licenses used are "Box Backup GPL" and a BSD-style license. + +Unless stated otherwise in the file, all files in the following directories +fall under the "Box Backup GPL" license, described below: + +bin/bbackupctl +bin/bbackupd +bin/bbackupobjdump +bin/bbackupquery +bin/bbstoreaccounts +bin/bbstored +bin/s3simulator +lib/backupclient +lib/backupstore +test/backupdiff +test/backupstore +test/backupstorefix +test/backupstorepatch +test/bbackupd +contrib/bbadmin +contrib/bbreporter +contrib/cygwin +contrib/debian +contrib/mac_osx +contrib/redhat +contrib/rpm +contrib/solaris +contrib/suse +contrib/windows +distribution/boxbackup + +The "Box Backup GPL" license follows: +--------------------------------------------------------------------- +The Box Backup GPL is based on the GPLv2 (GNU General Public License +version 2 or later) as published by the Free Software Foundation. +However, it includes optional exceptions which allow linking with +OpenSSL and Microsoft VSS, listed below. This means that Box Backup +is not under pure GPLv2 as published by the Free Software Foundation. +These are the only differences between the Box Backup GPL license and +the original GPL from the Free Software Foundation. + +Please note that while the Box Backup GPL is similar in spirit to the +original GPL, it is not fully compatible. Because of these optional +exceptions, you may not include or combine fully GPL source code +(such as the GNU readline library) with Box Backup, or distribute the +resulting binaries, under the Box Backup GPL license, without +specific permission from the authors of such code. You may do so +under the pure GPL license, by omitting the optional exclusion clauses +below, however you may not legally link such code with OpenSSL or +Microsoft VSS, which may limit the usefulness or functionality of the +resulting code. + +The license exemption section below was based on the license of the +Bacula project +[http://bacula.git.sourceforge.net/git/gitweb.cgi?p=bacula/bacula;a=blob_plain;f=bacula/LICENSE;hb=HEAD]. + +For the most part, Box Backup is licensed under the GPL version 2 +[http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt]. +The sections that follow are optional exemptions to the GPL version 2 +license, that apply to code that is copyrighted by Ben Summers and +Contributors. + +Linking: +As a special exception to the GPLv2, the Box Backup Project gives +permission to link any code falling under this license (the Box Backup +GPL) with any software that can be downloaded from +the OpenSSL website [http://www.openssl.org] under either the +"OpenSSL License" or the "Original SSLeay License", and to distribute +the linked executables under the terms of the "Box Backup GPL" license. + +As a special exception to the GPLv2, the Box Backup Project gives +permission to link any code falling under this license (the Box Backup +GPL) with any version of Microsoft's Volume Shadow Copy Service 7.2 SDK +or Microsoft Windows Software Development Kit (SDK), including +vssapi.lib, that can be downloaded from the Microsoft website +[*.microsoft.com], and to distribute the linked executables under the +terms of the "Box Backup GPL" license. + +The sections above are optional, and you may distribute the code that +falls under this license under the original GPLv2 license by omitting +them. The original GPLv2 license follows. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. +--------------------------------------------------------------------- + +Unless stated otherwise in the file or in the list of directories above, +all files in the following directories are dual licensed under the BSD and +GPL licenses. You may use and distribute them providing that you comply +EITHER with the terms of the BSD license, OR the GPL license. It is not +necessary to comply with both licenses, only one. + +lib/common +lib/compress +lib/crypto +lib/httpserver +lib/intercept +lib/raidfile +lib/server +lib/win32 +test/basicserver +test/common +test/compress +test/crypto +test/httpserver +test/raidfile +test/win32 +infrastructure +distribution + +The BSD license follows: +--------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the Box Backup nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------- +[http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22.29] + diff --git a/LICENSE-DUAL.txt b/LICENSE-DUAL.txt new file mode 100644 index 00000000..6aa31da2 --- /dev/null +++ b/LICENSE-DUAL.txt @@ -0,0 +1,59 @@ +Box Backup, http://www.boxbackup.org/ + +Copyright (c) 2003-2010, Ben Summers and contributors. +All rights reserved. + +Note that this project uses mixed licensing. Any file with this license +attached, or where the code LICENSE-DUAL appears on the first line, falls +under this license. See the file COPYING.txt for more information. + +This file is dual licensed. You may use and distribute it providing that you +comply EITHER with the terms of the BSD license, OR the GPL license. It is +not necessary to comply with both licenses, only one. + +The BSD license option follows: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the Box Backup nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +[http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22.29] + +The GPL license option follows: + +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 2 +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, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +[http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4] diff --git a/LICENSE-GPL.txt b/LICENSE-GPL.txt new file mode 100644 index 00000000..3e84b646 --- /dev/null +++ b/LICENSE-GPL.txt @@ -0,0 +1,41 @@ +Box Backup, http://www.boxbackup.org/ + +Copyright (c) 2003-2010, Ben Summers and contributors. +All rights reserved. + +Note that this project uses mixed licensing. Any file with this license +attached, or where the code LICENSE-GPL appears on the first line, falls +under the "Box Backup GPL" license. See the file COPYING.txt for more +information about this license. + +--------------------------------------------------------------------- +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 2 +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, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +[http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4] + +As a special exception to the GPLv2, the Box Backup Project gives +permission to link any code falling under this license (the Box Backup +GPL) with any software that can be downloaded from +the OpenSSL website [http://www.openssl.org] under either the +"OpenSSL License" or the "Original SSLeay License", and to distribute +the linked executables under the terms of the "Box Backup GPL" license. + +As a special exception to the GPLv2, the Box Backup Project gives +permission to link any code falling under this license (the Box Backup +GPL) with any version of Microsoft's Volume Shadow Copy Service 7.2 SDK +or Microsoft Windows Software Development Kit (SDK), including +vssapi.lib, that can be downloaded from the Microsoft website +[*.microsoft.com], and to distribute the linked executables under the +terms of the "Box Backup GPL" license. diff --git a/VERSION.txt b/VERSION.txt new file mode 100644 index 00000000..c29321a1 --- /dev/null +++ b/VERSION.txt @@ -0,0 +1,2 @@ +USE_SVN_VERSION +boxbackup diff --git a/bin/bbackupctl/bbackupctl.cpp b/bin/bbackupctl/bbackupctl.cpp new file mode 100644 index 00000000..8dc8f30e --- /dev/null +++ b/bin/bbackupctl/bbackupctl.cpp @@ -0,0 +1,366 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: bbackupctl.cpp +// Purpose: bbackupd daemon control program +// Created: 18/2/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include + +#ifdef HAVE_UNISTD_H + #include +#endif + +#include + +#include "MainHelper.h" +#include "BoxPortsAndFiles.h" +#include "BackupDaemonConfigVerify.h" +#include "Socket.h" +#include "SocketStream.h" +#include "IOStreamGetLine.h" + +#ifdef WIN32 + #include "WinNamedPipeStream.h" +#endif + +#include "MemLeakFindOn.h" + +enum Command +{ + Default, + WaitForSyncStart, + WaitForSyncEnd, + SyncAndWaitForEnd, +}; + +void PrintUsageAndExit() +{ + printf("Usage: bbackupctl [-q] [-c config_file] \n" + "Commands are:\n" + " sync -- start a synchronisation (backup) run now\n" + " force-sync -- force the start of a synchronisation run, " + "even if SyncAllowScript says no\n" + " reload -- reload daemon configuration\n" + " terminate -- terminate daemon now\n" + " wait-for-sync -- wait until the next sync starts, then exit\n" + " wait-for-end -- wait until the next sync finishes, then exit\n" + " sync-and-wait -- start sync, wait until it finishes, then exit\n" + ); + exit(1); +} + +int main(int argc, const char *argv[]) +{ + int returnCode = 0; + + MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT("bbackupctl.memleaks", + "bbackupctl") + + MAINHELPER_START + + Logging::SetProgramName("bbackupctl"); + + // Filename for configuration file? + std::string configFilename; + + #ifdef WIN32 + configFilename = BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE; + #else + configFilename = BOX_FILE_BBACKUPD_DEFAULT_CONFIG; + #endif + + // Quiet? + bool quiet = false; + + // See if there's another entry on the command line + int c; + while((c = getopt(argc, (char * const *)argv, "qc:l:")) != -1) + { + switch(c) + { + case 'q': + // Quiet mode + quiet = true; + break; + + case 'c': + // store argument + configFilename = optarg; + break; + + case '?': + default: + PrintUsageAndExit(); + } + } + // Adjust arguments + argc -= optind; + argv += optind; + + // Check there's a command + if(argc != 1) + { + PrintUsageAndExit(); + } + + // Read in the configuration file + if(!quiet) BOX_NOTICE("Using configuration file " << configFilename); + + std::string errs; + std::auto_ptr config( + Configuration::LoadAndVerify + (configFilename, &BackupDaemonConfigVerify, errs)); + + if(config.get() == 0 || !errs.empty()) + { + BOX_ERROR("Invalid configuration file: " << errs); + return 1; + } + // Easier coding + const Configuration &conf(*config); + + // Check there's a socket defined in the config file + if(!conf.KeyExists("CommandSocket")) + { + BOX_ERROR("Daemon isn't using a control socket, " + "could not execute command.\n" + "Add a CommandSocket declaration to the " + "bbackupd.conf file."); + return 1; + } + + // Connect to socket + +#ifndef WIN32 + SocketStream connection; +#else /* WIN32 */ + WinNamedPipeStream connection; +#endif /* ! WIN32 */ + + try + { +#ifdef WIN32 + std::string socket = conf.GetKeyValue("CommandSocket"); + connection.Connect(socket); +#else + connection.Open(Socket::TypeUNIX, conf.GetKeyValue("CommandSocket").c_str()); +#endif + } + catch(...) + { + BOX_ERROR("Failed to connect to daemon control socket.\n" + "Possible causes:\n" + " * Daemon not running\n" + " * Daemon busy syncing with store server\n" + " * Another bbackupctl process is communicating with the daemon\n" + " * Daemon is waiting to recover from an error" + ); + + return 1; + } + + // For receiving data + IOStreamGetLine getLine(connection); + + // Wait for the configuration summary + std::string configSummary; + if(!getLine.GetLine(configSummary)) + { + BOX_ERROR("Failed to receive configuration summary " + "from daemon"); + return 1; + } + + // Was the connection rejected by the server? + if(getLine.IsEOF()) + { + BOX_ERROR("Server rejected the connection. Are you running " + "bbackupctl as the same user as the daemon?"); + return 1; + } + + // Decode it + int autoBackup, updateStoreInterval, minimumFileAge, maxUploadWait; + if(::sscanf(configSummary.c_str(), "bbackupd: %d %d %d %d", &autoBackup, + &updateStoreInterval, &minimumFileAge, &maxUploadWait) != 4) + { + BOX_ERROR("Config summary didn't decode."); + return 1; + } + // Print summary? + if(!quiet) + { + BOX_INFO("Daemon configuration summary:\n" + " AutomaticBackup = " << + (autoBackup?"true":"false") << "\n" + " UpdateStoreInterval = " << updateStoreInterval << + " seconds\n" + " MinimumFileAge = " << minimumFileAge << " seconds\n" + " MaxUploadWait = " << maxUploadWait << " seconds"); + } + + std::string stateLine; + if(!getLine.GetLine(stateLine) || getLine.IsEOF()) + { + BOX_ERROR("Failed to receive state line from daemon"); + return 1; + } + + // Decode it + int currentState; + if(::sscanf(stateLine.c_str(), "state %d", ¤tState) != 1) + { + BOX_ERROR("Received invalid state line from daemon"); + return 1; + } + + Command command = Default; + std::string commandName(argv[0]); + + if (commandName == "wait-for-sync") + { + command = WaitForSyncStart; + } + else if (commandName == "wait-for-end") + { + command = WaitForSyncEnd; + } + else if (commandName == "sync-and-wait") + { + command = SyncAndWaitForEnd; + } + + switch (command) + { + case WaitForSyncStart: + case WaitForSyncEnd: + { + // Check that it's in automatic mode, + // because otherwise it'll never start + + if(!autoBackup) + { + BOX_ERROR("Daemon is not in automatic mode, " + "sync will never start!"); + return 1; + } + + } + break; + + case SyncAndWaitForEnd: + { + // send a sync command + commandName = "force-sync"; + std::string cmd = commandName + "\n"; + connection.Write(cmd.c_str(), cmd.size()); + connection.WriteAllBuffered(); + + if (currentState != 0) + { + BOX_INFO("Waiting for current sync/error state " + "to finish..."); + } + } + break; + + default: + { + // Normal case, just send the command given + // plus a quit command. + std::string cmd = commandName; + cmd += "\nquit\n"; + connection.Write(cmd.c_str(), cmd.size()); + } + } + + // Read the response + std::string line; + bool syncIsRunning = false; + bool finished = false; + + while(!finished && !getLine.IsEOF() && getLine.GetLine(line)) + { + switch (command) + { + case WaitForSyncStart: + { + // Need to wait for the state change... + if(line == "start-sync") + { + // Send a quit command to finish nicely + connection.Write("quit\n", 5); + + // And we're done + finished = true; + } + } + break; + + case WaitForSyncEnd: + case SyncAndWaitForEnd: + { + if(line == "start-sync") + { + if (!quiet) BOX_INFO("Sync started..."); + syncIsRunning = true; + } + else if(line == "finish-sync") + { + if (syncIsRunning) + { + if (!quiet) BOX_INFO("Sync finished."); + // Send a quit command to finish nicely + connection.Write("quit\n", 5); + + // And we're done + finished = true; + } + else + { + if (!quiet) BOX_INFO("Previous sync finished."); + } + // daemon must still be busy + } + } + break; + + default: + { + // Is this an OK or error line? + if(line == "ok") + { + if(!quiet) + { + BOX_INFO("Control command " + "sent: " << + commandName); + } + finished = true; + } + else if(line == "error") + { + BOX_ERROR("Control command failed: " << + commandName << ". Check " + "command spelling."); + returnCode = 1; + finished = true; + } + } + } + } + + MAINHELPER_END + +#if defined WIN32 && ! defined BOX_RELEASE_BUILD + closelog(); +#endif + + return returnCode; +} diff --git a/bin/bbackupd/BackupClientContext.cpp b/bin/bbackupd/BackupClientContext.cpp new file mode 100644 index 00000000..6b51b9e8 --- /dev/null +++ b/bin/bbackupd/BackupClientContext.cpp @@ -0,0 +1,578 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupClientContext.cpp +// Purpose: Keep track of context +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_SIGNAL_H + #include +#endif + +#ifdef HAVE_SYS_TIME_H + #include +#endif + +#include "BoxPortsAndFiles.h" +#include "BoxTime.h" +#include "BackupClientContext.h" +#include "SocketStreamTLS.h" +#include "Socket.h" +#include "BackupStoreConstants.h" +#include "BackupStoreException.h" +#include "BackupDaemon.h" +#include "autogen_BackupProtocolClient.h" +#include "BackupStoreFile.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::BackupClientContext(BackupDaemon &, TLSContext &, const std::string &, int32_t, bool, bool, std::string) +// Purpose: Constructor +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +BackupClientContext::BackupClientContext +( + LocationResolver &rResolver, + TLSContext &rTLSContext, + const std::string &rHostname, + int Port, + uint32_t AccountNumber, + bool ExtendedLogging, + bool ExtendedLogToFile, + std::string ExtendedLogFile, + ProgressNotifier& rProgressNotifier +) + : mrResolver(rResolver), + mrTLSContext(rTLSContext), + mHostname(rHostname), + mPort(Port), + mAccountNumber(AccountNumber), + mpSocket(0), + mpConnection(0), + mExtendedLogging(ExtendedLogging), + mExtendedLogToFile(ExtendedLogToFile), + mExtendedLogFile(ExtendedLogFile), + mpExtendedLogFileHandle(NULL), + mClientStoreMarker(ClientStoreMarker_NotKnown), + mpDeleteList(0), + mpCurrentIDMap(0), + mpNewIDMap(0), + mStorageLimitExceeded(false), + mpExcludeFiles(0), + mpExcludeDirs(0), + mKeepAliveTimer(0, "KeepAliveTime"), + mbIsManaged(false), + mrProgressNotifier(rProgressNotifier) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::~BackupClientContext() +// Purpose: Destructor +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +BackupClientContext::~BackupClientContext() +{ + CloseAnyOpenConnection(); + + // Delete delete list + if(mpDeleteList != 0) + { + delete mpDeleteList; + mpDeleteList = 0; + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::GetConnection() +// Purpose: Returns the connection, making the connection and logging into +// the backup store if necessary. +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +BackupProtocolClient &BackupClientContext::GetConnection() +{ + // Already got it? Just return it. + if(mpConnection != 0) + { + return *mpConnection; + } + + // Get a socket connection + if(mpSocket == 0) + { + mpSocket = new SocketStreamTLS; + ASSERT(mpSocket != 0); // will have exceptioned if this was a problem + } + + try + { + // Defensive. + if(mpConnection != 0) + { + delete mpConnection; + mpConnection = 0; + } + + // Log intention + BOX_INFO("Opening connection to server '" << + mHostname << "'..."); + + // Connect! + mpSocket->Open(mrTLSContext, Socket::TypeINET, + mHostname.c_str(), mPort); + + // And create a procotol object + mpConnection = new BackupProtocolClient(*mpSocket); + + // Set logging option + mpConnection->SetLogToSysLog(mExtendedLogging); + + if (mExtendedLogToFile) + { + ASSERT(mpExtendedLogFileHandle == NULL); + + mpExtendedLogFileHandle = fopen( + mExtendedLogFile.c_str(), "a+"); + + if (!mpExtendedLogFileHandle) + { + BOX_LOG_SYS_ERROR("Failed to open extended " + "log file: " << mExtendedLogFile); + } + else + { + mpConnection->SetLogToFile(mpExtendedLogFileHandle); + } + } + + // Handshake + mpConnection->Handshake(); + + // Check the version of the server + { + std::auto_ptr serverVersion(mpConnection->QueryVersion(BACKUP_STORE_SERVER_VERSION)); + if(serverVersion->GetVersion() != BACKUP_STORE_SERVER_VERSION) + { + THROW_EXCEPTION(BackupStoreException, WrongServerVersion) + } + } + + // Login -- if this fails, the Protocol will exception + std::auto_ptr loginConf(mpConnection->QueryLogin(mAccountNumber, 0 /* read/write */)); + + // Check that the client store marker is the one we expect + if(mClientStoreMarker != ClientStoreMarker_NotKnown) + { + if(loginConf->GetClientStoreMarker() != mClientStoreMarker) + { + // Not good... finish the connection, abort, etc, ignoring errors + try + { + mpConnection->QueryFinished(); + mpSocket->Shutdown(); + mpSocket->Close(); + } + catch(...) + { + // IGNORE + } + + // Then throw an exception about this + THROW_EXCEPTION(BackupStoreException, ClientMarkerNotAsExpected) + } + } + + // Log success + BOX_INFO("Connection made, login successful"); + + // Check to see if there is any space available on the server + if(loginConf->GetBlocksUsed() >= loginConf->GetBlocksHardLimit()) + { + // no -- flag so only things like deletions happen + mStorageLimitExceeded = true; + // Log + BOX_WARNING("Exceeded storage hard-limit on server, " + "not uploading changes to files"); + } + } + catch(...) + { + // Clean up. + delete mpConnection; + mpConnection = 0; + delete mpSocket; + mpSocket = 0; + throw; + } + + return *mpConnection; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::CloseAnyOpenConnection() +// Purpose: Closes a connection, if it's open +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +void BackupClientContext::CloseAnyOpenConnection() +{ + if(mpConnection) + { + try + { + // Need to set a client store marker? + if(mClientStoreMarker == ClientStoreMarker_NotKnown) + { + // Yes, choose one, the current time will do + box_time_t marker = GetCurrentBoxTime(); + + // Set it on the store + mpConnection->QuerySetClientStoreMarker(marker); + + // Record it so that it can be picked up later. + mClientStoreMarker = marker; + } + + // Quit nicely + mpConnection->QueryFinished(); + } + catch(...) + { + // Ignore errors here + } + + // Delete it anyway. + delete mpConnection; + mpConnection = 0; + } + + if(mpSocket) + { + try + { + // Be nice about closing the socket + mpSocket->Shutdown(); + mpSocket->Close(); + } + catch(...) + { + // Ignore errors + } + + // Delete object + delete mpSocket; + mpSocket = 0; + } + + // Delete any pending list + if(mpDeleteList != 0) + { + delete mpDeleteList; + mpDeleteList = 0; + } + + if (mpExtendedLogFileHandle != NULL) + { + fclose(mpExtendedLogFileHandle); + mpExtendedLogFileHandle = NULL; + } +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::GetTimeout() +// Purpose: Gets the current timeout time. +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +int BackupClientContext::GetTimeout() const +{ + if(mpConnection) + { + return mpConnection->GetTimeout(); + } + + return (15*60*1000); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::GetDeleteList() +// Purpose: Returns the delete list, creating one if necessary +// Created: 10/11/03 +// +// -------------------------------------------------------------------------- +BackupClientDeleteList &BackupClientContext::GetDeleteList() +{ + // Already created? + if(mpDeleteList == 0) + { + mpDeleteList = new BackupClientDeleteList; + } + + // Return reference to object + return *mpDeleteList; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::PerformDeletions() +// Purpose: Perform any pending file deletions. +// Created: 10/11/03 +// +// -------------------------------------------------------------------------- +void BackupClientContext::PerformDeletions() +{ + // Got a list? + if(mpDeleteList == 0) + { + // Nothing to do + return; + } + + // Delegate to the delete list object + mpDeleteList->PerformDeletions(*this); + + // Delete the object + delete mpDeleteList; + mpDeleteList = 0; +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::GetCurrentIDMap() const +// Purpose: Return a (const) reference to the current ID map +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +const BackupClientInodeToIDMap &BackupClientContext::GetCurrentIDMap() const +{ + ASSERT(mpCurrentIDMap != 0); + if(mpCurrentIDMap == 0) + { + THROW_EXCEPTION(CommonException, Internal) + } + return *mpCurrentIDMap; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::GetNewIDMap() const +// Purpose: Return a reference to the new ID map +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +BackupClientInodeToIDMap &BackupClientContext::GetNewIDMap() const +{ + ASSERT(mpNewIDMap != 0); + if(mpNewIDMap == 0) + { + THROW_EXCEPTION(CommonException, Internal) + } + return *mpNewIDMap; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::FindFilename(int64_t, int64_t, std::string &, bool &) const +// Purpose: Attempts to find the pathname of an object with a given ID on the server. +// Returns true if it can be found, in which case rPathOut is the local filename, +// and rIsDirectoryOut == true if the local object is a directory. +// Created: 12/11/03 +// +// -------------------------------------------------------------------------- +bool BackupClientContext::FindFilename(int64_t ObjectID, int64_t ContainingDirectory, std::string &rPathOut, bool &rIsDirectoryOut, + bool &rIsCurrentVersionOut, box_time_t *pModTimeOnServer, box_time_t *pAttributesHashOnServer, BackupStoreFilenameClear *pLeafname) +{ + // Make a connection to the server + BackupProtocolClient &connection(GetConnection()); + + // Request filenames from the server, in a "safe" manner to ignore errors properly + { + BackupProtocolClientGetObjectName send(ObjectID, ContainingDirectory); + connection.Send(send); + } + std::auto_ptr preply(connection.Receive()); + + // Is it of the right type? + if(preply->GetType() != BackupProtocolClientObjectName::TypeID) + { + // Was an error or something + return false; + } + + // Cast to expected type. + BackupProtocolClientObjectName *names = (BackupProtocolClientObjectName *)(preply.get()); + + // Anything found? + int32_t numElements = names->GetNumNameElements(); + if(numElements <= 0) + { + // No. + return false; + } + + // Get the stream containing all the names + std::auto_ptr nameStream(connection.ReceiveStream()); + + // Path + std::string path; + + // Remember this is in reverse order! + for(int l = 0; l < numElements; ++l) + { + BackupStoreFilenameClear elementName; + elementName.ReadFromStream(*nameStream, GetTimeout()); + + // Store leafname for caller? + if(l == 0 && pLeafname) + { + *pLeafname = elementName; + } + + // Is it part of the filename in the location? + if(l < (numElements - 1)) + { + // Part of filename within + path = (path.empty())?(elementName.GetClearFilename()):(elementName.GetClearFilename() + DIRECTORY_SEPARATOR_ASCHAR + path); + } + else + { + // Location name -- look up in daemon's records + std::string locPath; + if(!mrResolver.FindLocationPathName(elementName.GetClearFilename(), locPath)) + { + // Didn't find the location... so can't give the local filename + return false; + } + + // Add in location path + path = (path.empty())?(locPath):(locPath + DIRECTORY_SEPARATOR_ASCHAR + path); + } + } + + // Is it a directory? + rIsDirectoryOut = ((names->GetFlags() & BackupProtocolClientListDirectory::Flags_Dir) == BackupProtocolClientListDirectory::Flags_Dir); + + // Is it the current version? + rIsCurrentVersionOut = ((names->GetFlags() & (BackupProtocolClientListDirectory::Flags_OldVersion | BackupProtocolClientListDirectory::Flags_Deleted)) == 0); + + // And other information which may be required + if(pModTimeOnServer) *pModTimeOnServer = names->GetModificationTime(); + if(pAttributesHashOnServer) *pAttributesHashOnServer = names->GetAttributesHash(); + + // Tell caller about the pathname + rPathOut = path; + + // Found + return true; +} + +void BackupClientContext::SetMaximumDiffingTime(int iSeconds) +{ + mMaximumDiffingTime = iSeconds < 0 ? 0 : iSeconds; + BOX_TRACE("Set maximum diffing time to " << mMaximumDiffingTime << + " seconds"); +} + +void BackupClientContext::SetKeepAliveTime(int iSeconds) +{ + mKeepAliveTime = iSeconds < 0 ? 0 : iSeconds; + BOX_TRACE("Set keep-alive time to " << mKeepAliveTime << " seconds"); + mKeepAliveTimer = Timer(mKeepAliveTime, "KeepAliveTime"); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::ManageDiffProcess() +// Purpose: Initiates a file diff control timer +// Created: 04/19/2005 +// +// -------------------------------------------------------------------------- +void BackupClientContext::ManageDiffProcess() +{ + ASSERT(!mbIsManaged); + mbIsManaged = true; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::UnManageDiffProcess() +// Purpose: suspends file diff control timer +// Created: 04/19/2005 +// +// -------------------------------------------------------------------------- +void BackupClientContext::UnManageDiffProcess() +{ + // ASSERT(mbIsManaged); + mbIsManaged = false; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientContext::DoKeepAlive() +// Purpose: Check whether it's time to send a KeepAlive +// message over the SSL link, and if so, send it. +// Created: 04/19/2005 +// +// -------------------------------------------------------------------------- +void BackupClientContext::DoKeepAlive() +{ + if (!mpConnection) + { + return; + } + + if (mKeepAliveTime == 0) + { + return; + } + + if (!mKeepAliveTimer.HasExpired()) + { + return; + } + + BOX_TRACE("KeepAliveTime reached, sending keep-alive message"); + mpConnection->QueryGetIsAlive(); + + mKeepAliveTimer = Timer(mKeepAliveTime, "KeepAliveTime"); +} + +int BackupClientContext::GetMaximumDiffingTime() +{ + return mMaximumDiffingTime; +} diff --git a/bin/bbackupd/BackupClientContext.h b/bin/bbackupd/BackupClientContext.h new file mode 100644 index 00000000..404d2d77 --- /dev/null +++ b/bin/bbackupd/BackupClientContext.h @@ -0,0 +1,237 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupClientContext.h +// Purpose: Keep track of context +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#ifndef BACKUPCLIENTCONTEXT__H +#define BACKUPCLIENTCONTEXT__H + +#include "BoxTime.h" +#include "BackupClientDeleteList.h" +#include "BackupClientDirectoryRecord.h" +#include "BackupDaemonInterface.h" +#include "BackupStoreFile.h" +#include "ExcludeList.h" +#include "Timer.h" + +class TLSContext; +class BackupProtocolClient; +class SocketStreamTLS; +class BackupClientInodeToIDMap; +class BackupDaemon; +class BackupStoreFilenameClear; + +#include + + +// -------------------------------------------------------------------------- +// +// Class +// Name: BackupClientContext +// Purpose: +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +class BackupClientContext : public DiffTimer +{ +public: + BackupClientContext + ( + LocationResolver &rResolver, + TLSContext &rTLSContext, + const std::string &rHostname, + int32_t Port, + uint32_t AccountNumber, + bool ExtendedLogging, + bool ExtendedLogToFile, + std::string ExtendedLogFile, + ProgressNotifier &rProgressNotifier + ); + virtual ~BackupClientContext(); +private: + BackupClientContext(const BackupClientContext &); +public: + + BackupProtocolClient &GetConnection(); + + void CloseAnyOpenConnection(); + + int GetTimeout() const; + + BackupClientDeleteList &GetDeleteList(); + void PerformDeletions(); + + enum + { + ClientStoreMarker_NotKnown = 0 + }; + + void SetClientStoreMarker(int64_t ClientStoreMarker) {mClientStoreMarker = ClientStoreMarker;} + int64_t GetClientStoreMarker() const {return mClientStoreMarker;} + + bool StorageLimitExceeded() {return mStorageLimitExceeded;} + void SetStorageLimitExceeded() {mStorageLimitExceeded = true;} + + // -------------------------------------------------------------------------- + // + // Function + // Name: BackupClientContext::SetIDMaps(const BackupClientInodeToIDMap *, BackupClientInodeToIDMap *) + // Purpose: Store pointers to the Current and New ID maps + // Created: 11/11/03 + // + // -------------------------------------------------------------------------- + void SetIDMaps(const BackupClientInodeToIDMap *pCurrent, BackupClientInodeToIDMap *pNew) + { + ASSERT(pCurrent != 0); + ASSERT(pNew != 0); + mpCurrentIDMap = pCurrent; + mpNewIDMap = pNew; + } + const BackupClientInodeToIDMap &GetCurrentIDMap() const; + BackupClientInodeToIDMap &GetNewIDMap() const; + + + // -------------------------------------------------------------------------- + // + // Function + // Name: BackupClientContext::SetExcludeLists(ExcludeList *, ExcludeList *) + // Purpose: Sets the exclude lists for the operation. Can be 0. + // Created: 28/1/04 + // + // -------------------------------------------------------------------------- + void SetExcludeLists(ExcludeList *pExcludeFiles, ExcludeList *pExcludeDirs) + { + mpExcludeFiles = pExcludeFiles; + mpExcludeDirs = pExcludeDirs; + } + + // -------------------------------------------------------------------------- + // + // Function + // Name: BackupClientContext::ExcludeFile(const std::string &) + // Purpose: Returns true is this file should be excluded from the backup + // Created: 28/1/04 + // + // -------------------------------------------------------------------------- + inline bool ExcludeFile(const std::string &rFullFilename) + { + if(mpExcludeFiles != 0) + { + return mpExcludeFiles->IsExcluded(rFullFilename); + } + // If no list, don't exclude anything + return false; + } + + // -------------------------------------------------------------------------- + // + // Function + // Name: BackupClientContext::ExcludeDir(const std::string &) + // Purpose: Returns true is this directory should be excluded from the backup + // Created: 28/1/04 + // + // -------------------------------------------------------------------------- + inline bool ExcludeDir(const std::string &rFullDirName) + { + if(mpExcludeDirs != 0) + { + return mpExcludeDirs->IsExcluded(rFullDirName); + } + // If no list, don't exclude anything + return false; + } + + // Utility functions -- may do a lot of work + bool FindFilename(int64_t ObjectID, int64_t ContainingDirectory, std::string &rPathOut, bool &rIsDirectoryOut, + bool &rIsCurrentVersionOut, box_time_t *pModTimeOnServer = 0, box_time_t *pAttributesHashOnServer = 0, + BackupStoreFilenameClear *pLeafname = 0); // not const as may connect to server + + // -------------------------------------------------------------------------- + // + // Function + // Name: BackupClientContext::SetMaximumDiffingTime() + // Purpose: Sets the maximum time that will be spent diffing a file + // Created: 04/19/2005 + // + // -------------------------------------------------------------------------- + void SetMaximumDiffingTime(int iSeconds); + + // -------------------------------------------------------------------------- + // + // Function + // Name: BackupClientContext::SetKeepAliveTime() + // Purpose: Sets the time interval for repetitive keep-alive operation + // Created: 04/19/2005 + // + // -------------------------------------------------------------------------- + void SetKeepAliveTime(int iSeconds); + + // -------------------------------------------------------------------------- + // + // Function + // Name: BackupClientContext::ManageDiffProcess() + // Purpose: Initiates an SSL connection/session keep-alive process + // Created: 04/19/2005 + // + // -------------------------------------------------------------------------- + void ManageDiffProcess(); + + // -------------------------------------------------------------------------- + // + // Function + // Name: BackupClientContext::UnManageDiffProcess() + // Purpose: Suspends an SSL connection/session keep-alive process + // Created: 04/19/2005 + // + // -------------------------------------------------------------------------- + void UnManageDiffProcess(); + + // ------------------------------------------------------------------- + // + // Function + // Name: BackupClientContext::DoKeepAlive() + // Purpose: Check whether it's time to send a KeepAlive + // message over the SSL link, and if so, send it. + // Created: 04/19/2005 + // + // ------------------------------------------------------------------- + virtual void DoKeepAlive(); + virtual int GetMaximumDiffingTime(); + virtual bool IsManaged() { return mbIsManaged; } + + ProgressNotifier& GetProgressNotifier() const + { + return mrProgressNotifier; + } + +private: + LocationResolver &mrResolver; + TLSContext &mrTLSContext; + std::string mHostname; + int mPort; + uint32_t mAccountNumber; + SocketStreamTLS *mpSocket; + BackupProtocolClient *mpConnection; + bool mExtendedLogging; + bool mExtendedLogToFile; + std::string mExtendedLogFile; + FILE* mpExtendedLogFileHandle; + int64_t mClientStoreMarker; + BackupClientDeleteList *mpDeleteList; + const BackupClientInodeToIDMap *mpCurrentIDMap; + BackupClientInodeToIDMap *mpNewIDMap; + bool mStorageLimitExceeded; + ExcludeList *mpExcludeFiles; + ExcludeList *mpExcludeDirs; + Timer mKeepAliveTimer; + bool mbIsManaged; + int mKeepAliveTime; + int mMaximumDiffingTime; + ProgressNotifier &mrProgressNotifier; +}; + +#endif // BACKUPCLIENTCONTEXT__H diff --git a/bin/bbackupd/BackupClientDeleteList.cpp b/bin/bbackupd/BackupClientDeleteList.cpp new file mode 100644 index 00000000..b9b5b53e --- /dev/null +++ b/bin/bbackupd/BackupClientDeleteList.cpp @@ -0,0 +1,229 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupClientDeleteList.cpp +// Purpose: List of pending deletes for backup +// Created: 10/11/03 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include "BackupClientDeleteList.h" +#include "BackupClientContext.h" +#include "autogen_BackupProtocolClient.h" + +#include "MemLeakFindOn.h" + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDeleteList::BackupClientDeleteList() +// Purpose: Constructor +// Created: 10/11/03 +// +// -------------------------------------------------------------------------- +BackupClientDeleteList::BackupClientDeleteList() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDeleteList::~BackupClientDeleteList() +// Purpose: Destructor +// Created: 10/11/03 +// +// -------------------------------------------------------------------------- +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, +// const BackupStoreFilename&) +// Purpose: Add a directory to the list of directories to be deleted. +// Created: 10/11/03 +// +// -------------------------------------------------------------------------- +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()) + { + // Not in the list, so should delete it + mDirectoryList.push_back(DirToDelete(ObjectID, rLocalPath)); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDeleteList::AddFileDelete(int64_t, +// const BackupStoreFilename &) +// Purpose: +// Created: 10/11/03 +// +// -------------------------------------------------------------------------- +void BackupClientDeleteList::AddFileDelete(int64_t DirectoryID, + const BackupStoreFilename &rFilename, const std::string& rLocalPath) +{ + // Try to find it in the no delete list + std::vector >::iterator + delEntry(mFileNoDeleteList.begin()); + while(delEntry != mFileNoDeleteList.end()) + { + if((delEntry)->first == DirectoryID + && (delEntry)->second == rFilename) + { + // Found! + break; + } + ++delEntry; + } + + // Only add it to the delete list if it wasn't in the no delete list + if(delEntry == mFileNoDeleteList.end()) + { + mFileList.push_back(FileToDelete(DirectoryID, rFilename, + rLocalPath)); + } +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDeleteList::PerformDeletions(BackupClientContext &rContext) +// Purpose: Perform all the pending deletes +// Created: 10/11/03 +// +// -------------------------------------------------------------------------- +void BackupClientDeleteList::PerformDeletions(BackupClientContext &rContext) +{ + // Anything to do? + if(mDirectoryList.empty() && mFileList.empty()) + { + // Nothing! + return; + } + + // Get a connection + BackupProtocolClient &connection(rContext.GetConnection()); + + // Do the deletes + for(std::vector::iterator i(mDirectoryList.begin()); + i != mDirectoryList.end(); ++i) + { + connection.QueryDeleteDirectory(i->mObjectID); + rContext.GetProgressNotifier().NotifyDirectoryDeleted( + i->mObjectID, i->mLocalPath); + } + + // Clear the directory list + mDirectoryList.clear(); + + // Delete the files + for(std::vector::iterator i(mFileList.begin()); + i != mFileList.end(); ++i) + { + connection.QueryDeleteFile(i->mDirectoryID, i->mFilename); + rContext.GetProgressNotifier().NotifyFileDeleted( + i->mDirectoryID, i->mLocalPath); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDeleteList::StopDirectoryDeletion(int64_t) +// Purpose: Stop a directory being deleted +// Created: 19/11/03 +// +// -------------------------------------------------------------------------- +void BackupClientDeleteList::StopDirectoryDeletion(int64_t ObjectID) +{ + // First of all, is it in the delete vector? + std::vector::iterator delEntry(mDirectoryList.begin()); + for(; delEntry != mDirectoryList.end(); delEntry++) + { + if(delEntry->mObjectID == ObjectID) + { + // Found! + break; + } + } + if(delEntry != mDirectoryList.end()) + { + // erase this entry + mDirectoryList.erase(delEntry); + } + else + { + // Haven't been asked to delete it yet, put it in the + // no delete list + mDirectoryNoDeleteList.insert(ObjectID); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDeleteList::StopFileDeletion(int64_t, const BackupStoreFilename &) +// Purpose: Stop a file from being deleted +// Created: 19/11/03 +// +// -------------------------------------------------------------------------- +void BackupClientDeleteList::StopFileDeletion(int64_t DirectoryID, + const BackupStoreFilename &rFilename) +{ + // Find this in the delete list + std::vector::iterator delEntry(mFileList.begin()); + while(delEntry != mFileList.end()) + { + if(delEntry->mDirectoryID == DirectoryID + && delEntry->mFilename == rFilename) + { + // Found! + break; + } + ++delEntry; + } + + if(delEntry != mFileList.end()) + { + // erase this entry + mFileList.erase(delEntry); + } + else + { + // Haven't been asked to delete it yet, put it in the no delete list + mFileNoDeleteList.push_back(std::pair(DirectoryID, rFilename)); + } +} + diff --git a/bin/bbackupd/BackupClientDeleteList.h b/bin/bbackupd/BackupClientDeleteList.h new file mode 100644 index 00000000..b0fbf51a --- /dev/null +++ b/bin/bbackupd/BackupClientDeleteList.h @@ -0,0 +1,75 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupClientDeleteList.h +// Purpose: List of pending deletes for backup +// Created: 10/11/03 +// +// -------------------------------------------------------------------------- + +#ifndef BACKUPCLIENTDELETELIST__H +#define BACKUPCLIENTDELETELIST__H + +#include "BackupStoreFilename.h" + +class BackupClientContext; + +#include +#include +#include + +// -------------------------------------------------------------------------- +// +// Class +// Name: BackupClientDeleteList +// Purpose: List of pending deletes for backup +// Created: 10/11/03 +// +// -------------------------------------------------------------------------- +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, + 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 PerformDeletions(BackupClientContext &rContext); + +private: + std::vector mDirectoryList; + std::set mDirectoryNoDeleteList; // note: things only get in this list if they're not present in mDirectoryList when they are 'added' + std::vector mFileList; + std::vector > mFileNoDeleteList; +}; + +#endif // BACKUPCLIENTDELETELIST__H + diff --git a/bin/bbackupd/BackupClientDirectoryRecord.cpp b/bin/bbackupd/BackupClientDirectoryRecord.cpp new file mode 100644 index 00000000..84c17dab --- /dev/null +++ b/bin/bbackupd/BackupClientDirectoryRecord.cpp @@ -0,0 +1,1876 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupClientDirectoryRecord.cpp +// Purpose: Implementation of record about directory for +// backup client +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_DIRENT_H + #include +#endif + +#include +#include + +#include "BackupClientDirectoryRecord.h" +#include "autogen_BackupProtocolClient.h" +#include "BackupClientContext.h" +#include "IOStream.h" +#include "MemBlockStream.h" +#include "CommonException.h" +#include "CollectInBufferStream.h" +#include "BackupStoreFile.h" +#include "BackupClientInodeToIDMap.h" +#include "FileModificationTime.h" +#include "BackupDaemon.h" +#include "BackupStoreException.h" +#include "Archive.h" +#include "PathUtils.h" +#include "Logging.h" +#include "ReadLoggingStream.h" + +#include "MemLeakFindOn.h" + +typedef std::map DecryptedEntriesMap_t; + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::BackupClientDirectoryRecord() +// Purpose: Constructor +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +BackupClientDirectoryRecord::BackupClientDirectoryRecord(int64_t ObjectID, const std::string &rSubDirName) + : mObjectID(ObjectID), + mSubDirName(rSubDirName), + mInitialSyncDone(false), + mSyncDone(false), + mpPendingEntries(0) +{ + ::memset(mStateChecksum, 0, sizeof(mStateChecksum)); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::~BackupClientDirectoryRecord() +// Purpose: Destructor +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +BackupClientDirectoryRecord::~BackupClientDirectoryRecord() +{ + // Make deletion recursive + DeleteSubDirectories(); + + // Delete maps + if(mpPendingEntries != 0) + { + delete mpPendingEntries; + mpPendingEntries = 0; + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::DeleteSubDirectories(); +// Purpose: Delete all sub directory entries +// Created: 2003/10/09 +// +// -------------------------------------------------------------------------- +void BackupClientDirectoryRecord::DeleteSubDirectories() +{ + // Delete all pointers + for(std::map::iterator i = mSubDirectories.begin(); + i != mSubDirectories.end(); ++i) + { + delete i->second; + } + + // Empty list + mSubDirectories.clear(); +} + +// -------------------------------------------------------------------------- +// +// Function +// 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, + const std::string &rRemotePath, + bool ThisDirHasJustBeenCreated) +{ + BackupClientContext& rContext(rParams.mrContext); + ProgressNotifier& rNotifier(rContext.GetProgressNotifier()); + + // Signal received by daemon? + if(rParams.mrRunStatusProvider.StopRun()) + { + // Yes. Stop now. + THROW_EXCEPTION(BackupStoreException, SignalReceived) + } + + // Start by making some flag changes, marking this sync as not done, + // and on the immediate sub directories. + mSyncDone = false; + for(std::map::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; + + // 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) + { + 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. + rNotifier.NotifyDirStatFailed(this, rLocalPath, + strerror(errno)); + return; + } + // Store inode number in map so directories are tracked + // in case they're renamed + { + BackupClientInodeToIDMap &idMap( + rParams.mrContext.GetNewIDMap()); + idMap.AddToMap(dest_st.st_ino, mObjectID, + ContainingDirectoryID); + } + // Add attributes to checksum + 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(&dest_st.st_ino, + sizeof(dest_st.st_ino)); +#ifdef HAVE_STRUCT_STAT_ST_FLAGS + currentStateChecksum.Add(&dest_st.st_flags, + sizeof(dest_st.st_flags)); +#endif + + StreamableMemBlock xattr; + BackupClientFileAttributes::FillExtendedAttr(xattr, + rLocalPath.c_str()); + currentStateChecksum.Add(xattr.GetBuffer(), xattr.GetSize()); + } + + // Read directory entries, building arrays of names + // First, need to read the contents of the directory. + std::vector dirs; + std::vector files; + bool downloadDirectoryRecordBecauseOfFutureFiles = false; + + EMU_STRUCT_STAT link_st; + if(EMU_LSTAT(rLocalPath.c_str(), &link_st) != 0) + { + // Report the error (logs and + // eventual email to administrator) + rNotifier.NotifyFileStatFailed(this, rLocalPath, + strerror(errno)); + + // FIXME move to NotifyFileStatFailed() + SetErrorWhenReadingFilesystemObject(rParams, + rLocalPath.c_str()); + + // This shouldn't happen, so we'd better not continue + THROW_EXCEPTION(CommonException, OSFileError) + } + + // BLOCK + { + // read the contents... + DIR *dirHandle = 0; + try + { + rNotifier.NotifyScanDirectory(this, rLocalPath); + + dirHandle = ::opendir(rLocalPath.c_str()); + if(dirHandle == 0) + { + // Report the error (logs and + // eventual email to administrator) + if (errno == EACCES) + { + rNotifier.NotifyDirListFailed(this, + rLocalPath, "Access denied"); + } + else + { + rNotifier.NotifyDirListFailed(this, + rLocalPath, strerror(errno)); + } + + // Report the error (logs and eventual email + // to administrator) + SetErrorWhenReadingFilesystemObject(rParams, + rLocalPath.c_str()); + // Ignore this directory for now. + return; + } + + // Basic structure for checksum info + struct { + box_time_t mModificationTime; + box_time_t mAttributeModificationTime; + int64_t mSize; + // And then the name follows + } checksum_info; + // Be paranoid about structure packing + ::memset(&checksum_info, 0, sizeof(checksum_info)); + + struct dirent *en = 0; + 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 + + if(en->d_name[0] == '.' && + (en->d_name[1] == '\0' || (en->d_name[1] == '.' && en->d_name[2] == '\0'))) + { + // ignore, it's . or .. + continue; + } + + // Stat file to get info + filename = MakeFullPath(rLocalPath, en->d_name); + + #ifdef WIN32 + // Don't stat the file just yet, to ensure + // that users can exclude unreadable files + // to suppress warnings that they are + // not accessible. + // + // Our emulated readdir() abuses en->d_type, + // which would normally contain DT_REG, + // DT_DIR, etc, but we only use it here and + // prefer S_IFREG, S_IFDIR... + int type = en->d_type; + #else + if(EMU_LSTAT(filename.c_str(), &file_st) != 0) + { + if(!(rParams.mrContext.ExcludeDir( + filename))) + { + // Report the error (logs and + // eventual email to + // administrator) + rNotifier.NotifyFileStatFailed( + this, filename, + strerror(errno)); + + // FIXME move to + // NotifyFileStatFailed() + SetErrorWhenReadingFilesystemObject( + rParams, filename.c_str()); + } + + // Ignore this entry for now. + continue; + } + + if(file_st.st_dev != dest_st.st_dev) + { + if(!(rParams.mrContext.ExcludeDir( + filename))) + { + rNotifier.NotifyMountPointSkipped( + this, filename); + } + continue; + } + + int type = file_st.st_mode & S_IFMT; + #endif + + if(type == S_IFREG || type == S_IFLNK) + { + // File or symbolic link + + // Exclude it? + if(rParams.mrContext.ExcludeFile(filename)) + { + rNotifier.NotifyFileExcluded( + this, + filename); + + // Next item! + continue; + } + + // Store on list + files.push_back(std::string(en->d_name)); + } + else if(type == S_IFDIR) + { + // Directory + + // Exclude it? + if(rParams.mrContext.ExcludeDir(filename)) + { + rNotifier.NotifyDirExcluded( + this, + filename); + + // Next item! + continue; + } + + // 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)) + { + rNotifier.NotifyFileExcluded( + this, + filename); + } + else + { + rNotifier.NotifyUnsupportedFileType( + this, filename); + SetErrorWhenReadingFilesystemObject( + rParams, filename.c_str()); + } + + continue; + } + + // Here if the object is something to back up (file, symlink or dir, not excluded) + // So make the information for adding to the checksum + + #ifdef WIN32 + // We didn't stat the file before, + // but now we need the information. + if(emu_stat(filename.c_str(), &file_st) != 0) + { + rNotifier.NotifyFileStatFailed(this, + filename, + strerror(errno)); + + // Report the error (logs and + // eventual email to administrator) + SetErrorWhenReadingFilesystemObject( + rParams, filename.c_str()); + + // Ignore this entry for now. + continue; + } + + if(file_st.st_dev != link_st.st_dev) + { + rNotifier.NotifyMountPointSkipped(this, + filename); + continue; + } + #endif + + 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)); + + // If the file has been modified madly into the future, download the + // directory record anyway to ensure that it doesn't get uploaded + // every single time the disc is scanned. + if(checksum_info.mModificationTime > rParams.mUploadAfterThisTimeInTheFuture) + { + downloadDirectoryRecordBecauseOfFutureFiles = true; + // Log that this has happened + if(!rParams.mHaveLoggedWarningAboutFutureFileTimes) + { + rNotifier.NotifyFileModifiedInFuture( + this, filename); + rParams.mHaveLoggedWarningAboutFutureFileTimes = true; + } + } + } + + if(::closedir(dirHandle) != 0) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + dirHandle = 0; + } + catch(...) + { + if(dirHandle != 0) + { + ::closedir(dirHandle); + } + throw; + } + } + + // Finish off the checksum, and compare with the one currently stored + bool checksumDifferent = true; + currentStateChecksum.Finish(); + if(mInitialSyncDone && currentStateChecksum.DigestMatches(mStateChecksum)) + { + // The checksum is the same, and there was one to compare with + checksumDifferent = false; + } + + // Pointer to potentially downloaded store directory info + BackupStoreDirectory *pdirOnStore = 0; + + try + { + // Want to get the directory listing? + if(ThisDirHasJustBeenCreated) + { + // Avoid sending another command to the server when we know it's empty + pdirOnStore = new BackupStoreDirectory(mObjectID, ContainingDirectoryID); + } + else + { + // Consider asking the store for it + if(!mInitialSyncDone || checksumDifferent || downloadDirectoryRecordBecauseOfFutureFiles) + { + pdirOnStore = FetchDirectoryListing(rParams); + } + } + + // Make sure the attributes are up to date -- if there's space on the server + // and this directory has not just been created (because it's attributes will be correct in this case) + // and the checksum is different, implying they *MIGHT* be different. + if((!ThisDirHasJustBeenCreated) && checksumDifferent && (!rParams.mrContext.StorageLimitExceeded())) + { + UpdateAttributes(rParams, pdirOnStore, rLocalPath); + } + + // Create the list of pointers to directory entries + std::vector entriesLeftOver; + if(pdirOnStore) + { + entriesLeftOver.resize(pdirOnStore->GetNumberOfEntries(), 0); + BackupStoreDirectory::Iterator i(*pdirOnStore); + // Copy in pointers to all the entries + for(unsigned int l = 0; l < pdirOnStore->GetNumberOfEntries(); ++l) + { + entriesLeftOver[l] = i.Next(); + } + } + + // Do the directory reading + 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 + // But... only if 1) the storage limit isn't exceeded -- make sure things are done again if + // the directory is modified later + // and 2) All the objects within the directory were stored successfully. + if(!rParams.mrContext.StorageLimitExceeded() && updateCompleteSuccess) + { + currentStateChecksum.CopyDigestTo(mStateChecksum); + } + } + catch(...) + { + // Bad things have happened -- clean up + if(pdirOnStore != 0) + { + delete pdirOnStore; + pdirOnStore = 0; + } + + // Set things so that we get a full go at stuff later + ::memset(mStateChecksum, 0, sizeof(mStateChecksum)); + + throw; + } + + // Clean up directory on store + if(pdirOnStore != 0) + { + delete pdirOnStore; + pdirOnStore = 0; + } + + // Flag things as having happened. + mInitialSyncDone = true; + mSyncDone = true; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::FetchDirectoryListing(BackupClientDirectoryRecord::SyncParams &) +// Purpose: Fetch the directory listing of this directory from the store. +// Created: 2003/10/09 +// +// -------------------------------------------------------------------------- +BackupStoreDirectory *BackupClientDirectoryRecord::FetchDirectoryListing(BackupClientDirectoryRecord::SyncParams &rParams) +{ + BackupStoreDirectory *pdir = 0; + + try + { + // Get connection to store + BackupProtocolClient &connection(rParams.mrContext.GetConnection()); + + // Query the directory + std::auto_ptr dirreply(connection.QueryListDirectory( + mObjectID, + BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, // both files and directories + BackupProtocolClientListDirectory::Flags_Deleted | BackupProtocolClientListDirectory::Flags_OldVersion, // exclude old/deleted stuff + true /* want attributes */)); + + // Retrieve the directory from the stream following + pdir = new BackupStoreDirectory; + ASSERT(pdir != 0); + std::auto_ptr dirstream(connection.ReceiveStream()); + pdir->ReadFromStream(*dirstream, connection.GetTimeout()); + } + catch(...) + { + delete pdir; + pdir = 0; + throw; + } + + return pdir; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::UpdateAttributes(BackupClientDirectoryRecord::SyncParams &, const std::string &) +// Purpose: Sets the attributes of the directory on the store, if necessary +// Created: 2003/10/09 +// +// -------------------------------------------------------------------------- +void BackupClientDirectoryRecord::UpdateAttributes(BackupClientDirectoryRecord::SyncParams &rParams, BackupStoreDirectory *pDirOnStore, const std::string &rLocalPath) +{ + // Get attributes for the directory + BackupClientFileAttributes attr; + box_time_t attrModTime = 0; + attr.ReadAttributes(rLocalPath.c_str(), true /* directories have zero mod times */, + 0 /* no modification time */, &attrModTime); + + // Assume attributes need updating, unless proved otherwise + bool updateAttr = true; + + // Got a listing to compare with? + ASSERT(pDirOnStore == 0 || (pDirOnStore != 0 && pDirOnStore->HasAttributes())); + if(pDirOnStore != 0 && pDirOnStore->HasAttributes()) + { + const StreamableMemBlock &storeAttrEnc(pDirOnStore->GetAttributes()); + // Explict decryption + BackupClientFileAttributes storeAttr(storeAttrEnc); + + // Compare the attributes + if(attr.Compare(storeAttr, true, + true /* ignore both modification times */)) + { + // No update necessary + updateAttr = false; + } + } + + // Update them? + if(updateAttr) + { + // Get connection to store + BackupProtocolClient &connection(rParams.mrContext.GetConnection()); + + // Exception thrown if this doesn't work + MemBlockStream attrStream(attr); + connection.QueryChangeDirAttributes(mObjectID, attrModTime, attrStream); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncParams &, const std::string &, BackupStoreDirectory *, std::vector &) +// Purpose: Update the items stored on the server. The rFiles vector will be erased after it's used to save space. +// Returns true if all items were updated successfully. (If not, the failures will have been logged). +// Created: 2003/10/09 +// +// -------------------------------------------------------------------------- +bool BackupClientDirectoryRecord::UpdateItems( + BackupClientDirectoryRecord::SyncParams &rParams, + const std::string &rLocalPath, + const std::string &rRemotePath, + BackupStoreDirectory *pDirOnStore, + std::vector &rEntriesLeftOver, + std::vector &rFiles, + const std::vector &rDirs) +{ + BackupClientContext& rContext(rParams.mrContext); + ProgressNotifier& rNotifier(rContext.GetProgressNotifier()); + + bool allUpdatedSuccessfully = true; + + // Decrypt all the directory entries. + // It would be nice to be able to just compare the encrypted versions, however this doesn't work + // in practise because there can be multiple encodings of the same filename using different + // methods (although each method will result in the same string for the same filename.) This + // happens when the server fixes a broken store, and gives plain text generated filenames. + // So if we didn't do things like this, then you wouldn't be able to recover from bad things + // happening with the server. + DecryptedEntriesMap_t decryptedEntries; + if(pDirOnStore != 0) + { + BackupStoreDirectory::Iterator i(*pDirOnStore); + BackupStoreDirectory::Entry *en = 0; + while((en = i.Next()) != 0) + { + decryptedEntries[BackupStoreFilenameClear(en->GetName()).GetClearFilename()] = en; + } + } + + // Do files + for(std::vector::const_iterator f = rFiles.begin(); + f != rFiles.end(); ++f) + { + // Send keep-alive message if needed + rContext.DoKeepAlive(); + + // Filename of this file + std::string filename(MakeFullPath(rLocalPath, *f)); + + // Get relevant info about file + box_time_t modTime = 0; + uint64_t attributesHash = 0; + int64_t fileSize = 0; + InodeRefType inodeNum = 0; + bool hasMultipleHardLinks = true; + // BLOCK + { + // Stat the file + EMU_STRUCT_STAT st; + if(EMU_LSTAT(filename.c_str(), &st) != 0) + { + rNotifier.NotifyFileStatFailed(this, + filename, strerror(errno)); + + // Report the error (logs and + // eventual email to administrator) + SetErrorWhenReadingFilesystemObject(rParams, + filename.c_str()); + + // Ignore this entry for now. + continue; + } + + // Extract required data + modTime = FileModificationTime(st); + fileSize = st.st_size; + inodeNum = st.st_ino; + hasMultipleHardLinks = (st.st_nlink > 1); + attributesHash = BackupClientFileAttributes::GenerateAttributeHash(st, filename, *f); + } + + // See if it's in the listing (if we have one) + BackupStoreFilenameClear storeFilename(*f); + BackupStoreDirectory::Entry *en = 0; + int64_t latestObjectID = 0; + if(pDirOnStore != 0) + { + DecryptedEntriesMap_t::iterator i(decryptedEntries.find(*f)); + if(i != decryptedEntries.end()) + { + en = i->second; + latestObjectID = en->GetObjectID(); + } + } + + // Check that the entry which might have been found is in fact a file + 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, *f); + en = 0; + } + + // Check for renaming? + if(pDirOnStore != 0 && en == 0) + { + // We now know... + // 1) File has just been added + // 2) It's not in the store + + // Do we know about the inode number? + const BackupClientInodeToIDMap &idMap(rContext.GetCurrentIDMap()); + int64_t renameObjectID = 0, renameInDirectory = 0; + if(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; + box_time_t srvModTime = 0, srvAttributesHash = 0; + BackupStoreFilenameClear 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 + 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(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(!rContext.StorageLimitExceeded()) + { + // Rename the existing files (ie include old versions) on the server + connection.QueryMoveObject(renameObjectID, renameInDirectory, mObjectID /* move to this directory */, + BackupProtocolClientMoveObject::Flags_MoveAllWithSameName | BackupProtocolClientMoveObject::Flags_AllowMoveOverDeletedObject, + storeFilename); + + // Stop the attempt to delete the file in the original location + BackupClientDeleteList &rdelList(rContext.GetDeleteList()); + rdelList.StopFileDeletion(renameInDirectory, oldLeafname); + + // Create new entry in the directory for it + // -- will be near enough what's actually on the server for the rest to work. + en = pDirOnStore->AddEntry(storeFilename, srvModTime, renameObjectID, 0 /* size in blocks unknown, but not needed */, + BackupStoreDirectory::Entry::Flags_File, srvAttributesHash); + + // Store the object ID for the inode lookup map later + latestObjectID = renameObjectID; + } + } + } + } + } + } + + // Is it in the mPendingEntries list? + box_time_t pendingFirstSeenTime = 0; // ie not seen + if(mpPendingEntries != 0) + { + std::map::const_iterator i(mpPendingEntries->find(*f)); + if(i != mpPendingEntries->end()) + { + // found it -- set flag + pendingFirstSeenTime = i->second; + } + } + + // If pDirOnStore == 0, then this must have been after an initial sync: + ASSERT(pDirOnStore != 0 || mInitialSyncDone); + // So, if pDirOnStore == 0, then we know that everything before syncPeriodStart + // is either on the server, or in the toupload list. If the directory had changed, + // we'd have got a directory listing. + // + // At this point, if (pDirOnStore == 0 && en == 0), we can assume it's on the server with a + // mod time < syncPeriodStart, or didn't exist before that time. + // + // But if en != 0, then we need to compare modification times to avoid uploading it again. + + // Need to update? + // + // Condition for upload: + // modification time within sync period + // if it's been seen before but not uploaded, is the time from this first sight longer than the MaxUploadWait + // and if we know about it from a directory listing, that it hasn't got the same upload time as on the store + + bool doUpload = false; + + // Only upload a file if the mod time locally is + // different to that on the server. + + if (en == 0 || en->GetModificationTime() != modTime) + { + // Check the file modified within the acceptable time period we're checking + // If the file isn't on the server, the acceptable time starts at zero. + // Check pDirOnStore and en, because if we didn't download a directory listing, + // pDirOnStore will be zero, but we know it's on the server. + if (modTime < rParams.mSyncPeriodEnd) + { + if (pDirOnStore != 0 && en == 0) + { + doUpload = true; + BOX_TRACE("Upload decision: " << + filename << ": will upload " + "(not on server)"); + } + else if (modTime >= rParams.mSyncPeriodStart) + { + doUpload = true; + BOX_TRACE("Upload decision: " << + filename << ": will upload " + "(modified since last sync)"); + } + } + + // However, just in case things are continually + // modified, we check the first seen time. + // The two compares of syncPeriodEnd and + // pendingFirstSeenTime are because the values + // are unsigned. + + if (!doUpload && + pendingFirstSeenTime != 0 && + rParams.mSyncPeriodEnd > pendingFirstSeenTime && + (rParams.mSyncPeriodEnd - pendingFirstSeenTime) + > rParams.mMaxUploadWait) + { + doUpload = true; + BOX_TRACE("Upload decision: " << + filename << ": will upload " + "(continually modified)"); + } + + // Then make sure that if files are added with a + // time less than the sync period start + // (which can easily happen on file server), it + // gets uploaded. The directory contents checksum + // will pick up the fact it has been added, so the + // store listing will be available when this happens. + + if (!doUpload && + modTime <= rParams.mSyncPeriodStart && + en != 0 && + en->GetModificationTime() != modTime) + { + doUpload = true; + BOX_TRACE("Upload decision: " << + filename << ": will upload " + "(mod time changed)"); + } + + // And just to catch really badly off clocks in + // the future for file server clients, + // just upload the file if it's madly in the future. + + if (!doUpload && modTime > + rParams.mUploadAfterThisTimeInTheFuture) + { + doUpload = true; + BOX_TRACE("Upload decision: " << + filename << ": will upload " + "(mod time in the future)"); + } + } + + if (en != 0 && en->GetModificationTime() == modTime) + { + 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. + 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(!rContext.StorageLimitExceeded()) + { + // 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 errors, but still continue + bool uploadSuccess = false; + try + { + 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. + rNotifier.NotifyFileUploadException( + this, filename, e); + throw; + } + catch(BoxException &e) + { + 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()); + rNotifier.NotifyFileUploadException( + this, filename, e); + } + + // Update structures if the file was uploaded + // successfully. + if(uploadSuccess) + { + fileSynced = true; + + // delete from pending entries + if(pendingFirstSeenTime != 0 && mpPendingEntries != 0) + { + mpPendingEntries->erase(*f); + } + } + } + else + { + 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. + + // Get connection + 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(!rContext.StorageLimitExceeded()) + { + try + { + rNotifier.NotifyFileUploadingAttributes( + this, filename); + + // 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"); + } + } + } + + if(modTime >= rParams.mSyncPeriodEnd) + { + // Allocate? + if(mpPendingEntries == 0) + { + mpPendingEntries = new std::map; + } + // Adding to mPendingEntries list + if(pendingFirstSeenTime == 0) + { + // Haven't seen this before -- add to list! + (*mpPendingEntries)[*f] = modTime; + } + } + + // Zero pointer in rEntriesLeftOver, if we have a pointer to zero + if(en != 0) + { + for(unsigned int l = 0; l < rEntriesLeftOver.size(); ++l) + { + if(rEntriesLeftOver[l] == en) + { + rEntriesLeftOver[l] = 0; + break; + } + } + } + + // Does this file need an entry in the ID map? + if(fileSize >= rParams.mFileTrackingSizeThreshold) + { + // Get the map + 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 ¤tIDMap(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 indicate anything wrong. + // Run the release version for real life use, where this check is not made. + BOX_TRACE("Storing found file ID " << + inodeNum << " (" << filename << + ") in ID map as object " << + objid << " with parent " << + mObjectID); + idMap.AddToMap(inodeNum, objid, + mObjectID /* containing directory */); + } + } + } + + if (fileSynced) + { + rNotifier.NotifyFileSynchronised(this, filename, + fileSize); + } + } + + // Erase contents of files to save space when recursing + rFiles.clear(); + + // Delete the pending entries, if the map is entry + if(mpPendingEntries != 0 && mpPendingEntries->size() == 0) + { + BOX_TRACE("Deleting mpPendingEntries from dir ID " << + BOX_FORMAT_OBJECTID(mObjectID)); + delete mpPendingEntries; + mpPendingEntries = 0; + } + + // Do directories + for(std::vector::const_iterator d = rDirs.begin(); + d != rDirs.end(); ++d) + { + // Send keep-alive message if needed + rContext.DoKeepAlive(); + + // Get the local filename + std::string dirname(MakeFullPath(rLocalPath, *d)); + + // See if it's in the listing (if we have one) + BackupStoreFilenameClear storeFilename(*d); + BackupStoreDirectory::Entry *en = 0; + if(pDirOnStore != 0) + { + DecryptedEntriesMap_t::iterator i(decryptedEntries.find(*d)); + if(i != decryptedEntries.end()) + { + en = i->second; + } + } + + // 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(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 + // 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::iterator + e(mSubDirectories.find(*d)); + + if(e != mSubDirectories.end()) + { + // In the list, just use this pointer + psubDirRecord = e->second; + } + else + { + // Note: if we have exceeded our storage limit, then + // we should not upload any more data, nor create any + // DirectoryRecord representing data that would have + // been uploaded. This step will be repeated when + // there is some space available. + bool doCreateDirectoryRecord = true; + + // Need to create the record. But do we need to create the directory on the server? + int64_t subDirObjectID = 0; + if(en != 0) + { + // No. Exists on the server, and we know about it from the listing. + subDirObjectID = en->GetObjectID(); + } + else if(rContext.StorageLimitExceeded()) + // know we've got a connection if we get this far, + // as dir will have been modified. + { + doCreateDirectoryRecord = false; + } + else + { + // Yes, creation required! + // It is known that the it doesn't exist: + // if pDirOnStore == 0, then the directory has had an initial sync, and hasn't been modified. + // so it has definately been created already. + // if en == 0 but pDirOnStore != 0, well... obviously it doesn't exist. + + // Get attributes + box_time_t attrModTime = 0; + InodeRefType inodeNum = 0; + BackupClientFileAttributes attr; + 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( + 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(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 + 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. + renameDir = true; + } + } + } + } + + // Get connection + 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 + // in the else if(...) above will be correct. + + // Build attribute stream for sending + MemBlockStream attrStream(attr); + + if(renameDir) + { + // Rename the existing directory on the server + connection.QueryMoveObject(renameObjectID, renameInDirectory, mObjectID /* move to this directory */, + BackupProtocolClientMoveObject::Flags_MoveAllWithSameName | BackupProtocolClientMoveObject::Flags_AllowMoveOverDeletedObject, + storeFilename); + + // Put the latest attributes on it + connection.QueryChangeDirAttributes(renameObjectID, attrModTime, attrStream); + + // Stop it being deleted later + BackupClientDeleteList &rdelList( + rContext.GetDeleteList()); + rdelList.StopDirectoryDeletion(renameObjectID); + + // This is the ID for the renamed directory + subDirObjectID = renameObjectID; + } + else + { + // Create a new directory + std::auto_ptr dirCreate(connection.QueryCreateDirectory( + mObjectID, attrModTime, storeFilename, attrStream)); + subDirObjectID = dirCreate->GetObjectID(); + + // Flag as having done this for optimisation later + haveJustCreatedDirOnServer = true; + } + } + + if (doCreateDirectoryRecord) + { + // New an object for this + psubDirRecord = new BackupClientDirectoryRecord(subDirObjectID, *d); + + // Store in list + try + { + mSubDirectories[*d] = psubDirRecord; + } + catch(...) + { + delete psubDirRecord; + psubDirRecord = 0; + throw; + } + } + } + + ASSERT(psubDirRecord != 0 || rContext.StorageLimitExceeded()); + + if(psubDirRecord) + { + // Sync this sub directory too + psubDirRecord->SyncDirectory(rParams, mObjectID, + dirname, rRemotePath + "/" + *d, + haveJustCreatedDirOnServer); + } + + // Zero pointer in rEntriesLeftOver, if we have a pointer to zero + if(en != 0) + { + for(unsigned int l = 0; l < rEntriesLeftOver.size(); ++l) + { + if(rEntriesLeftOver[l] == en) + { + rEntriesLeftOver[l] = 0; + break; + } + } + } + } + + // Delete everything which is on the store, but not on disc + for(unsigned int l = 0; l < rEntriesLeftOver.size(); ++l) + { + if(rEntriesLeftOver[l] != 0) + { + BackupStoreDirectory::Entry *en = rEntriesLeftOver[l]; + + // These entries can't be deleted immediately, as it would prevent + // renaming and moving of objects working properly. So we add them + // 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(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(), + localName); + } + else if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) != 0) + { + // Set as a pending deletion for the directory + rdel.AddDirectoryDelete(en->GetObjectID(), + localName); + + // If there's a directory record for it in + // the sub directory map, delete it now + BackupStoreFilenameClear dirname(en->GetName()); + std::map::iterator e(mSubDirectories.find(dirname.GetClearFilename())); + if(e != mSubDirectories.end()) + { + // Carefully delete the entry from the map + BackupClientDirectoryRecord *rec = e->second; + mSubDirectories.erase(e); + delete rec; + + std::string name = MakeFullPath( + rLocalPath, + dirname.GetClearFilename()); + + BOX_TRACE("Deleted directory record " + "for " << name); + } + } + } + } + + // Return success flag (will be false if some files failed) + return allUpdatedSuccessfully; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::RemoveDirectoryInPlaceOfFile(SyncParams &, BackupStoreDirectory *, int64_t, const std::string &) +// Purpose: Called to resolve difficulties when a directory is found on the +// store where a file is to be uploaded. +// Created: 9/7/04 +// +// -------------------------------------------------------------------------- +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(pEntry->GetObjectID()); + + BackupStoreFilenameClear clear(pEntry->GetName()); + rParams.mrContext.GetProgressNotifier().NotifyDirectoryDeleted( + pEntry->GetObjectID(), clear.GetClearFilename()); + + // Then, delete any directory record + std::map::iterator + e(mSubDirectories.find(rFilename)); + + if(e != mSubDirectories.end()) + { + // A record exists for this, remove it + BackupClientDirectoryRecord *psubDirRecord = e->second; + mSubDirectories.erase(e); + + // And delete the object + delete psubDirRecord; + } +} + + + +// -------------------------------------------------------------------------- +// +// 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 +// 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) +{ + BackupClientContext& rContext(rParams.mrContext); + ProgressNotifier& rNotifier(rContext.GetProgressNotifier()); + + // Get the connection + BackupProtocolClient &connection(rContext.GetConnection()); + + // Info + int64_t objID = 0; + bool doNormalUpload = true; + + // 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) + { + // YES -- try to do diff, if possible + // First, query the server to see if there's an old version available + std::auto_ptr getBlockIndex(connection.QueryGetBlockIndexByName(mObjectID, rStoreFilename)); + int64_t diffFromID = getBlockIndex->GetObjectID(); + + if(diffFromID != 0) + { + // Found an old version + rNotifier.NotifyFileUploadingPatch(this, + rFilename); + + // Get the index + std::auto_ptr blockIndexStream(connection.ReceiveStream()); + + // + // Diff the file + // + + rContext.ManageDiffProcess(); + + bool isCompletelyDifferent = false; + std::auto_ptr patchStream( + BackupStoreFile::EncodeFileDiff( + rFilename.c_str(), + mObjectID, /* containing directory */ + rStoreFilename, diffFromID, *blockIndexStream, + connection.GetTimeout(), + &rContext, // DiffTimer implementation + 0 /* not interested in the modification time */, + &isCompletelyDifferent)); + + rContext.UnManageDiffProcess(); + + // + // Upload the patch to the store + // + std::auto_ptr 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; + } + } + + if(doNormalUpload) + { + // below threshold or nothing to diff from, so upload whole + rNotifier.NotifyFileUploading(this, rFilename); + + // Prepare to upload, getting a stream which will encode the file as we go along + std::auto_ptr upload( + BackupStoreFile::EncodeFile(rFilename.c_str(), + mObjectID, rStoreFilename, NULL, + &rParams, + &(rParams.mrRunStatusProvider))); + + // Send to store + std::auto_ptr stored( + connection.QueryStoreFile( + mObjectID, ModificationTime, + AttributesHash, + 0 /* no diff from file ID */, + rStoreFilename, *upload)); + + // Get object ID from the result + objID = stored->GetObjectID(); + } + } + catch(BoxException &e) + { + rContext.UnManageDiffProcess(); + + 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. + int type, subtype; + if(connection.GetLastError(type, subtype)) + { + if(type == BackupProtocolClientError::ErrorType + && subtype == BackupProtocolClientError::Err_StorageLimitExceeded) + { + // The hard limit was exceeded on the server, notify! + rParams.mrSysadminNotifier.NotifySysadmin( + SysadminNotifier::StoreFull); + // return an error code instead of + // throwing an exception that we + // can't debug. + return 0; + } + rNotifier.NotifyFileUploadServerError(this, + rFilename, type, subtype); + } + } + + // Send the error on it's way + throw; + } + + rNotifier.NotifyFileUploaded(this, rFilename, FileSize); + + // Return the new object ID of this file + return objID; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::SetErrorWhenReadingFilesystemObject(SyncParams &, const char *) +// Purpose: Sets the error state when there were problems reading an object +// from the filesystem. +// Created: 29/3/04 +// +// -------------------------------------------------------------------------- +void BackupClientDirectoryRecord::SetErrorWhenReadingFilesystemObject(BackupClientDirectoryRecord::SyncParams &rParams, const char *Filename) +{ + // Zero hash, so it gets synced properly next time round. + ::memset(mStateChecksum, 0, sizeof(mStateChecksum)); + + // Log the error - already done by caller + /* + rParams.GetProgressNotifier().NotifyFileReadFailed(this, + Filename, strerror(errno)); + */ + + // Mark that an error occured in the parameters object + rParams.mReadErrorsOnFilesystemObjects = true; +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::SyncParams::SyncParams(BackupClientContext &) +// Purpose: Constructor +// Created: 8/3/04 +// +// -------------------------------------------------------------------------- +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), + mrRunStatusProvider(rRunStatusProvider), + mrSysadminNotifier(rSysadminNotifier), + mrProgressNotifier(rProgressNotifier), + mrContext(rContext), + mReadErrorsOnFilesystemObjects(false), + mUploadAfterThisTimeInTheFuture(99999999999999999LL), + mHaveLoggedWarningAboutFutureFileTimes(false) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::SyncParams::~SyncParams() +// Purpose: Destructor +// Created: 8/3/04 +// +// -------------------------------------------------------------------------- +BackupClientDirectoryRecord::SyncParams::~SyncParams() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::Deserialize(Archive & rArchive) +// Purpose: Deserializes this object instance from a stream of bytes, using an Archive abstraction. +// +// Created: 2005/04/11 +// +// -------------------------------------------------------------------------- +void BackupClientDirectoryRecord::Deserialize(Archive & rArchive) +{ + // Make deletion recursive + DeleteSubDirectories(); + + // Delete maps + if(mpPendingEntries != 0) + { + delete mpPendingEntries; + mpPendingEntries = 0; + } + + // + // + // + rArchive.Read(mObjectID); + rArchive.Read(mSubDirName); + rArchive.Read(mInitialSyncDone); + rArchive.Read(mSyncDone); + + // + // + // + int64_t iCount = 0; + rArchive.Read(iCount); + + if (iCount != sizeof(mStateChecksum)/sizeof(mStateChecksum[0])) + { + // we have some kind of internal system representation change: throw for now + THROW_EXCEPTION(CommonException, Internal) + } + + for (int v = 0; v < iCount; v++) + { + // Load each checksum entry + rArchive.Read(mStateChecksum[v]); + } + + // + // + // + iCount = 0; + rArchive.Read(iCount); + + if (iCount > 0) + { + // load each pending entry + mpPendingEntries = new std::map; + if (!mpPendingEntries) + { + throw std::bad_alloc(); + } + + for (int v = 0; v < iCount; v++) + { + std::string strItem; + box_time_t btItem; + + rArchive.Read(strItem); + rArchive.Read(btItem); + (*mpPendingEntries)[strItem] = btItem; + } + } + + // + // + // + iCount = 0; + rArchive.Read(iCount); + + if (iCount > 0) + { + for (int v = 0; v < iCount; v++) + { + std::string strItem; + rArchive.Read(strItem); + + BackupClientDirectoryRecord* pSubDirRecord = + new BackupClientDirectoryRecord(0, ""); + // will be deserialized anyway, give it id 0 for now + + if (!pSubDirRecord) + { + throw std::bad_alloc(); + } + + /***** RECURSE *****/ + pSubDirRecord->Deserialize(rArchive); + mSubDirectories[strItem] = pSubDirRecord; + } + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientDirectoryRecord::Serialize(Archive & rArchive) +// Purpose: Serializes this object instance into a stream of bytes, using an Archive abstraction. +// +// Created: 2005/04/11 +// +// -------------------------------------------------------------------------- +void BackupClientDirectoryRecord::Serialize(Archive & rArchive) const +{ + // + // + // + rArchive.Write(mObjectID); + rArchive.Write(mSubDirName); + rArchive.Write(mInitialSyncDone); + rArchive.Write(mSyncDone); + + // + // + // + int64_t iCount = 0; + + // when reading back the archive, we will + // need to know how many items there are. + iCount = sizeof(mStateChecksum) / sizeof(mStateChecksum[0]); + rArchive.Write(iCount); + + for (int v = 0; v < iCount; v++) + { + rArchive.Write(mStateChecksum[v]); + } + + // + // + // + if (!mpPendingEntries) + { + iCount = 0; + rArchive.Write(iCount); + } + else + { + iCount = mpPendingEntries->size(); + rArchive.Write(iCount); + + for (std::map::const_iterator + i = mpPendingEntries->begin(); + i != mpPendingEntries->end(); i++) + { + rArchive.Write(i->first); + rArchive.Write(i->second); + } + } + // + // + // + iCount = mSubDirectories.size(); + rArchive.Write(iCount); + + for (std::map::const_iterator + i = mSubDirectories.begin(); + i != mSubDirectories.end(); i++) + { + const BackupClientDirectoryRecord* pSubItem = i->second; + ASSERT(pSubItem); + + rArchive.Write(i->first); + pSubItem->Serialize(rArchive); + } +} diff --git a/bin/bbackupd/BackupClientDirectoryRecord.h b/bin/bbackupd/BackupClientDirectoryRecord.h new file mode 100644 index 00000000..fce3fc04 --- /dev/null +++ b/bin/bbackupd/BackupClientDirectoryRecord.h @@ -0,0 +1,171 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupClientDirectoryRecord.h +// Purpose: Implementation of record about directory for backup client +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#ifndef BACKUPCLIENTDIRECTORYRECORD__H +#define BACKUPCLIENTDIRECTORYRECORD__H + +#include +#include + +#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; +class BackupDaemon; + +// -------------------------------------------------------------------------- +// +// Class +// Name: BackupClientDirectoryRecord +// Purpose: Implementation of record about directory for backup client +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +class BackupClientDirectoryRecord +{ +public: + BackupClientDirectoryRecord(int64_t ObjectID, const std::string &rSubDirName); + ~BackupClientDirectoryRecord(); + + void Deserialize(Archive & rArchive); + void Serialize(Archive & rArchive) const; +private: + BackupClientDirectoryRecord(const BackupClientDirectoryRecord &); +public: + + enum + { + UnknownDirectoryID = 0 + }; + + // -------------------------------------------------------------------------- + // + // Class + // Name: BackupClientDirectoryRecord::SyncParams + // Purpose: Holds parameters etc for directory syncing. Not passed as + // const, some parameters may be modified during sync. + // Created: 8/3/04 + // + // -------------------------------------------------------------------------- + class SyncParams : public ReadLoggingStream::Logger + { + public: + SyncParams( + RunStatusProvider &rRunStatusProvider, + SysadminNotifier &rSysadminNotifier, + ProgressNotifier &rProgressNotifier, + BackupClientContext &rContext); + ~SyncParams(); + private: + // No copying + SyncParams(const SyncParams&); + SyncParams &operator=(const SyncParams&); + + public: + // Data members are public, as accessors are not justified here + box_time_t mSyncPeriodStart; + box_time_t mSyncPeriodEnd; + box_time_t mMaxUploadWait; + box_time_t mMaxFileTimeInFuture; + int32_t mFileTrackingSizeThreshold; + int32_t mDiffingUploadSizeThreshold; + RunStatusProvider &mrRunStatusProvider; + SysadminNotifier &mrSysadminNotifier; + ProgressNotifier &mrProgressNotifier; + BackupClientContext &mrContext; + bool mReadErrorsOnFilesystemObjects; + + // Member variables modified by syncing process + 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, + 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, + const std::string &rRemotePath, + BackupStoreDirectory *pDirOnStore, + std::vector &rEntriesLeftOver, + std::vector &rFiles, + const std::vector &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; + std::string mSubDirName; + bool mInitialSyncDone; + bool mSyncDone; + + // Checksum of directory contents and attributes, used to detect changes + uint8_t mStateChecksum[MD5Digest::DigestLength]; + + std::map *mpPendingEntries; + std::map mSubDirectories; + // mpPendingEntries is a pointer rather than simple a member + // 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 new file mode 100644 index 00000000..b9f56c5a --- /dev/null +++ b/bin/bbackupd/BackupClientInodeToIDMap.cpp @@ -0,0 +1,327 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupClientInodeToIDMap.cpp +// Purpose: Map of inode numbers to file IDs on the store +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_DB + // Include db headers and other OS files if they're needed for the disc implementation + #include + #include + #include + #include + #include +#endif + +#define BACKIPCLIENTINODETOIDMAP_IMPLEMENTATION +#include "BackupClientInodeToIDMap.h" + +#include "BackupStoreException.h" + + +#include "MemLeakFindOn.h" + +// What type of Berkeley DB shall we use? +#define TABLE_DATABASE_TYPE DB_HASH + +typedef struct +{ + int64_t mObjectID; + int64_t mInDirectory; +} IDBRecord; + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientInodeToIDMap::BackupClientInodeToIDMap() +// Purpose: Constructor +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +BackupClientInodeToIDMap::BackupClientInodeToIDMap() +#ifndef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION + : mReadOnly(true), + mEmpty(false), + dbp(0) +#endif +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientInodeToIDMap::~BackupClientInodeToIDMap() +// Purpose: Destructor +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +BackupClientInodeToIDMap::~BackupClientInodeToIDMap() +{ +#ifndef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION + if(dbp != 0) + { +#if BDB_VERSION_MAJOR >= 3 + dbp->close(0); +#else + dbp->close(dbp); +#endif + } +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientInodeToIDMap::Open(const char *, bool, bool) +// Purpose: Open the database map, creating a file on disc to store everything +// Created: 20/11/03 +// +// -------------------------------------------------------------------------- +void BackupClientInodeToIDMap::Open(const char *Filename, bool ReadOnly, bool CreateNew) +{ +#ifndef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION + // Correct arguments? + ASSERT(!(CreateNew && ReadOnly)); + + // Correct usage? + ASSERT(dbp == 0); + ASSERT(!mEmpty); + + // Open the database file +#if BDB_VERSION_MAJOR >= 3 + dbp = new Db(0,0); + dbp->set_pagesize(1024); /* Page size: 1K. */ + dbp->set_cachesize(0, 32 * 1024, 0); + dbp->open(NULL, Filename, NULL, DB_HASH, DB_CREATE, 0664); +#else + dbp = dbopen(Filename, (CreateNew?O_CREAT:0) | (ReadOnly?O_RDONLY:O_RDWR), S_IRUSR | S_IWUSR | S_IRGRP, TABLE_DATABASE_TYPE, NULL); +#endif + if(dbp == NULL) + { + THROW_EXCEPTION(BackupStoreException, BerkelyDBFailure); + } + + // Read only flag + mReadOnly = ReadOnly; +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientInodeToIDMap::OpenEmpty() +// Purpose: 'Open' this map. Not associated with a disc file. Useful for when a map +// is required, but is against an empty file on disc which shouldn't be created. +// Implies read only. +// Created: 20/11/03 +// +// -------------------------------------------------------------------------- +void BackupClientInodeToIDMap::OpenEmpty() +{ +#ifndef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION + ASSERT(dbp == 0); + mEmpty = true; + mReadOnly = true; +#endif +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientInodeToIDMap::Close() +// Purpose: Close the database file +// Created: 20/11/03 +// +// -------------------------------------------------------------------------- +void BackupClientInodeToIDMap::Close() +{ +#ifndef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION + if(dbp != 0) + { +#if BDB_VERSION_MAJOR >= 3 + if(dbp->close(0) != 0) +#else + if(dbp->close(dbp) != 0) +#endif + { + THROW_EXCEPTION(BackupStoreException, BerkelyDBFailure); + } + dbp = 0; + } +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientInodeToIDMap::AddToMap(InodeRefType, int64_t, int64_t) +// Purpose: Adds an entry to the map. Overwrites any existing entry. +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +void BackupClientInodeToIDMap::AddToMap(InodeRefType InodeRef, int64_t ObjectID, int64_t InDirectory) +{ +#ifdef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION + mMap[InodeRef] = std::pair(ObjectID, InDirectory); +#else + if(mReadOnly) + { + THROW_EXCEPTION(BackupStoreException, InodeMapIsReadOnly); + } + + if(dbp == 0) + { + THROW_EXCEPTION(BackupStoreException, InodeMapNotOpen); + } + + // Setup structures + IDBRecord rec; + rec.mObjectID = ObjectID; + rec.mInDirectory = InDirectory; + +#if BDB_VERSION_MAJOR >= 3 + Dbt key(&InodeRef, sizeof(InodeRef)); + Dbt data(&rec, sizeof(rec)); + + if (dbp->put(0, &key, &data, 0) != 0) { + THROW_EXCEPTION(BackupStoreException, BerkelyDBFailure); + } +#else + + DBT key; + key.data = &InodeRef; + key.size = sizeof(InodeRef); + + DBT data; + data.data = &rec; + data.size = sizeof(rec); + + // Add to map (or replace existing entry) + if(dbp->put(dbp, &key, &data, 0) != 0) + { + THROW_EXCEPTION(BackupStoreException, BerkelyDBFailure); + } +#endif +#endif +} + +// -------------------------------------------------------------------------- +// +// 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. +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +bool BackupClientInodeToIDMap::Lookup(InodeRefType InodeRef, + int64_t &rObjectIDOut, int64_t &rInDirectoryOut) const +{ +#ifdef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION + std::map >::const_iterator i(mMap.find(InodeRef)); + + // Found? + if(i == mMap.end()) + { + return false; + } + + // Yes. Return the details + rObjectIDOut = i->second.first; + rInDirectoryOut = i->second.second; + return true; +#else + if(mEmpty) + { + // Map is empty + return false; + } + + if(dbp == 0) + { + THROW_EXCEPTION(BackupStoreException, InodeMapNotOpen); + } + +#if BDB_VERSION_MAJOR >= 3 + Dbt key(&InodeRef, sizeof(InodeRef)); + Dbt data(0, 0); + switch(dbp->get(NULL, &key, &data, 0)) +#else + DBT key; + key.data = &InodeRef; + key.size = sizeof(InodeRef); + + DBT data; + data.data = 0; + data.size = 0; + + switch(dbp->get(dbp, &key, &data, 0)) +#endif + + { + case 1: // key not in file + return false; + + case -1: // error + default: // not specified in docs + THROW_EXCEPTION(BackupStoreException, BerkelyDBFailure); + return false; + + case 0: // success, found it + break; + } + + // Check for sensible return +#if BDB_VERSION_MAJOR >= 3 + if(key.get_data() == 0 || data.get_size() != sizeof(IDBRecord)) + { + // Assert in debug version + ASSERT(key.get_data() == 0 || data.get_size() != sizeof(IDBRecord)); + + // Invalid entries mean it wasn't found + return false; + } + + // Data alignment isn't guaranteed to be on a suitable boundary + IDBRecord rec; + + ::memcpy(&rec, data.get_data(), sizeof(rec)); +#else + if(key.data == 0 || data.size != sizeof(IDBRecord)) + { + // Assert in debug version + ASSERT(key.data == 0 || data.size != sizeof(IDBRecord)); + + // Invalid entries mean it wasn't found + return false; + } + + // Data alignment isn't guaranteed to be on a suitable boundary + IDBRecord rec; + + ::memcpy(&rec, data.data, sizeof(rec)); +#endif + + // Return data + rObjectIDOut = rec.mObjectID; + rInDirectoryOut = rec.mInDirectory; + + // Don't have to worry about freeing the returned data + + // Found + return true; +#endif +} + + diff --git a/bin/bbackupd/BackupClientInodeToIDMap.h b/bin/bbackupd/BackupClientInodeToIDMap.h new file mode 100644 index 00000000..1dfef702 --- /dev/null +++ b/bin/bbackupd/BackupClientInodeToIDMap.h @@ -0,0 +1,73 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupClientInodeToIDMap.h +// Purpose: Map of inode numbers to file IDs on the store +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- + +#ifndef BACKUPCLIENTINODETOIDMAP_H +#define BACKUPCLIENTINODETOIDMAP__H + +#include + +#include +#include + +// Use in memory implementation if there isn't access to the Berkely DB on this platform +#ifndef HAVE_DB + #define BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION +#endif + +// avoid having to include the DB files when not necessary +#ifndef BACKIPCLIENTINODETOIDMAP_IMPLEMENTATION +#ifdef BERKELY_V4 + class Db; +#else + class DB; +#endif +#endif + +// -------------------------------------------------------------------------- +// +// Class +// Name: BackupClientInodeToIDMap +// Purpose: Map of inode numbers to file IDs on the store +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +class BackupClientInodeToIDMap +{ +public: + BackupClientInodeToIDMap(); + ~BackupClientInodeToIDMap(); +private: + BackupClientInodeToIDMap(const BackupClientInodeToIDMap &rToCopy); // not allowed +public: + + void Open(const char *Filename, bool ReadOnly, bool CreateNew); + void OpenEmpty(); + + void AddToMap(InodeRefType InodeRef, int64_t ObjectID, int64_t InDirectory); + bool Lookup(InodeRefType InodeRef, int64_t &rObjectIDOut, int64_t &rInDirectoryOut) const; + + void Close(); + +private: +#ifdef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION + std::map > mMap; +#else + bool mReadOnly; + bool mEmpty; +#ifdef BERKELY_V4 + Db *dbp; // c++ style implimentation +#else + DB *dbp; // C style interface, use notation from documentation +#endif // BERKELY_V4 +#endif // BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION +}; + +#endif // BACKUPCLIENTINODETOIDMAP__H + + diff --git a/bin/bbackupd/BackupDaemon.cpp b/bin/bbackupd/BackupDaemon.cpp new file mode 100644 index 00000000..b6f90cad --- /dev/null +++ b/bin/bbackupd/BackupDaemon.cpp @@ -0,0 +1,2883 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupDaemon.cpp +// Purpose: Backup daemon +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#ifdef HAVE_UNISTD_H + #include +#endif +#ifdef HAVE_SIGNAL_H + #include +#endif +#ifdef HAVE_SYS_PARAM_H + #include +#endif +#ifdef HAVE_SYS_WAIT_H + #include +#endif +#ifdef HAVE_SYS_MOUNT_H + #include +#endif +#ifdef HAVE_MNTENT_H + #include +#endif +#ifdef HAVE_SYS_MNTTAB_H + #include + #include +#endif +#ifdef HAVE_PROCESS_H + #include +#endif + +#include + +#include "Configuration.h" +#include "IOStream.h" +#include "MemBlockStream.h" +#include "CommonException.h" +#include "BoxPortsAndFiles.h" + +#include "SSLLib.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 "BackupClientFileAttributes.h" +#include "BackupClientInodeToIDMap.h" +#include "BackupClientMakeExcludeList.h" +#include "BackupDaemon.h" +#include "BackupDaemonConfigVerify.h" +#include "BackupStoreConstants.h" +#include "BackupStoreDirectory.h" +#include "BackupStoreException.h" +#include "BackupStoreFile.h" +#include "BackupStoreFilenameClear.h" +#include "BannerText.h" +#include "Conversion.h" +#include "ExcludeList.h" +#include "FileStream.h" +#include "IOStreamGetLine.h" +#include "LocalProcessStream.h" +#include "Logging.h" +#include "Random.h" +#include "Timer.h" +#include "Utils.h" + +#ifdef WIN32 + #include "Win32ServiceFunctions.h" + #include "Win32BackupService.h" + + extern Win32BackupService* gpDaemonService; +#endif + +#include "MemLeakFindOn.h" + +static const time_t MAX_SLEEP_TIME = 1024; + +// Make the actual sync period have a little bit of extra time, up to a 64th of the main sync period. +// This prevents repetative cycles of load on the server +#define SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY 6 + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::BackupDaemon() +// Purpose: constructor +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +BackupDaemon::BackupDaemon() + : mState(BackupDaemon::State_Initialising), + mDeleteRedundantLocationsAfter(0), + mLastNotifiedEvent(SysadminNotifier::MAX), + mDeleteUnusedRootDirEntriesAfter(0), + 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), + mRunAsService(false), + mServiceName("bbackupd") + #endif +{ + // Only ever one instance of a daemon + SSLLib::Initialise(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::~BackupDaemon() +// Purpose: Destructor +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +BackupDaemon::~BackupDaemon() +{ + DeleteAllLocations(); + DeleteAllIDMaps(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::DaemonName() +// Purpose: Get name of daemon +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +const char *BackupDaemon::DaemonName() const +{ + return "bbackupd"; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::DaemonBanner() +// Purpose: Daemon banner +// Created: 1/1/04 +// +// -------------------------------------------------------------------------- +std::string BackupDaemon::DaemonBanner() const +{ + return BANNER_TEXT("Backup Client"); +} + +void BackupDaemon::Usage() +{ + this->Daemon::Usage(); + +#ifdef WIN32 + std::cout << + " -s Run as a Windows Service, for internal use only\n" + " -i Install Windows Service (you may want to specify a config file)\n" + " -r Remove Windows Service\n" + " -S Service name for -i and -r options\n"; +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::GetConfigVerify() +// Purpose: Get configuration specification +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +const ConfigurationVerify *BackupDaemon::GetConfigVerify() const +{ + // Defined elsewhere + return &BackupDaemonConfigVerify; +} + +#ifdef PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::SetupInInitialProcess() +// Purpose: Platforms with non-checkable credentials on +// local sockets only. +// Prints a warning if the command socket is used. +// Created: 25/2/04 +// +// -------------------------------------------------------------------------- +void BackupDaemon::SetupInInitialProcess() +{ + // Print a warning on this platform if the CommandSocket is used. + 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" + ); + } +} +#endif + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::DeleteAllLocations() +// Purpose: Deletes all records stored +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +void BackupDaemon::DeleteAllLocations() +{ + // Run through, and delete everything + for(std::vector::iterator i = mLocations.begin(); + i != mLocations.end(); ++i) + { + delete *i; + } + + // Clear the contents of the map, so it is empty + mLocations.clear(); + + // And delete everything from the associated mount vector + mIDMapMounts.clear(); +} + +#ifdef WIN32 +std::string BackupDaemon::GetOptionString() +{ + std::string oldOpts = this->Daemon::GetOptionString(); + ASSERT(oldOpts.find("s") == std::string::npos); + ASSERT(oldOpts.find("S") == std::string::npos); + ASSERT(oldOpts.find("i") == std::string::npos); + ASSERT(oldOpts.find("r") == std::string::npos); + return oldOpts + "sS:ir"; +} + +int BackupDaemon::ProcessOption(signed int option) +{ + switch(option) + { + case 's': + { + mRunAsService = true; + return 0; + } + + case 'S': + { + mServiceName = optarg; + Logging::SetProgramName(mServiceName); + return 0; + } + + case 'i': + { + mInstallService = true; + return 0; + } + + case 'r': + { + mRemoveService = true; + return 0; + } + + default: + { + return this->Daemon::ProcessOption(option); + } + } +} + +int BackupDaemon::Main(const std::string &rConfigFileName) +{ + if (mInstallService) + { + return InstallService(rConfigFileName.c_str(), mServiceName); + } + + if (mRemoveService) + { + return RemoveService(mServiceName); + } + + int returnCode; + + if (mRunAsService) + { + // We will be called reentrantly by the Service Control + // Manager, and we had better not call OurService again! + mRunAsService = false; + + BOX_INFO("Box Backup service starting"); + returnCode = OurService(rConfigFileName.c_str()); + BOX_INFO("Box Backup service shut down"); + } + else + { + returnCode = this->Daemon::Main(rConfigFileName); + } + + return returnCode; +} +#endif + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::Run() +// Purpose: Run function for daemon +// Created: 18/2/04 +// +// -------------------------------------------------------------------------- +void BackupDaemon::Run() +{ + // initialise global timer mechanism + Timers::Init(); + + #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 + mapCommandSocketInfo.reset(new CommandSocketInfo); + const char *socketName = + conf.GetKeyValue("CommandSocket").c_str(); + #ifdef WIN32 + mapCommandSocketInfo->mListeningSocket.Listen( + socketName); + #else + ::unlink(socketName); + mapCommandSocketInfo->mListeningSocket.Listen( + Socket::TypeUNIX, socketName); + #endif + } + + // Handle things nicely on exceptions + try + { + Run2(); + } + catch(...) + { + if(mapCommandSocketInfo.get()) + { + try + { + mapCommandSocketInfo.reset(); + } + catch(std::exception &e) + { + BOX_WARNING("Internal error while " + "closing command socket after " + "another exception: " << e.what()); + } + catch(...) + { + BOX_WARNING("Error closing command socket " + "after exception, ignored."); + } + } + + Timers::Cleanup(); + + throw; + } + + // Clean up + mapCommandSocketInfo.reset(); + Timers::Cleanup(); +} + +void BackupDaemon::InitCrypto() +{ + // Read in the certificates creating a TLS context + const Configuration &conf(GetConfiguration()); + std::string certFile(conf.GetKeyValue("CertificateFile")); + std::string keyFile(conf.GetKeyValue("PrivateKeyFile")); + std::string caFile(conf.GetKeyValue("TrustedCAsFile")); + 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()); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::Run2() +// Purpose: Run function for daemon (second stage) +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +void BackupDaemon::Run2() +{ + InitCrypto(); + + const Configuration &conf(GetConfiguration()); + + // How often to connect to the store (approximate) + mUpdateStoreInterval = SecondsToBoxTime( + conf.GetKeyValueInt("UpdateStoreInterval")); + + // But are we connecting automatically? + bool automaticBackup = conf.GetKeyValueBool("AutomaticBackup"); + + // When the next sync should take place -- which is ASAP + mNextSyncTime = 0; + + // When the last sync started (only updated if the store was not full when the sync ended) + mLastSyncTime = 0; + + // -------------------------------------------------------------------------------------------- + + mDeleteStoreObjectInfoFile = DeserializeStoreObjectInfo( + mLastSyncTime, mNextSyncTime); + + // -------------------------------------------------------------------------------------------- + + + // Set state + SetState(State_Idle); + + mDoSyncForcedByPreviousSyncError = false; + + // Loop around doing backups + do + { + // Flags used below + bool storageLimitExceeded = false; + bool doSync = false; + bool mDoSyncForcedByCommand = false; + + // Is a delay necessary? + 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) + { + 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) + { + // A command socket exists, + // so sleep by waiting on it + WaitOnCommandSocket(requiredDelay, + doSync, mDoSyncForcedByCommand); + } + else + { + // No command socket or + // connection, just do a + // normal sleep + time_t sleepSeconds = + BoxTimeToSeconds(requiredDelay); + ::sleep((sleepSeconds <= 0) + ? 1 : sleepSeconds); + } + } + + 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 + mCurrentSyncStartTime = GetCurrentBoxTime(); + if((automaticBackup || mDoSyncForcedByPreviousSyncError) && + mCurrentSyncStartTime >= mNextSyncTime) + { + doSync = true; + } + + // Use a script to see if sync is allowed now? + if(!mDoSyncForcedByCommand && doSync && !StopRun()) + { + int d = UseScriptToSeeIfSyncAllowed(); + if(d > 0) + { + // Script has asked for a delay + mNextSyncTime = GetCurrentBoxTime() + + SecondsToBoxTime(d); + doSync = false; + } + } + + // Ready to sync? (but only if we're not supposed + // to be stopping) + if(doSync && !StopRun()) + { + RunSyncNowWithExceptionHandling(); + } + + // Set state + SetState(storageLimitExceeded?State_StorageLimitExceeded:State_Idle); + + } while(!StopRun()); + + // Make sure we have a clean start next time round (if restart) + DeleteAllLocations(); + DeleteAllIDMaps(); +} + +void BackupDaemon::RunSyncNowWithExceptionHandling() +{ + OnBackupStart(); + + // Do sync + bool errorOccurred = false; + int errorCode = 0, errorSubCode = 0; + const char* errorString = "unknown"; + + 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; + } + + // do not retry immediately without a good reason + mDoSyncForcedByPreviousSyncError = false; + + if(errorOccurred) + { + // Is it a berkely db failure? + bool isBerkelyDbFailure = false; + + if (errorCode == BackupStoreException::ExceptionType + && errorSubCode == BackupStoreException::BerkelyDBFailure) + { + isBerkelyDbFailure = true; + } + + if(isBerkelyDbFailure) + { + // Delete corrupt files + DeleteCorruptBerkelyDbFiles(); + } + + // Clear state data + // Go back to beginning of time + mLastSyncTime = 0; + mClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown; // no store marker, so download everything + DeleteAllLocations(); + DeleteAllIDMaps(); + + // 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); + } + } + // 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; + + OnBackupFinish(); +} + +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; + + const Configuration &conf(GetConfiguration()); + + std::auto_ptr fileLogger; + + 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)); + } + + 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.GetKeyValueUint32("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")); + + // 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; + } + + // Calculate the sync period of files to examine + box_time_t syncPeriodStart = mLastSyncTime; + box_time_t syncPeriodEnd = GetCurrentBoxTime() - minimumFileAge; + + 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(syncPeriodStart >= syncPeriodEnd) + { + BOX_ERROR("Invalid (negative) sync period: " + "perhaps your clock is going " + "backwards (" << syncPeriodStart << + " to " << syncPeriodEnd << ")"); + THROW_EXCEPTION(ClientException, + ClockWentBackwards); + } + + // 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; + + // Setup various timings + int maximumDiffingTime = 600; + int keepAliveTime = 60; + + // 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")); + + // Make sure all the directory records + // are set up + SetupLocations(clientContext, locations); + } + + mpProgressNotifier->NotifyIDMapsSetup(clientContext); + + // Get some ID maps going + SetupIDMapsForSync(); + + // Delete any unused directories? + DeleteUnusedRootDirEntries(clientContext); + + // Go through the records, syncing them + for(std::vector::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, 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 now (if configured). Returns -1 if it's +// allowed, time in seconds to wait otherwise. +// Created: 21/6/04 +// +// -------------------------------------------------------------------------- +int BackupDaemon::UseScriptToSeeIfSyncAllowed() +{ + const Configuration &conf(GetConfiguration()); + + // Got a script to run? + if(!conf.KeyExists("SyncAllowScript")) + { + // No. Do sync. + return -1; + } + + // If there's no result, try again in five minutes + int waitInSeconds = (60*5); + + std::string script(conf.GetKeyValue("SyncAllowScript") + + " \"" + GetConfigFileName() + "\""); + + // Run it? + pid_t pid = 0; + try + { + std::auto_ptr pscript(LocalProcessStream(script, + pid)); + + // Read in the result + IOStreamGetLine getLine(*pscript); + std::string line; + if(getLine.GetLine(line, true, 30000)) // 30 seconds should be enough + { + // Got a string, interpret + if(line == "now") + { + // Script says do it now. Obey. + waitInSeconds = -1; + } + else + { + try + { + // How many seconds to wait? + waitInSeconds = BoxConvert::Convert(line); + } + catch(ConversionException &e) + { + BOX_ERROR("Invalid output from " + "SyncAllowScript: '" << + line << "' (" << script << ")"); + throw; + } + + BOX_NOTICE("Delaying sync by " << waitInSeconds + << " seconds due to SyncAllowScript " + << "(" << script << ")"); + } + } + + } + catch(std::exception &e) + { + BOX_ERROR("Internal error running SyncAllowScript: " + << e.what() << " (" << script << ")"); + } + catch(...) + { + // Ignore any exceptions + // Log that something bad happened + BOX_ERROR("Unknown error running SyncAllowScript (" << + script << ")"); + } + + // Wait and then cleanup child process, if any + if(pid != 0) + { + int status = 0; + ::waitpid(pid, &status, 0); + } + + return waitInSeconds; +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::WaitOnCommandSocket(box_time_t, bool &, bool &) +// Purpose: Waits on a the command socket for a time of UP TO the required time +// but may be much less, and handles a command if necessary. +// Created: 18/2/04 +// +// -------------------------------------------------------------------------- +void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFlagOut, bool &SyncIsForcedOut) +{ + ASSERT(mapCommandSocketInfo.get()); + if(!mapCommandSocketInfo.get()) + { + // failure case isn't too bad + ::sleep(1); + return; + } + + BOX_TRACE("Wait on command socket, delay = " << RequiredDelay); + + try + { + // Timeout value for connections and things + int timeout = ((int)BoxTimeToMilliSeconds(RequiredDelay)) + 1; + // Handle bad boundary cases + if(timeout <= 0) timeout = 1; + if(timeout == INFTIM) timeout = 100000; + + // Wait for socket connection, or handle a command? + if(mapCommandSocketInfo->mpConnectedSocket.get() == 0) + { + // No connection, listen for a new one + mapCommandSocketInfo->mpConnectedSocket.reset(mapCommandSocketInfo->mListeningSocket.Accept(timeout).release()); + + 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. + return; + } + else + { +#ifdef PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET + bool uidOK = true; + BOX_WARNING("On this platform, no security check can be made on the credentials of peers connecting to the command socket. (bbackupctl)"); +#else + // Security check -- does the process connecting to this socket have + // the same UID as this process? + bool uidOK = false; + // BLOCK + { + uid_t remoteEUID = 0xffff; + gid_t remoteEGID = 0xffff; + if(mapCommandSocketInfo->mpConnectedSocket->GetPeerCredentials(remoteEUID, remoteEGID)) + { + // Credentials are available -- check UID + if(remoteEUID == ::getuid()) + { + // Acceptable + uidOK = true; + } + } + } +#endif // PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET + + // Is this an acceptable connection? + if(!uidOK) + { + // Dump the connection + BOX_ERROR("Incoming command connection from peer had different user ID than this process, or security check could not be completed."); + mapCommandSocketInfo->mpConnectedSocket.reset(); + return; + } + else + { + // Log + BOX_INFO("Connection from command socket"); + + // Send a header line summarising the configuration and current state + const Configuration &conf(GetConfiguration()); + char summary[256]; + int summarySize = sprintf(summary, "bbackupd: %d %d %d %d\nstate %d\n", + conf.GetKeyValueBool("AutomaticBackup"), + conf.GetKeyValueInt("UpdateStoreInterval"), + conf.GetKeyValueInt("MinimumFileAge"), + conf.GetKeyValueInt("MaxUploadWait"), + mState); + 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 + timeout = 10; // milliseconds + } + } + } + + // So there must be a connection now. + ASSERT(mapCommandSocketInfo->mpConnectedSocket.get() != 0); + + // Is there a getline object ready? + if(mapCommandSocketInfo->mpGetLine == 0) + { + // Create a new one + mapCommandSocketInfo->mpGetLine = new IOStreamGetLine(*(mapCommandSocketInfo->mpConnectedSocket.get())); + } + + // Ping the remote side, to provide errors which will mean the socket gets closed + mapCommandSocketInfo->mpConnectedSocket->Write("ping\n", 5); + + // Wait for a command or something on the socket + std::string command; + while(mapCommandSocketInfo->mpGetLine != 0 && !mapCommandSocketInfo->mpGetLine->IsEOF() + && mapCommandSocketInfo->mpGetLine->GetLine(command, false /* no preprocessing */, timeout)) + { + BOX_TRACE("Receiving command '" << command + << "' over command socket"); + + bool sendOK = false; + bool sendResponse = true; + + // Command to process! + if(command == "quit" || command == "") + { + // Close the socket. + CloseCommandConnection(); + sendResponse = false; + } + else if(command == "sync") + { + // Sync now! + DoSyncFlagOut = true; + SyncIsForcedOut = false; + sendOK = true; + } + else if(command == "force-sync") + { + // Sync now (forced -- overrides any SyncAllowScript) + DoSyncFlagOut = true; + SyncIsForcedOut = true; + sendOK = true; + } + else if(command == "reload") + { + // Reload the configuration + SetReloadConfigWanted(); + sendOK = true; + } + else if(command == "terminate") + { + // Terminate the daemon cleanly + SetTerminateWanted(); + sendOK = true; + } + + // Send a response back? + if(sendResponse) + { + 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 + timeout = 1; + } + + // Close on EOF? + 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("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 + } + else + { + // Close socket and ignore error + CloseCommandConnection(); + } + } + catch(...) + { + 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 + } + else + { + // Close socket and ignore error + CloseCommandConnection(); + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::CloseCommandConnection() +// Purpose: Close the command connection, ignoring any errors +// Created: 18/2/04 +// +// -------------------------------------------------------------------------- +void BackupDaemon::CloseCommandConnection() +{ + try + { + BOX_TRACE("Closing command connection"); + + if(mapCommandSocketInfo->mpGetLine) + { + delete mapCommandSocketInfo->mpGetLine; + mapCommandSocketInfo->mpGetLine = 0; + } + mapCommandSocketInfo->mpConnectedSocket.reset(); + } + catch(std::exception &e) + { + BOX_ERROR("Internal error while closing command " + "socket: " << e.what()); + } + catch(...) + { + // Ignore any errors + } +} + + +// -------------------------------------------------------------------------- +// +// File +// Name: BackupDaemon.cpp +// Purpose: Send a start or finish sync message to the command socket, if it's connected. +// +// Created: 18/2/04 +// +// -------------------------------------------------------------------------- +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(mapCommandSocketInfo.get() && + mapCommandSocketInfo->mpConnectedSocket.get() != 0) + { + std::string message = SendStart ? "start-sync" : "finish-sync"; + try + { + message += "\n"; + mapCommandSocketInfo->mpConnectedSocket->Write( + message.c_str(), message.size()); + } + catch(std::exception &e) + { + BOX_ERROR("Internal error while sending to " + "command socket client: " << e.what()); + CloseCommandConnection(); + } + catch(...) + { + CloseCommandConnection(); + } + } +} + + + + +#if !defined(HAVE_STRUCT_STATFS_F_MNTONNAME) && !defined(HAVE_STRUCT_STATVFS_F_NMTONNAME) + // string comparison ordering for when mount points are handled + // by code, rather than the OS. + typedef struct + { + bool operator()(const std::string &s1, const std::string &s2) + { + if(s1.size() == s2.size()) + { + // Equal size, sort according to natural sort order + return s1 < s2; + } + else + { + // Make sure longer strings go first + return s1.size() > s2.size(); + } + } + } mntLenCompare; +#endif + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::SetupLocations(BackupClientContext &, const Configuration &) +// Purpose: Makes sure that the list of directories records is correctly set up +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Configuration &rLocationsConf) +{ + if(!mLocations.empty()) + { + // Looks correctly set up + return; + } + + // Make sure that if a directory is reinstated, then it doesn't get deleted + mDeleteUnusedRootDirEntriesAfter = 0; + mUnusedRootDirEntries.clear(); + + // 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. + BackupProtocolClient &connection(rClientContext.GetConnection()); + + // Ask server for a list of everything in the root directory, + // which is a directory itself + std::auto_ptr dirreply( + connection.QueryListDirectory( + BackupProtocolClientListDirectory::RootDirectory, + // 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 + BackupStoreDirectory dir; + std::auto_ptr dirstream(connection.ReceiveStream()); + dir.ReadFromStream(*dirstream, connection.GetTimeout()); + + // Map of mount names to ID map index + std::map mounts; + int numIDMaps = 0; + +#ifdef HAVE_MOUNTS +#if !defined(HAVE_STRUCT_STATFS_F_MNTONNAME) && !defined(HAVE_STRUCT_STATVFS_F_MNTONNAME) + // Linux and others can't tell you where a directory is mounted. So we + // have to read the mount entries from /etc/mtab! Bizarre that the OS + // itself can't tell you, but there you go. + std::set mountPoints; + // BLOCK + FILE *mountPointsFile = 0; + +#ifdef HAVE_STRUCT_MNTENT_MNT_DIR + // Open mounts file + mountPointsFile = ::setmntent("/proc/mounts", "r"); + if(mountPointsFile == 0) + { + mountPointsFile = ::setmntent("/etc/mtab", "r"); + } + if(mountPointsFile == 0) + { + THROW_EXCEPTION(CommonException, OSFileError); + } + + try + { + // Read all the entries, and put them in the set + struct mntent *entry = 0; + while((entry = ::getmntent(mountPointsFile)) != 0) + { + BOX_TRACE("Found mount point at " << entry->mnt_dir); + mountPoints.insert(std::string(entry->mnt_dir)); + } + + // Close mounts file + ::endmntent(mountPointsFile); + } + catch(...) + { + ::endmntent(mountPointsFile); + throw; + } +#else // ! HAVE_STRUCT_MNTENT_MNT_DIR + // Open mounts file + mountPointsFile = ::fopen("/etc/mnttab", "r"); + if(mountPointsFile == 0) + { + THROW_EXCEPTION(CommonException, OSFileError); + } + + try + { + // Read all the entries, and put them in the set + struct mnttab entry; + while(getmntent(mountPointsFile, &entry) == 0) + { + BOX_TRACE("Found mount point at " << entry.mnt_mountp); + mountPoints.insert(std::string(entry.mnt_mountp)); + } + + // Close mounts file + ::fclose(mountPointsFile); + } + catch(...) + { + ::fclose(mountPointsFile); + throw; + } +#endif // HAVE_STRUCT_MNTENT_MNT_DIR + // Check sorting and that things are as we expect + ASSERT(mountPoints.size() > 0); +#ifndef BOX_RELEASE_BUILD + { + std::set::reverse_iterator i(mountPoints.rbegin()); + ASSERT(*i == "/"); + } +#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. + std::vector locNames = + rLocationsConf.GetSubConfigurationNames(); + + for(std::vector::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 apLoc(new Location); + + try + { + // Setup names in the location record + apLoc->mName = *pLocName; + apLoc->mPath = rConfig.GetKeyValue("Path"); + + // Read the exclude lists from the Configuration + 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 + // to stat the local directory, we still don't + // consider to remote one for deletion. + BackupStoreDirectory::Iterator iter(dir); + BackupStoreFilenameClear dirname(apLoc->mName); // generate the filename + BackupStoreDirectory::Entry *en = iter.FindMatchingClearName(dirname); + int64_t oid = 0; + if(en != 0) + { + oid = en->GetObjectID(); + + // Delete the entry from the directory, so we get a list of + // unused root directories at the end of this. + dir.DeleteEntry(oid); + } + + // Do a fsstat on the pathname to find out which mount it's on + { + +#if defined HAVE_STRUCT_STATFS_F_MNTONNAME || defined HAVE_STRUCT_STATVFS_F_MNTONNAME || defined WIN32 + + // BSD style statfs -- includes mount point, which is nice. +#ifdef HAVE_STRUCT_STATVFS_F_MNTONNAME + struct statvfs s; + if(::statvfs(apLoc->mPath.c_str(), &s) != 0) +#else // HAVE_STRUCT_STATVFS_F_MNTONNAME + struct statfs s; + if(::statfs(apLoc->mPath.c_str(), &s) != 0) +#endif // HAVE_STRUCT_STATVFS_F_MNTONNAME + { + BOX_LOG_SYS_WARNING("Failed to stat location " + "path '" << apLoc->mPath << + "', skipping location '" << + apLoc->mName << "'"); + continue; + } + + // Where the filesystem is mounted + std::string mountName(s.f_mntonname); + +#else // !HAVE_STRUCT_STATFS_F_MNTONNAME && !WIN32 + + // Warn in logs if the directory isn't absolute + if(apLoc->mPath[0] != '/') + { + BOX_WARNING("Location path '" + << apLoc->mPath + << "' is not absolute"); + } + // Go through the mount points found, and find a suitable one + std::string mountName("/"); + { + std::set::const_iterator i(mountPoints.begin()); + BOX_TRACE(mountPoints.size() + << " potential mount points"); + for(; i != mountPoints.end(); ++i) + { + // Compare first n characters with the filename + // If it matches, the file belongs in that mount point + // (sorting order ensures this) + BOX_TRACE("checking against mount point " << *i); + if(::strncmp(i->c_str(), apLoc->mPath.c_str(), i->size()) == 0) + { + // Match + mountName = *i; + break; + } + } + BOX_TRACE("mount point chosen for " + << apLoc->mPath << " is " + << mountName); + } + +#endif + + // Got it? + std::map::iterator f(mounts.find(mountName)); + if(f != mounts.end()) + { + // Yes -- store the index + apLoc->mIDMapIndex = f->second; + } + else + { + // No -- new index + apLoc->mIDMapIndex = numIDMaps; + mounts[mountName] = numIDMaps; + + // Store the mount name + mIDMapMounts.push_back(mountName); + + // Increment number of maps + ++numIDMaps; + } + } + + // Does this exist on the server? + if(en == 0) + { + // Doesn't exist, so it has to be created on the server. Let's go! + // First, get the directory's attributes and modification time + box_time_t attrModTime = 0; + BackupClientFileAttributes attr; + try + { + attr.ReadAttributes(apLoc->mPath.c_str(), + true /* directories have zero mod times */, + 0 /* not interested in mod time */, + &attrModTime /* get the attribute modification time */); + } + catch (BoxException &e) + { + BOX_ERROR("Failed to get attributes " + "for path '" << apLoc->mPath + << "', skipping location '" << + apLoc->mName << "'"); + continue; + } + + // Execute create directory command + try + { + MemBlockStream attrStream(attr); + std::auto_ptr + dirCreate(connection.QueryCreateDirectory( + BackupProtocolClientListDirectory::RootDirectory, + attrModTime, dirname, attrStream)); + + // Object ID for later creation + oid = dirCreate->GetObjectID(); + } + catch (BoxException &e) + { + BOX_ERROR("Failed to create remote " + "directory '/" << apLoc->mName << + "', skipping location '" << + apLoc->mName << "'"); + continue; + } + + } + + // Create and store the directory object for the root of this location + ASSERT(oid != 0); + BackupClientDirectoryRecord *precord = + new BackupClientDirectoryRecord(oid, *pLocName); + apLoc->mpDirectoryRecord.reset(precord); + + // Push it back on the vector of locations + mLocations.push_back(apLoc.release()); + } + catch (std::exception &e) + { + BOX_ERROR("Failed to configure location '" + << apLoc->mName << "' path '" + << apLoc->mPath << "': " << e.what() << + ": please check for previous errors"); + throw; + } + catch(...) + { + BOX_ERROR("Failed to configure location '" + << apLoc->mName << "' path '" + << apLoc->mPath << "': please check for " + "previous errors"); + throw; + } + } + + // Any entries in the root directory which need deleting? + if(dir.GetNumberOfEntries() > 0 && + mDeleteRedundantLocationsAfter == 0) + { + BOX_NOTICE(dir.GetNumberOfEntries() << " redundant locations " + "in root directory found, but will not delete because " + "DeleteRedundantLocationsAfter = 0"); + } + else if(dir.GetNumberOfEntries() > 0) + { + box_time_t now = GetCurrentBoxTime(); + + // This should reset the timer if the list of unused + // locations changes, but it will not if the number of + // unused locations does not change, but the locations + // do change, e.g. one mysteriously appears and another + // mysteriously appears. (FIXME) + if (dir.GetNumberOfEntries() != mUnusedRootDirEntries.size() || + mDeleteUnusedRootDirEntriesAfter == 0) + { + mDeleteUnusedRootDirEntriesAfter = now + + SecondsToBoxTime(mDeleteRedundantLocationsAfter); + } + + int secs = BoxTimeToSeconds(mDeleteUnusedRootDirEntriesAfter + - now); + + BOX_NOTICE(dir.GetNumberOfEntries() << " redundant locations " + "in root directory found, will delete from store " + "after " << secs << " seconds."); + + // Store directories in list of things to delete + mUnusedRootDirEntries.clear(); + BackupStoreDirectory::Iterator iter(dir); + BackupStoreDirectory::Entry *en = 0; + while((en = iter.Next()) != 0) + { + // Add name to list + BackupStoreFilenameClear clear(en->GetName()); + const std::string &name(clear.GetClearFilename()); + mUnusedRootDirEntries.push_back( + std::pair + (en->GetObjectID(), name)); + // Log this + BOX_INFO("Unused location in root: " << name); + } + ASSERT(mUnusedRootDirEntries.size() > 0); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::SetupIDMapsForSync() +// Purpose: Sets up ID maps for the sync process -- make sure they're all there +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +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. + +#ifdef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION + + // Make sure we have some blank, empty ID maps + 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) + if(mCurrentIDMaps.empty()) + { + FillIDMapVector(mCurrentIDMaps, false /* current maps */); + } + +#else + + // Make sure we have some blank, empty ID maps + DeleteIDMapVector(mNewIDMaps); + FillIDMapVector(mNewIDMaps, true /* new maps */); + DeleteIDMapVector(mCurrentIDMaps); + FillIDMapVector(mCurrentIDMaps, false /* new maps */); + +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::FillIDMapVector(std::vector &) +// Purpose: Fills the vector with the right number of empty ID maps +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +void BackupDaemon::FillIDMapVector(std::vector &rVector, bool NewMaps) +{ + ASSERT(rVector.size() == 0); + rVector.reserve(mIDMapMounts.size()); + + for(unsigned int l = 0; l < mIDMapMounts.size(); ++l) + { + // Create the object + BackupClientInodeToIDMap *pmap = new BackupClientInodeToIDMap(); + try + { + // Get the base filename of this map + std::string filename; + MakeMapBaseName(l, filename); + + // If it's a new one, add a suffix + if(NewMaps) + { + filename += ".n"; + } + + // If it's not a new map, it may not exist in which case an empty map should be created + if(!NewMaps && !FileExists(filename.c_str())) + { + pmap->OpenEmpty(); + } + else + { + // Open the map + pmap->Open(filename.c_str(), !NewMaps /* read only */, NewMaps /* create new */); + } + + // Store on vector + rVector.push_back(pmap); + } + catch(...) + { + delete pmap; + throw; + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::DeleteCorruptBerkelyDbFiles() +// Purpose: Delete the Berkely db files from disc after they have been corrupted. +// Created: 14/9/04 +// +// -------------------------------------------------------------------------- +void BackupDaemon::DeleteCorruptBerkelyDbFiles() +{ + for(unsigned int l = 0; l < mIDMapMounts.size(); ++l) + { + // Get the base filename of this map + std::string filename; + MakeMapBaseName(l, filename); + + // Delete the file + BOX_TRACE("Deleting " << filename); + ::unlink(filename.c_str()); + + // Add a suffix for the new map + filename += ".n"; + + // Delete that too + BOX_TRACE("Deleting " << filename); + ::unlink(filename.c_str()); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: MakeMapBaseName(unsigned int, std::string &) +// Purpose: Makes the base name for a inode map +// Created: 20/11/03 +// +// -------------------------------------------------------------------------- +void BackupDaemon::MakeMapBaseName(unsigned int MountNumber, std::string &rNameOut) const +{ + // Get the directory for the maps + const Configuration &config(GetConfiguration()); + std::string dir(config.GetKeyValue("DataDirectory")); + + // Make a leafname + std::string leaf(mIDMapMounts[MountNumber]); + for(unsigned int z = 0; z < leaf.size(); ++z) + { + if(leaf[z] == DIRECTORY_SEPARATOR_ASCHAR) + { + leaf[z] = '_'; + } + } + + // Build the final filename + rNameOut = dir + DIRECTORY_SEPARATOR "mnt" + leaf; +} + + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::CommitIDMapsAfterSync() +// Purpose: Commits the new ID maps, so the 'new' maps are now the 'current' maps. +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +void BackupDaemon::CommitIDMapsAfterSync() +{ + // 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 + // Remove the current ID maps + DeleteIDMapVector(mCurrentIDMaps); + + // Copy the (pointers to) "new" maps over to be the new "current" maps + mCurrentIDMaps = mNewIDMaps; + + // Clear the new ID maps vector (not delete them!) + mNewIDMaps.clear(); + +#else + + // Get rid of the maps in memory (leaving them on disc of course) + DeleteIDMapVector(mCurrentIDMaps); + DeleteIDMapVector(mNewIDMaps); + + // Then move the old maps into the new places + for(unsigned int l = 0; l < mIDMapMounts.size(); ++l) + { + std::string target; + MakeMapBaseName(l, target); + std::string newmap(target + ".n"); + + // Try to rename +#ifdef WIN32 + // win32 rename doesn't overwrite existing files + ::remove(target.c_str()); +#endif + if(::rename(newmap.c_str(), target.c_str()) != 0) + { + BOX_LOG_SYS_ERROR("Failed to rename ID map: " << + newmap << " to " << target); + THROW_EXCEPTION(CommonException, OSFileError) + } + } + +#endif +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::DeleteIDMapVector(std::vector &) +// Purpose: Deletes the contents of a vector of ID maps +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +void BackupDaemon::DeleteIDMapVector(std::vector &rVector) +{ + while(!rVector.empty()) + { + // Pop off list + BackupClientInodeToIDMap *toDel = rVector.back(); + rVector.pop_back(); + + // Close and delete + toDel->Close(); + delete toDel; + } + ASSERT(rVector.size() == 0); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::FindLocationPathName(const std::string &, std::string &) const +// Purpose: Tries to find the path of the root of a backup location. Returns true (and path in rPathOut) +// if it can be found, false otherwise. +// Created: 12/11/03 +// +// -------------------------------------------------------------------------- +bool BackupDaemon::FindLocationPathName(const std::string &rLocationName, std::string &rPathOut) const +{ + // Search for the location + for(std::vector::const_iterator i(mLocations.begin()); i != mLocations.end(); ++i) + { + if((*i)->mName == rLocationName) + { + rPathOut = (*i)->mPath; + return true; + } + } + + // Didn't find it + return false; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::SetState(int) +// Purpose: Record current action of daemon, and update process title to reflect this +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- +void BackupDaemon::SetState(int State) +{ + // Two little checks + if(State == mState) return; + if(State < 0) return; + + // Update + mState = State; + + // Set process title + const static char *stateText[] = {"idle", "connected", "error -- waiting for retry", "over limit on server -- not backing up"}; + SetProcessTitle(stateText[State]); + + // If there's a command socket connected, then inform it -- disconnecting from the + // command socket if there's an error + + char newState[64]; + sprintf(newState, "state %d", State); + std::string message = newState; + + message += "\n"; + + if(!mapCommandSocketInfo.get()) + { + return; + } + + if(mapCommandSocketInfo->mpConnectedSocket.get() == 0) + { + return; + } + + // Something connected to the command socket, tell it about the new state + try + { + 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("Failed to write state to command socket: " << + e.what()); + CloseCommandConnection(); + } + catch(...) + { + BOX_ERROR("Failed to write state to command socket: " + "unknown error"); + CloseCommandConnection(); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::TouchFileInWorkingDir(const char *) +// Purpose: Make sure a zero length file of the name exists in the working directory. +// Use for marking times of events in the filesystem. +// Created: 21/2/04 +// +// -------------------------------------------------------------------------- +void BackupDaemon::TouchFileInWorkingDir(const char *Filename) +{ + // Filename + const Configuration &config(GetConfiguration()); + std::string fn(config.GetKeyValue("DataDirectory") + DIRECTORY_SEPARATOR_ASCHAR); + fn += Filename; + + // Open and close it to update the timestamp + FileStream touch(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::NotifySysadmin(int) +// Purpose: Run the script to tell the sysadmin about events +// which need attention. +// Created: 25/2/04 +// +// -------------------------------------------------------------------------- +void BackupDaemon::NotifySysadmin(SysadminNotifier::EventCode Event) +{ + static const char *sEventNames[] = + { + "store-full", + "read-error", + "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)) == SysadminNotifier::MAX + 1); + + if(Event < 0 || Event >= SysadminNotifier::MAX) + { + BOX_ERROR("BackupDaemon::NotifySysadmin() called for " + "invalid event code " << Event); + THROW_EXCEPTION(BackupStoreException, + BadNotifySysadminEventCode); + } + + BOX_TRACE("BackupDaemon::NotifySysadmin() called, event = " << + sEventNames[Event]); + + if(!GetConfiguration().KeyExists("NotifyAlways") || + !GetConfiguration().GetKeyValueBool("NotifyAlways")) + { + // 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? + const Configuration &conf(GetConfiguration()); + if(!conf.KeyExists("NotifyScript")) + { + // Log, and then return + if(Event != SysadminNotifier::BackupStart && + Event != SysadminNotifier::BackupFinish) + { + BOX_INFO("Not notifying administrator about event " + << sEventNames[Event] << ", set NotifyScript " + "to do this in future"); + } + return; + } + + // Script to run + std::string script(conf.GetKeyValue("NotifyScript") + " " + + sEventNames[Event] + " \"" + GetConfigFileName() + "\""); + + // Log what we're about to do + BOX_INFO("About to notify administrator about event " + << sEventNames[Event] << ", running script '" << script << "'"); + + // Then do it + int returnCode = ::system(script.c_str()); + if(returnCode != 0) + { + BOX_WARNING("Notify script returned error code: " << + returnCode << " (" << script << ")"); + } + else if(Event != SysadminNotifier::BackupStart && + Event != SysadminNotifier::BackupFinish) + { + mLastNotifiedEvent = Event; + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::DeleteUnusedRootDirEntries(BackupClientContext &) +// Purpose: Deletes any unused entries in the root directory, if they're scheduled to be deleted. +// Created: 13/5/04 +// +// -------------------------------------------------------------------------- +void BackupDaemon::DeleteUnusedRootDirEntries(BackupClientContext &rContext) +{ + if(mUnusedRootDirEntries.empty()) + { + BOX_INFO("Not deleting unused entries - none in list"); + return; + } + + if(mDeleteUnusedRootDirEntriesAfter == 0) + { + BOX_INFO("Not deleting unused entries - " + "zero delete time (bad)"); + return; + } + + // Check time + box_time_t now = GetCurrentBoxTime(); + if(now < mDeleteUnusedRootDirEntriesAfter) + { + int secs = BoxTimeToSeconds(mDeleteUnusedRootDirEntriesAfter + - now); + BOX_INFO("Not deleting unused entries - too early (" + << secs << " seconds remaining)"); + return; + } + + // 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 >::iterator + i(mUnusedRootDirEntries.begin()); + i != mUnusedRootDirEntries.end(); ++i) + { + connection.QueryDeleteDirectory(i->first); + rContext.GetProgressNotifier().NotifyFileDeleted( + i->first, i->second); + } + + // Reset state + mDeleteUnusedRootDirEntriesAfter = 0; + mUnusedRootDirEntries.clear(); +} + +// -------------------------------------------------------------------------- + +typedef struct +{ + int32_t mMagicValue; // also the version number + int32_t mNumEntries; + int64_t mObjectID; // this object ID + int64_t mContainerID; // ID of container + uint64_t mAttributesModTime; + int32_t mOptionsPresent; // bit mask of optional sections / features present + +} loc_StreamFormat; + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::Location::Location() +// Purpose: Constructor +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +BackupDaemon::Location::Location() + : mIDMapIndex(0), + mpExcludeFiles(0), + mpExcludeDirs(0) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::Location::~Location() +// Purpose: Destructor +// Created: 11/11/03 +// +// -------------------------------------------------------------------------- +BackupDaemon::Location::~Location() +{ + // Clean up exclude locations + if(mpExcludeDirs != 0) + { + delete mpExcludeDirs; + mpExcludeDirs = 0; + } + if(mpExcludeFiles != 0) + { + delete mpExcludeFiles; + mpExcludeFiles = 0; + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::Location::Deserialize(Archive & rArchive) +// Purpose: Deserializes this object instance from a stream of bytes, using an Archive abstraction. +// +// Created: 2005/04/11 +// +// -------------------------------------------------------------------------- +void BackupDaemon::Location::Deserialize(Archive &rArchive) +{ + // + // + // + mpDirectoryRecord.reset(NULL); + if(mpExcludeFiles) + { + delete mpExcludeFiles; + mpExcludeFiles = NULL; + } + if(mpExcludeDirs) + { + delete mpExcludeDirs; + mpExcludeDirs = NULL; + } + + // + // + // + rArchive.Read(mName); + rArchive.Read(mPath); + rArchive.Read(mIDMapIndex); + + // + // + // + int64_t aMagicMarker = 0; + rArchive.Read(aMagicMarker); + + if(aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP) + { + // NOOP + } + else if(aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE) + { + BackupClientDirectoryRecord *pSubRecord = new BackupClientDirectoryRecord(0, ""); + if(!pSubRecord) + { + throw std::bad_alloc(); + } + + mpDirectoryRecord.reset(pSubRecord); + mpDirectoryRecord->Deserialize(rArchive); + } + else + { + // there is something going on here + THROW_EXCEPTION(ClientException, CorruptStoreObjectInfoFile); + } + + // + // + // + rArchive.Read(aMagicMarker); + + if(aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP) + { + // NOOP + } + else if(aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE) + { + mpExcludeFiles = new ExcludeList; + if(!mpExcludeFiles) + { + throw std::bad_alloc(); + } + + mpExcludeFiles->Deserialize(rArchive); + } + else + { + // there is something going on here + THROW_EXCEPTION(ClientException, CorruptStoreObjectInfoFile); + } + + // + // + // + rArchive.Read(aMagicMarker); + + if(aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP) + { + // NOOP + } + else if(aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE) + { + mpExcludeDirs = new ExcludeList; + if(!mpExcludeDirs) + { + throw std::bad_alloc(); + } + + mpExcludeDirs->Deserialize(rArchive); + } + else + { + // there is something going on here + THROW_EXCEPTION(ClientException, CorruptStoreObjectInfoFile); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::Location::Serialize(Archive & rArchive) +// Purpose: Serializes this object instance into a stream of bytes, using an Archive abstraction. +// +// Created: 2005/04/11 +// +// -------------------------------------------------------------------------- +void BackupDaemon::Location::Serialize(Archive & rArchive) const +{ + // + // + // + rArchive.Write(mName); + rArchive.Write(mPath); + rArchive.Write(mIDMapIndex); + + // + // + // + if(mpDirectoryRecord.get() == NULL) + { + int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP; + rArchive.Write(aMagicMarker); + } + else + { + int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows + rArchive.Write(aMagicMarker); + + mpDirectoryRecord->Serialize(rArchive); + } + + // + // + // + if(!mpExcludeFiles) + { + int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP; + rArchive.Write(aMagicMarker); + } + else + { + int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows + rArchive.Write(aMagicMarker); + + mpExcludeFiles->Serialize(rArchive); + } + + // + // + // + if(!mpExcludeDirs) + { + int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP; + rArchive.Write(aMagicMarker); + } + else + { + int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows + rArchive.Write(aMagicMarker); + + mpExcludeDirs->Serialize(rArchive); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::CommandSocketInfo::CommandSocketInfo() +// Purpose: Constructor +// Created: 18/2/04 +// +// -------------------------------------------------------------------------- +BackupDaemon::CommandSocketInfo::CommandSocketInfo() + : mpGetLine(0) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::CommandSocketInfo::~CommandSocketInfo() +// Purpose: Destructor +// Created: 18/2/04 +// +// -------------------------------------------------------------------------- +BackupDaemon::CommandSocketInfo::~CommandSocketInfo() +{ + if(mpGetLine) + { + delete mpGetLine; + mpGetLine = 0; + } +} + +// -------------------------------------------------------------------------- +// +// Function +// 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 +// +// -------------------------------------------------------------------------- + +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(box_time_t theLastSyncTime, + box_time_t theNextSyncTime) const +{ + if(!GetConfiguration().KeyExists("StoreObjectInfoFile")) + { + return false; + } + + std::string StoreObjectInfoFile = + GetConfiguration().GetKeyValue("StoreObjectInfoFile"); + + if(StoreObjectInfoFile.size() <= 0) + { + return false; + } + + bool created = false; + + try + { + FileStream aFile(StoreObjectInfoFile.c_str(), + O_WRONLY | O_CREAT | O_TRUNC); + created = true; + + Archive anArchive(aFile, 0); + + anArchive.Write(STOREOBJECTINFO_MAGIC_ID_VALUE); + anArchive.Write(STOREOBJECTINFO_MAGIC_ID_STRING); + anArchive.Write(STOREOBJECTINFO_VERSION); + anArchive.Write(GetLoadedConfigModifiedTime()); + anArchive.Write(mClientStoreMarker); + anArchive.Write(theLastSyncTime); + anArchive.Write(theNextSyncTime); + + // + // + // + int64_t iCount = mLocations.size(); + anArchive.Write(iCount); + + for(int v = 0; v < iCount; v++) + { + ASSERT(mLocations[v]); + mLocations[v]->Serialize(anArchive); + } + + // + // + // + iCount = mIDMapMounts.size(); + anArchive.Write(iCount); + + for(int v = 0; v < iCount; v++) + anArchive.Write(mIDMapMounts[v]); + + // + // + // + iCount = mUnusedRootDirEntries.size(); + anArchive.Write(iCount); + + for(int v = 0; v < iCount; v++) + { + anArchive.Write(mUnusedRootDirEntries[v].first); + anArchive.Write(mUnusedRootDirEntries[v].second); + } + + if (iCount > 0) + { + anArchive.Write(mDeleteUnusedRootDirEntriesAfter); + } + + // + // + // + aFile.Close(); + BOX_INFO("Saved store object info file version " << + STOREOBJECTINFO_VERSION << " (" << + StoreObjectInfoFile << ")"); + } + catch(std::exception &e) + { + BOX_ERROR("Failed to write StoreObjectInfoFile: " << + StoreObjectInfoFile << ": " << e.what()); + } + catch(...) + { + BOX_ERROR("Failed to write StoreObjectInfoFile: " << + StoreObjectInfoFile << ": unknown error"); + } + + return created; +} + +// -------------------------------------------------------------------------- +// +// Function +// 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(box_time_t & theLastSyncTime, + box_time_t & theNextSyncTime) +{ + // + // + // + DeleteAllLocations(); + + // + // + // + if(!GetConfiguration().KeyExists("StoreObjectInfoFile")) + { + return false; + } + + std::string StoreObjectInfoFile = + GetConfiguration().GetKeyValue("StoreObjectInfoFile"); + + if(StoreObjectInfoFile.size() <= 0) + { + return false; + } + + try + { + FileStream aFile(StoreObjectInfoFile.c_str(), O_RDONLY); + Archive anArchive(aFile, 0); + + // + // see if the content looks like a valid serialised archive + // + int iMagicValue = 0; + anArchive.Read(iMagicValue); + + if(iMagicValue != STOREOBJECTINFO_MAGIC_ID_VALUE) + { + BOX_WARNING("Store object info file " + "is not a valid or compatible serialised " + "archive. Will re-cache from store. " + "(" << StoreObjectInfoFile << ")"); + return false; + } + + // + // get a bit optimistic and read in a string identifier + // + std::string strMagicValue; + anArchive.Read(strMagicValue); + + if(strMagicValue != STOREOBJECTINFO_MAGIC_ID_STRING) + { + BOX_WARNING("Store object info file " + "is not a valid or compatible serialised " + "archive. Will re-cache from store. " + "(" << StoreObjectInfoFile << ")"); + return false; + } + + // + // check if we are loading some future format + // version by mistake + // + int iVersion = 0; + anArchive.Read(iVersion); + + if(iVersion != STOREOBJECTINFO_VERSION) + { + BOX_WARNING("Store object info file " + "version " << iVersion << " unsupported. " + "Will re-cache from store. " + "(" << StoreObjectInfoFile << ")"); + return false; + } + + // + // check if this state file is even valid + // for the loaded bbackupd.conf file + // + box_time_t lastKnownConfigModTime; + anArchive.Read(lastKnownConfigModTime); + + if(lastKnownConfigModTime != GetLoadedConfigModifiedTime()) + { + BOX_WARNING("Store object info file " + "out of date. Will re-cache from store. " + "(" << StoreObjectInfoFile << ")"); + return false; + } + + // + // this is it, go at it + // + anArchive.Read(mClientStoreMarker); + anArchive.Read(theLastSyncTime); + anArchive.Read(theNextSyncTime); + + // + // + // + int64_t iCount = 0; + anArchive.Read(iCount); + + for(int v = 0; v < iCount; v++) + { + Location* pLocation = new Location; + if(!pLocation) + { + throw std::bad_alloc(); + } + + pLocation->Deserialize(anArchive); + mLocations.push_back(pLocation); + } + + // + // + // + iCount = 0; + anArchive.Read(iCount); + + for(int v = 0; v < iCount; v++) + { + std::string strItem; + anArchive.Read(strItem); + + mIDMapMounts.push_back(strItem); + } + + // + // + // + iCount = 0; + anArchive.Read(iCount); + + for(int v = 0; v < iCount; v++) + { + int64_t anId; + anArchive.Read(anId); + + std::string aName; + anArchive.Read(aName); + + mUnusedRootDirEntries.push_back(std::pair(anId, aName)); + } + + if (iCount > 0) + anArchive.Read(mDeleteUnusedRootDirEntriesAfter); + + // + // + // + aFile.Close(); + BOX_INFO("Loaded store object info file version " << iVersion + << " (" << StoreObjectInfoFile << ")"); + + return true; + } + catch(std::exception &e) + { + BOX_ERROR("Internal error reading store object info file: " + << StoreObjectInfoFile << ": " << e.what()); + } + catch(...) + { + BOX_ERROR("Internal error reading store object info file: " + << StoreObjectInfoFile << ": unknown error"); + } + + DeleteAllLocations(); + + mClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown; + theLastSyncTime = 0; + theNextSyncTime = 0; + + BOX_WARNING("Store object info file is missing, not accessible, " + "or inconsistent. Will re-cache from store. " + "(" << StoreObjectInfoFile << ")"); + + return false; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupDaemon::DeleteStoreObjectInfo() +// Purpose: Deletes the serialised state file, to prevent us +// from using it again if a backup is interrupted. +// +// Created: 2006/02/12 +// +// -------------------------------------------------------------------------- + +bool BackupDaemon::DeleteStoreObjectInfo() const +{ + if(!GetConfiguration().KeyExists("StoreObjectInfoFile")) + { + return false; + } + + std::string storeObjectInfoFile(GetConfiguration().GetKeyValue("StoreObjectInfoFile")); + + // 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("StoreObjectInfoFile did not exist when it " + "was supposed to: " << storeObjectInfoFile); + + // Return true to stop things going around in a loop + return true; + } + + // Actually delete it + if(::unlink(storeObjectInfoFile.c_str()) != 0) + { + BOX_LOG_SYS_ERROR("Failed to delete the old " + "StoreObjectInfoFile: " << storeObjectInfoFile); + return false; + } + + return true; +} diff --git a/bin/bbackupd/BackupDaemon.h b/bin/bbackupd/BackupDaemon.h new file mode 100644 index 00000000..b41c6508 --- /dev/null +++ b/bin/bbackupd/BackupDaemon.h @@ -0,0 +1,525 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupDaemon.h +// Purpose: Backup daemon +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#ifndef BACKUPDAEMON__H +#define BACKUPDAEMON__H + +#include +#include +#include + +#include "BackupClientContext.h" +#include "BackupClientDirectoryRecord.h" +#include "BoxTime.h" +#include "Daemon.h" +#include "Logging.h" +#include "Socket.h" +#include "SocketListen.h" +#include "SocketStream.h" +#include "TLSContext.h" + +#include "autogen_BackupProtocolClient.h" + +#ifdef WIN32 + #include "WinNamedPipeListener.h" + #include "WinNamedPipeStream.h" +#endif + +class BackupClientDirectoryRecord; +class BackupClientContext; +class Configuration; +class BackupClientInodeToIDMap; +class ExcludeList; +class IOStreamGetLine; +class Archive; + +// -------------------------------------------------------------------------- +// +// Class +// Name: BackupDaemon +// Purpose: Backup daemon +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +class BackupDaemon : public Daemon, ProgressNotifier, LocationResolver, +RunStatusProvider, SysadminNotifier +{ +public: + BackupDaemon(); + ~BackupDaemon(); + +private: + // methods below do partial (specialized) serialization of + // client state only + 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 &); + +public: + #ifdef WIN32 + // add command-line options to handle Windows services + 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(); + virtual const char *DaemonName() const; + virtual std::string DaemonBanner() const; + virtual void Usage(); + const ConfigurationVerify *GetConfigVerify() const; + + bool FindLocationPathName(const std::string &rLocationName, std::string &rPathOut) const; + + enum + { + // Add stuff to this, make sure the textual equivalents in SetState() are changed too. + State_Initialising = -1, + State_Idle = 0, + State_Connected = 1, + State_Error = 2, + State_StorageLimitExceeded = 3 + }; + + int GetState() {return mState;} + + // Allow other classes to call this too + 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); + + void DeleteIDMapVector(std::vector &rVector); + void DeleteAllIDMaps() + { + DeleteIDMapVector(mCurrentIDMaps); + DeleteIDMapVector(mNewIDMaps); + } + void FillIDMapVector(std::vector &rVector, bool NewMaps); + + void SetupIDMapsForSync(); + void CommitIDMapsAfterSync(); + void DeleteCorruptBerkelyDbFiles(); + + void MakeMapBaseName(unsigned int MountNumber, std::string &rNameOut) const; + + void SetState(int State); + + void WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFlagOut, bool &SyncIsForcedOut); + void CloseCommandConnection(); + void SendSyncStartOrFinish(bool SendStart); + + void DeleteUnusedRootDirEntries(BackupClientContext &rContext); + +#ifdef PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET + // For warning user about potential security hole + virtual void SetupInInitialProcess(); +#endif + + int UseScriptToSeeIfSyncAllowed(); + +public: + class Location + { + public: + Location(); + ~Location(); + + void Deserialize(Archive & rArchive); + void Serialize(Archive & rArchive) const; + private: + Location(const Location &); // copy not allowed + Location &operator=(const Location &); + public: + std::string mName; + std::string mPath; + std::auto_ptr mpDirectoryRecord; + int mIDMapIndex; + ExcludeList *mpExcludeFiles; + ExcludeList *mpExcludeDirs; + }; + + typedef const std::vector Locations; + Locations GetLocations() { return mLocations; } + +private: + int mState; // what the daemon is currently doing + + std::vector mLocations; + + std::vector mIDMapMounts; + std::vector mCurrentIDMaps; + std::vector mNewIDMaps; + + int mDeleteRedundantLocationsAfter; + + // For the command socket + class CommandSocketInfo + { + public: + CommandSocketInfo(); + ~CommandSocketInfo(); + private: + CommandSocketInfo(const CommandSocketInfo &); // no copying + CommandSocketInfo &operator=(const CommandSocketInfo &); + public: +#ifdef WIN32 + WinNamedPipeListener<1 /* listen backlog */> mListeningSocket; + std::auto_ptr mpConnectedSocket; +#else + SocketListen mListeningSocket; + std::auto_ptr mpConnectedSocket; +#endif + IOStreamGetLine *mpGetLine; + }; + + // Using a socket? + std::auto_ptr mapCommandSocketInfo; + + // Stop notifications being repeated. + 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 > 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) + { + if (mLogAllFileAccess) + { + BOX_INFO("Scanning directory: " << rLocalPath); + } + } + virtual void NotifyDirStatFailed( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath, + const std::string& rErrorMsg) + { + BOX_WARNING("Failed to access directory: " << rLocalPath + << ": " << rErrorMsg); + } + virtual void NotifyFileStatFailed( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath, + const std::string& rErrorMsg) + { + BOX_WARNING("Failed to access file: " << rLocalPath + << ": " << rErrorMsg); + } + virtual void NotifyDirListFailed( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath, + const std::string& rErrorMsg) + { + BOX_WARNING("Failed to list directory: " << rLocalPath + << ": " << rErrorMsg); + } + virtual void NotifyMountPointSkipped( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath) + { + #ifdef WIN32 + BOX_WARNING("Ignored directory: " << rLocalPath << + ": is an NTFS junction/reparse point; create " + "a new location if you want to back it up"); + #else + BOX_WARNING("Ignored directory: " << rLocalPath << + ": is a mount point; create a new location " + "if you want to back it up"); + #endif + } + virtual void NotifyFileExcluded( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath) + { + if (mLogAllFileAccess) + { + BOX_INFO("Skipping excluded file: " << rLocalPath); + } + } + virtual void NotifyDirExcluded( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath) + { + if (mLogAllFileAccess) + { + BOX_INFO("Skipping excluded directory: " << rLocalPath); + } + } + virtual void NotifyUnsupportedFileType( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath) + { + BOX_WARNING("Ignoring file of unknown type: " << rLocalPath); + } + virtual void NotifyFileReadFailed( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath, + const std::string& rErrorMsg) + { + BOX_WARNING("Error reading file: " << rLocalPath + << ": " << rErrorMsg); + } + virtual void NotifyFileModifiedInFuture( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath) + { + BOX_WARNING("Some files have modification times excessively " + "in the future. Check clock synchronisation. " + "Example file (only one shown): " << rLocalPath); + } + virtual void NotifyFileSkippedServerFull( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath) + { + BOX_WARNING("Skipped file: server is full: " << rLocalPath); + } + virtual void NotifyFileUploadException( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath, + const BoxException& rException) + { + if (rException.GetType() == CommonException::ExceptionType && + rException.GetSubType() == CommonException::AccessDenied) + { + BOX_ERROR("Failed to upload file: " << rLocalPath + << ": Access denied"); + } + else + { + BOX_ERROR("Failed to upload file: " << rLocalPath + << ": caught exception: " << rException.what() + << " (" << rException.GetType() + << "/" << rException.GetSubType() << ")"); + } + } + virtual void NotifyFileUploadServerError( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath, + int type, int subtype) + { + std::ostringstream msgs; + if (type != BackupProtocolClientError::ErrorType) + { + msgs << "unknown error type " << type; + } + else + { + switch(subtype) + { + case BackupProtocolClientError::Err_WrongVersion: + msgs << "WrongVersion"; + break; + case BackupProtocolClientError::Err_NotInRightProtocolPhase: + msgs << "NotInRightProtocolPhase"; + break; + case BackupProtocolClientError::Err_BadLogin: + msgs << "BadLogin"; + break; + case BackupProtocolClientError::Err_CannotLockStoreForWriting: + msgs << "CannotLockStoreForWriting"; + break; + case BackupProtocolClientError::Err_SessionReadOnly: + msgs << "SessionReadOnly"; + break; + case BackupProtocolClientError::Err_FileDoesNotVerify: + msgs << "FileDoesNotVerify"; + break; + case BackupProtocolClientError::Err_DoesNotExist: + msgs << "DoesNotExist"; + break; + case BackupProtocolClientError::Err_DirectoryAlreadyExists: + msgs << "DirectoryAlreadyExists"; + break; + case BackupProtocolClientError::Err_CannotDeleteRoot: + msgs << "CannotDeleteRoot"; + break; + case BackupProtocolClientError::Err_TargetNameExists: + msgs << "TargetNameExists"; + break; + case BackupProtocolClientError::Err_StorageLimitExceeded: + msgs << "StorageLimitExceeded"; + break; + case BackupProtocolClientError::Err_DiffFromFileDoesNotExist: + msgs << "DiffFromFileDoesNotExist"; + break; + case BackupProtocolClientError::Err_DoesNotExistInDirectory: + msgs << "DoesNotExistInDirectory"; + break; + case BackupProtocolClientError::Err_PatchConsistencyError: + msgs << "PatchConsistencyError"; + break; + default: + msgs << "unknown error subtype " << subtype; + } + } + + BOX_ERROR("Failed to upload file: " << rLocalPath + << ": server error: " << msgs.str()); + } + virtual void NotifyFileUploading( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath) + { + if (mLogAllFileAccess) + { + BOX_NOTICE("Uploading complete file: " << rLocalPath); + } + } + virtual void NotifyFileUploadingPatch( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath) + { + if (mLogAllFileAccess) + { + BOX_NOTICE("Uploading patch to file: " << rLocalPath); + } + } + virtual void NotifyFileUploadingAttributes( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath) + { + if (mLogAllFileAccess) + { + BOX_NOTICE("Uploading new file attributes: " << + rLocalPath); + } + } + virtual void NotifyFileUploaded( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath, + int64_t FileSize) + { + if (mLogAllFileAccess) + { + BOX_NOTICE("Uploaded file: " << rLocalPath); + } + } + virtual void NotifyFileSynchronised( + const BackupClientDirectoryRecord* pDirRecord, + const std::string& rLocalPath, + int64_t FileSize) + { + if (mLogAllFileAccess) + { + 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 + private: + bool mInstallService, mRemoveService, mRunAsService; + std::string mServiceName; +#endif +}; + +#endif // BACKUPDAEMON__H diff --git a/bin/bbackupd/BackupDaemonInterface.h b/bin/bbackupd/BackupDaemonInterface.h new file mode 100644 index 00000000..2a2d8d4b --- /dev/null +++ b/bin/bbackupd/BackupDaemonInterface.h @@ -0,0 +1,167 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupDaemonInterface.h +// Purpose: Interfaces for managing a BackupDaemon +// Created: 2008/12/30 +// +// -------------------------------------------------------------------------- + +#ifndef BACKUPDAEMONINTERFACE__H +#define BACKUPDAEMONINTERFACE__H + +#include +// #include + +// #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 NotifyFileUploadingAttributes( + 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/ClientException.txt b/bin/bbackupd/ClientException.txt new file mode 100644 index 00000000..04f88620 --- /dev/null +++ b/bin/bbackupd/ClientException.txt @@ -0,0 +1,11 @@ + +# NOTE: Exception descriptions are for public distributions of Box Backup only -- do not rely for other applications. + + +EXCEPTION Client 13 + +Internal 0 +AssertFailed 1 +ClockWentBackwards 2 Invalid (negative) sync period: perhaps your clock is going backwards? +FailedToDeleteStoreObjectInfoFile 3 Failed to delete the StoreObjectInfoFile, backup cannot continue safely. +CorruptStoreObjectInfoFile 4 The store object info file contained an invalid value and is probably corrupt. Try deleting it. diff --git a/bin/bbackupd/Makefile.extra b/bin/bbackupd/Makefile.extra new file mode 100644 index 00000000..25ceb1e7 --- /dev/null +++ b/bin/bbackupd/Makefile.extra @@ -0,0 +1,7 @@ + +MAKEEXCEPTION = ../../lib/common/makeexception.pl + +# AUTOGEN SEEDING +autogen_ClientException.h autogen_ClientException.cpp: $(MAKEEXCEPTION) ClientException.txt + $(_PERL) $(MAKEEXCEPTION) ClientException.txt + diff --git a/bin/bbackupd/Win32BackupService.cpp b/bin/bbackupd/Win32BackupService.cpp new file mode 100644 index 00000000..6d027abf --- /dev/null +++ b/bin/bbackupd/Win32BackupService.cpp @@ -0,0 +1,48 @@ +// Win32 service functions for Box Backup, by Nick Knight + +#ifdef WIN32 + +#include "Box.h" +#include "BackupDaemon.h" +#include "MainHelper.h" +#include "BoxPortsAndFiles.h" +#include "BackupStoreException.h" + +#include "MemLeakFindOn.h" + +#include "Win32BackupService.h" + +Win32BackupService* gpDaemonService = NULL; +extern HANDLE gStopServiceEvent; +extern DWORD gServiceReturnCode; + +unsigned int WINAPI RunService(LPVOID lpParameter) +{ + DWORD retVal = gpDaemonService->WinService((const char*) lpParameter); + gServiceReturnCode = retVal; + SetEvent(gStopServiceEvent); + return retVal; +} + +void TerminateService(void) +{ + gpDaemonService->SetTerminateWanted(); +} + +DWORD Win32BackupService::WinService(const char* pConfigFileName) +{ + DWORD ret; + + if (pConfigFileName != NULL) + { + ret = this->Main(pConfigFileName); + } + else + { + ret = this->Main(BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE); + } + + return ret; +} + +#endif // WIN32 diff --git a/bin/bbackupd/Win32BackupService.h b/bin/bbackupd/Win32BackupService.h new file mode 100644 index 00000000..e7f077f2 --- /dev/null +++ b/bin/bbackupd/Win32BackupService.h @@ -0,0 +1,21 @@ +// Box Backup service daemon implementation by Nick Knight + +#ifndef WIN32BACKUPSERVICE_H +#define WIN32BACKUPSERVICE_H + +#ifdef WIN32 + +class Configuration; +class ConfigurationVerify; +class BackupDaemon; + +class Win32BackupService : public BackupDaemon +{ +public: + DWORD WinService(const char* pConfigFileName); +}; + +#endif // WIN32 + +#endif // WIN32BACKUPSERVICE_H + diff --git a/bin/bbackupd/Win32ServiceFunctions.cpp b/bin/bbackupd/Win32ServiceFunctions.cpp new file mode 100644 index 00000000..2df914a7 --- /dev/null +++ b/bin/bbackupd/Win32ServiceFunctions.cpp @@ -0,0 +1,384 @@ +//*************************************************************** +// From the book "Win32 System Services: The Heart of Windows 98 +// and Windows 2000" +// by Marshall Brain +// Published by Prentice Hall +// Copyright 1995 Prentice Hall. +// +// This code implements the Windows API Service interface +// for the Box Backup for Windows native port. +// Adapted for Box Backup by Nick Knight. +//*************************************************************** + +#ifdef WIN32 + +#include "Box.h" + +#ifdef HAVE_UNISTD_H + #include +#endif +#ifdef HAVE_PROCESS_H + #include +#endif + +extern void TerminateService(void); +extern unsigned int WINAPI RunService(LPVOID lpParameter); + +// Global variables + +TCHAR* gServiceName = TEXT("Box Backup Service"); +SERVICE_STATUS gServiceStatus; +SERVICE_STATUS_HANDLE gServiceStatusHandle = 0; +HANDLE gStopServiceEvent = 0; +DWORD gServiceReturnCode = 0; + +#define SERVICE_NAME "boxbackup" + +void ShowMessage(char *s) +{ + MessageBox(0, s, "Box Backup Message", + MB_OK | MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY); +} + +void ErrorHandler(char *s, DWORD err) +{ + char buf[256]; + memset(buf, 0, sizeof(buf)); + _snprintf(buf, sizeof(buf)-1, "%s: %s", s, + GetErrorMessage(err).c_str()); + BOX_ERROR(buf); + MessageBox(0, buf, "Error", + MB_OK | MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY); + ExitProcess(err); +} + +void WINAPI ServiceControlHandler( DWORD controlCode ) +{ + switch ( controlCode ) + { + case SERVICE_CONTROL_INTERROGATE: + break; + + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + Beep(1000,100); + TerminateService(); + gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(gServiceStatusHandle, &gServiceStatus); + + SetEvent(gStopServiceEvent); + return; + + case SERVICE_CONTROL_PAUSE: + break; + + case SERVICE_CONTROL_CONTINUE: + break; + + default: + if ( controlCode >= 128 && controlCode <= 255 ) + // user defined control code + break; + else + // unrecognised control code + break; + } + + SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); +} + +// ServiceMain is called when the SCM wants to +// start the service. When it returns, the service +// has stopped. It therefore waits on an event +// just before the end of the function, and +// that event gets set when it is time to stop. +// It also returns on any error because the +// service cannot start if there is an eror. + +static char* spConfigFileName; + +VOID ServiceMain(DWORD argc, LPTSTR *argv) +{ + // initialise service status + gServiceStatus.dwServiceType = SERVICE_WIN32; + gServiceStatus.dwCurrentState = SERVICE_STOPPED; + gServiceStatus.dwControlsAccepted = 0; + gServiceStatus.dwWin32ExitCode = NO_ERROR; + gServiceStatus.dwServiceSpecificExitCode = NO_ERROR; + gServiceStatus.dwCheckPoint = 0; + gServiceStatus.dwWaitHint = 0; + + gServiceStatusHandle = RegisterServiceCtrlHandler(gServiceName, + ServiceControlHandler); + + if (gServiceStatusHandle) + { + // service is starting + gServiceStatus.dwCurrentState = SERVICE_START_PENDING; + SetServiceStatus(gServiceStatusHandle, &gServiceStatus); + + // do initialisation here + gStopServiceEvent = CreateEvent(0, TRUE, FALSE, 0); + if (!gStopServiceEvent) + { + gServiceStatus.dwControlsAccepted &= + ~(SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_SHUTDOWN); + gServiceStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(gServiceStatusHandle, &gServiceStatus); + return; + } + + HANDLE ourThread = (HANDLE)_beginthreadex( + NULL, + 0, + RunService, + spConfigFileName, + CREATE_SUSPENDED, + NULL); + + SetThreadPriority(ourThread, THREAD_PRIORITY_LOWEST); + ResumeThread(ourThread); + + // we are now running so tell the SCM + gServiceStatus.dwControlsAccepted |= + (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + gServiceStatus.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(gServiceStatusHandle, &gServiceStatus); + + // do cleanup here + WaitForSingleObject(gStopServiceEvent, INFINITE); + CloseHandle(gStopServiceEvent); + gStopServiceEvent = 0; + + // service was stopped + gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(gServiceStatusHandle, &gServiceStatus); + + // service is now stopped + gServiceStatus.dwControlsAccepted &= + ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + + gServiceStatus.dwCurrentState = SERVICE_STOPPED; + + if (gServiceReturnCode != 0) + { + gServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + gServiceStatus.dwServiceSpecificExitCode = gServiceReturnCode; + } + + SetServiceStatus(gServiceStatusHandle, &gServiceStatus); + } +} + +int OurService(const char* pConfigFileName) +{ + spConfigFileName = strdup(pConfigFileName); + + SERVICE_TABLE_ENTRY serviceTable[] = + { + { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain }, + { NULL, NULL } + }; + BOOL success; + + // Register with the SCM + success = StartServiceCtrlDispatcher(serviceTable); + + free(spConfigFileName); + spConfigFileName = NULL; + + if (!success) + { + ErrorHandler("Failed to start service. Did you start " + "Box Backup from the Service Control Manager? " + "(StartServiceCtrlDispatcher)", GetLastError()); + return 1; + } + + return 0; +} + +int InstallService(const char* pConfigFileName, const std::string& rServiceName) +{ + if (pConfigFileName != NULL) + { + EMU_STRUCT_STAT st; + + if (emu_stat(pConfigFileName, &st) != 0) + { + BOX_LOG_SYS_ERROR("Failed to open configuration file " + "'" << pConfigFileName << "'"); + return 1; + } + + if (!(st.st_mode & S_IFREG)) + { + + BOX_ERROR("Failed to open configuration file '" << + pConfigFileName << "': not a file"); + return 1; + } + } + + SC_HANDLE scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + + if (!scm) + { + BOX_ERROR("Failed to open service control manager: " << + GetErrorMessage(GetLastError())); + return 1; + } + + char cmd[MAX_PATH]; + GetModuleFileName(NULL, cmd, sizeof(cmd)-1); + cmd[sizeof(cmd)-1] = 0; + + std::string cmdWithArgs(cmd); + cmdWithArgs += " -s -S \"" + rServiceName + "\""; + + if (pConfigFileName != NULL) + { + cmdWithArgs += " \""; + cmdWithArgs += pConfigFileName; + cmdWithArgs += "\""; + } + + std::string serviceDesc = "Box Backup (" + rServiceName + ")"; + + SC_HANDLE newService = CreateService( + scm, + rServiceName.c_str(), + serviceDesc.c_str(), + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + cmdWithArgs.c_str(), + 0,0,0,0,0); + + DWORD err = GetLastError(); + CloseServiceHandle(scm); + + if (!newService) + { + switch (err) + { + case ERROR_SERVICE_EXISTS: + { + BOX_ERROR("Failed to create Box Backup " + "service: it already exists"); + } + break; + + case ERROR_SERVICE_MARKED_FOR_DELETE: + { + BOX_ERROR("Failed to create Box Backup " + "service: it is waiting to be deleted"); + } + break; + + case ERROR_DUPLICATE_SERVICE_NAME: + { + BOX_ERROR("Failed to create Box Backup " + "service: a service with this name " + "already exists"); + } + break; + + default: + { + BOX_ERROR("Failed to create Box Backup " + "service: error " << + GetErrorMessage(GetLastError())); + } + } + + return 1; + } + + BOX_INFO("Created Box Backup service"); + + SERVICE_DESCRIPTION desc; + desc.lpDescription = "Backs up your data files over the Internet"; + + if (!ChangeServiceConfig2(newService, SERVICE_CONFIG_DESCRIPTION, + &desc)) + { + BOX_WARNING("Failed to set description for Box Backup " + "service: " << GetErrorMessage(GetLastError())); + } + + CloseServiceHandle(newService); + + return 0; +} + +int RemoveService(const std::string& rServiceName) +{ + SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); + + if (!scm) + { + BOX_ERROR("Failed to open service control manager: " << + GetErrorMessage(GetLastError())); + return 1; + } + + SC_HANDLE service = OpenService(scm, rServiceName.c_str(), + SERVICE_ALL_ACCESS|DELETE); + DWORD err = GetLastError(); + CloseServiceHandle(scm); + + if (!service) + { + if (err == ERROR_SERVICE_DOES_NOT_EXIST || + err == ERROR_IO_PENDING) + // hello microsoft? anyone home? + { + BOX_ERROR("Failed to open Box Backup service: " + "not installed or not found"); + } + else + { + BOX_ERROR("Failed to open Box Backup service: " << + GetErrorMessage(err)); + } + return 1; + } + + SERVICE_STATUS status; + if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) + { + err = GetLastError(); + if (err != ERROR_SERVICE_NOT_ACTIVE) + { + BOX_WARNING("Failed to stop Box Backup service: " << + GetErrorMessage(err)); + } + } + + BOOL deleted = DeleteService(service); + err = GetLastError(); + CloseServiceHandle(service); + + if (deleted) + { + BOX_INFO("Box Backup service deleted"); + return 0; + } + else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) + { + BOX_ERROR("Failed to remove Box Backup service: " + "it is already being deleted"); + } + else + { + BOX_ERROR("Failed to remove Box Backup service: " << + GetErrorMessage(err)); + } + + return 1; +} + +#endif // WIN32 diff --git a/bin/bbackupd/Win32ServiceFunctions.h b/bin/bbackupd/Win32ServiceFunctions.h new file mode 100644 index 00000000..e04c368f --- /dev/null +++ b/bin/bbackupd/Win32ServiceFunctions.h @@ -0,0 +1,19 @@ +//*************************************************************** +// From the book "Win32 System Services: The Heart of Windows 98 +// and Windows 2000" +// by Marshall Brain +// Published by Prentice Hall +// Copyright 1995 Prentice Hall. +// +// This code implements the Windows API Service interface +// for the Box Backup for Windows native port. +//*************************************************************** + +#ifndef WIN32SERVICEFUNCTIONS_H +#define WIN32SERVICEFUNCTIONS_H + +int RemoveService (const std::string& rServiceName); +int InstallService (const char* pConfigFilePath, const std::string& rServiceName); +int OurService (const char* pConfigFileName); + +#endif diff --git a/bin/bbackupd/bbackupd-config.in b/bin/bbackupd/bbackupd-config.in new file mode 100755 index 00000000..98dc8b6e --- /dev/null +++ b/bin/bbackupd/bbackupd-config.in @@ -0,0 +1,601 @@ +#!@PERL@ +use strict; + +# should be running as root +if($> != 0) +{ + printf "\nWARNING: this should be run as root\n\n" +} + +sub error_print_usage +{ + print <<__E; + +Setup bbackupd config utility. + +Bad command line parameters. +Usage: + bbackupd-config config-dir backup-mode account-num server-hostname + working-dir [backup directories] + +Parameters: + config-dir is usually @sysconfdir_expanded@/boxbackup + backup-mode is lazy or snapshot: + lazy mode runs continously, uploading files over a specified age + snapshot mode uploads a snapshot of the filesystem when instructed + explicitly, using bbackupctl sync + account-num (hexdecimal) and server-hostname + are supplied by the server administrator + working-dir is usually @localstatedir_expanded@/bbackupd + backup directories is list of directories to back up + +__E + print "=========\nERROR:\n",$_[0],"\n\n" if $_[0] ne ''; + exit(1); +} + +# check and get command line parameters +if($#ARGV < 4) +{ + error_print_usage(); +} + +# check for OPENSSL_CONF environment var being set +if(exists $ENV{'OPENSSL_CONF'}) +{ + print <<__E; + +--------------------------------------- + +WARNING: + You have the OPENSSL_CONF environment variable set. + Use of non-standard openssl configs may cause problems. + +--------------------------------------- + +__E +} + +# default locations +my $default_config_location = '@sysconfdir_expanded@/boxbackup/bbackupd.conf'; + +# command line parameters +my ($config_dir,$backup_mode,$account_num,$server,$working_dir,@tobackup) = @ARGV; + +# check backup mode is valid +if($backup_mode ne 'lazy' && $backup_mode ne 'snapshot') +{ + error_print_usage("ERROR: backup mode must be 'lazy' or 'snapshot'"); +} + +# check server exists +{ + my @r = gethostbyname($server); + if($#r < 0) + { + error_print_usage("Backup server specified as '$server', but it could not found.\n(A test DNS lookup failed -- check arguments)"); + } +} + +if($working_dir !~ m~\A/~) +{ + error_print_usage("Working directory $working_dir is not specified as an absolute path"); +} + +# ssl stuff +my $private_key = "$config_dir/bbackupd/$account_num-key.pem"; +my $certificate_request = "$config_dir/bbackupd/$account_num-csr.pem"; +my $certificate = "$config_dir/bbackupd/$account_num-cert.pem"; +my $ca_root_cert = "$config_dir/bbackupd/serverCA.pem"; + +# encryption keys +my $enc_key_file = "$config_dir/bbackupd/$account_num-FileEncKeys.raw"; + +# other files +my $config_file = "$config_dir/bbackupd.conf"; +my $notify_script = "$config_dir/bbackupd/NotifySysadmin.sh"; + +# check that the directories are allowable +for(@tobackup) +{ + if($_ eq '/') + { + die "It is not recommended that you backup the root directory of your disc"; + } + if($_ !~ m/\A\//) + { + die "Directory $_ is not specified as an absolute path"; + } + if(!-d $_) + { + die "$_ is not a directory"; + } +} + +# summarise configuration + +print <<__E; + +Setup bbackupd config utility. + +Configuration: + Writing configuration file: $config_file + Account: $account_num + Server hostname: $server + Directories to back up: +__E +print ' ',$_,"\n" for(@tobackup); +print <<__E; + +Note: If other file systems are mounted inside these directories, then +they will NOT be backed up. You will have to create separate locations for +any mounted filesystems inside your backup locations. + +__E + +# create directories +if(!-d $config_dir) +{ + printf "Creating $config_dir...\n"; + mkdir $config_dir,0755 or die "Can't create $config_dir"; +} + +if(!-d "$config_dir/bbackupd") +{ + printf "Creating $config_dir/bbackupd\n"; + mkdir "$config_dir/bbackupd",0700 or die "Can't create $config_dir/bbackupd"; +} + +if(!-d "$working_dir") +{ + printf "Creating $working_dir\n"; + if(!mkdir($working_dir,0700)) + { + die "Couldn't create $working_dir -- create this manually and try again\n"; + } +} + +# generate the private key for the server +if(!-f $private_key) +{ + print "Generating private key...\n"; + if(system("openssl genrsa -out $private_key 2048") != 0) + { + die "Couldn't generate private key." + } +} + +# generate a certificate request +if(!-f $certificate_request) +{ + die "Couldn't run openssl for CSR generation" unless + open(CSR,"|openssl req -new -key $private_key -sha1 -out $certificate_request"); + print CSR <<__E; +. +. +. +. +. +BACKUP-$account_num +. +. +. + +__E + close CSR; + print "\n\n"; + die "Certificate request wasn't created.\n" unless -f $certificate_request +} + +# generate the key material for the file +if(!-f $enc_key_file) +{ + print "Generating keys for file backup\n"; + if(system("openssl rand -out $enc_key_file 1024") != 0) + { + die "Couldn't generate file backup keys." + } +} + +# write the notify when store full script +print "Writing notify script $notify_script\n"; +open NOTIFY,">$notify_script" or die "Can't open for writing"; + +my $hostname = `hostname`; chomp $hostname; +my $current_username = `whoami`; chomp $current_username; +my $sendmail = `whereis sendmail`; chomp $sendmail; +$sendmail =~ s/\n.\Z//s; +# for Linux style whereis +$sendmail = $1 if $sendmail =~ /^sendmail:\s+([\S]+)/; +# last ditch guess +$sendmail = 'sendmail' if $sendmail !~ m/\S/; + +print NOTIFY <<__EOS; +#!/bin/sh + +# This script is run whenever bbackupd changes state or encounters a +# problem which requires the system administrator to assist: +# +# 1) The store is full, and no more data can be uploaded. +# 2) Some files or directories were not readable. +# 3) A backup run starts or finishes. +# +# The default script emails the system administrator, except for backups +# starting and stopping, where it does nothing. + +SUBJECT="BACKUP PROBLEM on host $hostname" +SENDTO="$current_username" + +if [ "\$1" = "" ]; then + echo "Usage: \$0 " >&2 + exit 2 +elif [ "\$1" = store-full ]; then + $sendmail \$SENDTO <$config_file" or die "Can't open config file for writing"; +print CONFIG <<__E; + +StoreHostname = $server +AccountNumber = 0x$account_num +KeysFile = $enc_key_file + +CertificateFile = $certificate +PrivateKeyFile = $private_key +TrustedCAsFile = $ca_root_cert + +DataDirectory = $working_dir + + +# This script is run whenever bbackupd changes state or encounters a +# problem which requires the system administrator to assist: +# +# 1) The store is full, and no more data can be uploaded. +# 2) Some files or directories were not readable. +# 3) A backup run starts or finishes. +# +# The default script emails the system administrator, except for backups +# starting and stopping, where it does nothing. + +NotifyScript = $notify_script + +__E + +if($backup_mode eq 'lazy') +{ + # lazy mode configuration + print CONFIG <<__E; + +# The number of seconds between backup runs under normal conditions. To avoid +# cycles of load on the server, this time is randomly adjusted by a small +# percentage as the daemon runs. + +UpdateStoreInterval = 3600 + + +# The minimum age of a file, in seconds, that will be uploaded. Avoids +# repeated uploads of a file which is constantly being modified. + +MinimumFileAge = 21600 + + +# If a file is modified repeated, it won't be uploaded immediately in case +# it's modified again, due to the MinimumFileAge specified above. However, it +# should be uploaded eventually even if it is being modified repeatedly. This +# is how long we should wait, in seconds, after first noticing a change. +# (86400 seconds = 1 day) + +MaxUploadWait = 86400 + +# If the connection is idle for some time (e.g. over 10 minutes or 600 +# seconds, not sure exactly how long) then the server will give up and +# disconnect the client, resulting in Connection Protocol_Timeout errors +# on the server and TLSReadFailed or TLSWriteFailed errors on the client. +# Also, some firewalls and NAT gateways will kill idle connections after +# similar lengths of time. +# +# This can happen for example when most files are backed up already and +# don't need to be sent to the store again, while scanning a large +# directory, or while calculating diffs of a large file. To avoid this, +# KeepAliveTime specifies that special keep-alive messages should be sent +# when the connection is otherwise idle for a certain length of time, +# specified here in seconds. +# +# The default is that these messages are never sent, equivalent to setting +# this option to zero, but we recommend that all users enable this. + +KeepAliveTime = 120 + +__E +} +else +{ + # snapshot configuration + print CONFIG <<__E; + +# This configuration file is written for snapshot mode. +# You will need to run bbackupctl to instruct the daemon to upload files. + +AutomaticBackup = no +UpdateStoreInterval = 0 +MinimumFileAge = 0 +MaxUploadWait = 0 + +__E +} + +print CONFIG <<__E; + +# Files above this size (in bytes) are tracked, and if they are renamed they will simply be +# renamed on the server, rather than being uploaded again. (64k - 1) + +FileTrackingSizeThreshold = 65535 + + +# The daemon does "changes only" uploads for files above this size (in bytes). +# Files less than it are uploaded whole without this extra processing. + +DiffingUploadSizeThreshold = 8192 + + +# The limit on how much time is spent diffing files, in seconds. Most files +# shouldn't take very long, but if you have really big files you can use this +# to limit the time spent diffing them. +# +# * Reduce if you are having problems with processor usage. +# +# * Increase if you have large files, and think the upload of changes is too +# large and you want bbackupd to spend more time searching for unchanged +# blocks. + +MaximumDiffingTime = 120 + + +# Uncomment this line to see exactly what the daemon is going when it's connected to the server. + +# ExtendedLogging = yes + + +# This specifies a program or script script which is run just before each +# sync, and ideally the full path to the interpreter. It will be run as the +# same user bbackupd is running as, usually root. +# +# The script must output (print) either "now" or a number to STDOUT (and a +# terminating newline, no quotes). +# +# If the result was "now", then the sync will happen. If it's a number, then +# no backup will happen for that number of seconds (bbackupd will pause) and +# then the script will be run again. +# +# Use this to temporarily stop bbackupd from syncronising or connecting to the +# store. For example, you could use this on a laptop to only backup when on a +# specific network, or when it has a working Internet connection. + +# SyncAllowScript = /path/to/intepreter/or/exe script-name parameters etc + + +# Where the command socket is created in the filesystem. + +CommandSocket = $working_dir/bbackupd.sock + +# Uncomment the StoreObjectInfoFile to enable the experimental archiving +# of the daemon's state (including client store marker and configuration) +# between backup runs. This saves time and increases efficiency when +# bbackupd is frequently stopped and started, since it removes the need +# to rescan all directories on the remote server. However, it is new and +# not yet heavily tested, so use with caution. + +# StoreObjectInfoFile = $working_dir/bbackupd.state + +Server +{ + PidFile = $working_dir/bbackupd.pid +} + + +# BackupLocations specifies which locations on disc should be backed up. Each +# directory is in the format +# +# name +# { +# Path = /path/of/directory +# (optional exclude directives) +# } +# +# 'name' is derived from the Path by the config script, but should merely be +# unique. +# +# The exclude directives are of the form +# +# [Exclude|AlwaysInclude][File|Dir][|sRegex] = regex or full pathname +# +# (The regex suffix is shown as 'sRegex' to make File or Dir plural) +# +# For example: +# +# ExcludeDir = /home/guest-user +# ExcludeFilesRegex = \.(mp3|MP3)\$ +# AlwaysIncludeFile = /home/username/veryimportant.mp3 +# +# This excludes the directory /home/guest-user from the backup along with all mp3 +# files, except one MP3 file in particular. +# +# In general, Exclude excludes a file or directory, unless the directory is +# explicitly mentioned in a AlwaysInclude directive. However, Box Backup +# does NOT scan inside excluded directories and will never back up an +# AlwaysIncluded file or directory inside an excluded directory or any +# subdirectory thereof. +# +# To back up a directory inside an excluded directory, use a configuration +# like this, to ensure that each directory in the path to the important +# files is included, but none of their contents will be backed up except +# the directories further down that path to the important one. +# +# ExcludeDirsRegex = ^/home/user/bigfiles/ +# ExcludeFilesRegex = ^/home/user/bigfiles/ +# AlwaysIncludeDir = /home/user/bigfiles/path +# AlwaysIncludeDir = /home/user/bigfiles/path/to +# AlwaysIncludeDir = /home/user/bigfiles/path/important +# AlwaysIncludeDir = /home/user/bigfiles/path/important/files +# AlwaysIncludeDirsRegex = ^/home/user/bigfiles/path/important/files/ +# AlwaysIncludeFilesRegex = ^/home/user/bigfiles/path/important/files/ +# +# If a directive ends in Regex, then it is a regular expression rather than a +# explicit full pathname. See +# +# man 7 re_format +# +# for the regex syntax on your platform. + +BackupLocations +{ +__E + +# write the dirs to backup +for my $d (@tobackup) +{ + $d =~ m/\A.(.+)\Z/; + my $n = $1; + $n =~ tr`/`-`; + + my $excludekeys = ''; + if(substr($enc_key_file, 0, length($d)+1) eq $d.'/') + { + $excludekeys = "\t\tExcludeFile = $enc_key_file\n"; + print <<__E; + +NOTE: Keys file has been explicitly excluded from the backup. + +__E + } + + print CONFIG <<__E + $n + { + Path = $d +$excludekeys } +__E +} + +print CONFIG "}\n\n"; +close CONFIG; + +# explain to the user what they need to do next +my $daemon_args = ($config_file eq $default_config_location)?'':" $config_file"; +my $ctl_daemon_args = ($config_file eq $default_config_location)?'':" -c $config_file"; + +print <<__E; + +=================================================================== + +bbackupd basic configuration complete. + +What you need to do now... + +1) Make a backup of $enc_key_file + This should be a secure offsite backup. + Without it, you cannot restore backups. Everything else can + be replaced. But this cannot. + KEEP IT IN A SAFE PLACE, OTHERWISE YOUR BACKUPS ARE USELESS. + +2) Send $certificate_request + to the administrator of the backup server, and ask for it to + be signed. + +3) The administrator will send you two files. Install them as + $certificate + $ca_root_cert + after checking their authenticity. + +4) You may wish to read the configuration file + $config_file + and adjust as appropriate. + + There are some notes in it on excluding files you do not + wish to be backed up. + +5) Review the script + $notify_script + and check that it will email the right person when the store + becomes full. This is important -- when the store is full, no + more files will be backed up. You want to know about this. + +6) Start the backup daemon with the command + @sbindir_expanded@/bbackupd$daemon_args + in /etc/rc.local, or your local equivalent. + Note that bbackupd must run as root. +__E +if($backup_mode eq 'snapshot') +{ + print <<__E; + +7) Set up a cron job to run whenever you want a snapshot of the + file system to be taken. Run the command + @bindir_expanded@/bbackupctl -q$ctl_daemon_args sync +__E +} +print <<__E; + +=================================================================== + +Remember to make a secure, offsite backup of your backup keys, +as described in step 1 above. If you do not, you have no backups. + +__E + diff --git a/bin/bbackupd/bbackupd.cpp b/bin/bbackupd/bbackupd.cpp new file mode 100644 index 00000000..d334a2df --- /dev/null +++ b/bin/bbackupd/bbackupd.cpp @@ -0,0 +1,56 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: bbackupd.cpp +// Purpose: main file for backup daemon +// Created: 2003/10/11 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "BackupDaemon.h" +#include "MainHelper.h" +#include "BoxPortsAndFiles.h" +#include "BackupStoreException.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +#ifdef WIN32 + #include "Win32ServiceFunctions.h" + #include "Win32BackupService.h" + + extern Win32BackupService* gpDaemonService; +#endif + +int main(int argc, const char *argv[]) +{ + int ExitCode = 0; + + MAINHELPER_START + + Logging::SetProgramName("bbackupd"); + Logging::ToConsole(true); + Logging::ToSyslog (true); + +#ifdef WIN32 + + EnableBackupRights(); + + gpDaemonService = new Win32BackupService(); + ExitCode = gpDaemonService->Daemon::Main( + BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE, + argc, argv); + delete gpDaemonService; + +#else // !WIN32 + + BackupDaemon daemon; + ExitCode = daemon.Main(BOX_FILE_BBACKUPD_DEFAULT_CONFIG, argc, argv); + +#endif // WIN32 + + MAINHELPER_END + + return ExitCode; +} diff --git a/bin/bbackupd/win32/NotifySysAdmin.vbs b/bin/bbackupd/win32/NotifySysAdmin.vbs new file mode 100644 index 00000000..712d92da --- /dev/null +++ b/bin/bbackupd/win32/NotifySysAdmin.vbs @@ -0,0 +1,113 @@ +Dim hostname +Dim account +Dim from +Dim sendto +Dim subjtmpl +Dim subject +Dim body +Dim smtpserver + +Set WshNet = CreateObject("WScript.Network") +hostname = WshNet.ComputerName + +account = "0x1" +from = "boxbackup@" & hostname +sendto = "admin@example.com" +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 + SendMail from,sendto,subject,body +ElseIf args(0) = "read-error" Then + subject = subjtmpl & " (read errors)" + 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" _ + 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 + SendMail from,sendto,subject,body +End If + +Function CheckSMTPSvc() + Set objWMISvc = GetObject("winmgmts:" _ + & "{impersonationLevel=impersonate}!\\.\root\cimv2") + Set colSMTPSvc = objWMISvc.ExecQuery("Select * From Win32_Service " _ + & "Where Name='SMTPSVC'") + If colSMTPSvc.Count > 0 Then + CheckSMTPSvc = True + Else + CheckSMTPSvc = False + End If +End Function + +Sub SendMail(from,sendto,subject,body) + Set objEmail = CreateObject("CDO.Message") + Set WshShell = CreateObject("WScript.Shell") + Dim cdoschema + cdoschema = "http://schemas.microsoft.com/cdo/configuration/" + + With objEmail + .From = from + .To = sendto + .Subject = subject + .TextBody = body + If CheckSMTPSvc = False Then + .Configuration.Fields.Item(cdoschema & "sendusing") = 2 + .Configuration.Fields.Item(cdoschema & "smtpserver") = smtpserver + .Configuration.Fields.Item(cdoschema & "smtpserverport") = 25 + .Configuration.Fields.Update + End If + End With + On Error Resume Next + rc = objEmail.Send + If rc Then + WshShell.Exec "eventcreate /L Application /ID 201 /T WARNING " _ + & "/SO ""Box Backup"" /D """ & args(0) _ + & " notification sent to " & sendto & ".""" + Else + WshShell.Exec "eventcreate /L Application /ID 202 /T ERROR " _ + & "/SO ""Box Backup"" /D ""Failed to send " & args(0) _ + & " notification to " & sendto & ".""" + End If +End Sub diff --git a/bin/bbackupd/win32/bbackupd.conf b/bin/bbackupd/win32/bbackupd.conf new file mode 100644 index 00000000..b0793b29 --- /dev/null +++ b/bin/bbackupd/win32/bbackupd.conf @@ -0,0 +1,238 @@ + +StoreHostname = yourhost +AccountNumber = 0x1 +KeysFile = C:\Program Files\Box Backup\1-FileEncKeys.raw + +CertificateFile = C:\Program Files\Box Backup\1-cert.pem +PrivateKeyFile = C:\Program Files\Box Backup\1-key.pem +TrustedCAsFile = C:\Program Files\Box Backup\serverCA.pem + +DataDirectory = C:\Program Files\Box Backup\bbackupd + +# If you do not install it in the default location - also do not forget to +# change the pid file location (below) + +# This script is run whenever bbackupd changes state or encounters a +# problem which requires the system administrator to assist: +# +# 1) The store is full, and no more data can be uploaded. +# 2) Some files or directories were not readable. +# 3) A backup run starts or finishes. +# +# The default script emails the system administrator, except for backups +# starting and stopping, where it does nothing. +# +# NOTE: You need to edit the following variables in the script before +# enabling it: +# +# account = "accountnumber" +# sendto = "your@email.address" +# smtpserver = "your.smtp.server" +# +# You do not need to set smtpserver if the client has the SMTP Service +# installed, the script will connect directly to the SMTP service. + +NotifyScript = cscript "C:\Program Files\Box Backup\NotifySysAdmin.vbs" + + +# The number of seconds between backup runs under normal conditions. To avoid +# cycles of load on the server, this time is randomly adjusted by a small +# percentage as the daemon runs. + +UpdateStoreInterval = 3600 + + +# The minimum age of a file, in seconds, that will be uploaded. Avoids +# repeated uploads of a file which is constantly being modified. + +MinimumFileAge = 21600 + + +# If a file is modified repeated, it won't be uploaded immediately in case +# it's modified again, due to the MinimumFileAge specified above. However, it +# should be uploaded eventually even if it is being modified repeatedly. This +# is how long we should wait, in seconds, after first noticing a change. +# (86400 seconds = 1 day) + +MaxUploadWait = 86400 + +# If the connection is idle for some time (e.g. over 10 minutes or 600 +# seconds, not sure exactly how long) then the server will give up and +# disconnect the client, resulting in Connection Protocol_Timeout errors +# on the server and TLSReadFailed or TLSWriteFailed errors on the client. +# Also, some firewalls and NAT gateways will kill idle connections after +# similar lengths of time. +# +# This can happen for example when most files are backed up already and +# don't need to be sent to the store again, while scanning a large +# directory, or while calculating diffs of a large file. To avoid this, +# KeepAliveTime specifies that special keep-alive messages should be sent +# when the connection is otherwise idle for a certain length of time, +# specified here in seconds. +# +# The default is that these messages are never sent, equivalent to setting +# this option to zero, but we recommend that all users enable this. + +KeepAliveTime = 120 + + +# Files above this size (in bytes) are tracked, and if they are renamed they will simply be +# renamed on the server, rather than being uploaded again. (64k - 1) + +FileTrackingSizeThreshold = 65535 + + +# The daemon does "changes only" uploads for files above this size (in bytes). +# Files less than it are uploaded whole without this extra processing. + +DiffingUploadSizeThreshold = 8192 + + +# The limit on how much time is spent diffing files, in seconds. Most files +# shouldn't take very long, but if you have really big files you can use this +# to limit the time spent diffing them. +# +# * Reduce if you are having problems with processor usage. +# +# * Increase if you have large files, and think the upload of changes is too +# large and you want bbackupd to spend more time searching for unchanged +# blocks. + +MaximumDiffingTime = 120 + + +# Uncomment this line to see exactly what the daemon is going when it's connected to the server. + +# ExtendedLogging = yes + + +# This specifies a program or script script which is run just before each +# sync, and ideally the full path to the interpreter. It will be run as the +# same user bbackupd is running as, usually root. +# +# The script must output (print) either "now" or a number to STDOUT (and a +# terminating newline, no quotes). +# +# If the result was "now", then the sync will happen. If it's a number, then +# no backup will happen for that number of seconds (bbackupd will pause) and +# then the script will be run again. +# +# Use this to temporarily stop bbackupd from syncronising or connecting to the +# store. For example, you could use this on a laptop to only backup when on a +# specific network, or when it has a working Internet connection. + +# SyncAllowScript = /path/to/intepreter/or/exe script-name parameters etc + + +# Where the command socket is created in the filesystem. + +CommandSocket = pipe + +# Uncomment the StoreObjectInfoFile to enable the experimental archiving +# of the daemon's state (including client store marker and configuration) +# between backup runs. This saves time and increases efficiency when +# bbackupd is frequently stopped and started, since it removes the need +# to rescan all directories on the remote server. However, it is new and +# not yet heavily tested, so use with caution. + +StoreObjectInfoFile = C:\Program Files\Box Backup\bbackupd\bbackupd.state + +Server +{ + PidFile = C:\Program Files\Box Backup\bbackupd\bbackupd.pid +} + + +# BackupLocations specifies which locations on disc should be backed up. Each +# directory is in the format +# +# name +# { +# Path = /path/of/directory +# (optional exclude directives) +# } +# +# 'name' is derived from the Path by the config script, but should merely be +# unique. +# +# The exclude directives are of the form +# +# [Exclude|AlwaysInclude][File|Dir][|sRegex] = regex or full pathname +# +# (The regex suffix is shown as 'sRegex' to make File or Dir plural) +# +# For example: +# +# ExcludeDir = /home/guest-user +# ExcludeFilesRegex = \.(mp3|MP3)$ +# AlwaysIncludeFile = /home/username/veryimportant.mp3 +# +# This excludes the directory /home/guest-user from the backup along with all mp3 +# files, except one MP3 file in particular. +# +# If a directive ends in Regex, then it is a regular expression rather than a +# explicit full pathname. See: +# +# http://www.boxbackup.org/trac/wiki/Win32Regex +# +# for more information about regular expressions on Windows. +# +# In general, Exclude excludes a file or directory, unless the directory is +# explicitly mentioned in a AlwaysInclude directive. However, Box Backup +# does NOT scan inside excluded directories and will never back up an +# AlwaysIncluded file or directory inside an excluded directory or any +# subdirectory thereof. +# +# To back up a directory inside an excluded directory, use a configuration +# like this, to ensure that each directory in the path to the important +# files is included, but none of their contents will be backed up except +# the directories further down that path to the important one. +# +# ExcludeDirsRegex = ^/home/user/bigfiles/ +# ExcludeFilesRegex = ^/home/user/bigfiles/ +# AlwaysIncludeDir = /home/user/bigfiles/path +# AlwaysIncludeDir = /home/user/bigfiles/path/to +# AlwaysIncludeDir = /home/user/bigfiles/path/important +# AlwaysIncludeDir = /home/user/bigfiles/path/important/files +# AlwaysIncludeDirsRegex = ^/home/user/bigfiles/path/important/files/ +# AlwaysIncludeFilesRegex = ^/home/user/bigfiles/path/important/files/ +# +# Here are some more examples of possible regular expressions for Windows: +# +# ExcludeDir = C:\Documents and Settings\Owner +# ExcludeFilesRegex = \.(mp3|MP3)$ +# AlwaysIncludeFile = C:\Documents and Settings\Owner\My Documents\My Music\veryimportant.mp3 +# ExcludeFilesRegex = \.pst$ +# AlwaysIncludeFilesRegex = \.*backup.*\.pst$ +# ExcludeFilesRegex = \.avi$ +# ExcludeDirsRegex = \\Temporary Internet Files$ +# ExcludeFilesRegex = \\pagefile\.sys$ +# ExcludeDirsRegex = \\pagefile\.sys$ +# ExcludeFilesRegex = \\boot\.ini$ +# ExcludeFilesRegex = \\NTDETECT\.COM$ +# ExcludeFilesRegex = \\UsrClass\.dat\.LOG$ +# ExcludeDirsRegex = \\System Volume Information$ +# ExcludeFilesRegex = \\ntldr$ +# ExcludeDirsRegex = \\Local Settings\\.*\\Cache$ +# ExcludeFilesRegex = \\thumbs\.db$ +# ExcludeFilesRegex = \\~.* +# ExcludeFilesRegex = \\Perflib.* +# ExcludeDirsRegex = \\Application Data$ +# ExcludeFilesRegex = \.bk[~!0-9]$ +# ExcludeFilesRegex = \.iso$ +# ExcludeFilesRegex = \.mpe?[2345g]$ +# ExcludeFilesRegex = \.qbw$ +# AlwaysIncludeFilesRegex = \.qbb$ +# ExcludeFilesRegex = \.tif[f]$ +# ExcludeFilesRegex = \.wmv$ +# ExcludeFilesRegex = \.avi$ +# ExcludeFilesRegex = \.(avi|iso|mp(e)?[g345]|bk[~!1-9]|[mt]bk)$ + +BackupLocations +{ + MyDocuments + { + Path = C:\Documents and Settings\ + } +} + diff --git a/bin/bbackupd/win32/installer.iss b/bin/bbackupd/win32/installer.iss new file mode 100644 index 00000000..20e3addb --- /dev/null +++ b/bin/bbackupd/win32/installer.iss @@ -0,0 +1,51 @@ +; Script to generate output file for Box Backup client for the Windows Platform +; +; Very important - this is the release process +; +; 1/ Upgrade BOX_VERSION in the file emu.h to the current version for example 0.09eWin32 - then perform a full rebuild +; +; 2/ Upgrade the AppVerName below to reflect the version +; +; 3/ Generate the output file, then rename it to the relevent filename to reflect the version + +[Setup] +AppName=Box Backup +AppVerName=BoxWin32 0.09h +AppPublisher=Fluffy & Omniis +AppPublisherURL=http://www.omniis.com +AppSupportURL=http://www.omniis.com +AppUpdatesURL=http://www.omniis.com +DefaultDirName={pf}\Box Backup +DefaultGroupName=Box Backup +Compression=lzma +SolidCompression=yes +PrivilegesRequired=admin + +[Files] +Source: "..\..\Release\bbackupd.exe"; DestDir: "{app}"; Flags: ignoreversion restartreplace +Source: "..\..\Release\bbackupctl.exe"; DestDir: "{app}"; Flags: ignoreversion restartreplace +Source: "..\..\Release\bbackupquery.exe"; DestDir: "{app}"; Flags: ignoreversion restartreplace +Source: "..\..\ExceptionCodes.txt"; DestDir: "{app}"; Flags: ignoreversion restartreplace +Source: "icon.ico"; DestDir: "{app}\"; Flags: ignoreversion restartreplace +Source: "msvcr71.dll"; DestDir: "{app}\"; Flags: restartreplace +Source: "bbackupd.conf"; DestDir: "{app}"; Flags: confirmoverwrite +Source: "..\..\..\zlib\zlib1.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace +Source: "..\..\..\openssl\bin\libeay32.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace +Source: "..\..\..\openssl\bin\ssleay32.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace +Source: "ReadMe.txt"; DestDir: "{app}"; Flags: ignoreversion restartreplace + +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[Icons] +Name: "{group}\Box Backup Query"; Filename: "{app}\bbackupquery.exe"; IconFilename: "{app}\icon.ico" ;Parameters: "-c bbackupd.conf"; WorkingDir: "{app}" +Name: "{group}\Service\Install Service"; Filename: "{app}\bbackupd.exe"; IconFilename: "{app}\icon.ico" ;Parameters: "-i"; WorkingDir: "{app}" +Name: "{group}\Service\Remove Service"; Filename: "{app}\bbackupd.exe"; IconFilename: "{app}\icon.ico" ;Parameters: "-r"; WorkingDir: "{app}" +Name: "{group}\Initiate Backup Now"; Filename: "{app}\bbackupctl.exe"; IconFilename: "{app}\icon.ico" ;Parameters: "-c bbackupd.conf sync"; WorkingDir: "{app}" + +[Dirs] +Name: "{app}\bbackupd" + +[Run] +Filename: "{app}\bbackupd.exe"; Description: "Install Boxbackup as service"; Parameters: "-i"; Flags: postinstall +Filename: "{app}\Readme.txt"; Description: "View upgrade notes"; Flags: postinstall shellexec skipifsilent + diff --git a/bin/bbackupobjdump/bbackupobjdump.cpp b/bin/bbackupobjdump/bbackupobjdump.cpp new file mode 100644 index 00000000..5b6c44f7 --- /dev/null +++ b/bin/bbackupobjdump/bbackupobjdump.cpp @@ -0,0 +1,83 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: bbackupobjdump.cpp +// Purpose: Dump contents of backup objects +// Created: 3/5/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include + +#include "MainHelper.h" +#include "FileStream.h" +#include "BackupStoreDirectory.h" +#include "BackupStoreFile.h" +#include "BackupStoreObjectMagic.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: int main(int, const char *[]) +// Purpose: Main fn for bbackupobjdump +// Created: 3/5/04 +// +// -------------------------------------------------------------------------- +int main(int argc, const char *argv[]) +{ + MAINHELPER_START + + if(argc != 2) + { + ::printf("Input file not specified.\nUsage: bbackupobjdump \n"); + return 1; + } + + // Open file + FileStream file(argv[1]); + + // Read magic number + uint32_t signature; + if(file.Read(&signature, sizeof(signature)) != sizeof(signature)) + { + // Too short, can't read signature from it + return false; + } + // Seek back to beginning + file.Seek(0, IOStream::SeekType_Absolute); + + // Then... check depending on the type + switch(ntohl(signature)) + { + case OBJECTMAGIC_FILE_MAGIC_VALUE_V1: +#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE + case OBJECTMAGIC_FILE_MAGIC_VALUE_V0: +#endif + BackupStoreFile::DumpFile(stdout, false, file); + break; + + case OBJECTMAGIC_DIR_MAGIC_VALUE: + { + BackupStoreDirectory dir; + dir.ReadFromStream(file, IOStream::TimeOutInfinite); + dir.Dump(stdout, false); + if(dir.CheckAndFix()) + { + ::printf("Directory didn't pass checking\n"); + } + } + break; + + default: + ::printf("File does not appear to be a valid box backup object.\n"); + break; + } + + MAINHELPER_END +} + diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp new file mode 100644 index 00000000..60724800 --- /dev/null +++ b/bin/bbackupquery/BackupQueries.cpp @@ -0,0 +1,2340 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupQueries.cpp +// Purpose: Perform various queries on the backup store server. +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_UNISTD_H + #include +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DIRENT_H + #include +#endif + +#include +#include +#include +#include +#include + +#include "BackupClientFileAttributes.h" +#include "BackupClientMakeExcludeList.h" +#include "BackupClientRestore.h" +#include "BackupQueries.h" +#include "BackupStoreDirectory.h" +#include "BackupStoreException.h" +#include "BackupStoreFile.h" +#include "BackupStoreFilenameClear.h" +#include "BoxTimeToText.h" +#include "CommonException.h" +#include "Configuration.h" +#include "ExcludeList.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" + +// min() and max() macros from stdlib.h break numeric_limits<>::min(), etc. +#undef min +#undef max + +#define COMPARE_RETURN_SAME 1 +#define COMPARE_RETURN_DIFFERENT 2 +#define COMPARE_RETURN_ERROR 3 +#define COMMAND_RETURN_ERROR 4 + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::BackupQueries() +// Purpose: Constructor +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +BackupQueries::BackupQueries(BackupProtocolClient &rConnection, + const Configuration &rConfiguration, bool readWrite) + : mReadWrite(readWrite), + mrConnection(rConnection), + mrConfiguration(rConfiguration), + mQuitNow(false), + mRunningAsRoot(false), + mWarnedAboutOwnerAttributes(false), + mReturnCode(0) // default return code +{ + #ifdef WIN32 + mRunningAsRoot = TRUE; + #else + mRunningAsRoot = (::geteuid() == 0); + #endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::~BackupQueries() +// Purpose: Destructor +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +BackupQueries::~BackupQueries() +{ +} + +typedef struct +{ + const char* name; + const char* opts; +} QueryCommandSpecification; + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::DoCommand(const char *, bool) +// Purpose: Perform a command +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) +{ + // is the command a shell command? + if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0') + { + // Yes, run shell command + int result = ::system(Command + 3); + if(result != 0) + { + BOX_WARNING("System command returned error code " << + result); + SetReturnCode(ReturnCode::Command_Error); + } + return; + } + + // split command into components + std::vector cmdElements; + std::string options; + { + const char *c = Command; + bool inQuoted = false; + bool inOptions = false; + + std::string s; + while(*c != 0) + { + // Terminating char? + if(*c == ((inQuoted)?'"':' ')) + { + if(!s.empty()) cmdElements.push_back(s); + s.resize(0); + inQuoted = false; + inOptions = false; + } + else + { + // No. Start of quoted parameter? + if(s.empty() && *c == '"') + { + inQuoted = true; + } + // Start of options? + else if(s.empty() && *c == '-') + { + inOptions = true; + } + else + { + if(inOptions) + { + // Option char + options += *c; + } + else + { + // Normal string char + s += *c; + } + } + } + + ++c; + } + if(!s.empty()) cmdElements.push_back(s); + } + + #ifdef WIN32 + if (isFromCommandLine) + { + for (std::vector::iterator + i = cmdElements.begin(); + i != cmdElements.end(); i++) + { + std::string converted; + if (!ConvertEncoding(*i, CP_ACP, converted, + GetConsoleCP())) + { + BOX_ERROR("Failed to convert encoding"); + return; + } + *i = converted; + } + } + #endif + + // Check... + if(cmdElements.size() < 1) + { + // blank command + return; + } + + // Data about commands + static QueryCommandSpecification commands[] = + { + { "quit", "" }, + { "exit", "" }, + { "list", "rodIFtTash", }, + { "pwd", "" }, + { "cd", "od" }, + { "lcd", "" }, + { "sh", "" }, + { "getobject", "" }, + { "get", "i" }, + { "compare", "alcqAEQ" }, + { "restore", "drif" }, + { "help", "" }, + { "usage", "m" }, + { "undelete", "i" }, + { "delete", "i" }, + { NULL, NULL } + }; + + 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; + while(commands[cmd].name != 0 && ::strcmp(cmdElements[0].c_str(), commands[cmd].name) != 0) + { + cmd++; + } + if(commands[cmd].name == 0) + { + // Check for aliases + int a; + for(a = 0; alias[a] != 0; ++a) + { + if(::strcmp(cmdElements[0].c_str(), alias[a]) == 0) + { + // Found an alias + cmd = aliasIs[a]; + break; + } + } + + // No such command + if(alias[a] == 0) + { + BOX_ERROR("Unrecognised command: " << Command); + return; + } + } + + // Arguments + std::vector args(cmdElements.begin() + 1, cmdElements.end()); + + // Set up options + bool opts[256]; + for(int o = 0; o < 256; ++o) opts[o] = false; + // BLOCK + { + // options + const char *c = options.c_str(); + while(*c != 0) + { + // Valid option? + if(::strchr(commands[cmd].opts, *c) == NULL) + { + BOX_ERROR("Invalid option '" << *c << "' for " + "command " << commands[cmd].name); + return; + } + opts[(int)*c] = true; + ++c; + } + } + + if(cmd != Command_Quit && cmd != Command_Exit) + { + // If not a quit command, set the return code to zero + SetReturnCode(ReturnCode::Command_OK); + } + + // Handle command + switch(cmd) + { + case Command_Quit: + case Command_Exit: + mQuitNow = true; + break; + + case Command_List: + CommandList(args, opts); + break; + + case Command_pwd: + { + // Simple implementation, so do it here + BOX_NOTICE(GetCurrentDirectoryName() << " (" << + BOX_FORMAT_OBJECTID(GetCurrentDirectoryID()) << + ")"); + } + break; + + case Command_cd: + CommandChangeDir(args, opts); + break; + + case Command_lcd: + CommandChangeLocalDir(args); + break; + + case Command_sh: + BOX_ERROR("The command to run must be specified as an argument."); + break; + + case Command_GetObject: + CommandGetObject(args, opts); + break; + + case Command_Get: + CommandGet(args, opts); + break; + + case Command_Compare: + CommandCompare(args, opts); + break; + + case Command_Restore: + CommandRestore(args, opts); + break; + + case Command_Usage: + CommandUsage(opts); + break; + + case Command_Help: + CommandHelp(args); + break; + + case Command_Undelete: + CommandUndelete(args, opts); + break; + + case Command_Delete: + CommandDelete(args, opts); + break; + + default: + BOX_ERROR("Unknown command: " << Command); + break; + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandList(const std::vector &, const bool *) +// Purpose: List directories (optionally recursive) +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandList(const std::vector &args, const bool *opts) +{ + #define LIST_OPTION_RECURSIVE 'r' + #define LIST_OPTION_ALLOWOLD 'o' + #define LIST_OPTION_ALLOWDELETED 'd' + #define LIST_OPTION_NOOBJECTID 'I' + #define LIST_OPTION_NOFLAGS 'F' + #define LIST_OPTION_TIMES_LOCAL 't' + #define LIST_OPTION_TIMES_UTC 'T' + #define LIST_OPTION_TIMES_ATTRIBS 'a' + #define LIST_OPTION_SIZEINBLOCKS 's' + #define LIST_OPTION_DISPLAY_HASH 'h' + + // default to using the current directory + int64_t rootDir = GetCurrentDirectoryID(); + + // name of base directory + std::string listRoot; // blank + + // Got a directory in the arguments? + if(args.size() > 0) + { +#ifdef WIN32 + std::string storeDirEncoded; + if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded)) + return; +#else + const std::string& storeDirEncoded(args[0]); +#endif + + // Attempt to find the directory + rootDir = FindDirectoryObjectID(storeDirEncoded, + opts[LIST_OPTION_ALLOWOLD], + opts[LIST_OPTION_ALLOWDELETED]); + + if(rootDir == 0) + { + BOX_ERROR("Directory '" << args[0] << "' not found " + "on store."); + SetReturnCode(ReturnCode::Command_Error); + return; + } + } + + // List it + List(rootDir, listRoot, opts, true /* first level to list */); +} + +static std::string GetTimeString(BackupStoreDirectory::Entry& en, + bool useLocalTime, bool showAttrModificationTimes) +{ + std::ostringstream out; + box_time_t originalTime, newAttributesTime; + + // there is no attribute modification time in the directory + // entry, unfortunately, so we can't display it. + originalTime = en.GetModificationTime(); + out << BoxTimeToISO8601String(originalTime, useLocalTime); + + if(en.HasAttributes()) + { + const StreamableMemBlock &storeAttr(en.GetAttributes()); + BackupClientFileAttributes attr(storeAttr); + + box_time_t NewModificationTime, NewAttrModificationTime; + attr.GetModificationTimes(&NewModificationTime, + &NewAttrModificationTime); + + if (showAttrModificationTimes) + { + newAttributesTime = NewAttrModificationTime; + } + else + { + newAttributesTime = NewModificationTime; + } + + if (newAttributesTime == originalTime) + { + out << "*"; + } + else + { + out << "~" << BoxTimeToISO8601String(newAttributesTime, + useLocalTime); + } + } + else + { + out << " "; + } + + return out.str(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::List(int64_t, const std::string &, const bool *, bool) +// Purpose: Do the actual listing of directories and files +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool *opts, bool FirstLevel) +{ + // Generate exclude flags + int16_t excludeFlags = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING; + if(!opts[LIST_OPTION_ALLOWOLD]) excludeFlags |= BackupProtocolClientListDirectory::Flags_OldVersion; + if(!opts[LIST_OPTION_ALLOWDELETED]) excludeFlags |= BackupProtocolClientListDirectory::Flags_Deleted; + + // Do communication + 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; + std::auto_ptr dirstream(mrConnection.ReceiveStream()); + dir.ReadFromStream(*dirstream, mrConnection.GetTimeout()); + + // Then... display everything + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + while((en = i.Next()) != 0) + { + // Display this entry + BackupStoreFilenameClear clear(en->GetName()); + + // Object ID? + if(!opts[LIST_OPTION_NOOBJECTID]) + { + // add object ID to line +#ifdef _MSC_VER + printf("%08I64x ", (int64_t)en->GetObjectID()); +#else + printf("%08llx ", (long long)en->GetObjectID()); +#endif + } + + // Flags? + if(!opts[LIST_OPTION_NOFLAGS]) + { + static const char *flags = BACKUPSTOREDIRECTORY_ENTRY_FLAGS_DISPLAY_NAMES; + char displayflags[16]; + // make sure f is big enough + ASSERT(sizeof(displayflags) >= sizeof(BACKUPSTOREDIRECTORY_ENTRY_FLAGS_DISPLAY_NAMES) + 3); + // Insert flags + char *f = displayflags; + const char *t = flags; + int16_t en_flags = en->GetFlags(); + while(*t != 0) + { + *f = ((en_flags&1) == 0)?'-':*t; + en_flags >>= 1; + f++; + t++; + } + // attributes flags + *(f++) = (en->HasAttributes())?'a':'-'; + + // terminate + *(f++) = ' '; + *(f++) = '\0'; + printf(displayflags); + + if(en_flags != 0) + { + printf("[ERROR: Entry has additional flags set] "); + } + } + + if(opts[LIST_OPTION_TIMES_UTC]) + { + // Show UTC times... + printf("%s ", GetTimeString(*en, false, + opts[LIST_OPTION_TIMES_ATTRIBS]).c_str()); + } + + if(opts[LIST_OPTION_TIMES_LOCAL]) + { + // Show local times... + printf("%s ", GetTimeString(*en, true, + opts[LIST_OPTION_TIMES_ATTRIBS]).c_str()); + } + + if(opts[LIST_OPTION_DISPLAY_HASH]) + { +#ifdef _MSC_VER + printf("%016I64x ", (int64_t)en->GetAttributesHash()); +#else + printf("%016llx ", (long long)en->GetAttributesHash()); +#endif + } + + if(opts[LIST_OPTION_SIZEINBLOCKS]) + { +#ifdef _MSC_VER + printf("%05I64d ", (int64_t)en->GetSizeInBlocks()); +#else + printf("%05lld ", (long long)en->GetSizeInBlocks()); +#endif + } + + // add name + if(!FirstLevel) + { +#ifdef WIN32 + std::string listRootDecoded; + if(!ConvertUtf8ToConsole(rListRoot.c_str(), + listRootDecoded)) return; + printf("%s/", listRootDecoded.c_str()); +#else + printf("%s/", rListRoot.c_str()); +#endif + } + +#ifdef WIN32 + { + std::string fileName; + if(!ConvertUtf8ToConsole( + clear.GetClearFilename().c_str(), fileName)) + return; + printf("%s", fileName.c_str()); + } +#else + printf("%s", clear.GetClearFilename().c_str()); +#endif + + if(!en->GetName().IsEncrypted()) + { + printf("[FILENAME NOT ENCRYPTED]"); + } + + printf("\n"); + + // Directory? + if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) != 0) + { + // Recurse? + if(opts[LIST_OPTION_RECURSIVE]) + { + std::string subroot(rListRoot); + if(!FirstLevel) subroot += '/'; + subroot += clear.GetClearFilename(); + List(en->GetObjectID(), subroot, opts, false /* not the first level to list */); + } + } + } +} + + +// -------------------------------------------------------------------------- +// +// 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. +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, + bool AllowOldVersion, bool AllowDeletedDirs, + std::vector > *pStack) +{ + // Split up string into elements + std::vector dirElements; + SplitString(rDirName, '/', dirElements); + + // Start from current stack, or root, whichever is required + std::vector > stack; + int64_t dirID = BackupProtocolClientListDirectory::RootDirectory; + if(rDirName.size() > 0 && rDirName[0] == '/') + { + // Root, do nothing + } + else + { + // Copy existing stack + stack = mDirStack; + if(stack.size() > 0) + { + dirID = stack[stack.size() - 1].second; + } + } + + // Generate exclude flags + int16_t excludeFlags = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING; + if(!AllowOldVersion) excludeFlags |= BackupProtocolClientListDirectory::Flags_OldVersion; + if(!AllowDeletedDirs) excludeFlags |= BackupProtocolClientListDirectory::Flags_Deleted; + + // Read directories + for(unsigned int e = 0; e < dirElements.size(); ++e) + { + if(dirElements[e].size() > 0) + { + if(dirElements[e] == ".") + { + // Ignore. + } + else if(dirElements[e] == "..") + { + // Up one! + if(stack.size() > 0) + { + // Remove top element + stack.pop_back(); + + // New dir ID + dirID = (stack.size() > 0)?(stack[stack.size() - 1].second):BackupProtocolClientListDirectory::RootDirectory; + } + else + { + // At root anyway + dirID = BackupProtocolClientListDirectory::RootDirectory; + } + } + else + { + // Not blank element. Read current directory. + std::auto_ptr dirreply(mrConnection.QueryListDirectory( + dirID, + BackupProtocolClientListDirectory::Flags_Dir, // just directories + excludeFlags, + true /* want attributes */)); + + // Retrieve the directory from the stream following + BackupStoreDirectory dir; + std::auto_ptr dirstream(mrConnection.ReceiveStream()); + dir.ReadFromStream(*dirstream, mrConnection.GetTimeout()); + + // Then... find the directory within it + BackupStoreDirectory::Iterator i(dir); + BackupStoreFilenameClear dirname(dirElements[e]); + BackupStoreDirectory::Entry *en = i.FindMatchingClearName(dirname); + if(en == 0) + { + // Not found + return 0; + } + + // Object ID for next round of searching + dirID = en->GetObjectID(); + + // Push onto stack + stack.push_back(std::pair(dirElements[e], dirID)); + } + } + } + + // If required, copy the new stack to the caller + if(pStack) + { + *pStack = stack; + } + + return dirID; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::GetCurrentDirectoryID() +// Purpose: Returns the ID of the current directory +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +int64_t BackupQueries::GetCurrentDirectoryID() +{ + // Special case for root + if(mDirStack.size() == 0) + { + return BackupProtocolClientListDirectory::RootDirectory; + } + + // Otherwise, get from the last entry on the stack + return mDirStack[mDirStack.size() - 1].second; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::GetCurrentDirectoryName() +// Purpose: Gets the name of the current directory +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +std::string BackupQueries::GetCurrentDirectoryName() +{ + // Special case for root + if(mDirStack.size() == 0) + { + return std::string("/"); + } + + // Build path + std::string r; + for(unsigned int l = 0; l < mDirStack.size(); ++l) + { + r += "/"; +#ifdef WIN32 + std::string dirName; + if(!ConvertUtf8ToConsole(mDirStack[l].first.c_str(), dirName)) + return "error"; + r += dirName; +#else + r += mDirStack[l].first; +#endif + } + + return r; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandChangeDir(const std::vector &) +// Purpose: Change directory command +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandChangeDir(const std::vector &args, const bool *opts) +{ + if(args.size() != 1 || args[0].size() == 0) + { + BOX_ERROR("Incorrect usage. cd [-o] [-d] "); + SetReturnCode(ReturnCode::Command_Error); + return; + } + +#ifdef WIN32 + std::string dirName; + if(!ConvertConsoleToUtf8(args[0].c_str(), dirName)) return; +#else + const std::string& dirName(args[0]); +#endif + + std::vector > newStack; + int64_t id = FindDirectoryObjectID(dirName, opts['o'], opts['d'], + &newStack); + + if(id == 0) + { + BOX_ERROR("Directory '" << args[0] << "' not found."); + SetReturnCode(ReturnCode::Command_Error); + return; + } + + // Store new stack + mDirStack = newStack; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandChangeLocalDir(const std::vector &) +// Purpose: Change local directory command +// Created: 2003/10/11 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandChangeLocalDir(const std::vector &args) +{ + if(args.size() != 1 || args[0].size() == 0) + { + BOX_ERROR("Incorrect usage. lcd "); + SetReturnCode(ReturnCode::Command_Error); + return; + } + + // Try changing directory +#ifdef WIN32 + std::string dirName; + if(!ConvertConsoleToUtf8(args[0].c_str(), dirName)) + { + BOX_ERROR("Failed to convert path from console encoding."); + SetReturnCode(ReturnCode::Command_Error); + return; + } + int result = ::chdir(dirName.c_str()); +#else + int result = ::chdir(args[0].c_str()); +#endif + if(result != 0) + { + if(errno == ENOENT || errno == ENOTDIR) + { + BOX_ERROR("Directory '" << args[0] << "' does not exist."); + } + else + { + BOX_LOG_SYS_ERROR("Failed to change to directory " + "'" << args[0] << "'"); + } + + SetReturnCode(ReturnCode::Command_Error); + return; + } + + // Report current dir + char wd[PATH_MAX]; + if(::getcwd(wd, PATH_MAX) == 0) + { + BOX_LOG_SYS_ERROR("Error getting current directory"); + SetReturnCode(ReturnCode::Command_Error); + return; + } + +#ifdef WIN32 + if(!ConvertUtf8ToConsole(wd, dirName)) + { + BOX_ERROR("Failed to convert new path from console encoding."); + SetReturnCode(ReturnCode::Command_Error); + return; + } + BOX_INFO("Local current directory is now '" << dirName << "'."); +#else + BOX_INFO("Local current directory is now '" << wd << "'."); +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandGetObject(const std::vector &, const bool *) +// Purpose: Gets an object without any translation. +// Created: 2003/10/11 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandGetObject(const std::vector &args, const bool *opts) +{ + // Check args + if(args.size() != 2) + { + BOX_ERROR("Incorrect usage. getobject " + ""); + return; + } + + int64_t id = ::strtoll(args[0].c_str(), 0, 16); + if(id == std::numeric_limits::min() || id == std::numeric_limits::max() || id == 0) + { + BOX_ERROR("Not a valid object ID (specified in hex)."); + return; + } + + // Does file exist? + 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; + } + + // Open file + FileStream out(args[1].c_str(), O_WRONLY | O_CREAT | O_EXCL); + + // Request that object + try + { + // Request object + std::auto_ptr getobj(mrConnection.QueryGetObject(id)); + if(getobj->GetObjectID() != BackupProtocolClientGetObject::NoObject) + { + // Stream that object out to the file + std::auto_ptr objectStream(mrConnection.ReceiveStream()); + objectStream->CopyStreamTo(out); + + BOX_INFO("Object ID " << BOX_FORMAT_OBJECTID(id) << + " fetched successfully."); + } + else + { + BOX_ERROR("Object ID " << BOX_FORMAT_OBJECTID(id) << + " does not exist on store."); + ::unlink(args[1].c_str()); + } + } + catch(...) + { + ::unlink(args[1].c_str()); + BOX_ERROR("Error occured fetching object."); + } +} + + +// -------------------------------------------------------------------------- +// +// 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 in *pFileNameOut (if not NULL). +// 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; + } + } + } + + 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 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::min() || + fileId == std::numeric_limits::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(); + } + + if(pFileNameOut) + { + BackupStoreFilenameClear entryName(en->GetName()); + *pFileNameOut = entryName.GetClearFilename(); + } + + return fileId; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandGet(const std::vector &, const bool *) +// Purpose: Command to get a file from the store +// Created: 2003/10/12 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandGet(std::vector args, const bool *opts) +{ + // At least one argument? + // Check args + if(args.size() < 1 || (opts['i'] && args.size() != 2) || args.size() > 2) + { + BOX_ERROR("Incorrect usage.\n" + "get [] or\n" + "get -i "); + return; + } + + // Find object ID somehow + int64_t fileId, dirId; + std::string localName; + +#ifdef WIN32 + for (std::vector::iterator + i = args.begin(); i != args.end(); i++) + { + std::string out; + if(!ConvertConsoleToUtf8(i->c_str(), out)) + { + BOX_ERROR("Failed to convert encoding."); + return; + } + *i = out; + } +#endif + + int16_t flagsExclude; + + 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; + } + + + fileId = FindFileID(args[0], opts, &dirId, &localName, + BackupProtocolClientListDirectory::Flags_File, // just files + flagsExclude, NULL /* don't care about flags found */); + + if (fileId == 0) + { + // error already reported + return; + } + + 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) + { + localName = args[1]; + } + } + + // Does local file already exist? (don't want to overwrite) + 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(ReturnCode::Command_Error); + return; + } + + // Request it from the store + try + { + // Request object + mrConnection.QueryGetFile(dirId, fileId); + + // Stream containing encoded file + std::auto_ptr objectStream(mrConnection.ReceiveStream()); + + // Decode it + BackupStoreFile::DecodeFile(*objectStream, localName.c_str(), mrConnection.GetTimeout()); + + // Done. + BOX_INFO("Object ID " << BOX_FORMAT_OBJECTID(fileId) << + " fetched successfully."); + } + catch (BoxException &e) + { + BOX_ERROR("Failed to fetch file: " << + e.what()); + ::unlink(localName.c_str()); + } + catch(std::exception &e) + { + BOX_ERROR("Failed to fetch file: " << + e.what()); + ::unlink(localName.c_str()); + } + catch(...) + { + BOX_ERROR("Failed to fetch file: unknown error"); + ::unlink(localName.c_str()); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CompareParams::CompareParams() +// Purpose: Constructor +// Created: 29/1/04 +// +// -------------------------------------------------------------------------- +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) +{ } + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandCompare(const std::vector &, const bool *) +// Purpose: Command to compare data on the store with local data +// Created: 2003/10/12 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandCompare(const std::vector &args, const bool *opts) +{ + 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 + 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 + LatestFileUploadTime = FileModificationTime(st) - + SecondsToBoxTime(mrConfiguration.GetKeyValueInt("MinimumFileAge")); + } + else + { + BOX_WARNING("Failed to determine the time of the last " + "synchronisation -- checks not performed."); + } + } + + // 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.QuickCompare()) + { + BOX_WARNING("Quick compare used -- file attributes are not " + "checked."); + } + + if(!opts['l'] && opts['a'] && args.size() == 0) + { + // Compare all locations + const Configuration &rLocations( + mrConfiguration.GetSubConfiguration("BackupLocations")); + std::vector locNames = + rLocations.GetSubConfigurationNames(); + for(std::vector::iterator + pLocName = locNames.begin(); + pLocName != locNames.end(); + pLocName++) + { + CompareLocation(*pLocName, params); + } + } + else if(opts['l'] && !opts['a'] && args.size() == 1) + { + // Compare one location + CompareLocation(args[0], params); + } + else if(!opts['l'] && !opts['a'] && args.size() == 2) + { + // 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.IgnoreExcludes()) + { + BOX_ERROR("Cannot use excludes on directory to directory comparison -- use -E flag to specify ignored excludes."); + return; + } + else + { + // Do compare + Compare(args[0], args[1], params); + } + } + else + { + BOX_ERROR("Incorrect usage.\ncompare -a\n or compare -l \n or compare "); + return; + } + + if (!params.mQuietCompare) + { + BOX_INFO("[ " << + params.mDifferencesExplainedByModTime << " (of " << + params.mDifferences << ") differences probably " + "due to file modifications after the last upload ]"); + } + + BOX_INFO("Differences: " << params.mDifferences << " (" << + params.mExcludedDirs << " dirs excluded, " << + params.mExcludedFiles << " files excluded, " << + params.mUncheckedFiles << " files not checked)"); + + // Set return code? + if(opts['c']) + { + if (params.mUncheckedFiles != 0) + { + SetReturnCode(ReturnCode::Compare_Error); + } + else if (params.mDifferences != 0) + { + SetReturnCode(ReturnCode::Compare_Different); + } + else + { + SetReturnCode(ReturnCode::Compare_Same); + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CompareLocation(const std::string &, BackupQueries::CompareParams &) +// Purpose: Compare a location +// Created: 2003/10/13 +// +// -------------------------------------------------------------------------- +void BackupQueries::CompareLocation(const std::string &rLocation, + BoxBackupCompareParams &rParams) +{ + // Find the location's sub configuration + const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations")); + if(!locations.SubConfigurationExists(rLocation.c_str())) + { + BOX_ERROR("Location " << rLocation << " does not exist."); + return; + } + const Configuration &loc(locations.GetSubConfiguration(rLocation.c_str())); + + #ifdef WIN32 + { + std::string path = loc.GetKeyValue("Path"); + if (path.size() > 0 && path[path.size()-1] == + DIRECTORY_SEPARATOR_ASCHAR) + { + BOX_WARNING("Location '" << rLocation << "' path ends " + "with '" DIRECTORY_SEPARATOR "', " + "compare may fail!"); + } + } + #endif + + // Generate the exclude lists + if(!rParams.IgnoreExcludes()) + { + rParams.LoadExcludeLists(loc); + } + + // Then get it compared + Compare(std::string("/") + rLocation, loc.GetKeyValue("Path"), rParams); +} + + +// -------------------------------------------------------------------------- +// +// Function +// 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, 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 + + // Get the directory ID of the directory -- only use current data + int64_t dirID = FindDirectoryObjectID(storeDirEncoded); + + // Found? + if(dirID == 0) + { + 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; + } + + // Go! + Compare(dirID, storeDirEncoded, localDirEncoded, rParams); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::Compare(int64_t, const std::string &, +// const std::string &, BackupQueries::CompareParams &) +// Purpose: Compare a store directory against a local directory +// Created: 2003/10/13 +// +// -------------------------------------------------------------------------- +void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, + const std::string &rLocalDir, BoxBackupCompareParams &rParams) +{ + rParams.NotifyDirComparing(rLocalDir, rStoreDir); + + // Get info on the local directory + EMU_STRUCT_STAT st; + if(EMU_LSTAT(rLocalDir.c_str(), &st) != 0) + { + // What kind of error? + if(errno == ENOTDIR || errno == ENOENT) + { + rParams.NotifyLocalDirMissing(rLocalDir, rStoreDir); + } + else + { + 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 */); + + // Retrieve the directory from the stream following + BackupStoreDirectory dir; + std::auto_ptr dirstream(mrConnection.ReceiveStream()); + dir.ReadFromStream(*dirstream, mrConnection.GetTimeout()); + + // Test out the attributes + if(!dir.HasAttributes()) + { + rParams.NotifyStoreDirMissingAttributes(rLocalDir, rStoreDir); + } + else + { + // Fetch the attributes + const StreamableMemBlock &storeAttr(dir.GetAttributes()); + BackupClientFileAttributes attr(storeAttr); + + // Get attributes of local directory + BackupClientFileAttributes localAttr; + localAttr.ReadAttributes(rLocalDir.c_str(), + true /* directories have zero mod times */); + + if(attr.Compare(localAttr, true, true /* ignore modification times */)) + { + 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); + } + } + + // Open the local directory + DIR *dirhandle = ::opendir(rLocalDir.c_str()); + if(dirhandle == 0) + { + rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir); + return; + } + + try + { + // Read the files and directories into sets + std::set localFiles; + std::set localDirs; + struct dirent *localDirEn = 0; + while((localDirEn = readdir(dirhandle)) != 0) + { + // Not . and ..! + if(localDirEn->d_name[0] == '.' && + (localDirEn->d_name[1] == '\0' || (localDirEn->d_name[1] == '.' && localDirEn->d_name[2] == '\0'))) + { + // ignore, it's . or .. + +#ifdef HAVE_VALID_DIRENT_D_TYPE + if (localDirEn->d_type != DT_DIR) + { + BOX_ERROR("d_type does not really " + "work on your platform. " + "Reconfigure Box!"); + return; + } +#endif + + continue; + } + + std::string localDirPath(MakeFullPath(rLocalDir, + localDirEn->d_name)); + std::string storeDirPath(rStoreDir + "/" + + localDirEn->d_name); + +#ifndef HAVE_VALID_DIRENT_D_TYPE + EMU_STRUCT_STAT st; + if(EMU_LSTAT(localDirPath.c_str(), &st) != 0) + { + // Check whether dir is excluded before trying + // to stat it, to fix problems with .gvfs + // directories that are not readable by root + // causing compare to crash: + // http://lists.boxbackup.org/pipermail/boxbackup/2010-January/000013.html + if(rParams.IsExcludedDir(localDirPath)) + { + rParams.NotifyExcludedDir(localDirPath, + storeDirPath); + continue; + } + else + { + THROW_EXCEPTION_MESSAGE(CommonException, + OSFileError, localDirPath); + } + } + + // Entry -- file or dir? + if(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) + { + // File or symbolic link + localFiles.insert(std::string(localDirEn->d_name)); + } + else if(S_ISDIR(st.st_mode)) + { + // Directory + localDirs.insert(std::string(localDirEn->d_name)); + } +#else + // Entry -- file or dir? + if(localDirEn->d_type == DT_REG || localDirEn->d_type == DT_LNK) + { + // File or symbolic link + localFiles.insert(std::string(localDirEn->d_name)); + } + else if(localDirEn->d_type == DT_DIR) + { + // Directory + localDirs.insert(std::string(localDirEn->d_name)); + } +#endif + } + // Close directory + if(::closedir(dirhandle) != 0) + { + BOX_LOG_SYS_ERROR("Failed to close local directory " + "'" << rLocalDir << "'"); + } + dirhandle = 0; + + // Do the same for the store directories + std::set > storeFiles; + std::set > storeDirs; + + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *storeDirEn = 0; + while((storeDirEn = i.Next()) != 0) + { + // Decrypt filename + BackupStoreFilenameClear name(storeDirEn->GetName()); + + // What is it? + if((storeDirEn->GetFlags() & BackupStoreDirectory::Entry::Flags_File) == BackupStoreDirectory::Entry::Flags_File) + { + // File + storeFiles.insert(std::pair(name.GetClearFilename(), storeDirEn)); + } + else + { + // Dir + storeDirs.insert(std::pair(name.GetClearFilename(), storeDirEn)); + } + } + +#ifdef _MSC_VER + typedef std::set::iterator string_set_iter_t; +#else + typedef std::set::const_iterator string_set_iter_t; +#endif + + // Now compare files. + for(std::set >::const_iterator i = storeFiles.begin(); i != storeFiles.end(); ++i) + { + const std::string& fileName(i->first); + + 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 + 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? + bool equal = true; + + // File modified after last sync flag + bool modifiedAfterLastSync = false; + + bool hasDifferentAttribs = false; + + if(rParams.QuickCompare()) + { + // Compare file -- fetch it + mrConnection.QueryGetBlockIndexByID(i->second->GetObjectID()); + + // Stream containing block index + std::auto_ptr blockIndexStream(mrConnection.ReceiveStream()); + + // Compare + equal = BackupStoreFile::CompareFileContentsAgainstBlockIndex(localPath.c_str(), *blockIndexStream, mrConnection.GetTimeout()); + } + else + { + // Compare file -- fetch it + mrConnection.QueryGetFile(DirID, i->second->GetObjectID()); + + // Stream containing encoded file + std::auto_ptr objectStream(mrConnection.ReceiveStream()); + + // Decode it + std::auto_ptr fileOnServerStream; + // Got additional attributes? + if(i->second->HasAttributes()) + { + // Use these attributes + const StreamableMemBlock &storeAttr(i->second->GetAttributes()); + BackupClientFileAttributes attr(storeAttr); + fileOnServerStream.reset(BackupStoreFile::DecodeFileStream(*objectStream, mrConnection.GetTimeout(), &attr).release()); + } + else + { + // Use attributes stored in file + fileOnServerStream.reset(BackupStoreFile::DecodeFileStream(*objectStream, mrConnection.GetTimeout()).release()); + } + + // Should always be something in the auto_ptr, it's how the interface is defined. But be paranoid. + if(!fileOnServerStream.get()) + { + THROW_EXCEPTION(BackupStoreException, Internal) + } + + // Compare attributes + BackupClientFileAttributes localAttr; + box_time_t fileModTime = 0; + localAttr.ReadAttributes(localPath.c_str(), false /* don't zero mod times */, &fileModTime); + modifiedAfterLastSync = (fileModTime > rParams.LatestFileUploadTime()); + bool ignoreAttrModTime = true; + + #ifdef WIN32 + // attr mod time is really + // creation time, so check it + ignoreAttrModTime = false; + #endif + + if(!rParams.IgnoreAttributes() && + #ifdef PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE + !fileOnServerStream->IsSymLink() && + #endif + !localAttr.Compare(fileOnServerStream->GetAttributes(), + ignoreAttrModTime, + fileOnServerStream->IsSymLink() /* ignore modification time if it's a symlink */)) + { + 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()); + equal = l.CompareWith(*fileOnServerStream, + mrConnection.GetTimeout()); + } + } + + rParams.NotifyFileCompared(localPath, + storePath, fileSize, + hasDifferentAttribs, !equal, + modifiedAfterLastSync, + i->second->HasAttributes()); + } + catch(BoxException &e) + { + rParams.NotifyDownloadFailed(localPath, + storePath, fileSize, e); + } + catch(std::exception &e) + { + rParams.NotifyDownloadFailed(localPath, + storePath, fileSize, e); + } + catch(...) + { + rParams.NotifyDownloadFailed(localPath, + storePath, fileSize); + } + + // Remove from set so that we know it's been compared + localFiles.erase(local); + } + } + + // Report any files which exist locally, but not on the store + for(string_set_iter_t i = localFiles.begin(); i != localFiles.end(); ++i) + { + std::string localPath(MakeFullPath(rLocalDir, *i)); + std::string storePath(rStoreDir + "/" + *i); + + // Should this be ignored (ie is excluded)? + if(!rParams.IsExcludedFile(localPath)) + { + bool modifiedAfterLastSync = false; + + EMU_STRUCT_STAT st; + if(EMU_STAT(localPath.c_str(), &st) == 0) + { + if(FileModificationTime(st) > + rParams.LatestFileUploadTime()) + { + modifiedAfterLastSync = true; + } + } + + rParams.NotifyRemoteFileMissing(localPath, + storePath, modifiedAfterLastSync); + } + else + { + rParams.NotifyExcludedFile(localPath, + storePath); + } + } + + // Finished with the files, clear the sets to reduce memory usage slightly + localFiles.clear(); + storeFiles.clear(); + + // Now do the directories, recursively to check subdirectories + for(std::set >::const_iterator i = storeDirs.begin(); i != storeDirs.end(); ++i) + { + std::string localPath(MakeFullPath(rLocalDir, i->first)); + std::string storePath(rStoreDir + "/" + i->first); + + // Does the directory exist locally? + string_set_iter_t local(localDirs.find(i->first)); + if(local == localDirs.end() && + rParams.IsExcludedDir(localPath)) + { + rParams.NotifyExcludedFileNotDeleted(localPath, + storePath); + } + else if(local == localDirs.end()) + { + // Not found -- report + rParams.NotifyLocalFileMissing(localPath, + storePath); + } + else if(rParams.IsExcludedDir(localPath)) + { + // don't recurse into excluded directories + } + else + { + // Compare directory + Compare(i->second->GetObjectID(), + storePath, localPath, rParams); + + // Remove from set so that we know it's been compared + localDirs.erase(local); + } + } + + // Report any directories which exist locally, but not on the store + for(std::set::const_iterator + i = localDirs.begin(); + i != localDirs.end(); ++i) + { + std::string localPath(MakeFullPath(rLocalDir, *i)); + std::string storePath(rStoreDir + "/" + *i); + + // Should this be ignored (ie is excluded)? + if(!rParams.IsExcludedDir(localPath)) + { + 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.NotifyExcludedDir(localPath, storePath); + } + } + } + catch(...) + { + if(dirhandle != 0) + { + ::closedir(dirhandle); + } + throw; + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandRestore(const std::vector &, const bool *) +// Purpose: Restore a directory +// Created: 23/11/03 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandRestore(const std::vector &args, const bool *opts) +{ + // Check arguments + if(args.size() != 2) + { + BOX_ERROR("Incorrect usage. restore [-drif] "); + return; + } + + // Restoring deleted things? + bool restoreDeleted = opts['d']; + + std::string storeDirEncoded; + + // Get directory ID + int64_t dirID = 0; + if(opts['i']) + { + // Specified as ID. + dirID = ::strtoll(args[0].c_str(), 0, 16); + if(dirID == std::numeric_limits::min() || dirID == std::numeric_limits::max() || dirID == 0) + { + BOX_ERROR("Not a valid object ID (specified in hex)"); + return; + } + std::ostringstream oss; + oss << BOX_FORMAT_OBJECTID(args[0]); + storeDirEncoded = oss.str(); + } + else + { +#ifdef WIN32 + if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded)) + return; +#else + storeDirEncoded = args[0]; +#endif + + // Look up directory ID + dirID = FindDirectoryObjectID(storeDirEncoded, + false /* no old versions */, + restoreDeleted /* find deleted dirs */); + } + + // Allowable? + if(dirID == 0) + { + BOX_ERROR("Directory '" << args[0] << "' not found on server"); + return; + } + if(dirID == BackupProtocolClientListDirectory::RootDirectory) + { + BOX_ERROR("Cannot restore the root directory -- restore locations individually."); + return; + } + +#ifdef WIN32 + std::string localName; + if(!ConvertConsoleToUtf8(args[1].c_str(), localName)) return; +#else + std::string localName(args[1]); +#endif + + // Go and restore... + int result; + + try + { + // At TRACE level, we print a line for each file and + // directory, so we don't need dots. + + bool printDots = ! Logging::IsEnabled(Log::TRACE); + + result = BackupClientRestore(mrConnection, dirID, + storeDirEncoded.c_str(), localName.c_str(), + printDots /* print progress dots */, restoreDeleted, + false /* don't undelete after restore! */, + opts['r'] /* resume? */, + opts['f'] /* force continue after errors */); + } + catch(std::exception &e) + { + BOX_ERROR("Failed to restore: " << e.what()); + SetReturnCode(ReturnCode::Command_Error); + return; + } + catch(...) + { + BOX_ERROR("Failed to restore: unknown exception"); + SetReturnCode(ReturnCode::Command_Error); + return; + } + + switch(result) + { + case Restore_Complete: + 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(ReturnCode::Command_Error); + break; + + case Restore_TargetExists: + BOX_ERROR("The target directory exists. You cannot restore " + "over an existing directory."); + SetReturnCode(ReturnCode::Command_Error); + break; + + 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(ReturnCode::Command_Error); + break; + + case Restore_UnknownError: + BOX_ERROR("Unknown error during restore."); + SetReturnCode(ReturnCode::Command_Error); + break; + + default: + BOX_ERROR("Unknown restore result " << result << "."); + SetReturnCode(ReturnCode::Command_Error); + break; + } +} + + + +// These are autogenerated by a script. +extern char *help_commands[]; +extern char *help_text[]; + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandHelp(const std::vector &args) +// Purpose: Display help on commands +// Created: 15/2/04 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandHelp(const std::vector &args) +{ + if(args.size() == 0) + { + // Display a list of all commands + printf("Available commands are:\n"); + for(int c = 0; help_commands[c] != 0; ++c) + { + printf(" %s\n", help_commands[c]); + } + printf("Type \"help \" for more information on a command.\n\n"); + } + else + { + // Display help on a particular command + int c; + for(c = 0; help_commands[c] != 0; ++c) + { + if(::strcmp(help_commands[c], args[0].c_str()) == 0) + { + // Found the command, print help + printf("\n%s\n", help_text[c]); + break; + } + } + if(help_commands[c] == 0) + { + printf("No help found for command '%s'\n", args[0].c_str()); + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandUsage() +// Purpose: Display storage space used on server +// Created: 19/4/04 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandUsage(const bool *opts) +{ + bool MachineReadable = opts['m']; + + // Request full details from the server + std::auto_ptr usage(mrConnection.QueryGetAccountUsage()); + + // Display each entry in turn + int64_t hardLimit = usage->GetBlocksHardLimit(); + int32_t blockSize = usage->GetBlockSize(); + 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, 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, bool MachineReadable) +{ + std::cout << FormatUsageLineStart(Name, MachineReadable) << + FormatUsageBar(Size, Size * BlockSize, HardLimit * BlockSize, + MachineReadable) << std::endl; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandUndelete(const std::vector &, const bool *) +// Purpose: Undelete a directory +// Created: 23/11/03 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandUndelete(const std::vector &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 or undelete -i "); + return; + } + +#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, + /* include old and deleted files */ + BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING, + &flagsOut); + + if (fileId == 0) + { + // error already reported + return; + } + + // 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 &, const bool *) +// Purpose: Deletes a file +// Created: 23/11/03 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandDelete(const std::vector &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. delete "); + return; + } + +#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 new file mode 100644 index 00000000..392aa428 --- /dev/null +++ b/bin/bbackupquery/BackupQueries.h @@ -0,0 +1,346 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupQueries.h +// Purpose: Perform various queries on the backup store server. +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- + +#ifndef BACKUPQUERIES__H +#define BACKUPQUERIES__H + +#include +#include + +#include "BoxTime.h" +#include "BoxBackupCompareParams.h" + +class BackupProtocolClient; +class Configuration; +class ExcludeList; + +// -------------------------------------------------------------------------- +// +// Class +// Name: BackupQueries +// Purpose: Perform various queries on the backup store server. +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +class BackupQueries +{ +public: + BackupQueries(BackupProtocolClient &rConnection, + const Configuration &rConfiguration, + bool readWrite); + ~BackupQueries(); +private: + BackupQueries(const BackupQueries &); +public: + + void DoCommand(const char *Command, bool isFromCommandLine); + + // Ready to stop? + bool Stop() {return mQuitNow;} + + // Return code? + int GetReturnCode() {return mReturnCode;} + +private: + // Commands + void CommandList(const std::vector &args, const bool *opts); + void CommandChangeDir(const std::vector &args, const bool *opts); + void CommandChangeLocalDir(const std::vector &args); + void CommandGetObject(const std::vector &args, const bool *opts); + void CommandGet(std::vector args, const bool *opts); + void CommandCompare(const std::vector &args, const bool *opts); + void CommandRestore(const std::vector &args, const bool *opts); + void CommandUndelete(const std::vector &args, const bool *opts); + void CommandDelete(const std::vector &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 &args); + + // Implementations + void List(int64_t DirID, const std::string &rListRoot, const bool *opts, + bool FirstLevel); + +public: + class CompareParams : public BoxBackupCompareParams + { + public: + CompareParams(bool QuickCompare, bool IgnoreExcludes, + bool IgnoreAttributes, box_time_t LatestFileUploadTime); + + bool mQuietCompare; + int mDifferences; + int mDifferencesExplainedByModTime; + int mUncheckedFiles; + int mExcludedDirs; + int mExcludedFiles; + + 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, + 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 > *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; + std::vector > mDirStack; + bool mRunningAsRoot; + bool mWarnedAboutOwnerAttributes; + int mReturnCode; +}; + +#endif // BACKUPQUERIES__H + 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 +#include + +#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 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/Makefile.extra b/bin/bbackupquery/Makefile.extra new file mode 100644 index 00000000..e1049b6d --- /dev/null +++ b/bin/bbackupquery/Makefile.extra @@ -0,0 +1,6 @@ + +# AUTOGEN SEEDING +autogen_Documentation.cpp: makedocumentation.pl documentation.txt + $(_PERL) makedocumentation.pl + + diff --git a/bin/bbackupquery/bbackupquery.cpp b/bin/bbackupquery/bbackupquery.cpp new file mode 100644 index 00000000..5aa7e97e --- /dev/null +++ b/bin/bbackupquery/bbackupquery.cpp @@ -0,0 +1,441 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: bbackupquery.cpp +// Purpose: Backup query utility +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_UNISTD_H + #include +#endif + +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H + #include +#endif + +#ifdef HAVE_LIBREADLINE + #ifdef HAVE_READLINE_READLINE_H + #include + #elif defined(HAVE_EDITLINE_READLINE_H) + #include + #elif defined(HAVE_READLINE_H) + #include + #endif +#endif +#ifdef HAVE_READLINE_HISTORY + #ifdef HAVE_READLINE_HISTORY_H + #include + #elif defined(HAVE_HISTORY_H) + #include + #endif +#endif + +#include + +#include "MainHelper.h" +#include "BoxPortsAndFiles.h" +#include "BackupDaemonConfigVerify.h" +#include "SocketStreamTLS.h" +#include "Socket.h" +#include "TLSContext.h" +#include "SSLLib.h" +#include "BackupStoreConstants.h" +#include "BackupStoreException.h" +#include "autogen_BackupProtocolClient.h" +#include "BackupQueries.h" +#include "FdGetLine.h" +#include "BackupClientCryptoKeys.h" +#include "BannerText.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +void PrintUsageAndExit() +{ + printf("Usage: bbackupquery [-q*|v*|V|W] [-w] " +#ifdef WIN32 + "[-u] " +#endif + "\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 the quit command unless you want to end up in interactive mode.\n"); + exit(1); +} + +int main(int argc, const char *argv[]) +{ + int returnCode = 0; + + MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT("bbackupquery.memleaks", + "bbackupquery") + MAINHELPER_START + +#ifdef WIN32 + WSADATA info; + + // Under Win32 we must initialise the Winsock library + // before using it. + + if (WSAStartup(0x0101, &info) == SOCKET_ERROR) + { + // throw error? perhaps give it its own id in the future + THROW_EXCEPTION(BackupStoreException, Internal) + } +#endif + + // Really don't want trace statements happening, even in debug mode + #ifndef BOX_RELEASE_BUILD + BoxDebugTraceOn = false; + #endif + + FILE *logFile = 0; + + // Filename for configuration file? + std::string configFilename; + + #ifdef WIN32 + configFilename = BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE; + #else + configFilename = BOX_FILE_BBACKUPD_DEFAULT_CONFIG; + #endif + + // Flags + bool readWrite = false; + + Logging::SetProgramName("bbackupquery"); + + #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 = "qvVwuc:l:o:O:W:"; + bool unicodeConsole = false; +#else + const char* validOpts = "qvVwc: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) + { + switch(c) + { + case 'q': + { + if(masterLevel == Log::NOTHING) + { + BOX_FATAL("Too many '-q': " + "Cannot reduce logging " + "level any more"); + return 2; + } + masterLevel--; + } + break; + + case 'v': + { + if(masterLevel == Log::EVERYTHING) + { + BOX_FATAL("Too many '-v': " + "Cannot increase logging " + "level any more"); + return 2; + } + masterLevel++; + } + break; + + case 'V': + { + masterLevel = Log::EVERYTHING; + } + 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; + break; + + case 'c': + // store argument + configFilename = optarg; + break; + + case 'l': + // open log file + logFile = ::fopen(optarg, "w"); + if(logFile == 0) + { + 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; + +#ifdef WIN32 + case 'u': + unicodeConsole = true; + break; +#endif + + case '?': + default: + PrintUsageAndExit(); + } + } + // Adjust arguments + argc -= optind; + argv += optind; + + Logging::SetGlobalLevel((Log::Level)masterLevel); + + std::auto_ptr 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) + { + const char *banner = BANNER_TEXT("Backup Query Tool"); + BOX_NOTICE(banner); + } + +#ifdef WIN32 + if (unicodeConsole) + { + if (!SetConsoleCP(CP_UTF8)) + { + BOX_ERROR("Failed to set input codepage: " << + GetErrorMessage(GetLastError())); + } + + if (!SetConsoleOutputCP(CP_UTF8)) + { + BOX_ERROR("Failed to set output codepage: " << + GetErrorMessage(GetLastError())); + } + + // enable input of Unicode characters + if (_fileno(stdin) != -1 && + _setmode(_fileno(stdin), _O_TEXT) == -1) + { + perror("Failed to set the console input to " + "binary mode"); + } + } +#endif // WIN32 + + // Read in the configuration file + if(!quiet) BOX_INFO("Using configuration file " << configFilename); + + std::string errs; + std::auto_ptr config( + Configuration::LoadAndVerify + (configFilename, &BackupDaemonConfigVerify, errs)); + + if(config.get() == 0 || !errs.empty()) + { + BOX_FATAL("Invalid configuration file: " << errs); + return 1; + } + // Easier coding + const Configuration &conf(*config); + + // Setup and connect + // 1. TLS context + SSLLib::Initialise(); + // Read in the certificates creating a TLS context + TLSContext tlsContext; + 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()); + + // Initialise keys + BackupClientCryptoKeys_Setup(conf.GetKeyValue("KeysFile").c_str()); + + // 2. Connect to server + if(!quiet) BOX_INFO("Connecting to store..."); + SocketStreamTLS socket; + 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..."); + BackupProtocolClient connection(socket); + connection.Handshake(); + + // logging? + if(logFile != 0) + { + connection.SetLogToFile(logFile); + } + + // 4. Log in to server + if(!quiet) BOX_INFO("Login to store..."); + // Check the version of the server + { + std::auto_ptr serverVersion(connection.QueryVersion(BACKUP_STORE_SERVER_VERSION)); + if(serverVersion->GetVersion() != BACKUP_STORE_SERVER_VERSION) + { + THROW_EXCEPTION(BackupStoreException, WrongServerVersion) + } + } + // Login -- if this fails, the Protocol will exception + connection.QueryLogin(conf.GetKeyValueInt("AccountNumber"), + (readWrite)?0:(BackupProtocolClientLogin::Flags_ReadOnly)); + + // 5. Tell user. + 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, readWrite); + + // Start running commands... first from the command line + { + int c = 0; + while(c < argc && !context.Stop()) + { + context.DoCommand(argv[c++], true); + } + } + + // Get commands from input + +#ifdef HAVE_LIBREADLINE + // Must initialise the locale before using editline's readline(), + // otherwise cannot enter international characters. + if (setlocale(LC_ALL, "") == NULL) + { + BOX_ERROR("Failed to initialise locale. International " + "character support may not work."); + } + +#ifdef HAVE_READLINE_HISTORY + using_history(); +#endif + char *last_cmd = 0; + while(!context.Stop()) + { + char *command = readline("query > "); + if(command == NULL) + { + // Ctrl-D pressed -- terminate now + break; + } + context.DoCommand(command, false); + if(last_cmd != 0 && ::strcmp(last_cmd, command) == 0) + { + free(command); + } + else + { +#ifdef HAVE_READLINE_HISTORY + add_history(command); +#else + free(last_cmd); +#endif + last_cmd = command; + } + } +#ifndef HAVE_READLINE_HISTORY + free(last_cmd); + last_cmd = 0; +#endif +#else + // Version for platforms which don't have readline by default + if(fileno(stdin) >= 0) + { + FdGetLine getLine(fileno(stdin)); + while(!context.Stop()) + { + printf("query > "); + fflush(stdout); + std::string command(getLine.GetLine()); + context.DoCommand(command.c_str(), false); + } + } +#endif + + // Done... stop nicely + if(!quiet) BOX_INFO("Logging off..."); + connection.QueryFinished(); + if(!quiet) BOX_INFO("Session finished."); + + // Return code + returnCode = context.GetReturnCode(); + + // Close log file? + if(logFile) + { + ::fclose(logFile); + } + + // Let everything be cleaned up on exit. + +#ifdef WIN32 + // Clean up our sockets + // FIXME we should do this, but I get an abort() when I try + // WSACleanup(); +#endif + + MAINHELPER_END + + return returnCode; +} + diff --git a/bin/bbackupquery/documentation.txt b/bin/bbackupquery/documentation.txt new file mode 100644 index 00000000..96eda158 --- /dev/null +++ b/bin/bbackupquery/documentation.txt @@ -0,0 +1,187 @@ + +bbackupquery utility -- examine store, compare files, restore, etc. + +This file has markers for automatic help generation script -- '>' marks a start of a command/help topic, +and '<' marks the end of a section. + +Command line: +============= + +> bbackupquery [-q] [-c configfile] [commands ...] + + -q -- quiet, no information prompts + -c -- specify another bbackupd configuation file + +The commands following the options are executed, then (if there was no quit +command) an interactive mode is entered. + +If a command contains a space, enclose it in quotes. Example + + bbackupquery "list testdir1" quit + +to list the contents of testdir1, and then exit without interactive mode. +< + +Commands: +========= + +All directory names relative to a "current" directory, or from root if they +start with '/'. The initial directory is always the root directory. + + +> list [options] [directory-name] + + List contents of current directory, or specified directory. + + -r -- recursively list all files + -d -- list deleted files/directories + -o -- list old versions of files/directories + -I -- don't display object ID + -F -- don't display flags + -t -- show file modification time in local time + (and attr mod time if has the object has attributes, ~ separated) + -T -- show file modification time in GMT + -a -- show updated attribute instead of file modification time + -s -- show file size in blocks used on server + (only very approximate indication of size locally) + -h -- show file attributes hash + +ls can be used as an alias. +< + +> ls + + Alias for 'list'. Type 'help list' for options. +< + +> cd [options] + + Change directory + + -d -- consider deleted directories for traversal + -o -- consider old versions of directories for traversal + (this option should never be useful in a correctly formed store) +< + +> pwd + + Print current directory, always root relative. +< + +> lcd + + Change local directory. + + Type "sh ls" to list the contents. +< + +> sh + + All of the parameters after the "sh" are run as a shell command. + + For example, to list the contents of the location directory, type "sh ls" +< + +> get [] +get -i + + Gets a file from the store. Object is specified as the filename within + the current directory, and local filename is optional. Ignores old and + deleted files when searching the directory for the file to retrieve. + + To get an old or deleted file, use the -i option and select the object + as a hex object ID (first column in listing). The local filename must + be specified. +< + +> compare -a +compare -l +compare + + Compares the (current) data on the store with the data on the disc. + All the data will be downloaded -- this is potentially a very long + operation. + + -a -- compare all locations + -l -- compare one backup location as specified in the configuration file. + -c -- set return code + -q -- quick compare. Only checks file contents against checksums, + doesn't do a full download + -A -- ignore attribute differences + -E -- ignore exclusion settings + + Comparing with the root directory is an error, use -a option instead. + + If -c is set, then the return code (if quit is the next command) will be + 1 Comparison was exact + 2 Differences were found + 3 An error occured + This can be used for automated tests. +< + +> restore [-drif] + + Restores a directory to the local disc. The local directory specified + must not exist (unless a previous restore is being restarted). + + The root cannot be restored -- restore locations individually. + + -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 + regular intervals during the restore operation to allow restarts. +< + +> getobject + + Gets the object specified by the object id (in hex) and stores the raw + contents in the local file specified. + + This is only useful for debugging as it does not decode files from the + stored format, which is encrypted and compressed. +< + +> 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 + Directories: Space used by the directory structure. + + When Used exceeds the soft limit, the server will start to remove old and + deleted files until the usage drops below the soft limit. + + After a while, you would expect to see the usage stay at just below the + soft limit. You only need more space if the space used by old and deleted + files is near zero. +< + +> undelete +undelete -i + + 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 + + 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/bbackupquery/makedocumentation.pl.in b/bin/bbackupquery/makedocumentation.pl.in new file mode 100755 index 00000000..530c4ff6 --- /dev/null +++ b/bin/bbackupquery/makedocumentation.pl.in @@ -0,0 +1,75 @@ +#!@PERL@ +use strict; + +print "Creating built-in documentation for bbackupquery...\n"; + +open DOC,"documentation.txt" or die "Can't open documentation.txt file"; +my $section; +my %help; +my @in_order; + +while() +{ + if(m/\A>\s+(\w+)/) + { + $section = $1; + m/\A>\s+(.+)\Z/; + $help{$section} = $1."\n"; + push @in_order,$section; + } + elsif(m/\Aautogen_Documentation.cpp" or die "Can't open output file for writing"; + +print OUT <<__E; +// +// Automatically generated file, do not edit. +// + +#include "Box.h" + +#include "MemLeakFindOn.h" + +const char *help_commands[] = +{ +__E + +for(@in_order) +{ + print OUT qq:\t"$_",\n:; +} + +print OUT <<__E; + 0 +}; + +const char *help_text[] = +{ +__E + +for(@in_order) +{ + my $t = $help{$_}; + $t =~ s/\t/ /g; + $t =~ s/\n/\\n/g; + $t =~ s/"/\\"/g; + print OUT qq:\t"$t",\n:; +} + +print OUT <<__E; + 0 +}; + +__E + +close OUT; diff --git a/bin/bbstoreaccounts/bbstoreaccounts.cpp b/bin/bbstoreaccounts/bbstoreaccounts.cpp new file mode 100644 index 00000000..a9d2b0af --- /dev/null +++ b/bin/bbstoreaccounts/bbstoreaccounts.cpp @@ -0,0 +1,626 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: bbstoreaccounts +// Purpose: backup store administration tool +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "BoxPortsAndFiles.h" +#include "BackupStoreConfigVerify.h" +#include "RaidFileController.h" +#include "BackupStoreAccounts.h" +#include "BackupStoreAccountDatabase.h" +#include "MainHelper.h" +#include "BackupStoreInfo.h" +#include "StoreStructure.h" +#include "NamedLock.h" +#include "UnixUser.h" +#include "BackupStoreCheck.h" +#include "Utils.h" + +#include "MemLeakFindOn.h" + +#include + +// 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) + { + BOX_FATAL("Soft limit must be less than the hard limit."); + exit(1); + } + if(SoftLimit > ((HardLimit * MAX_SOFT_LIMIT_SIZE) / 100)) + { + BOX_WARNING("We recommend setting the soft limit below " << + MAX_SOFT_LIMIT_SIZE << "% of the hard limit, or " << + HumanReadableSize((HardLimit * MAX_SOFT_LIMIT_SIZE) + / 100) << " in this case."); + } +} + +int BlockSizeOfDiscSet(int DiscSet) +{ + // Get controller, check disc set number + RaidFileController &controller(RaidFileController::GetController()); + if(DiscSet < 0 || DiscSet >= controller.GetNumDiscSets()) + { + BOX_FATAL("Disc set " << DiscSet << " does not exist."); + exit(1); + } + + // Return block size + return controller.GetDiscSet(DiscSet).GetBlockSize(); +} + +std::string BlockSizeToString(int64_t Blocks, int64_t MaxBlocks, int DiscSet) +{ + return FormatUsageBar(Blocks, Blocks * BlockSizeOfDiscSet(DiscSet), + MaxBlocks * BlockSizeOfDiscSet(DiscSet), + sMachineReadableOutput); +} + +int64_t SizeStringToBlocks(const char *string, int DiscSet) +{ + // Find block size + int blockSize = BlockSizeOfDiscSet(DiscSet); + + // Get number + char *endptr = (char*)string; + int64_t number = strtol(string, &endptr, 0); + if(endptr == string || number == LONG_MIN || number == LONG_MAX) + { + BOX_FATAL("'" << string << "' is not a valid number."); + exit(1); + } + + // Check units + switch(*endptr) + { + case 'M': + case 'm': + // Units: Mb + return (number * 1024*1024) / blockSize; + break; + + case 'G': + case 'g': + // Units: Gb + return (number * 1024*1024*1024) / blockSize; + break; + + case 'B': + case 'b': + // Units: Blocks + // Easy! Just return the number specified. + return number; + break; + + default: + BOX_FATAL(string << " has an invalid units specifier " + "(use B for blocks, M for MB, G for GB, eg 2GB)"); + exit(1); + break; + } +} + +bool GetWriteLockOnAccount(NamedLock &rLock, const std::string rRootDir, int DiscSetNum) +{ + std::string writeLockFilename; + StoreStructure::MakeWriteLockFilename(rRootDir, DiscSetNum, writeLockFilename); + + bool gotLock = false; + int triesLeft = 8; + do + { + gotLock = rLock.TryAndGetLock(writeLockFilename.c_str(), 0600 /* restrictive file permissions */); + + if(!gotLock) + { + --triesLeft; + ::sleep(1); + } + } while(!gotLock && triesLeft > 0); + + if(!gotLock) + { + // Couldn't lock the account -- just stop now + BOX_ERROR("Failed to lock the account, did not change limits. " + "Try again later."); + } + + return gotLock; +} + +int SetLimit(Configuration &rConfig, const std::string &rUsername, int32_t ID, const char *SoftLimitStr, const char *HardLimitStr) +{ + // Become the user specified in the config file? + std::auto_ptr user; + if(!rUsername.empty()) + { + // Username specified, change... + user.reset(new UnixUser(rUsername.c_str())); + user->ChangeProcessUser(true /* temporary */); + // Change will be undone at the end of this function + } + + // Load in the account database + std::auto_ptr db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); + + // Already exists? + if(!db->EntryExists(ID)) + { + BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << + " does not exist."); + return 1; + } + + // Load it in + BackupStoreAccounts acc(*db); + std::string rootDir; + int discSet; + acc.GetAccountRoot(ID, rootDir, discSet); + + // Attempt to lock + NamedLock writeLock; + if(!GetWriteLockOnAccount(writeLock, rootDir, discSet)) + { + // Failed to get lock + return 1; + } + + // Load the info + std::auto_ptr info(BackupStoreInfo::Load(ID, rootDir, discSet, false /* Read/Write */)); + + // Change the limits + int64_t softlimit = SizeStringToBlocks(SoftLimitStr, discSet); + int64_t hardlimit = SizeStringToBlocks(HardLimitStr, discSet); + CheckSoftHardLimits(softlimit, hardlimit); + info->ChangeLimits(softlimit, hardlimit); + + // Save + info->Save(); + + BOX_NOTICE("Limits on account " << BOX_FORMAT_ACCOUNT(ID) << + " changed to " << softlimit << " soft, " << + hardlimit << " hard."); + + return 0; +} + +int AccountInfo(Configuration &rConfig, int32_t ID) +{ + // Load in the account database + std::auto_ptr db( + BackupStoreAccountDatabase::Read( + rConfig.GetKeyValue("AccountDatabase").c_str())); + + // Exists? + if(!db->EntryExists(ID)) + { + BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << + " does not exist."); + return 1; + } + + // Load it in + BackupStoreAccounts acc(*db); + std::string rootDir; + int discSet; + acc.GetAccountRoot(ID, rootDir, discSet); + std::auto_ptr info(BackupStoreInfo::Load(ID, + rootDir, discSet, true /* ReadOnly */)); + + // Then print out lots of info + 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; +} + +int DeleteAccount(Configuration &rConfig, const std::string &rUsername, int32_t ID, bool AskForConfirmation) +{ + // Check user really wants to do this + if(AskForConfirmation) + { + BOX_WARNING("Really delete account " << + BOX_FORMAT_ACCOUNT(ID) << "? (type 'yes' to confirm)"); + char response[256]; + if(::fgets(response, sizeof(response), stdin) == 0 || ::strcmp(response, "yes\n") != 0) + { + BOX_NOTICE("Deletion cancelled."); + return 0; + } + } + + // Load in the account database + std::auto_ptr db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); + + // Exists? + if(!db->EntryExists(ID)) + { + BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << + " does not exist."); + return 1; + } + + // Get info from the database + BackupStoreAccounts acc(*db); + std::string rootDir; + int discSetNum; + acc.GetAccountRoot(ID, rootDir, discSetNum); + + // Obtain a write lock, as the daemon user + NamedLock writeLock; + { + // Bbecome the user specified in the config file + std::auto_ptr user; + if(!rUsername.empty()) + { + // Username specified, change... + user.reset(new UnixUser(rUsername.c_str())); + user->ChangeProcessUser(true /* temporary */); + // Change will be undone at the end of this function + } + + // Get a write lock + if(!GetWriteLockOnAccount(writeLock, rootDir, discSetNum)) + { + // Failed to get lock + return 1; + } + + // Back to original user, but write is maintained + } + + // Delete from account database + db->DeleteEntry(ID); + + // Write back to disc + db->Write(); + + // Remove the store files... + + // First, become the user specified in the config file + std::auto_ptr user; + if(!rUsername.empty()) + { + // Username specified, change... + user.reset(new UnixUser(rUsername.c_str())); + user->ChangeProcessUser(true /* temporary */); + // Change will be undone at the end of this function + } + + // Secondly, work out which directories need wiping + std::vector toDelete; + RaidFileController &rcontroller(RaidFileController::GetController()); + RaidFileDiscSet discSet(rcontroller.GetDiscSet(discSetNum)); + for(RaidFileDiscSet::const_iterator i(discSet.begin()); i != discSet.end(); ++i) + { + if(std::find(toDelete.begin(), toDelete.end(), *i) == toDelete.end()) + { + toDelete.push_back((*i) + DIRECTORY_SEPARATOR + rootDir); + } + } + + int retcode = 0; + + // Thirdly, delete the directories... + for(std::vector::const_iterator d(toDelete.begin()); d != toDelete.end(); ++d) + { + BOX_NOTICE("Deleting store directory " << (*d) << "..."); + // Just use the rm command to delete the files + std::string cmd("rm -rf "); + cmd += *d; + // Run command + if(::system(cmd.c_str()) != 0) + { + BOX_ERROR("Failed to delete files in " << (*d) << + ", delete them manually."); + retcode = 1; + } + } + + // Success! + return retcode; +} + +int CheckAccount(Configuration &rConfig, const std::string &rUsername, int32_t ID, bool FixErrors, bool Quiet) +{ + // Load in the account database + std::auto_ptr db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); + + // Exists? + if(!db->EntryExists(ID)) + { + BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << + " does not exist."); + return 1; + } + + // Get info from the database + BackupStoreAccounts acc(*db); + std::string rootDir; + int discSetNum; + acc.GetAccountRoot(ID, rootDir, discSetNum); + + // Become the right user + std::auto_ptr user; + if(!rUsername.empty()) + { + // Username specified, change... + user.reset(new UnixUser(rUsername.c_str())); + user->ChangeProcessUser(true /* temporary */); + // Change will be undone at the end of this function + } + + // Check it + BackupStoreCheck check(rootDir, discSetNum, ID, FixErrors, Quiet); + check.Check(); + + return check.ErrorsFound()?1:0; +} + +int CreateAccount(Configuration &rConfig, const std::string &rUsername, int32_t ID, int32_t DiscNumber, int32_t SoftLimit, int32_t HardLimit) +{ + // Load in the account database + std::auto_ptr db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); + + // Already exists? + if(db->EntryExists(ID)) + { + BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << + " already exists."); + return 1; + } + + // Create it. + BackupStoreAccounts acc(*db); + acc.Create(ID, DiscNumber, SoftLimit, HardLimit, rUsername); + + BOX_NOTICE("Account " << BOX_FORMAT_ACCOUNT(ID) << " created."); + + return 0; +} + +void PrintUsageAndExit() +{ + 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 \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] \n" +" Prints information about the specified account including number\n" +" of blocks used. The -m option enable machine-readable output.\n" +" setlimit \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 [yes]\n" +" Deletes the specified account. Prompts for confirmation unless\n" +" the optional 'yes' parameter is provided.\n" +" check [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[]) +{ + MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT("bbstoreaccounts.memleaks", + "bbstoreaccounts") + + MAINHELPER_START + + Logging::SetProgramName("bbstoreaccounts"); + + // Filename for configuration file? + std::string configFilename; + + #ifdef WIN32 + configFilename = BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE; + #else + configFilename = BOX_FILE_BBSTORED_DEFAULT_CONFIG; + #endif + + // See if there's another entry on the command line + int c; + while((c = getopt(argc, (char * const *)argv, "c:m")) != -1) + { + switch(c) + { + case 'c': + // store argument + configFilename = optarg; + break; + + case 'm': + // enable machine readable output + sMachineReadableOutput = true; + break; + + case '?': + default: + PrintUsageAndExit(); + } + } + // Adjust arguments + argc -= optind; + argv += optind; + + // Read in the configuration file + std::string errs; + std::auto_ptr config( + Configuration::LoadAndVerify + (configFilename, &BackupConfigFileVerify, errs)); + + if(config.get() == 0 || !errs.empty()) + { + BOX_ERROR("Invalid configuration file " << configFilename << + ":" << errs); + } + + // Get the user under which the daemon runs + std::string username; + { + const Configuration &rserverConfig(config->GetSubConfiguration("Server")); + if(rserverConfig.KeyExists("User")) + { + username = rserverConfig.GetKeyValue("User"); + } + } + + // Initialise the raid file controller + RaidFileController &rcontroller(RaidFileController::GetController()); + rcontroller.Initialise(config->GetKeyValue("RaidFileConf").c_str()); + + // Then... check we have two arguments + if(argc < 2) + { + PrintUsageAndExit(); + } + + // Get the id + int32_t id; + if(::sscanf(argv[1], "%x", &id) != 1) + { + PrintUsageAndExit(); + } + + // Now do the command. + if(::strcmp(argv[0], "create") == 0) + { + // which disc? + int32_t discnum; + int32_t softlimit; + int32_t hardlimit; + if(argc < 5 + || ::sscanf(argv[2], "%d", &discnum) != 1) + { + BOX_ERROR("create requires raid file disc number, " + "soft and hard limits."); + return 1; + } + + // Decode limits + softlimit = SizeStringToBlocks(argv[3], discnum); + hardlimit = SizeStringToBlocks(argv[4], discnum); + CheckSoftHardLimits(softlimit, hardlimit); + + // Create the account... + return CreateAccount(*config, username, id, discnum, softlimit, hardlimit); + } + else if(::strcmp(argv[0], "info") == 0) + { + // Print information on this account + return AccountInfo(*config, id); + } + else if(::strcmp(argv[0], "setlimit") == 0) + { + // Change the limits on this account + if(argc < 4) + { + BOX_ERROR("setlimit requires soft and hard limits."); + return 1; + } + + return SetLimit(*config, username, id, argv[2], argv[3]); + } + else if(::strcmp(argv[0], "delete") == 0) + { + // Delete an account + bool askForConfirmation = true; + if(argc >= 3 && (::strcmp(argv[2], "yes") == 0)) + { + askForConfirmation = false; + } + return DeleteAccount(*config, username, id, askForConfirmation); + } + else if(::strcmp(argv[0], "check") == 0) + { + bool fixErrors = false; + bool quiet = false; + + // Look at other options + for(int o = 2; o < argc; ++o) + { + if(::strcmp(argv[o], "fix") == 0) + { + fixErrors = true; + } + else if(::strcmp(argv[o], "quiet") == 0) + { + quiet = true; + } + else + { + BOX_ERROR("Unknown option " << argv[o] << "."); + return 2; + } + } + + // Check the account + return CheckAccount(*config, username, id, fixErrors, quiet); + } + else + { + BOX_ERROR("Unknown command '" << argv[0] << "'."); + return 1; + } + + return 0; + + MAINHELPER_END +} + + diff --git a/bin/bbstored/BBStoreDHousekeeping.cpp b/bin/bbstored/BBStoreDHousekeeping.cpp new file mode 100644 index 00000000..7f799008 --- /dev/null +++ b/bin/bbstored/BBStoreDHousekeeping.cpp @@ -0,0 +1,240 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BBStoreDHousekeeping.cpp +// Purpose: Implementation of housekeeping functions for bbstored +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include "BackupStoreDaemon.h" +#include "BackupStoreAccountDatabase.h" +#include "BackupStoreAccounts.h" +#include "HousekeepStoreAccount.h" +#include "BoxTime.h" +#include "Configuration.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::HousekeepingProcess() +// Purpose: Do housekeeping +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- +void BackupStoreDaemon::HousekeepingInit() +{ + + mLastHousekeepingRun = 0; +} + +void BackupStoreDaemon::HousekeepingProcess() +{ + HousekeepingInit(); + + // Get the time between housekeeping runs + const Configuration &rconfig(GetConfiguration()); + int64_t housekeepingInterval = SecondsToBoxTime(rconfig.GetKeyValueInt("TimeBetweenHousekeeping")); + + while(!StopRun()) + { + RunHousekeepingIfNeeded(); + + // Stop early? + if(StopRun()) + { + break; + } + + // Calculate how long should wait before doing the next + // housekeeping run + int64_t timeNow = GetCurrentBoxTime(); + time_t secondsToGo = BoxTimeToSeconds( + (mLastHousekeepingRun + housekeepingInterval) - + timeNow); + if(secondsToGo < 1) secondsToGo = 1; + if(secondsToGo > 60) secondsToGo = 60; + int32_t millisecondsToGo = ((int)secondsToGo) * 1000; + + // Check to see if there's any message pending + CheckForInterProcessMsg(0 /* no account */, millisecondsToGo); + } +} + +void BackupStoreDaemon::RunHousekeepingIfNeeded() +{ + // Get the time between housekeeping runs + const Configuration &rconfig(GetConfiguration()); + int64_t housekeepingInterval = SecondsToBoxTime(rconfig.GetKeyValueInt("TimeBetweenHousekeeping")); + + // Time now + int64_t timeNow = GetCurrentBoxTime(); + + // Do housekeeping if the time interval has elapsed since the last check + if((timeNow - mLastHousekeepingRun) < housekeepingInterval) + { + return; + } + + // Store the time + mLastHousekeepingRun = timeNow; + BOX_INFO("Starting housekeeping"); + + // Get the list of accounts + std::vector accounts; + if(mpAccountDatabase) + { + mpAccountDatabase->GetAllAccountIDs(accounts); + } + + SetProcessTitle("housekeeping, active"); + + // Check them all + for(std::vector::const_iterator i = accounts.begin(); i != accounts.end(); ++i) + { + try + { + if(mpAccounts) + { + // Get the account root + std::string rootDir; + int discSet = 0; + mpAccounts->GetAccountRoot(*i, rootDir, discSet); + + // Do housekeeping on this account + HousekeepStoreAccount housekeeping(*i, rootDir, + discSet, this); + housekeeping.DoHousekeeping(); + } + } + catch(BoxException &e) + { + BOX_ERROR("Housekeeping on account " << + BOX_FORMAT_ACCOUNT(*i) << " threw exception, " + "aborting run for this account: " << + e.what() << " (" << + e.GetType() << "/" << e.GetSubType() << ")"); + } + catch(std::exception &e) + { + BOX_ERROR("Housekeeping on account " << + BOX_FORMAT_ACCOUNT(*i) << " threw exception, " + "aborting run for this account: " << + e.what()); + } + catch(...) + { + BOX_ERROR("Housekeeping on account " << + BOX_FORMAT_ACCOUNT(*i) << " threw exception, " + "aborting run for this account: " + "unknown exception"); + } + + int64_t timeNow = GetCurrentBoxTime(); + time_t secondsToGo = BoxTimeToSeconds( + (mLastHousekeepingRun + housekeepingInterval) - + timeNow); + if(secondsToGo < 1) secondsToGo = 1; + if(secondsToGo > 60) secondsToGo = 60; + int32_t millisecondsToGo = ((int)secondsToGo) * 1000; + + // Check to see if there's any message pending + CheckForInterProcessMsg(0 /* no account */, millisecondsToGo); + + // Stop early? + if(StopRun()) + { + break; + } + } + + BOX_INFO("Finished housekeeping"); + + // Placed here for accuracy, if StopRun() is true, for example. + SetProcessTitle("housekeeping, idle"); +} + +void BackupStoreDaemon::OnIdle() +{ + if (!IsSingleProcess()) + { + return; + } + + if (!mHousekeepingInited) + { + HousekeepingInit(); + mHousekeepingInited = true; + } + + RunHousekeepingIfNeeded(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::CheckForInterProcessMsg(int, int) +// Purpose: Process a message, returning true if the housekeeping process +// should abort for the specified account. +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- +bool BackupStoreDaemon::CheckForInterProcessMsg(int AccountNum, int MaximumWaitTime) +{ + if(!mInterProcessCommsSocket.IsOpened()) + { + return false; + } + + // First, check to see if it's EOF -- this means something has gone wrong, and the housekeeping should terminate. + if(mInterProcessComms.IsEOF()) + { + SetTerminateWanted(); + return true; + } + + // Get a line, and process the message + std::string line; + if(mInterProcessComms.GetLine(line, false /* no pre-processing */, MaximumWaitTime)) + { + BOX_TRACE("Housekeeping received command '" << line << + "' over interprocess comms"); + + int account = 0; + + if(line == "h") + { + // HUP signal received by main process + SetReloadConfigWanted(); + return true; + } + else if(line == "t") + { + // Terminate signal received by main process + SetTerminateWanted(); + return true; + } + else if(sscanf(line.c_str(), "r%x", &account) == 1) + { + // Main process is trying to lock an account -- are we processing it? + if(account == AccountNum) + { + // Yes! -- need to stop now so when it retries to get the lock, it will succeed + BOX_INFO("Housekeeping on account " << + BOX_FORMAT_ACCOUNT(AccountNum) << + "giving way to client connection"); + return true; + } + } + } + + return false; +} + + diff --git a/bin/bbstored/BackupCommands.cpp b/bin/bbstored/BackupCommands.cpp new file mode 100644 index 00000000..c27cb7ab --- /dev/null +++ b/bin/bbstored/BackupCommands.cpp @@ -0,0 +1,970 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupCommands.cpp +// Purpose: Implement commands for the Backup store protocol +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include + +#include "autogen_BackupProtocolServer.h" +#include "autogen_RaidFileException.h" +#include "BackupConstants.h" +#include "BackupStoreContext.h" +#include "BackupStoreConstants.h" +#include "BackupStoreDirectory.h" +#include "BackupStoreException.h" +#include "BackupStoreFile.h" +#include "BackupStoreInfo.h" +#include "BufferedStream.h" +#include "CollectInBufferStream.h" +#include "FileStream.h" +#include "InvisibleTempFileStream.h" +#include "RaidFileController.h" +#include "StreamableMemBlock.h" + +#include "MemLeakFindOn.h" + +#define CHECK_PHASE(phase) \ + if(rContext.GetPhase() != BackupStoreContext::phase) \ + { \ + return std::auto_ptr(new BackupProtocolServerError( \ + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_NotInRightProtocolPhase)); \ + } + +#define CHECK_WRITEABLE_SESSION \ + if(rContext.SessionIsReadOnly()) \ + { \ + return std::auto_ptr(new BackupProtocolServerError( \ + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_SessionReadOnly)); \ + } + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupProtocolServerVersion::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Version) + + // Correct version? + if(mVersion != BACKUP_STORE_SERVER_VERSION) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_WrongVersion)); + } + + // Mark the next phase + rContext.SetPhase(BackupStoreContext::Phase_Login); + + // Return our version + return std::auto_ptr(new BackupProtocolServerVersion(BACKUP_STORE_SERVER_VERSION)); +} + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupProtocolServerLogin::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Login) + + // Check given client ID against the ID in the certificate certificate + // and that the client actually has an account on this machine + if(mClientID != rContext.GetClientID()) + { + BOX_WARNING("Failed login from client ID " << + BOX_FORMAT_ACCOUNT(mClientID) << + ": wrong certificate for this account"); + return std::auto_ptr( + new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, + BackupProtocolServerError::Err_BadLogin)); + } + + if(!rContext.GetClientHasAccount()) + { + BOX_WARNING("Failed login from client ID " << + BOX_FORMAT_ACCOUNT(mClientID) << + ": no such account on this server"); + return std::auto_ptr( + new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, + BackupProtocolServerError::Err_BadLogin)); + } + + // If we need to write, check that nothing else has got a write lock + if((mFlags & Flags_ReadOnly) != Flags_ReadOnly) + { + // See if the context will get the lock + if(!rContext.AttemptToGetWriteLock()) + { + BOX_WARNING("Failed to get write lock for Client ID " << + BOX_FORMAT_ACCOUNT(mClientID)); + return std::auto_ptr( + new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, + BackupProtocolServerError::Err_CannotLockStoreForWriting)); + } + + // Debug: check we got the lock + ASSERT(!rContext.SessionIsReadOnly()); + } + + // Load the store info + rContext.LoadStoreInfo(); + + // Get the last client store marker + int64_t clientStoreMarker = rContext.GetClientStoreMarker(); + + // Mark the next phase + rContext.SetPhase(BackupStoreContext::Phase_Commands); + + // Log login + BOX_NOTICE("Login from Client ID " << + BOX_FORMAT_ACCOUNT(mClientID) << + " " << + (((mFlags & Flags_ReadOnly) != Flags_ReadOnly) + ?"Read/Write":"Read-only")); + + // Get the usage info for reporting to the client + int64_t blocksUsed = 0, blocksSoftLimit = 0, blocksHardLimit = 0; + rContext.GetStoreDiscUsageInfo(blocksUsed, blocksSoftLimit, blocksHardLimit); + + // Return success + return std::auto_ptr(new BackupProtocolServerLoginConfirmed(clientStoreMarker, blocksUsed, blocksSoftLimit, blocksHardLimit)); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerFinished::DoCommand(Protocol &, BackupStoreContext &) +// Purpose: Marks end of conversation (Protocol framework handles this) +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerFinished::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + BOX_NOTICE("Session finished for Client ID " << + BOX_FORMAT_ACCOUNT(rContext.GetClientID())); + + // Let the context know about it + rContext.ReceivedFinishCommand(); + + // can be called in any phase + return std::auto_ptr(new BackupProtocolServerFinished); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerListDirectory::DoCommand(Protocol &, BackupStoreContext &) +// Purpose: Command to list a directory +// Created: 2003/09/02 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerListDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + + // Store the listing to a stream + std::auto_ptr stream(new CollectInBufferStream); + + 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( + 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( + new BackupProtocolServerSuccess(mObjectID)); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerStoreFile::DoCommand(Protocol &, BackupStoreContext &) +// Purpose: Command to store a file on the server +// Created: 2003/09/02 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerStoreFile::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + CHECK_WRITEABLE_SESSION + + std::auto_ptr 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, BackupStoreContext::ObjectExists_File)) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DiffFromFileDoesNotExist)); + } + } + + // A stream follows, which contains the file + std::auto_ptr dirstream(rProtocol.ReceiveStream()); + + // Ask the context to store it + int64_t id = 0; + try + { + id = rContext.AddFile(*dirstream, mDirectoryObjectID, mModificationTime, mAttributesHash, mDiffFromFileID, + mFilename, true /* mark files with same name as old versions */); + } + catch(BackupStoreException &e) + { + if(e.GetSubType() == BackupStoreException::AddedFileDoesNotVerify) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_FileDoesNotVerify)); + } + else if(e.GetSubType() == BackupStoreException::AddedFileExceedsStorageLimit) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_StorageLimitExceeded)); + } + else + { + throw; + } + } + + // Tell the caller what the file was + return std::auto_ptr(new BackupProtocolServerSuccess(id)); +} + + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerGetObject::DoCommand(Protocol &, BackupStoreContext &) +// Purpose: Command to get an arbitary object from the server +// Created: 2003/09/03 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerGetObject::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + + // Check the object exists + if(!rContext.ObjectExists(mObjectID)) + { + return std::auto_ptr(new BackupProtocolServerSuccess(NoObject)); + } + + // Open the object + std::auto_ptr object(rContext.OpenObject(mObjectID)); + + // Stream it to the peer + rProtocol.SendStreamAfterCommand(object.release()); + + // Tell the caller what the file was + return std::auto_ptr(new BackupProtocolServerSuccess(mObjectID)); +} + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupProtocolServerGetFile::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + + // Check the objects exist + if(!rContext.ObjectExists(mObjectID) + || !rContext.ObjectExists(mInDirectory)) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DoesNotExist)); + } + + // Get the directory it's in + const BackupStoreDirectory &rdir(rContext.GetDirectory(mInDirectory)); + + // Find the object within the directory + BackupStoreDirectory::Entry *pfileEntry = rdir.FindEntryByID(mObjectID); + if(pfileEntry == 0) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DoesNotExistInDirectory)); + } + + // The result + std::auto_ptr stream; + + // Does this depend on anything? + if(pfileEntry->GetDependsNewer() != 0) + { + // File exists, but is a patch from a new version. Generate the older version. + std::vector patchChain; + int64_t id = mObjectID; + BackupStoreDirectory::Entry *en = 0; + do + { + patchChain.push_back(id); + en = rdir.FindEntryByID(id); + if(en == 0) + { + BOX_ERROR("Object " << + BOX_FORMAT_OBJECTID(mObjectID) << + " in dir " << + BOX_FORMAT_OBJECTID(mInDirectory) << + " for account " << + BOX_FORMAT_ACCOUNT(rContext.GetClientID()) << + " references object " << + BOX_FORMAT_OBJECTID(id) << + " which does not exist in dir"); + return std::auto_ptr( + new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, + BackupProtocolServerError::Err_PatchConsistencyError)); + } + id = en->GetDependsNewer(); + } + while(en != 0 && id != 0); + + // OK! The last entry in the chain is the full file, the others are patches back from it. + // Open the last one, which is the current from file + std::auto_ptr from(rContext.OpenObject(patchChain[patchChain.size() - 1])); + + // Then, for each patch in the chain, do a combine + for(int p = ((int)patchChain.size()) - 2; p >= 0; --p) + { + // ID of patch + int64_t patchID = patchChain[p]; + + // Open it a couple of times + std::auto_ptr diff(rContext.OpenObject(patchID)); + std::auto_ptr diff2(rContext.OpenObject(patchID)); + + // Choose a temporary filename for the result of the combination + std::ostringstream fs; + fs << rContext.GetStoreRoot() << ".recombinetemp." << p; + std::string tempFn = + RaidFileController::DiscSetPathToFileSystemPath( + rContext.GetStoreDiscSet(), fs.str(), + p + 16); + + // Open the temporary file + std::auto_ptr combined; + try + { + { + // Write nastily to allow this to work with gcc 2.x + std::auto_ptr t( + new InvisibleTempFileStream( + tempFn.c_str(), + O_RDWR | O_CREAT | + O_EXCL | O_BINARY | + O_TRUNC)); + combined = t; + } + } + catch(...) + { + // Make sure it goes + ::unlink(tempFn.c_str()); + throw; + } + + // Do the combining + BackupStoreFile::CombineFile(*diff, *diff2, *from, *combined); + + // Move to the beginning of the combined file + combined->Seek(0, IOStream::SeekType_Absolute); + + // Then shuffle round for the next go + if (from.get()) from->Close(); + from = combined; + } + + // Now, from contains a nice file to send to the client. Reorder it + { + // Write nastily to allow this to work with gcc 2.x + std::auto_ptr t(BackupStoreFile::ReorderFileToStreamOrder(from.get(), true /* take ownership */)); + stream = t; + } + + // Release from file to avoid double deletion + from.release(); + } + else + { + // Simple case: file already exists on disc ready to go + + // Open the object + std::auto_ptr object(rContext.OpenObject(mObjectID)); + BufferedStream buf(*object); + + // Verify it + if(!BackupStoreFile::VerifyEncodedFileFormat(buf)) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_FileDoesNotVerify)); + } + + // Reset stream -- seek to beginning + object->Seek(0, IOStream::SeekType_Absolute); + + // Reorder the stream/file into stream order + { + // Write nastily to allow this to work with gcc 2.x + std::auto_ptr t(BackupStoreFile::ReorderFileToStreamOrder(object.get(), true /* take ownership */)); + stream = t; + } + + // Object will be deleted when the stream is deleted, + // so can release the object auto_ptr here to avoid + // premature deletion + object.release(); + } + + // Stream the reordered stream to the peer + rProtocol.SendStreamAfterCommand(stream.get()); + + // Don't delete the stream here + stream.release(); + + // Tell the caller what the file was + return std::auto_ptr(new BackupProtocolServerSuccess(mObjectID)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerCreateDirectory::DoCommand(Protocol &, BackupStoreContext &) +// Purpose: Create directory command +// Created: 2003/09/04 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerCreateDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + CHECK_WRITEABLE_SESSION + + // Get the stream containing the attributes + std::auto_ptr attrstream(rProtocol.ReceiveStream()); + // Collect the attributes -- do this now so no matter what the outcome, + // the data has been absorbed. + StreamableMemBlock attr; + attr.Set(*attrstream, rProtocol.GetTimeout()); + + // Check to see if the hard limit has been exceeded + if(rContext.HardLimitExceeded()) + { + // Won't allow creation if the limit has been exceeded + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_StorageLimitExceeded)); + } + + bool alreadyExists = false; + int64_t id = rContext.AddDirectory(mContainingDirectoryID, mDirectoryName, attr, mAttributesModTime, alreadyExists); + + if(alreadyExists) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DirectoryAlreadyExists)); + } + + // Tell the caller what the file was + return std::auto_ptr(new BackupProtocolServerSuccess(id)); +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerChangeDirAttributes::DoCommand(Protocol &, BackupStoreContext &) +// Purpose: Change attributes on directory +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerChangeDirAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + CHECK_WRITEABLE_SESSION + + // Get the stream containing the attributes + std::auto_ptr attrstream(rProtocol.ReceiveStream()); + // Collect the attributes -- do this now so no matter what the outcome, + // the data has been absorbed. + StreamableMemBlock attr; + attr.Set(*attrstream, rProtocol.GetTimeout()); + + // Get the context to do it's magic + rContext.ChangeDirAttributes(mObjectID, attr, mAttributesModTime); + + // Tell the caller what the file was + return std::auto_ptr(new BackupProtocolServerSuccess(mObjectID)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerSetReplacementFileAttributes::DoCommand(Protocol &, BackupStoreContext &) +// Purpose: Change attributes on directory +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerSetReplacementFileAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + CHECK_WRITEABLE_SESSION + + // Get the stream containing the attributes + std::auto_ptr attrstream(rProtocol.ReceiveStream()); + // Collect the attributes -- do this now so no matter what the outcome, + // the data has been absorbed. + StreamableMemBlock attr; + attr.Set(*attrstream, rProtocol.GetTimeout()); + + // Get the context to do it's magic + int64_t objectID = 0; + if(!rContext.ChangeFileAttributes(mFilename, mInDirectory, attr, mAttributesHash, objectID)) + { + // Didn't exist + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DoesNotExist)); + } + + // Tell the caller what the file was + return std::auto_ptr(new BackupProtocolServerSuccess(objectID)); +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &, BackupStoreContext &) +// Purpose: Delete a file +// Created: 2003/10/21 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + CHECK_WRITEABLE_SESSION + + // Context handles this + int64_t objectID = 0; + rContext.DeleteFile(mFilename, mInDirectory, objectID); + + // return the object ID or zero for not found + return std::auto_ptr(new BackupProtocolServerSuccess(objectID)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerUndeleteFile::DoCommand( +// BackupProtocolServer &, BackupStoreContext &) +// Purpose: Undelete a file +// Created: 2008-09-12 +// +// -------------------------------------------------------------------------- +std::auto_ptr 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( + new BackupProtocolServerSuccess(result ? mObjectID : 0)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &, BackupStoreContext &) +// Purpose: Delete a directory +// Created: 2003/10/21 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + CHECK_WRITEABLE_SESSION + + // Check it's not asking for the root directory to be deleted + if(mObjectID == BACKUPSTORE_ROOT_DIRECTORY_ID) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_CannotDeleteRoot)); + } + + // Context handles this + rContext.DeleteDirectory(mObjectID); + + // return the object ID + return std::auto_ptr(new BackupProtocolServerSuccess(mObjectID)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &, BackupStoreContext &) +// Purpose: Undelete a directory +// Created: 23/11/03 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + CHECK_WRITEABLE_SESSION + + // Check it's not asking for the root directory to be deleted + if(mObjectID == BACKUPSTORE_ROOT_DIRECTORY_ID) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_CannotDeleteRoot)); + } + + // Context handles this + rContext.DeleteDirectory(mObjectID, true /* undelete */); + + // return the object ID + return std::auto_ptr(new BackupProtocolServerSuccess(mObjectID)); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &, BackupStoreContext &) +// Purpose: Command to set the client's store marker +// Created: 2003/10/29 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + CHECK_WRITEABLE_SESSION + + // Set the marker + rContext.SetClientStoreMarker(mClientStoreMarker); + + // return store marker set + return std::auto_ptr(new BackupProtocolServerSuccess(mClientStoreMarker)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &, BackupStoreContext &) +// Purpose: Command to move an object from one directory to another +// Created: 2003/11/12 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + CHECK_WRITEABLE_SESSION + + // Let context do this, but modify error reporting on exceptions... + try + { + rContext.MoveObject(mObjectID, mMoveFromDirectory, mMoveToDirectory, + mNewFilename, (mFlags & Flags_MoveAllWithSameName) == Flags_MoveAllWithSameName, + (mFlags & Flags_AllowMoveOverDeletedObject) == Flags_AllowMoveOverDeletedObject); + } + catch(BackupStoreException &e) + { + if(e.GetSubType() == BackupStoreException::CouldNotFindEntryInDirectory) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DoesNotExist)); + } + else if(e.GetSubType() == BackupStoreException::NameAlreadyExistsInDirectory) + { + return std::auto_ptr(new BackupProtocolServerError( + BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_TargetNameExists)); + } + else + { + throw; + } + } + + // Return the object ID + return std::auto_ptr(new BackupProtocolServerSuccess(mObjectID)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &, BackupStoreContext &) +// Purpose: Command to find the name of an object +// Created: 12/11/03 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + + // Create a stream for the list of filenames + std::auto_ptr stream(new CollectInBufferStream); + + // Object and directory IDs + int64_t objectID = mObjectID; + int64_t dirID = mContainingDirectoryID; + + // Data to return in the reply + int32_t numNameElements = 0; + int16_t objectFlags = 0; + int64_t modTime = 0; + uint64_t attrModHash = 0; + bool haveModTimes = false; + + do + { + // Check the directory really exists + if(!rContext.ObjectExists(dirID, BackupStoreContext::ObjectExists_Directory)) + { + return std::auto_ptr(new BackupProtocolServerObjectName(BackupProtocolServerObjectName::NumNameElements_ObjectDoesntExist, 0, 0, 0)); + } + + // Load up the directory + const BackupStoreDirectory &rdir(rContext.GetDirectory(dirID)); + + // Find the element in this directory and store it's name + if(objectID != ObjectID_DirectoryOnly) + { + const BackupStoreDirectory::Entry *en = rdir.FindEntryByID(objectID); + + // If this can't be found, then there is a problem... tell the caller it can't be found + if(en == 0) + { + // Abort! + return std::auto_ptr(new BackupProtocolServerObjectName(BackupProtocolServerObjectName::NumNameElements_ObjectDoesntExist, 0, 0, 0)); + } + + // Store flags? + if(objectFlags == 0) + { + objectFlags = en->GetFlags(); + } + + // Store modification times? + if(!haveModTimes) + { + modTime = en->GetModificationTime(); + attrModHash = en->GetAttributesHash(); + haveModTimes = true; + } + + // Store the name in the stream + en->GetName().WriteToStream(*stream); + + // Count of name elements + ++numNameElements; + } + + // Setup for next time round + objectID = dirID; + dirID = rdir.GetContainerID(); + + } while(objectID != 0 && objectID != BACKUPSTORE_ROOT_DIRECTORY_ID); + + // Stream to send? + if(numNameElements > 0) + { + // Get the stream ready to go + stream->SetForReading(); + // Tell the protocol to send the stream + rProtocol.SendStreamAfterCommand(stream.release()); + } + + // Make reply + return std::auto_ptr(new BackupProtocolServerObjectName(numNameElements, modTime, attrModHash, objectFlags)); +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &, BackupStoreContext &) +// Purpose: Get the block index from a file, by ID +// Created: 19/1/04 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + + // Open the file + std::auto_ptr stream(rContext.OpenObject(mObjectID)); + + // Move the file pointer to the block index + BackupStoreFile::MoveStreamPositionToBlockIndex(*stream); + + // Return the stream to the client + rProtocol.SendStreamAfterCommand(stream.release()); + + // Return the object ID + return std::auto_ptr(new BackupProtocolServerSuccess(mObjectID)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + + // Get the directory + const BackupStoreDirectory &dir(rContext.GetDirectory(mInDirectory)); + + // Find the latest object ID within it which has the same name + int64_t objectID = 0; + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + while((en = i.Next(BackupStoreDirectory::Entry::Flags_File)) != 0) + { + if(en->GetName() == mFilename) + { + // Store the ID, if it's a newer ID than the last one + if(en->GetObjectID() > objectID) + { + objectID = en->GetObjectID(); + } + } + } + + // Found anything? + if(objectID == 0) + { + // No... return a zero object ID + return std::auto_ptr(new BackupProtocolServerSuccess(0)); + } + + // Open the file + std::auto_ptr stream(rContext.OpenObject(objectID)); + + // Move the file pointer to the block index + BackupStoreFile::MoveStreamPositionToBlockIndex(*stream); + + // Return the stream to the client + rProtocol.SendStreamAfterCommand(stream.release()); + + // Return the object ID + return std::auto_ptr(new BackupProtocolServerSuccess(objectID)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &, BackupStoreContext &) +// Purpose: Return the amount of disc space used +// Created: 19/4/04 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + + // Get store info from context + const BackupStoreInfo &rinfo(rContext.GetBackupStoreInfo()); + + // Find block size + RaidFileController &rcontroller(RaidFileController::GetController()); + RaidFileDiscSet &rdiscSet(rcontroller.GetDiscSet(rinfo.GetDiscSetNumber())); + + // Return info + return std::auto_ptr(new BackupProtocolServerAccountUsage( + rinfo.GetBlocksUsed(), + rinfo.GetBlocksInOldFiles(), + rinfo.GetBlocksInDeletedFiles(), + rinfo.GetBlocksInDirectories(), + rinfo.GetBlocksSoftLimit(), + rinfo.GetBlocksHardLimit(), + rdiscSet.GetBlockSize() + )); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &, BackupStoreContext &) +// Purpose: Return the amount of disc space used +// Created: 19/4/04 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext) +{ + CHECK_PHASE(Phase_Commands) + + // + // NOOP + // + return std::auto_ptr(new BackupProtocolServerIsAlive()); +} diff --git a/bin/bbstored/BackupConstants.h b/bin/bbstored/BackupConstants.h new file mode 100644 index 00000000..19d06a15 --- /dev/null +++ b/bin/bbstored/BackupConstants.h @@ -0,0 +1,21 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupConstants.h +// Purpose: Constants for the backup server and client +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- + +#ifndef BACKUPCONSTANTS__H +#define BACKUPCONSTANTS__H + +// 15 minutes to timeout (milliseconds) +#define BACKUP_STORE_TIMEOUT (15*60*1000) + +// Should the store daemon convert files to Raid immediately? +#define BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY true + +#endif // BACKUPCONSTANTS__H + + diff --git a/bin/bbstored/BackupStoreContext.cpp b/bin/bbstored/BackupStoreContext.cpp new file mode 100644 index 00000000..5ee13faa --- /dev/null +++ b/bin/bbstored/BackupStoreContext.cpp @@ -0,0 +1,1785 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupStoreContext.cpp +// Purpose: Context for backup store server +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include "BackupConstants.h" +#include "BackupStoreContext.h" +#include "BackupStoreDaemon.h" +#include "BackupStoreDirectory.h" +#include "BackupStoreException.h" +#include "BackupStoreFile.h" +#include "BackupStoreInfo.h" +#include "BackupStoreObjectMagic.h" +#include "BufferedStream.h" +#include "BufferedWriteStream.h" +#include "FileStream.h" +#include "InvisibleTempFileStream.h" +#include "RaidFileController.h" +#include "RaidFileRead.h" +#include "RaidFileWrite.h" +#include "StoreStructure.h" + +#include "MemLeakFindOn.h" + + +// Maximum number of directories to keep in the cache +// When the cache is bigger than this, everything gets +// deleted. +#ifdef BOX_RELEASE_BUILD + #define MAX_CACHE_SIZE 32 +#else + #define MAX_CACHE_SIZE 2 +#endif + +// Allow the housekeeping process 4 seconds to release an account +#define MAX_WAIT_FOR_HOUSEKEEPING_TO_RELEASE_ACCOUNT 4 + +// Maximum amount of store info updates before it's actually saved to disc. +#define STORE_INFO_SAVE_DELAY 96 + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::BackupStoreContext() +// Purpose: Constructor +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +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), + mpTestHook(NULL) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::~BackupStoreContext() +// Purpose: Destructor +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +BackupStoreContext::~BackupStoreContext() +{ + // Delete the objects in the cache + for(std::map::iterator i(mDirectoryCache.begin()); i != mDirectoryCache.end(); ++i) + { + delete (i->second); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::CleanUp() +// Purpose: Clean up after a connection +// Created: 16/12/03 +// +// -------------------------------------------------------------------------- +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()) + { + mpStoreInfo->Save(); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::ReceivedFinishCommand() +// Purpose: Called when the finish command is received by the protocol +// Created: 16/12/03 +// +// -------------------------------------------------------------------------- +void BackupStoreContext::ReceivedFinishCommand() +{ + if(!mReadOnly && mpStoreInfo.get()) + { + // Save the store info, not delayed + SaveStoreInfo(false); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupStoreContext::AttemptToGetWriteLock() +{ + // Make the filename of the write lock file + std::string writeLockFile; + StoreStructure::MakeWriteLockFilename(mStoreRoot, mStoreDiscSet, writeLockFile); + + // Request the lock + bool gotLock = mWriteLock.TryAndGetLock(writeLockFile.c_str(), 0600 /* restrictive file permissions */); + + if(!gotLock) + { + // The housekeeping process might have the thing open -- ask it to stop + char msg[256]; + int msgLen = sprintf(msg, "r%x\n", mClientID); + // Send message + mrDaemon.SendMessageToHousekeepingProcess(msg, msgLen); + + // Then try again a few times + int tries = MAX_WAIT_FOR_HOUSEKEEPING_TO_RELEASE_ACCOUNT; + do + { + ::sleep(1 /* second */); + --tries; + gotLock = mWriteLock.TryAndGetLock(writeLockFile.c_str(), 0600 /* restrictive file permissions */); + + } while(!gotLock && tries > 0); + } + + if(gotLock) + { + // Got the lock, mark as not read only + mReadOnly = false; + } + + return gotLock; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::LoadStoreInfo() +// Purpose: Load the store info from disc +// Created: 2003/09/03 +// +// -------------------------------------------------------------------------- +void BackupStoreContext::LoadStoreInfo() +{ + if(mpStoreInfo.get() != 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoAlreadyLoaded) + } + + // Load it up! + std::auto_ptr i(BackupStoreInfo::Load(mClientID, mStoreRoot, mStoreDiscSet, mReadOnly)); + + // Check it + if(i->GetAccountID() != mClientID) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoForWrongAccount) + } + + // Keep the pointer to it + mpStoreInfo = i; + + BackupStoreAccountDatabase::Entry account(mClientID, mStoreDiscSet); + + // try to load the reference count database + try + { + mapRefCount = BackupStoreRefCountDatabase::Load(account, false); + } + catch(BoxException &e) + { + BOX_WARNING("Reference count database is missing or corrupted, " + "creating a new one, expect housekeeping to find and " + "fix problems with reference counts later."); + + BackupStoreRefCountDatabase::CreateForRegeneration(account); + mapRefCount = BackupStoreRefCountDatabase::Load(account, false); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::SaveStoreInfo(bool) +// Purpose: Potentially delayed saving of the store info +// Created: 16/12/03 +// +// -------------------------------------------------------------------------- +void BackupStoreContext::SaveStoreInfo(bool AllowDelay) +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + if(mReadOnly) + { + THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly) + } + + // Can delay saving it a little while? + if(AllowDelay) + { + --mSaveStoreInfoDelay; + if(mSaveStoreInfoDelay > 0) + { + return; + } + } + + // Want to save now + mpStoreInfo->Save(); + + // Set count for next delay + mSaveStoreInfoDelay = STORE_INFO_SAVE_DELAY; +} + + + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupStoreContext::MakeObjectFilename(int64_t ObjectID, std::string &rOutput, bool EnsureDirectoryExists) +{ + // Delegate to utility function + StoreStructure::MakeObjectFilename(ObjectID, mStoreRoot, mStoreDiscSet, rOutput, EnsureDirectoryExists); +} + + +// -------------------------------------------------------------------------- +// +// Function +// 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. +// Private version of this, which returns non-const directories. +// Created: 2003/09/02 +// +// -------------------------------------------------------------------------- +BackupStoreDirectory &BackupStoreContext::GetDirectoryInternal(int64_t ObjectID) +{ + // Get the filename + std::string filename; + MakeObjectFilename(ObjectID, filename); + + // Already in cache? + std::map::iterator item(mDirectoryCache.find(ObjectID)); + if(item != mDirectoryCache.end()) + { + // Check the revision ID of the file -- does it need refreshing? + int64_t revID = 0; + if(!RaidFileRead::FileExists(mStoreDiscSet, filename, &revID)) + { + THROW_EXCEPTION(BackupStoreException, DirectoryHasBeenDeleted) + } + + 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); + } + + // Need to load it up + + // First check to see if the cache is too big + if(mDirectoryCache.size() > MAX_CACHE_SIZE) + { + // Very simple. Just delete everything! + for(std::map::iterator i(mDirectoryCache.begin()); i != mDirectoryCache.end(); ++i) + { + delete (i->second); + } + mDirectoryCache.clear(); + } + + // Get a RaidFileRead to read it + int64_t revID = 0; + std::auto_ptr objectFile(RaidFileRead::Open(mStoreDiscSet, filename, &revID)); + ASSERT(revID != 0); + + // New directory object + std::auto_ptr dir(new BackupStoreDirectory); + + // Read it from the stream, then set it's revision ID + BufferedStream buf(*objectFile); + dir->ReadFromStream(buf, IOStream::TimeOutInfinite); + dir->SetRevisionID(revID); + + // Make sure the size of the directory is available for writing the dir back + int64_t dirSize = objectFile->GetDiscUsageInBlocks(); + ASSERT(dirSize > 0); + dir->SetUserInfo1_SizeInBlocks(dirSize); + + // Store in cache + BackupStoreDirectory *pdir = dir.release(); + try + { + mDirectoryCache[ObjectID] = pdir; + } + catch(...) + { + delete pdir; + throw; + } + + // Return it + return *pdir; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::AllocateObjectID() +// Purpose: Allocate a new object ID, tolerant of failures to save store info +// Created: 16/12/03 +// +// -------------------------------------------------------------------------- +int64_t BackupStoreContext::AllocateObjectID() +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + + // Given that the store info may not be saved for STORE_INFO_SAVE_DELAY + // times after it has been updated, this is a reasonable number of times + // to try for finding an unused ID. + // (Sizes used in the store info are fixed by the housekeeping process) + int retryLimit = (STORE_INFO_SAVE_DELAY * 2); + + while(retryLimit > 0) + { + // Attempt to allocate an ID from the store + int64_t id = mpStoreInfo->AllocateObjectID(); + + // Generate filename + std::string filename; + MakeObjectFilename(id, filename); + // Check it doesn't exist + if(!RaidFileRead::FileExists(mStoreDiscSet, filename)) + { + // Success! + return id; + } + + // Decrement retry count, and try again + --retryLimit; + + // Mark that the store info should be saved as soon as possible + mSaveStoreInfoDelay = 0; + + BOX_WARNING("When allocating object ID, found that " << + BOX_FORMAT_OBJECTID(id) << " is already in use"); + } + + THROW_EXCEPTION(BackupStoreException, CouldNotFindUnusedIDDuringAllocation) +} + + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupStoreContext::AddFile(IOStream &rFile, int64_t InDirectory, + int64_t ModificationTime, int64_t AttributesHash, + int64_t DiffFromFileID, const BackupStoreFilename &rFilename, + bool MarkFileWithSameNameAsOldVersions) +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + if(mReadOnly) + { + THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly) + } + + // This is going to be a bit complex to make sure it copes OK + // with things going wrong. + // The only thing which isn't safe is incrementing the object ID + // and keeping the blocks used entirely accurate -- but these + // aren't big problems if they go horribly wrong. The sizes will + // be corrected the next time the account has a housekeeping run, + // and the object ID allocation code is tolerant of missed IDs. + // (the info is written lazily, so these are necessary) + + // Get the directory we want to modify + BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory)); + + // Allocate the next ID + int64_t id = AllocateObjectID(); + + // Stream the file to disc + std::string fn; + MakeObjectFilename(id, fn, true /* make sure the directory it's in exists */); + int64_t blocksUsed = 0; + RaidFileWrite *ppreviousVerStoreFile = 0; + bool reversedDiffIsCompletelyDifferent = false; + int64_t oldVersionNewBlocksUsed = 0; + try + { + RaidFileWrite storeFile(mStoreDiscSet, fn); + storeFile.Open(false /* no overwriting */); + int64_t spaceAdjustFromDiff = 0; // size adjustment from use of patch in old file + + // Diff or full file? + if(DiffFromFileID == 0) + { + // A full file, just store to disc + if(!rFile.CopyStreamTo(storeFile, BACKUP_STORE_TIMEOUT)) + { + THROW_EXCEPTION(BackupStoreException, ReadFileFromStreamTimedOut) + } + } + else + { + // Check that the diffed from ID actually exists in the directory + if(dir.FindEntryByID(DiffFromFileID) == 0) + { + THROW_EXCEPTION(BackupStoreException, DiffFromIDNotFoundInDirectory) + } + + // Diff file, needs to be recreated. + // Choose a temporary filename. + std::string tempFn(RaidFileController::DiscSetPathToFileSystemPath(mStoreDiscSet, fn + ".difftemp", + 1 /* NOT the same disc as the write file, to avoid using lots of space on the same disc unnecessarily */)); + + try + { + // Open it twice +#ifdef WIN32 + InvisibleTempFileStream diff(tempFn.c_str(), + O_RDWR | O_CREAT | O_BINARY); + InvisibleTempFileStream diff2(tempFn.c_str(), + O_RDWR | O_BINARY); +#else + FileStream diff(tempFn.c_str(), O_RDWR | O_CREAT | O_EXCL); + FileStream diff2(tempFn.c_str(), O_RDONLY); + + // Unlink it immediately, so it definitely goes away + if(::unlink(tempFn.c_str()) != 0) + { + THROW_EXCEPTION(CommonException, OSFileError); + } +#endif + + // Stream the incoming diff to this temporary file + if(!rFile.CopyStreamTo(diff, BACKUP_STORE_TIMEOUT)) + { + THROW_EXCEPTION(BackupStoreException, ReadFileFromStreamTimedOut) + } + + // Verify the diff + diff.Seek(0, IOStream::SeekType_Absolute); + if(!BackupStoreFile::VerifyEncodedFileFormat(diff)) + { + THROW_EXCEPTION(BackupStoreException, AddedFileDoesNotVerify) + } + + // Seek to beginning of diff file + diff.Seek(0, IOStream::SeekType_Absolute); + + // Filename of the old version + std::string oldVersionFilename; + MakeObjectFilename(DiffFromFileID, oldVersionFilename, false /* no need to make sure the directory it's in exists */); + + // Reassemble that diff -- open previous file, and combine the patch and file + std::auto_ptr from(RaidFileRead::Open(mStoreDiscSet, oldVersionFilename)); + BackupStoreFile::CombineFile(diff, diff2, *from, storeFile); + + // Then... reverse the patch back (open the from file again, and create a write file to overwrite it) + std::auto_ptr from2(RaidFileRead::Open(mStoreDiscSet, oldVersionFilename)); + ppreviousVerStoreFile = new RaidFileWrite(mStoreDiscSet, oldVersionFilename); + ppreviousVerStoreFile->Open(true /* allow overwriting */); + from->Seek(0, IOStream::SeekType_Absolute); + diff.Seek(0, IOStream::SeekType_Absolute); + BackupStoreFile::ReverseDiffFile(diff, *from, *from2, *ppreviousVerStoreFile, + DiffFromFileID, &reversedDiffIsCompletelyDifferent); + + // Store disc space used + oldVersionNewBlocksUsed = ppreviousVerStoreFile->GetDiscUsageInBlocks(); + + // And make a space adjustment for the size calculation + spaceAdjustFromDiff = from->GetDiscUsageInBlocks() - oldVersionNewBlocksUsed; + + // Everything cleans up here... + } + catch(...) + { + // Be very paranoid about deleting this temp file -- we could only leave a zero byte file anyway + ::unlink(tempFn.c_str()); + throw; + } + } + + // Get the blocks used + blocksUsed = storeFile.GetDiscUsageInBlocks(); + + // Exceeds the hard limit? + if((mpStoreInfo->GetBlocksUsed() + blocksUsed - spaceAdjustFromDiff) > mpStoreInfo->GetBlocksHardLimit()) + { + THROW_EXCEPTION(BackupStoreException, AddedFileExceedsStorageLimit) + // The store file will be deleted automatically by the RaidFile object + } + + // Commit the file + storeFile.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY); + } + catch(...) + { + // Delete any previous version store file + if(ppreviousVerStoreFile != 0) + { + delete ppreviousVerStoreFile; + ppreviousVerStoreFile = 0; + } + + throw; + } + + // Verify the file -- only necessary for non-diffed versions + // NOTE: No need to catch exceptions and delete ppreviousVerStoreFile, because + // in the non-diffed code path it's never allocated. + if(DiffFromFileID == 0) + { + std::auto_ptr checkFile(RaidFileRead::Open(mStoreDiscSet, fn)); + if(!BackupStoreFile::VerifyEncodedFileFormat(*checkFile)) + { + // Error! Delete the file + RaidFileWrite del(mStoreDiscSet, fn); + del.Delete(); + + // Exception + THROW_EXCEPTION(BackupStoreException, AddedFileDoesNotVerify) + } + } + + // Modify the directory -- first make all files with the same name + // marked as an old version + int64_t blocksInOldFiles = 0; + try + { + if(MarkFileWithSameNameAsOldVersions) + { + BackupStoreDirectory::Iterator i(dir); + + BackupStoreDirectory::Entry *e = 0; + while((e = i.Next()) != 0) + { + // First, check it's not an old version (cheaper comparison) + if((e->GetFlags() & BackupStoreDirectory::Entry::Flags_OldVersion) == 0) + { + // Compare name + if(e->GetName() == rFilename) + { + // Check that it's definately not an old version + ASSERT((e->GetFlags() & BackupStoreDirectory::Entry::Flags_OldVersion) == 0); + // Set old version flag + e->AddFlags(BackupStoreDirectory::Entry::Flags_OldVersion); + // Can safely do this, because we know we won't be here if it's already + // an old version + blocksInOldFiles += e->GetSizeInBlocks(); + } + } + } + } + + // Then the new entry + BackupStoreDirectory::Entry *pnewEntry = dir.AddEntry(rFilename, + ModificationTime, id, blocksUsed, BackupStoreDirectory::Entry::Flags_File, AttributesHash); + + // Adjust for the patch back stuff? + if(DiffFromFileID != 0) + { + // Get old version entry + BackupStoreDirectory::Entry *poldEntry = dir.FindEntryByID(DiffFromFileID); + ASSERT(poldEntry != 0); + + // Adjust dependency info of file? + if(!reversedDiffIsCompletelyDifferent) + { + poldEntry->SetDependsNewer(id); + pnewEntry->SetDependsOlder(DiffFromFileID); + } + + // Adjust size of old entry + int64_t oldSize = poldEntry->GetSizeInBlocks(); + poldEntry->SetSizeInBlocks(oldVersionNewBlocksUsed); + + // And adjust blocks used count, for later adjustment + blocksUsed += (oldVersionNewBlocksUsed - oldSize); + blocksInOldFiles += (oldVersionNewBlocksUsed - oldSize); + } + + // Write the directory back to disc + SaveDirectory(dir, InDirectory); + + // Commit the old version's new patched version, now that the directory safely reflects + // the state of the files on disc. + if(ppreviousVerStoreFile != 0) + { + ppreviousVerStoreFile->Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY); + delete ppreviousVerStoreFile; + ppreviousVerStoreFile = 0; + } + } + catch(...) + { + // Back out on adding that file + RaidFileWrite del(mStoreDiscSet, fn); + del.Delete(); + + // Remove this entry from the cache + RemoveDirectoryFromCache(InDirectory); + + // Delete any previous version store file + if(ppreviousVerStoreFile != 0) + { + delete ppreviousVerStoreFile; + ppreviousVerStoreFile = 0; + } + + // Don't worry about the incremented number in the store info + throw; + } + + // Check logic + ASSERT(ppreviousVerStoreFile == 0); + + // Modify the store info + mpStoreInfo->ChangeBlocksUsed(blocksUsed); + mpStoreInfo->ChangeBlocksInOldFiles(blocksInOldFiles); + + // Increment reference count on the new directory to one + mapRefCount->AddReference(id); + + // Save the store info -- can cope if this exceptions because infomation + // will be rebuilt by housekeeping, and ID allocation can recover. + SaveStoreInfo(); + + // Return the ID to the caller + return id; +} + + + +// -------------------------------------------------------------------------- +// +// 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::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) + } + + // Find the directory the file is in (will exception if it fails) + BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory)); + + // Setup flags + bool fileExisted = false; + bool madeChanges = false; + rObjectIDOut = 0; // not found + + // Count of deleted blocks + int64_t blocksDel = 0; + + try + { + // Iterate through directory, only looking at files which haven't been deleted + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *e = 0; + while((e = i.Next(BackupStoreDirectory::Entry::Flags_File, + BackupStoreDirectory::Entry::Flags_Deleted)) != 0) + { + // Compare name + if(e->GetName() == rFilename) + { + // Check that it's definately not already deleted + ASSERT((e->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) == 0); + // Set deleted flag + e->AddFlags(BackupStoreDirectory::Entry::Flags_Deleted); + // Mark as made a change + madeChanges = true; + // Can safely do this, because we know we won't be here if it's already + // an old version + blocksDel += e->GetSizeInBlocks(); + // Is this the last version? + if((e->GetFlags() & BackupStoreDirectory::Entry::Flags_OldVersion) == 0) + { + // Yes. It's been found. + rObjectIDOut = e->GetObjectID(); + 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: BackupStoreContext::UndeleteFile(int64_t, int64_t) +// Purpose: Undeletes a file, if it exists, returning true if +// the file existed. +// 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: BackupStoreContext::RemoveDirectoryFromCache(int64_t) +// Purpose: Remove directory from cache +// Created: 2003/09/04 +// +// -------------------------------------------------------------------------- +void BackupStoreContext::RemoveDirectoryFromCache(int64_t ObjectID) +{ + std::map::iterator item(mDirectoryCache.find(ObjectID)); + if(item != mDirectoryCache.end()) + { + // Delete this cached object + delete item->second; + // Erase the entry form the map + mDirectoryCache.erase(item); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::SaveDirectory(BackupStoreDirectory &, int64_t) +// Purpose: Save directory back to disc, update time in cache +// Created: 2003/09/04 +// +// -------------------------------------------------------------------------- +void BackupStoreContext::SaveDirectory(BackupStoreDirectory &rDir, int64_t ObjectID) +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + if(rDir.GetObjectID() != ObjectID) + { + THROW_EXCEPTION(BackupStoreException, Internal) + } + + try + { + // Write to disc, adjust size in store info + std::string dirfn; + MakeObjectFilename(ObjectID, dirfn); + { + RaidFileWrite writeDir(mStoreDiscSet, dirfn); + writeDir.Open(true /* allow overwriting */); + + BufferedWriteStream buffer(writeDir); + rDir.WriteToStream(buffer); + buffer.Flush(); + + // get the disc usage (must do this before commiting it) + int64_t dirSize = writeDir.GetDiscUsageInBlocks(); + + // Commit directory + writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY); + + // Make sure the size of the directory is available for writing the dir back + ASSERT(dirSize > 0); + int64_t sizeAdjustment = dirSize - rDir.GetUserInfo1_SizeInBlocks(); + mpStoreInfo->ChangeBlocksUsed(sizeAdjustment); + mpStoreInfo->ChangeBlocksInDirectories(sizeAdjustment); + // Update size stored in directory + rDir.SetUserInfo1_SizeInBlocks(dirSize); + } + // Refresh revision ID in cache + { + int64_t revid = 0; + if(!RaidFileRead::FileExists(mStoreDiscSet, dirfn, &revid)) + { + THROW_EXCEPTION(BackupStoreException, Internal) + } + rDir.SetRevisionID(revid); + } + } + catch(...) + { + // Remove it from the cache if anything went wrong + RemoveDirectoryFromCache(ObjectID); + throw; + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupStoreContext::AddDirectory(int64_t InDirectory, const BackupStoreFilename &rFilename, const StreamableMemBlock &Attributes, int64_t AttributesModTime, bool &rAlreadyExists) +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + if(mReadOnly) + { + THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly) + } + + // Flags as not already existing + rAlreadyExists = false; + + // Get the directory we want to modify + BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory)); + + // Scan the directory for the name (only looking for directories which already exist) + { + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + while((en = i.Next(BackupStoreDirectory::Entry::Flags_INCLUDE_EVERYTHING, + BackupStoreDirectory::Entry::Flags_Deleted | BackupStoreDirectory::Entry::Flags_OldVersion)) != 0) // Ignore deleted and old directories + { + if(en->GetName() == rFilename) + { + // Already exists + rAlreadyExists = true; + return en->GetObjectID(); + } + } + } + + // Allocate the next ID + int64_t id = AllocateObjectID(); + + // Create an empty directory with the given attributes on disc + std::string fn; + MakeObjectFilename(id, fn, true /* make sure the directory it's in exists */); + { + BackupStoreDirectory emptyDir(id, InDirectory); + // add the atttribues + emptyDir.SetAttributes(Attributes, AttributesModTime); + + // Write... + RaidFileWrite dirFile(mStoreDiscSet, fn); + dirFile.Open(false /* no overwriting */); + emptyDir.WriteToStream(dirFile); + // Get disc usage, before it's commited + int64_t dirSize = dirFile.GetDiscUsageInBlocks(); + // Commit the file + dirFile.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY); + + // Make sure the size of the directory is added to the usage counts in the info + ASSERT(dirSize > 0); + mpStoreInfo->ChangeBlocksUsed(dirSize); + mpStoreInfo->ChangeBlocksInDirectories(dirSize); + // Not added to cache, so don't set the size in the directory + } + + // Then add it into the parent directory + try + { + dir.AddEntry(rFilename, 0 /* modification time */, id, 0 /* blocks used */, BackupStoreDirectory::Entry::Flags_Dir, 0 /* attributes mod time */); + SaveDirectory(dir, InDirectory); + + // Increment reference count on the new directory to one + mapRefCount->AddReference(id); + } + catch(...) + { + // Back out on adding that directory + RaidFileWrite del(mStoreDiscSet, fn); + del.Delete(); + + // Remove this entry from the cache + RemoveDirectoryFromCache(InDirectory); + + // Don't worry about the incremented number in the store info + throw; + } + + // Save the store info (may be postponed) + SaveStoreInfo(); + + // tell caller what the ID was + return id; +} + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupStoreContext::DeleteDirectory(int64_t ObjectID, bool Undelete) +{ + // Essential checks! + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + if(mReadOnly) + { + THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly) + } + + // Containing directory + int64_t InDirectory = 0; + + // Count of blocks deleted + int64_t blocksDeleted = 0; + + try + { + // Get the directory that's to be deleted + { + // In block, because dir may not be valid after the delete directory call + BackupStoreDirectory &dir(GetDirectoryInternal(ObjectID)); + + // Store the directory it's in for later + InDirectory = dir.GetContainerID(); + + // Depth first delete of contents + DeleteDirectoryRecurse(ObjectID, blocksDeleted, Undelete); + } + + // Remove the entry from the directory it's in + ASSERT(InDirectory != 0); + BackupStoreDirectory &parentDir(GetDirectoryInternal(InDirectory)); + + BackupStoreDirectory::Iterator i(parentDir); + BackupStoreDirectory::Entry *en = 0; + while((en = i.Next(Undelete?(BackupStoreDirectory::Entry::Flags_Deleted):(BackupStoreDirectory::Entry::Flags_INCLUDE_EVERYTHING), + Undelete?(0):(BackupStoreDirectory::Entry::Flags_Deleted))) != 0) // Ignore deleted directories (or not deleted if Undelete) + { + if(en->GetObjectID() == ObjectID) + { + // This is the one to delete + if(Undelete) + { + en->RemoveFlags(BackupStoreDirectory::Entry::Flags_Deleted); + } + else + { + en->AddFlags(BackupStoreDirectory::Entry::Flags_Deleted); + } + + // Save it + SaveDirectory(parentDir, InDirectory); + + // Done + break; + } + } + + // Update blocks deleted count + mpStoreInfo->ChangeBlocksInDeletedFiles(Undelete?(0 - blocksDeleted):(blocksDeleted)); + + // Save store info, may be postponed + SaveStoreInfo(); + } + catch(...) + { + RemoveDirectoryFromCache(InDirectory); + throw; + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::DeleteDirectoryRecurse(BackupStoreDirectory &, int64_t) +// Purpose: Private. Deletes a directory depth-first recusively. +// Created: 2003/10/21 +// +// -------------------------------------------------------------------------- +void BackupStoreContext::DeleteDirectoryRecurse(int64_t ObjectID, int64_t &rBlocksDeletedOut, bool Undelete) +{ + try + { + // Does things carefully to avoid using a directory in the cache after recursive call + // because it may have been deleted. + + // Do sub directories + { + // Get the directory... + BackupStoreDirectory &dir(GetDirectoryInternal(ObjectID)); + + // Then scan it for directories + std::vector subDirs; + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + if(Undelete) + { + while((en = i.Next(BackupStoreDirectory::Entry::Flags_Dir | BackupStoreDirectory::Entry::Flags_Deleted, // deleted dirs + BackupStoreDirectory::Entry::Flags_EXCLUDE_NOTHING)) != 0) + { + // Store the directory ID. + subDirs.push_back(en->GetObjectID()); + } + } + else + { + while((en = i.Next(BackupStoreDirectory::Entry::Flags_Dir, // dirs only + BackupStoreDirectory::Entry::Flags_Deleted)) != 0) // but not deleted ones + { + // Store the directory ID. + subDirs.push_back(en->GetObjectID()); + } + } + + // Done with the directory for now. Recurse to sub directories + for(std::vector::const_iterator i = subDirs.begin(); i != subDirs.end(); ++i) + { + DeleteDirectoryRecurse((*i), rBlocksDeletedOut, Undelete); + } + } + + // Then, delete the files. Will need to load the directory again because it might have + // been removed from the cache. + { + // Get the directory... + BackupStoreDirectory &dir(GetDirectoryInternal(ObjectID)); + + // Changes made? + bool changesMade = false; + + // Run through files + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + + while((en = i.Next(Undelete?(BackupStoreDirectory::Entry::Flags_Deleted):(BackupStoreDirectory::Entry::Flags_INCLUDE_EVERYTHING), + Undelete?(0):(BackupStoreDirectory::Entry::Flags_Deleted))) != 0) // Ignore deleted directories (or not deleted if Undelete) + { + // Add/remove the deleted flags + if(Undelete) + { + en->RemoveFlags(BackupStoreDirectory::Entry::Flags_Deleted); + } + else + { + en->AddFlags(BackupStoreDirectory::Entry::Flags_Deleted); + } + + // Keep count of the deleted blocks + if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_File) != 0) + { + rBlocksDeletedOut += en->GetSizeInBlocks(); + } + + // Did something + changesMade = true; + } + + // Save the directory + if(changesMade) + { + SaveDirectory(dir, ObjectID); + } + } + } + catch(...) + { + RemoveDirectoryFromCache(ObjectID); + throw; + } +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::ChangeDirAttributes(int64_t, const StreamableMemBlock &, int64_t) +// Purpose: Change the attributes of a directory +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- +void BackupStoreContext::ChangeDirAttributes(int64_t Directory, const StreamableMemBlock &Attributes, int64_t AttributesModTime) +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + if(mReadOnly) + { + THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly) + } + + try + { + // Get the directory we want to modify + BackupStoreDirectory &dir(GetDirectoryInternal(Directory)); + + // Set attributes + dir.SetAttributes(Attributes, AttributesModTime); + + // Save back + SaveDirectory(dir, Directory); + } + catch(...) + { + RemoveDirectoryFromCache(Directory); + throw; + } +} + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupStoreContext::ChangeFileAttributes(const BackupStoreFilename &rFilename, int64_t InDirectory, const StreamableMemBlock &Attributes, int64_t AttributesHash, int64_t &rObjectIDOut) +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + if(mReadOnly) + { + THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly) + } + + try + { + // Get the directory we want to modify + BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory)); + + // Find the file entry + BackupStoreDirectory::Entry *en = 0; + // Iterate through current versions of files, only + BackupStoreDirectory::Iterator i(dir); + while((en = i.Next( + BackupStoreDirectory::Entry::Flags_File, + BackupStoreDirectory::Entry::Flags_Deleted | BackupStoreDirectory::Entry::Flags_OldVersion) + ) != 0) + { + if(en->GetName() == rFilename) + { + // Set attributes + en->SetAttributes(Attributes, AttributesHash); + + // Tell caller the object ID + rObjectIDOut = en->GetObjectID(); + + // Done + break; + } + } + if(en == 0) + { + // Didn't find it + return false; + } + + // Save back + SaveDirectory(dir, InDirectory); + } + catch(...) + { + RemoveDirectoryFromCache(InDirectory); + throw; + } + + // Changed, everything OK + return true; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::ObjectExists(int64_t) +// Purpose: Test to see if an object of this ID exists in the store +// Created: 2003/09/03 +// +// -------------------------------------------------------------------------- +bool BackupStoreContext::ObjectExists(int64_t ObjectID, int MustBe) +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + + // Note that we need to allow object IDs a little bit greater than the last one in the store info, + // because the store info may not have got saved in an error condition. Max greater ID is + // STORE_INFO_SAVE_DELAY in this case, *2 to be safe. + if(ObjectID <= 0 || ObjectID > (mpStoreInfo->GetLastObjectIDUsed() + (STORE_INFO_SAVE_DELAY * 2))) + { + // Obviously bad object ID + return false; + } + + // Test to see if it exists on the disc + std::string filename; + MakeObjectFilename(ObjectID, filename); + if(!RaidFileRead::FileExists(mStoreDiscSet, filename)) + { + // RaidFile reports no file there + return false; + } + + // Do we need to be more specific? + if(MustBe != ObjectExists_Anything) + { + // Open the file + std::auto_ptr objectFile(RaidFileRead::Open(mStoreDiscSet, filename)); + + // Read the first integer + u_int32_t magic; + if(!objectFile->ReadFullBuffer(&magic, sizeof(magic), 0 /* not interested in how many read if failure */)) + { + // Failed to get any bytes, must have failed + return false; + } + +#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE + if(MustBe == ObjectExists_File && ntohl(magic) == OBJECTMAGIC_FILE_MAGIC_VALUE_V0) + { + // Old version detected + return true; + } +#endif + + // Right one? + u_int32_t requiredMagic = (MustBe == ObjectExists_File)?OBJECTMAGIC_FILE_MAGIC_VALUE_V1:OBJECTMAGIC_DIR_MAGIC_VALUE; + + // Check + if(ntohl(magic) != requiredMagic) + { + return false; + } + + // File is implicitly closed + } + + return true; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::OpenObject(int64_t) +// Purpose: Opens an object +// Created: 2003/09/03 +// +// -------------------------------------------------------------------------- +std::auto_ptr BackupStoreContext::OpenObject(int64_t ObjectID) +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + + // Attempt to open the file + std::string fn; + MakeObjectFilename(ObjectID, fn); + return std::auto_ptr(RaidFileRead::Open(mStoreDiscSet, fn).release()); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::GetClientStoreMarker() +// Purpose: Retrieve the client store marker +// Created: 2003/10/29 +// +// -------------------------------------------------------------------------- +int64_t BackupStoreContext::GetClientStoreMarker() +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + + return mpStoreInfo->GetClientStoreMarker(); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::GetStoreDiscUsageInfo(int64_t &, int64_t &, int64_t &) +// Purpose: Get disc usage info from store info +// Created: 1/1/04 +// +// -------------------------------------------------------------------------- +void BackupStoreContext::GetStoreDiscUsageInfo(int64_t &rBlocksUsed, int64_t &rBlocksSoftLimit, int64_t &rBlocksHardLimit) +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + + rBlocksUsed = mpStoreInfo->GetBlocksUsed(); + rBlocksSoftLimit = mpStoreInfo->GetBlocksSoftLimit(); + rBlocksHardLimit = mpStoreInfo->GetBlocksHardLimit(); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::HardLimitExceeded() +// Purpose: Returns true if the hard limit has been exceeded +// Created: 1/1/04 +// +// -------------------------------------------------------------------------- +bool BackupStoreContext::HardLimitExceeded() +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + + return mpStoreInfo->GetBlocksUsed() > mpStoreInfo->GetBlocksHardLimit(); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::SetClientStoreMarker(int64_t) +// Purpose: Sets the client store marker, and commits it to disc +// Created: 2003/10/29 +// +// -------------------------------------------------------------------------- +void BackupStoreContext::SetClientStoreMarker(int64_t ClientStoreMarker) +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + if(mReadOnly) + { + THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly) + } + + mpStoreInfo->SetClientStoreMarker(ClientStoreMarker); + SaveStoreInfo(false /* don't delay saving this */); +} + + +// -------------------------------------------------------------------------- +// +// Function +// 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 BackupStoreContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int64_t MoveToDirectory, const BackupStoreFilename &rNewFilename, bool MoveAllWithSameName, bool AllowMoveOverDeletedObject) +{ + if(mReadOnly) + { + THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly) + } + + // Should deleted files be excluded when checking for the existance of objects with the target name? + int64_t targetSearchExcludeFlags = (AllowMoveOverDeletedObject) + ?(BackupStoreDirectory::Entry::Flags_Deleted) + :(BackupStoreDirectory::Entry::Flags_EXCLUDE_NOTHING); + + // Special case if the directories are the same... + if(MoveFromDirectory == MoveToDirectory) + { + try + { + // Get the first directory + BackupStoreDirectory &dir(GetDirectoryInternal(MoveFromDirectory)); + + // Find the file entry + BackupStoreDirectory::Entry *en = dir.FindEntryByID(ObjectID); + + // Error if not found + if(en == 0) + { + THROW_EXCEPTION(BackupStoreException, CouldNotFindEntryInDirectory) + } + + // Check the new name doens't already exist (optionally ignoring deleted files) + { + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *c = 0; + while((c = i.Next(BackupStoreDirectory::Entry::Flags_INCLUDE_EVERYTHING, targetSearchExcludeFlags)) != 0) + { + if(c->GetName() == rNewFilename) + { + THROW_EXCEPTION(BackupStoreException, NameAlreadyExistsInDirectory) + } + } + } + + // Need to get all the entries with the same name? + if(MoveAllWithSameName) + { + // Iterate through the directory, copying all with matching names + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *c = 0; + while((c = i.Next()) != 0) + { + if(c->GetName() == en->GetName()) + { + // Rename this one + c->SetName(rNewFilename); + } + } + } + else + { + // Just copy this one + en->SetName(rNewFilename); + } + + // Save the directory back + SaveDirectory(dir, MoveFromDirectory); + } + catch(...) + { + RemoveDirectoryFromCache(MoveToDirectory); // either will do, as they're the same + throw; + } + + return; + } + + // Got to be careful how this is written, as we can't guarentte that if we have two + // directories open, the first won't be deleted as the second is opened. (cache) + + // List of entries to move + std::vector moving; + + // list of directory IDs which need to have containing dir id changed + std::vector dirsToChangeContainingID; + + try + { + // First of all, get copies of the entries to move to the to directory. + + { + // Get the first directory + BackupStoreDirectory &from(GetDirectoryInternal(MoveFromDirectory)); + + // Find the file entry + BackupStoreDirectory::Entry *en = from.FindEntryByID(ObjectID); + + // Error if not found + if(en == 0) + { + THROW_EXCEPTION(BackupStoreException, CouldNotFindEntryInDirectory) + } + + // Need to get all the entries with the same name? + if(MoveAllWithSameName) + { + // Iterate through the directory, copying all with matching names + BackupStoreDirectory::Iterator i(from); + BackupStoreDirectory::Entry *c = 0; + while((c = i.Next()) != 0) + { + if(c->GetName() == en->GetName()) + { + // Copy + moving.push_back(new BackupStoreDirectory::Entry(*c)); + + // Check for containing directory correction + if(c->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) dirsToChangeContainingID.push_back(c->GetObjectID()); + } + } + ASSERT(!moving.empty()); + } + else + { + // Just copy this one + moving.push_back(new BackupStoreDirectory::Entry(*en)); + + // Check for containing directory correction + if(en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) dirsToChangeContainingID.push_back(en->GetObjectID()); + } + } + + // Secondly, insert them into the to directory, and save it + + { + // To directory + BackupStoreDirectory &to(GetDirectoryInternal(MoveToDirectory)); + + // Check the new name doens't already exist + { + BackupStoreDirectory::Iterator i(to); + BackupStoreDirectory::Entry *c = 0; + while((c = i.Next(BackupStoreDirectory::Entry::Flags_INCLUDE_EVERYTHING, targetSearchExcludeFlags)) != 0) + { + if(c->GetName() == rNewFilename) + { + THROW_EXCEPTION(BackupStoreException, NameAlreadyExistsInDirectory) + } + } + } + + // Copy the entries into it, changing the name as we go + for(std::vector::iterator i(moving.begin()); i != moving.end(); ++i) + { + BackupStoreDirectory::Entry *en = (*i); + en->SetName(rNewFilename); + to.AddEntry(*en); // adds copy + } + + // Save back + SaveDirectory(to, MoveToDirectory); + } + + // Thirdly... remove them from the first directory -- but if it fails, attempt to delete them from the to directory + try + { + // Get directory + BackupStoreDirectory &from(GetDirectoryInternal(MoveFromDirectory)); + + // Delete each one + for(std::vector::iterator i(moving.begin()); i != moving.end(); ++i) + { + from.DeleteEntry((*i)->GetObjectID()); + } + + // Save back + SaveDirectory(from, MoveFromDirectory); + } + catch(...) + { + // UNDO modification to To directory + + // Get directory + BackupStoreDirectory &to(GetDirectoryInternal(MoveToDirectory)); + + // Delete each one + for(std::vector::iterator i(moving.begin()); i != moving.end(); ++i) + { + to.DeleteEntry((*i)->GetObjectID()); + } + + // Save back + SaveDirectory(to, MoveToDirectory); + + // Throw the error + throw; + } + + // Finally... for all the directories we moved, modify their containing directory ID + for(std::vector::iterator i(dirsToChangeContainingID.begin()); i != dirsToChangeContainingID.end(); ++i) + { + // Load the directory + BackupStoreDirectory &change(GetDirectoryInternal(*i)); + + // Modify containing dir ID + change.SetContainerID(MoveToDirectory); + + // Save it back + SaveDirectory(change, *i); + } + } + catch(...) + { + // Make sure directories aren't in the cache, as they may have been modified + RemoveDirectoryFromCache(MoveToDirectory); + RemoveDirectoryFromCache(MoveFromDirectory); + for(std::vector::iterator i(dirsToChangeContainingID.begin()); i != dirsToChangeContainingID.end(); ++i) + { + RemoveDirectoryFromCache(*i); + } + + while(!moving.empty()) + { + delete moving.back(); + moving.pop_back(); + } + throw; + } + + // Clean up + while(!moving.empty()) + { + delete moving.back(); + moving.pop_back(); + } +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreContext::GetBackupStoreInfo() +// Purpose: Return the backup store info object, exception if it isn't loaded +// Created: 19/4/04 +// +// -------------------------------------------------------------------------- +const BackupStoreInfo &BackupStoreContext::GetBackupStoreInfo() const +{ + if(mpStoreInfo.get() == 0) + { + THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded) + } + + return *(mpStoreInfo.get()); +} + + diff --git a/bin/bbstored/BackupStoreContext.h b/bin/bbstored/BackupStoreContext.h new file mode 100644 index 00000000..6053e4b8 --- /dev/null +++ b/bin/bbstored/BackupStoreContext.h @@ -0,0 +1,186 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupStoreContext.h +// Purpose: Context for backup store server +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- + +#ifndef BACKUPCONTEXT__H +#define BACKUPCONTEXT__H + +#include +#include +#include + +#include "BackupStoreRefCountDatabase.h" +#include "NamedLock.h" +#include "ProtocolObject.h" +#include "Utils.h" + +class BackupStoreDirectory; +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: BackupStoreContext +// Purpose: Context for backup store server +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +class BackupStoreContext +{ +public: + BackupStoreContext(int32_t ClientID, HousekeepingInterface &rDaemon); + ~BackupStoreContext(); +private: + BackupStoreContext(const BackupStoreContext &rToCopy); +public: + + void ReceivedFinishCommand(); + void CleanUp(); + + int32_t GetClientID() {return mClientID;} + + enum + { + Phase_START = 0, + Phase_Version = 0, + Phase_Login = 1, + Phase_Commands = 2 + }; + + int GetPhase() const {return mProtocolPhase;} + void SetPhase(int NewPhase) {mProtocolPhase = NewPhase;} + + // Read only locking + bool SessionIsReadOnly() {return mReadOnly;} + bool AttemptToGetWriteLock(); + + void SetClientHasAccount(const std::string &rStoreRoot, int StoreDiscSet) {mClientHasAccount = true; mStoreRoot = rStoreRoot; mStoreDiscSet = StoreDiscSet;} + bool GetClientHasAccount() const {return mClientHasAccount;} + const std::string &GetStoreRoot() const {return mStoreRoot;} + int GetStoreDiscSet() const {return mStoreDiscSet;} + + // Store info + void LoadStoreInfo(); + void SaveStoreInfo(bool AllowDelay = true); + const BackupStoreInfo &GetBackupStoreInfo() const; + + // Client marker + int64_t GetClientStoreMarker(); + void SetClientStoreMarker(int64_t ClientStoreMarker); + + // Usage information + void GetStoreDiscUsageInfo(int64_t &rBlocksUsed, int64_t &rBlocksSoftLimit, int64_t &rBlocksHardLimit); + bool HardLimitExceeded(); + + // Reading directories + // -------------------------------------------------------------------------- + // + // Function + // 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. + // Created: 2003/09/02 + // + // -------------------------------------------------------------------------- + const BackupStoreDirectory &GetDirectory(int64_t ObjectID) + { + // External callers aren't allowed to change it -- this function + // merely turns the the returned directory const. + return GetDirectoryInternal(ObjectID); + } + + // Manipulating files/directories + int64_t AddFile(IOStream &rFile, int64_t InDirectory, int64_t ModificationTime, int64_t AttributesHash, int64_t DiffFromFileID, const BackupStoreFilename &rFilename, bool MarkFileWithSameNameAsOldVersions); + int64_t AddDirectory(int64_t InDirectory, const BackupStoreFilename &rFilename, const StreamableMemBlock &Attributes, int64_t AttributesModTime, bool &rAlreadyExists); + 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); + + // Manipulating objects + enum + { + ObjectExists_Anything = 0, + ObjectExists_File = 1, + ObjectExists_Directory = 2 + }; + bool ObjectExists(int64_t ObjectID, int MustBe = ObjectExists_Anything); + std::auto_ptr OpenObject(int64_t ObjectID); + + // Info + int32_t GetClientID() const {return mClientID;} + +private: + void MakeObjectFilename(int64_t ObjectID, std::string &rOutput, bool EnsureDirectoryExists = false); + BackupStoreDirectory &GetDirectoryInternal(int64_t ObjectID); + void SaveDirectory(BackupStoreDirectory &rDir, int64_t ObjectID); + void RemoveDirectoryFromCache(int64_t ObjectID); + void DeleteDirectoryRecurse(int64_t ObjectID, int64_t &rBlocksDeletedOut, bool Undelete); + int64_t AllocateObjectID(); + + int32_t mClientID; + HousekeepingInterface &mrDaemon; + int mProtocolPhase; + bool mClientHasAccount; + std::string mStoreRoot; // has final directory separator + int mStoreDiscSet; + bool mReadOnly; + NamedLock mWriteLock; + int mSaveStoreInfoDelay; // how many times to delay saving the store info + + // Store info + std::auto_ptr mpStoreInfo; + + // Refcount database + std::auto_ptr mapRefCount; + + // Directory cache + std::map mDirectoryCache; + +public: + class TestHook + { + public: + virtual std::auto_ptr StartCommand(BackupProtocolObject& + rCommand) = 0; + virtual ~TestHook() { } + }; + void SetTestHook(TestHook& rTestHook) + { + mpTestHook = &rTestHook; + } + std::auto_ptr StartCommandHook(BackupProtocolObject& rCommand) + { + if(mpTestHook) + { + return mpTestHook->StartCommand(rCommand); + } + return std::auto_ptr(); + } + +private: + TestHook* mpTestHook; +}; + +#endif // BACKUPCONTEXT__H + diff --git a/bin/bbstored/BackupStoreDaemon.cpp b/bin/bbstored/BackupStoreDaemon.cpp new file mode 100644 index 00000000..4de0a078 --- /dev/null +++ b/bin/bbstored/BackupStoreDaemon.cpp @@ -0,0 +1,371 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupStoreDaemon.cpp +// Purpose: Backup store daemon +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#ifdef HAVE_SYSLOG_H + #include +#endif + +#include "BackupStoreContext.h" +#include "BackupStoreDaemon.h" +#include "BackupStoreConfigVerify.h" +#include "autogen_BackupProtocolServer.h" +#include "RaidFileController.h" +#include "BackupStoreAccountDatabase.h" +#include "BackupStoreAccounts.h" +#include "BannerText.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::BackupStoreDaemon() +// Purpose: Constructor +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +BackupStoreDaemon::BackupStoreDaemon() + : mpAccountDatabase(0), + mpAccounts(0), + mExtendedLogging(false), + mHaveForkedHousekeeping(false), + mIsHousekeepingProcess(false), + mHousekeepingInited(false), + mInterProcessComms(mInterProcessCommsSocket), + mpTestHook(NULL) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::~BackupStoreDaemon() +// Purpose: Destructor +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +BackupStoreDaemon::~BackupStoreDaemon() +{ + // Must delete this one before the database ... + if(mpAccounts != 0) + { + delete mpAccounts; + mpAccounts = 0; + } + // ... as the mpAccounts object has a reference to it + if(mpAccountDatabase != 0) + { + delete mpAccountDatabase; + mpAccountDatabase = 0; + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::DaemonName() +// Purpose: Name of daemon +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +const char *BackupStoreDaemon::DaemonName() const +{ + return "bbstored"; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::DaemonBanner() +// Purpose: Daemon banner +// Created: 1/1/04 +// +// -------------------------------------------------------------------------- +std::string BackupStoreDaemon::DaemonBanner() const +{ + return BANNER_TEXT("Backup Store Server"); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::GetConfigVerify() +// Purpose: Configuration definition +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +const ConfigurationVerify *BackupStoreDaemon::GetConfigVerify() const +{ + return &BackupConfigFileVerify; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::SetupInInitialProcess() +// Purpose: Setup before we fork -- get raid file controller going +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +void BackupStoreDaemon::SetupInInitialProcess() +{ + const Configuration &config(GetConfiguration()); + + // Initialise the raid files controller + RaidFileController &rcontroller = RaidFileController::GetController(); + + std::string raidFileConfig; + + #ifdef WIN32 + if (!config.KeyExists("RaidFileConf")) + { + raidFileConfig = BOX_GET_DEFAULT_RAIDFILE_CONFIG_FILE; + } + else + { + raidFileConfig = config.GetKeyValue("RaidFileConf"); + } + #else + raidFileConfig = config.GetKeyValue("RaidFileConf"); + #endif + + rcontroller.Initialise(raidFileConfig); + + // Load the account database + std::auto_ptr pdb(BackupStoreAccountDatabase::Read(config.GetKeyValue("AccountDatabase").c_str())); + mpAccountDatabase = pdb.release(); + + // Create a accounts object + mpAccounts = new BackupStoreAccounts(*mpAccountDatabase); + + // Ready to go! +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::Run() +// Purpose: Run shim for the store daemon -- read some config details +// Created: 2003/10/24 +// +// -------------------------------------------------------------------------- +void BackupStoreDaemon::Run() +{ + // Get extended logging flag + mExtendedLogging = false; + const Configuration &config(GetConfiguration()); + mExtendedLogging = config.GetKeyValueBool("ExtendedLogging"); + + // 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}; + if(::socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sv) != 0) + { + THROW_EXCEPTION(ServerException, SocketPairFailed) + } + int whichSocket = 0; + + // Fork + switch(::fork()) + { + case -1: + { + // Error + THROW_EXCEPTION(ServerException, ServerForkError) + } + break; + case 0: + { + // In child process + mIsHousekeepingProcess = true; + SetProcessTitle("housekeeping, idle"); + whichSocket = 1; + // Change the log name + ::openlog("bbstored/hk", LOG_PID, LOG_LOCAL6); + // 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! + ::signal(SIGHUP, SIG_IGN); + ::signal(SIGTERM, SIG_IGN); + } + break; + default: + { + // Parent process + whichSocket = 0; + } + break; + } + + // Mark that this has been, so -HUP doesn't try and do this again + mHaveForkedHousekeeping = true; + + // Attach the comms thing to the right socket, and close the other one + mInterProcessCommsSocket.Attach(sv[whichSocket]); + + if(::close(sv[(whichSocket == 0)?1:0]) != 0) + { + THROW_EXCEPTION(ServerException, SocketCloseError) + } + } +#endif // WIN32 + + if(mIsHousekeepingProcess) + { + // Housekeeping process -- do other stuff + HousekeepingProcess(); + } + else + { + // In server process -- use the base class to do the magic + ServerTLS::Run(); + + if (!mInterProcessCommsSocket.IsOpened()) + { + return; + } + + // Why did it stop? Tell the housekeeping process to do the same + if(IsReloadConfigWanted()) + { + mInterProcessCommsSocket.Write("h\n", 2); + } + + if(IsTerminateWanted()) + { + mInterProcessCommsSocket.Write("t\n", 2); + } + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::Connection(SocketStreamTLS &) +// Purpose: Handles a connection, by catching exceptions and +// delegating to Connection2 +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +void BackupStoreDaemon::Connection(SocketStreamTLS &rStream) +{ + try + { + Connection2(rStream); + } + catch(BoxException &e) + { + BOX_ERROR("Error in child process, terminating connection: " << + e.what() << " (" << e.GetType() << "/" << + e.GetSubType() << ")"); + } + catch(std::exception &e) + { + BOX_ERROR("Error in child process, terminating connection: " << + e.what()); + } + catch(...) + { + BOX_ERROR("Error in child process, terminating connection: " << + "unknown exception"); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupStoreDaemon::Connection2(SocketStreamTLS &) +// Purpose: Handles a connection from bbackupd +// Created: 2006/11/12 +// +// -------------------------------------------------------------------------- +void BackupStoreDaemon::Connection2(SocketStreamTLS &rStream) +{ + // Get the common name from the certificate + std::string clientCommonName(rStream.GetPeerCommonName()); + + // Log the name + BOX_INFO("Client certificate CN: " << clientCommonName); + + // Check it + int32_t id; + if(::sscanf(clientCommonName.c_str(), "BACKUP-%x", &id) != 1) + { + // Bad! Disconnect immediately + return; + } + + // Make ps listings clearer + 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 + BackupStoreContext context(id, *this); + + if (mpTestHook) + { + context.SetTestHook(*mpTestHook); + } + + // See if the client has an account? + if(mpAccounts && mpAccounts->AccountExists(id)) + { + std::string root; + int discSet; + mpAccounts->GetAccountRoot(id, root, discSet); + context.SetClientHasAccount(root, discSet); + } + + // Handle a connection with the backup protocol + BackupProtocolServer server(rStream); + server.SetLogToSysLog(mExtendedLogging); + server.SetTimeout(BACKUP_STORE_TIMEOUT); + try + { + server.DoServer(context); + } + catch(...) + { + LogConnectionStats(clientCommonName.c_str(), rStream); + throw; + } + LogConnectionStats(clientCommonName.c_str(), rStream); + context.CleanUp(); +} + +void BackupStoreDaemon::LogConnectionStats(const char *commonName, + const SocketStreamTLS &s) +{ + // Log the amount of data transferred + 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 new file mode 100644 index 00000000..49af5b81 --- /dev/null +++ b/bin/bbstored/BackupStoreDaemon.h @@ -0,0 +1,100 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BackupStoreDaemon.h +// Purpose: Backup store daemon +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- + +#ifndef BACKUPSTOREDAEMON__H +#define BACKUPSTOREDAEMON__H + +#include "ServerTLS.h" +#include "BoxPortsAndFiles.h" +#include "BackupConstants.h" +#include "BackupStoreContext.h" +#include "HousekeepStoreAccount.h" +#include "IOStreamGetLine.h" + +class BackupStoreAccounts; +class BackupStoreAccountDatabase; + +// -------------------------------------------------------------------------- +// +// Class +// Name: BackupStoreDaemon +// Purpose: Backup store daemon implementation +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- +class BackupStoreDaemon : public ServerTLS, + HousekeepingInterface, HousekeepingCallback +{ +public: + BackupStoreDaemon(); + ~BackupStoreDaemon(); +private: + BackupStoreDaemon(const BackupStoreDaemon &rToCopy); +public: + + // For BackupStoreContext to communicate with housekeeping process + void SendMessageToHousekeepingProcess(const void *Msg, int MsgLen) + { +#ifndef WIN32 + mInterProcessCommsSocket.Write(Msg, MsgLen); +#endif + } + +protected: + + virtual void SetupInInitialProcess(); + + virtual void Run(); + + virtual void Connection(SocketStreamTLS &rStream); + void Connection2(SocketStreamTLS &rStream); + + virtual const char *DaemonName() const; + virtual std::string DaemonBanner() const; + + const ConfigurationVerify *GetConfigVerify() const; + + // Housekeeping functions + void HousekeepingProcess(); + + void LogConnectionStats(const char *commonName, const SocketStreamTLS &s); + +public: + // HousekeepingInterface implementation + virtual bool CheckForInterProcessMsg(int AccountNum = 0, int MaximumWaitTime = 0); + +private: + BackupStoreAccountDatabase *mpAccountDatabase; + BackupStoreAccounts *mpAccounts; + bool mExtendedLogging; + bool mHaveForkedHousekeeping; + bool mIsHousekeepingProcess; + bool mHousekeepingInited; + + SocketStream mInterProcessCommsSocket; + IOStreamGetLine mInterProcessComms; + + virtual void OnIdle(); + void HousekeepingInit(); + void RunHousekeepingIfNeeded(); + int64_t mLastHousekeepingRun; + +public: + void SetTestHook(BackupStoreContext::TestHook& rTestHook) + { + mpTestHook = &rTestHook; + } + +private: + BackupStoreContext::TestHook* mpTestHook; +}; + + +#endif // BACKUPSTOREDAEMON__H + diff --git a/bin/bbstored/HousekeepStoreAccount.cpp b/bin/bbstored/HousekeepStoreAccount.cpp new file mode 100644 index 00000000..0ccbcf23 --- /dev/null +++ b/bin/bbstored/HousekeepStoreAccount.cpp @@ -0,0 +1,1067 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: HousekeepStoreAccount.cpp +// Purpose: +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include + +#include "HousekeepStoreAccount.h" +#include "BackupStoreDaemon.h" +#include "StoreStructure.h" +#include "BackupStoreConstants.h" +#include "RaidFileRead.h" +#include "RaidFileWrite.h" +#include "BackupStoreDirectory.h" +#include "BackupStoreInfo.h" +#include "NamedLock.h" +#include "autogen_BackupStoreException.h" +#include "BackupStoreFile.h" +#include "BufferedStream.h" + +#include "MemLeakFindOn.h" + +// check every 32 directories scanned/files deleted +#define POLL_INTERPROCESS_MSG_CHECK_FREQUENCY 32 + +// -------------------------------------------------------------------------- +// +// Function +// Name: HousekeepStoreAccount::HousekeepStoreAccount(int, const std::string &, int, BackupStoreDaemon &) +// Purpose: Constructor +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- +HousekeepStoreAccount::HousekeepStoreAccount(int AccountID, + const std::string &rStoreRoot, int StoreDiscSet, + HousekeepingCallback* pHousekeepingCallback) + : mAccountID(AccountID), + mStoreRoot(rStoreRoot), + mStoreDiscSet(StoreDiscSet), + mpHousekeepingCallback(pHousekeepingCallback), + mDeletionSizeTarget(0), + mPotentialDeletionsTotalSize(0), + mMaxSizeInPotentialDeletions(0), + mBlocksUsed(0), + mBlocksInOldFiles(0), + mBlocksInDeletedFiles(0), + mBlocksInDirectories(0), + mBlocksUsedDelta(0), + mBlocksInOldFilesDelta(0), + mBlocksInDeletedFilesDelta(0), + mBlocksInDirectoriesDelta(0), + mFilesDeleted(0), + mEmptyDirectoriesDeleted(0), + mSuppressRefCountChangeWarnings(false), + mCountUntilNextInterprocessMsgCheck(POLL_INTERPROCESS_MSG_CHECK_FREQUENCY) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: HousekeepStoreAccount::~HousekeepStoreAccount() +// Purpose: Destructor +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- +HousekeepStoreAccount::~HousekeepStoreAccount() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: HousekeepStoreAccount::DoHousekeeping() +// Purpose: Perform the housekeeping +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- +void HousekeepStoreAccount::DoHousekeeping(bool KeepTryingForever) +{ + // Attempt to lock the account + std::string writeLockFilename; + StoreStructure::MakeWriteLockFilename(mStoreRoot, mStoreDiscSet, + writeLockFilename); + NamedLock writeLock; + if(!writeLock.TryAndGetLock(writeLockFilename.c_str(), + 0600 /* restrictive file permissions */)) + { + if(KeepTryingForever) + { + BOX_WARNING("Failed to lock account for housekeeping, " + "still trying..."); + while(!writeLock.TryAndGetLock(writeLockFilename, + 0600 /* restrictive file permissions */)) + { + sleep(1); + } + } + else + { + // Couldn't lock the account -- just stop now + return; + } + } + + // Load the store info to find necessary info for the housekeeping + std::auto_ptr info(BackupStoreInfo::Load(mAccountID, + mStoreRoot, mStoreDiscSet, false /* Read/Write */)); + + // Calculate how much should be deleted + mDeletionSizeTarget = info->GetBlocksUsed() - info->GetBlocksSoftLimit(); + if(mDeletionSizeTarget < 0) + { + mDeletionSizeTarget = 0; + } + + // initialise the refcount database + mNewRefCounts.clear(); + // try to pre-allocate as much memory as we need + mNewRefCounts.reserve(info->GetLastObjectIDUsed()); + // initialise the refcount of the root entry + mNewRefCounts.resize(BACKUPSTORE_ROOT_DIRECTORY_ID + 1, 0); + mNewRefCounts[BACKUPSTORE_ROOT_DIRECTORY_ID] = 1; + + // Scan the directory for potential things to delete + // 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 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) + { + info->ChangeBlocksUsed(mBlocksUsedDelta); + info->ChangeBlocksInOldFiles(mBlocksInOldFilesDelta); + info->ChangeBlocksInDeletedFiles(mBlocksInDeletedFilesDelta); + + // Save the store info back + info->Save(); + } + + return; + } + + // 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(); + int64_t usedOld = info->GetBlocksInOldFiles(); + 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) + { + // Log this + BOX_ERROR("Housekeeping on account " << + BOX_FORMAT_ACCOUNT(mAccountID) << " found " + "and fixed wrong block counts: " + "used (" << + (used + mBlocksUsedDelta) << "," << + mBlocksUsed << "), old (" << + (usedOld + mBlocksInOldFilesDelta) << "," << + mBlocksInOldFiles << "), deleted (" << + (usedDeleted + mBlocksInDeletedFilesDelta) << + "," << mBlocksInDeletedFiles << "), dirs (" << + usedDirectories << "," << mBlocksInDirectories + << ")"); + } + + // If the current values don't match, store them + if(used != mBlocksUsed + || usedOld != mBlocksInOldFiles + || usedDeleted != mBlocksInDeletedFiles + || usedDirectories != (mBlocksInDirectories + mBlocksInDirectoriesDelta)) + { + // Set corrected values in store info + 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. + + // keep for reporting + int64_t removeASAPBlocksUsedDelta = mBlocksUsedDelta; + + mBlocksUsedDelta = 0; + mBlocksInOldFilesDelta = 0; + mBlocksInDeletedFilesDelta = 0; + + // 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(!deleteInterrupted) + { + deleteInterrupted = DeleteEmptyDirectories(); + } + + // Log deletion if anything was deleted + if(mFilesDeleted > 0 || mEmptyDirectoriesDeleted > 0) + { + BOX_INFO("Housekeeping on account " << + BOX_FORMAT_ACCOUNT(mAccountID) << " " + "removed " << + (0 - (mBlocksUsedDelta + removeASAPBlocksUsedDelta)) << + " blocks (" << mFilesDeleted << " files, " << + mEmptyDirectoriesDeleted << " dirs)" << + (deleteInterrupted?" and was interrupted":"")); + } + + // We can only update the refcount database if we successfully + // finished our scan of all directories, otherwise we don't actually + // know which of the new counts are valid and which aren't + // (we might not have seen second references to some objects, etc.) + + BackupStoreAccountDatabase::Entry account(mAccountID, mStoreDiscSet); + std::auto_ptr apReferences; + + // try to load the reference count database + try + { + apReferences = BackupStoreRefCountDatabase::Load(account, + false); + } + catch(BoxException &e) + { + BOX_WARNING("Reference count database is missing or corrupted " + "during housekeeping, creating a new one."); + mSuppressRefCountChangeWarnings = true; + BackupStoreRefCountDatabase::CreateForRegeneration(account); + apReferences = BackupStoreRefCountDatabase::Load(account, + false); + } + + int64_t LastUsedObjectIdOnDisk = apReferences->GetLastObjectIDUsed(); + + for (int64_t ObjectID = BACKUPSTORE_ROOT_DIRECTORY_ID; + ObjectID < mNewRefCounts.size(); ObjectID++) + { + if (ObjectID > LastUsedObjectIdOnDisk) + { + if (!mSuppressRefCountChangeWarnings) + { + BOX_WARNING("Reference count of object " << + BOX_FORMAT_OBJECTID(ObjectID) << + " not found in database, added" + " with " << mNewRefCounts[ObjectID] << + " references"); + } + apReferences->SetRefCount(ObjectID, + mNewRefCounts[ObjectID]); + LastUsedObjectIdOnDisk = ObjectID; + continue; + } + + BackupStoreRefCountDatabase::refcount_t OldRefCount = + apReferences->GetRefCount(ObjectID); + + if (OldRefCount != mNewRefCounts[ObjectID]) + { + BOX_WARNING("Reference count of object " << + BOX_FORMAT_OBJECTID(ObjectID) << + " changed from " << OldRefCount << + " to " << mNewRefCounts[ObjectID]); + apReferences->SetRefCount(ObjectID, + mNewRefCounts[ObjectID]); + } + } + + // zero excess references in the database + for (int64_t ObjectID = mNewRefCounts.size(); + ObjectID <= LastUsedObjectIdOnDisk; ObjectID++) + { + BackupStoreRefCountDatabase::refcount_t OldRefCount = + apReferences->GetRefCount(ObjectID); + BackupStoreRefCountDatabase::refcount_t NewRefCount = 0; + + if (OldRefCount != NewRefCount) + { + BOX_WARNING("Reference count of object " << + BOX_FORMAT_OBJECTID(ObjectID) << + " changed from " << OldRefCount << + " to " << NewRefCount << " (not found)"); + apReferences->SetRefCount(ObjectID, NewRefCount); + } + } + + // force file to be saved and closed before releasing the lock below + apReferences.reset(); + + // 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()); + } + if(mBlocksInOldFilesDelta < (0 - info->GetBlocksInOldFiles())) + { + mBlocksInOldFilesDelta = (0 - info->GetBlocksInOldFiles()); + } + if(mBlocksInDeletedFilesDelta < (0 - info->GetBlocksInDeletedFiles())) + { + mBlocksInDeletedFilesDelta = (0 - info->GetBlocksInDeletedFiles()); + } + if(mBlocksInDirectoriesDelta < (0 - info->GetBlocksInDirectories())) + { + mBlocksInDirectoriesDelta = (0 - info->GetBlocksInDirectories()); + } + + // Update the usage counts in the store + info->ChangeBlocksUsed(mBlocksUsedDelta); + info->ChangeBlocksInOldFiles(mBlocksInOldFilesDelta); + info->ChangeBlocksInDeletedFiles(mBlocksInDeletedFilesDelta); + info->ChangeBlocksInDirectories(mBlocksInDirectoriesDelta); + + // Save the store info back + info->Save(); + + // Explicity release the lock (would happen automatically on + // going out of scope, included for code clarity) + writeLock.ReleaseLock(); +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: HousekeepStoreAccount::MakeObjectFilename(int64_t, std::string &) +// Purpose: Generate and place the filename for a given object ID +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- +void HousekeepStoreAccount::MakeObjectFilename(int64_t ObjectID, std::string &rFilenameOut) +{ + // Delegate to utility function + StoreStructure::MakeObjectFilename(ObjectID, mStoreRoot, mStoreDiscSet, rFilenameOut, false /* don't bother ensuring the directory exists */); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: HousekeepStoreAccount::ScanDirectory(int64_t) +// 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 +// +// -------------------------------------------------------------------------- +bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID) +{ +#ifndef WIN32 + if((--mCountUntilNextInterprocessMsgCheck) <= 0) + { + mCountUntilNextInterprocessMsgCheck = + POLL_INTERPROCESS_MSG_CHECK_FREQUENCY; + + // Check for having to stop + // Include account ID here as the specified account is locked + if(mpHousekeepingCallback && mpHousekeepingCallback->CheckForInterProcessMsg(mAccountID)) + { + // Need to abort now + return false; + } + } +#endif + + // Get the filename + std::string objectFilename; + MakeObjectFilename(ObjectID, objectFilename); + + // Open it. + std::auto_ptr dirStream(RaidFileRead::Open(mStoreDiscSet, + objectFilename)); + + // Add the size of the directory on disc to the size being calculated + int64_t originalDirSizeInBlocks = dirStream->GetDiscUsageInBlocks(); + mBlocksInDirectories += originalDirSizeInBlocks; + mBlocksUsed += originalDirSizeInBlocks; + + // Read the directory in + BackupStoreDirectory dir; + BufferedStream buf(*dirStream); + dir.ReadFromStream(buf, IOStream::TimeOutInfinite); + dirStream->Close(); + + // Is it empty? + if(dir.GetNumberOfEntries() == 0) + { + // Add it to the list of directories to potentially delete + mEmptyDirectories.push_back(dir.GetObjectID()); + } + + // Calculate reference counts first, before we start requesting + // files to be deleted. + // BLOCK + { + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + + while((en = i.Next()) != 0) + { + // This directory references this object + if (mNewRefCounts.size() <= en->GetObjectID()) + { + mNewRefCounts.resize(en->GetObjectID() + 1, 0); + } + mNewRefCounts[en->GetObjectID()]++; + } + } + + // BLOCK + { + // Remove any files which are marked for removal as soon + // as they become old or deleted. + bool deletedSomething = false; + do + { + // Iterate through the directory + deletedSomething = false; + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + while((en = i.Next(BackupStoreDirectory::Entry::Flags_File)) != 0) + { + int16_t enFlags = en->GetFlags(); + if((enFlags & BackupStoreDirectory::Entry::Flags_RemoveASAP) != 0 + && (enFlags & (BackupStoreDirectory::Entry::Flags_Deleted | BackupStoreDirectory::Entry::Flags_OldVersion)) != 0) + { + // Delete this immediately. + DeleteFile(ObjectID, en->GetObjectID(), dir, objectFilename, originalDirSizeInBlocks); + + // flag as having done something + deletedSomething = true; + + // Must start the loop from the beginning again, as iterator is now + // probably invalid. + break; + } + } + } while(deletedSomething); + } + + // BLOCK + { + // Add files to the list of potential deletions + + // map to count the distance from the mark + typedef std::pair version_t; + std::map markVersionAges; + // map of pair (filename, mark number) -> version age + + // NOTE: use a reverse iterator to allow the distance from mark stuff to work + BackupStoreDirectory::ReverseIterator i(dir); + BackupStoreDirectory::Entry *en = 0; + + while((en = i.Next(BackupStoreDirectory::Entry::Flags_File)) != 0) + { + // Update recalculated usage sizes + int16_t enFlags = en->GetFlags(); + int64_t enSizeInBlocks = en->GetSizeInBlocks(); + mBlocksUsed += enSizeInBlocks; + if(enFlags & BackupStoreDirectory::Entry::Flags_OldVersion) mBlocksInOldFiles += enSizeInBlocks; + if(enFlags & BackupStoreDirectory::Entry::Flags_Deleted) mBlocksInDeletedFiles += enSizeInBlocks; + + // Work out ages of this version from the last mark + int32_t enVersionAge = 0; + std::map::iterator enVersionAgeI( + markVersionAges.find( + version_t(en->GetName().GetEncodedFilename(), + en->GetMarkNumber()))); + if(enVersionAgeI != markVersionAges.end()) + { + enVersionAge = enVersionAgeI->second + 1; + enVersionAgeI->second = enVersionAge; + } + else + { + markVersionAges[version_t(en->GetName().GetEncodedFilename(), en->GetMarkNumber())] = enVersionAge; + } + // enVersionAge is now the age of this version. + + // Potentially add it to the list if it's deleted, if it's an old version or deleted + if((enFlags & (BackupStoreDirectory::Entry::Flags_Deleted | BackupStoreDirectory::Entry::Flags_OldVersion)) != 0) + { + // Is deleted / old version. + DelEn d; + d.mObjectID = en->GetObjectID(); + d.mInDirectory = 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); + + // Update various counts + mPotentialDeletionsTotalSize += d.mSizeInBlocks; + if(d.mSizeInBlocks > mMaxSizeInPotentialDeletions) mMaxSizeInPotentialDeletions = d.mSizeInBlocks; + + // Too much in the list of potential deletions? + // (check against the deletion target + the max size in deletions, so that we never delete things + // and take the total size below the deletion size target) + if(mPotentialDeletionsTotalSize > (mDeletionSizeTarget + mMaxSizeInPotentialDeletions)) + { + int64_t sizeToRemove = mPotentialDeletionsTotalSize - (mDeletionSizeTarget + mMaxSizeInPotentialDeletions); + bool recalcMaxSize = false; + + while(sizeToRemove > 0) + { + // Make iterator for the last element, while checking that there's something there in the first place. + std::set::iterator i(mPotentialDeletions.end()); + if(i != mPotentialDeletions.begin()) + { + // Nothing left in set + break; + } + // Make this into an iterator pointing to the last element in the set + --i; + + // Delete this one? + if(sizeToRemove > i->mSizeInBlocks) + { + sizeToRemove -= i->mSizeInBlocks; + if(i->mSizeInBlocks >= mMaxSizeInPotentialDeletions) + { + // Will need to recalculate the maximum size now, because we've just deleted that element + recalcMaxSize = true; + } + mPotentialDeletions.erase(i); + } + else + { + // Over the size to remove, so stop now + break; + } + } + + if(recalcMaxSize) + { + // Because an object which was the maximum size recorded was deleted from the set + // it's necessary to recalculate this maximum. + mMaxSizeInPotentialDeletions = 0; + std::set::const_iterator i(mPotentialDeletions.begin()); + for(; i != mPotentialDeletions.end(); ++i) + { + if(i->mSizeInBlocks > mMaxSizeInPotentialDeletions) + { + mMaxSizeInPotentialDeletions = i->mSizeInBlocks; + } + } + } + } + } + } + } + + { + // Recurse into subdirectories + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + while((en = i.Next(BackupStoreDirectory::Entry::Flags_Dir)) != 0) + { + // Next level + ASSERT((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) == BackupStoreDirectory::Entry::Flags_Dir); + + if(!ScanDirectory(en->GetObjectID())) + { + // Halting operation + return false; + } + } + } + + return true; +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: HousekeepStoreAccount::DelEnCompare::operator()(const HousekeepStoreAccount::DelEn &, const HousekeepStoreAccount::DelEnd &) +// Purpose: Comparison function for set +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- +bool HousekeepStoreAccount::DelEnCompare::operator()(const HousekeepStoreAccount::DelEn &x, const HousekeepStoreAccount::DelEn &y) +{ + // STL spec says this: + // A Strict Weak Ordering is a Binary Predicate that compares two objects, returning true if the first precedes the second. + + // The sort order here is intended to preserve the entries of most value, that is, the newest objects + // which are on a mark boundary. + + // Reverse order age, so oldest goes first + if(x.mVersionAgeWithinMark > y.mVersionAgeWithinMark) + { + return true; + } + else if(x.mVersionAgeWithinMark < y.mVersionAgeWithinMark) + { + return false; + } + + // but mark number in ascending order, so that the oldest marks are deleted first + if(x.mMarkNumber < y.mMarkNumber) + { + return true; + } + else if(x.mMarkNumber > y.mMarkNumber) + { + return false; + } + + // Just compare object ID now to put the oldest objects first + return x.mObjectID < y.mObjectID; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: HousekeepStoreAccount::DeleteFiles() +// Purpose: Delete the files targetted for deletion, returning true if the operation was interrupted +// Created: 15/12/03 +// +// -------------------------------------------------------------------------- +bool HousekeepStoreAccount::DeleteFiles() +{ + // Only delete files if the deletion target is greater than zero + // (otherwise we delete one file each time round, which gradually deletes the old versions) + if(mDeletionSizeTarget <= 0) + { + // Not interrupted + return false; + } + + // Iterate through the set of potential deletions, until enough has been deleted. + // (there is likely to be more in the set than should be actually deleted). + for(std::set::iterator i(mPotentialDeletions.begin()); i != mPotentialDeletions.end(); ++i) + { +#ifndef WIN32 + if((--mCountUntilNextInterprocessMsgCheck) <= 0) + { + mCountUntilNextInterprocessMsgCheck = POLL_INTERPROCESS_MSG_CHECK_FREQUENCY; + // Check for having to stop + if(mpHousekeepingCallback && mpHousekeepingCallback->CheckForInterProcessMsg(mAccountID)) // include account ID here as the specified account is now locked + { + // Need to abort now + return true; + } + } +#endif + + // Load up the directory it's in + // Get the filename + std::string dirFilename; + BackupStoreDirectory dir; + int64_t dirSizeInBlocksOrig = 0; + { + MakeObjectFilename(i->mInDirectory, dirFilename); + std::auto_ptr dirStream(RaidFileRead::Open(mStoreDiscSet, dirFilename)); + dirSizeInBlocksOrig = dirStream->GetDiscUsageInBlocks(); + dir.ReadFromStream(*dirStream, IOStream::TimeOutInfinite); + } + + // 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 + // space to slightly less than the soft limit, which will allow the backup + // client to start uploading files again) + if((0 - mBlocksUsedDelta) >= mDeletionSizeTarget) + { + break; + } + } + + return false; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: HousekeepStoreAccount::DeleteFile(int64_t, int64_t, BackupStoreDirectory &, const std::string &, int64_t) +// Purpose: Delete a file. Takes the directory already loaded in and the filename, +// for efficiency in both the usage senarios. +// Created: 15/7/04 +// +// -------------------------------------------------------------------------- +void HousekeepStoreAccount::DeleteFile(int64_t InDirectory, int64_t ObjectID, BackupStoreDirectory &rDirectory, const std::string &rDirectoryFilename, int64_t OriginalDirSizeInBlocks) +{ + // Find the entry inside the directory + bool wasDeleted = false; + bool wasOldVersion = false; + int64_t deletedFileSizeInBlocks = 0; + // A pointer to an object which requires commiting if the directory save goes OK + std::auto_ptr padjustedEntry; + // BLOCK + { + BackupStoreDirectory::Entry *pentry = rDirectory.FindEntryByID(ObjectID); + if(pentry == 0) + { + BOX_ERROR("Housekeeping on account " << + BOX_FORMAT_ACCOUNT(mAccountID) << " " + "found error: object " << + BOX_FORMAT_OBJECTID(ObjectID) << " " + "not found in dir " << + BOX_FORMAT_OBJECTID(InDirectory) << ", " + "indicates logic error/corruption? Run " + "bbstoreaccounts check fix"); + return; + } + + // Record the flags it's got set + wasDeleted = ((pentry->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) != 0); + wasOldVersion = ((pentry->GetFlags() & BackupStoreDirectory::Entry::Flags_OldVersion) != 0); + // Check this should be deleted + if(!wasDeleted && !wasOldVersion) + { + // Things changed size we were last around + return; + } + + // Record size + deletedFileSizeInBlocks = pentry->GetSizeInBlocks(); + + // If the entry is involved in a chain of patches, it needs to be handled + // a bit more carefully. + if(pentry->GetDependsNewer() != 0 && pentry->GetDependsOlder() == 0) + { + // This entry is a patch from a newer entry. Just need to update the info on that entry. + BackupStoreDirectory::Entry *pnewer = rDirectory.FindEntryByID(pentry->GetDependsNewer()); + if(pnewer == 0 || pnewer->GetDependsOlder() != ObjectID) + { + THROW_EXCEPTION(BackupStoreException, PatchChainInfoBadInDirectory); + } + // Change the info in the newer entry so that this no longer points to this entry + pnewer->SetDependsOlder(0); + } + else if(pentry->GetDependsOlder() != 0) + { + BackupStoreDirectory::Entry *polder = rDirectory.FindEntryByID(pentry->GetDependsOlder()); + if(pentry->GetDependsNewer() == 0) + { + // There exists an older version which depends on this one. Need to combine the two over that one. + + // Adjust the other entry in the directory + if(polder == 0 || polder->GetDependsNewer() != ObjectID) + { + THROW_EXCEPTION(BackupStoreException, PatchChainInfoBadInDirectory); + } + // Change the info in the older entry so that this no longer points to this entry + polder->SetDependsNewer(0); + } + else + { + // This entry is in the middle of a chain, and two patches need combining. + + // First, adjust the directory entries + BackupStoreDirectory::Entry *pnewer = rDirectory.FindEntryByID(pentry->GetDependsNewer()); + if(pnewer == 0 || pnewer->GetDependsOlder() != ObjectID + || polder == 0 || polder->GetDependsNewer() != ObjectID) + { + THROW_EXCEPTION(BackupStoreException, PatchChainInfoBadInDirectory); + } + // Remove the middle entry from the linked list by simply using the values from this entry + pnewer->SetDependsOlder(pentry->GetDependsOlder()); + polder->SetDependsNewer(pentry->GetDependsNewer()); + } + + // COMMON CODE to both cases + + // Generate the filename of the older version + std::string objFilenameOlder; + MakeObjectFilename(pentry->GetDependsOlder(), objFilenameOlder); + // Open it twice (it's the diff) + std::auto_ptr pdiff(RaidFileRead::Open(mStoreDiscSet, objFilenameOlder)); + std::auto_ptr pdiff2(RaidFileRead::Open(mStoreDiscSet, objFilenameOlder)); + // Open this file + std::string objFilename; + MakeObjectFilename(ObjectID, objFilename); + std::auto_ptr pobjectBeingDeleted(RaidFileRead::Open(mStoreDiscSet, objFilename)); + // And open a write file to overwrite the other directory entry + padjustedEntry.reset(new RaidFileWrite(mStoreDiscSet, objFilenameOlder)); + padjustedEntry->Open(true /* allow overwriting */); + + if(pentry->GetDependsNewer() == 0) + { + // There exists an older version which depends on this one. Need to combine the two over that one. + BackupStoreFile::CombineFile(*pdiff, *pdiff2, *pobjectBeingDeleted, *padjustedEntry); + } + else + { + // This entry is in the middle of a chain, and two patches need combining. + BackupStoreFile::CombineDiffs(*pobjectBeingDeleted, *pdiff, *pdiff2, *padjustedEntry); + } + // The file will be committed later when the directory is safely commited. + + // Work out the adjusted size + int64_t newSize = padjustedEntry->GetDiscUsageInBlocks(); + int64_t sizeDelta = newSize - polder->GetSizeInBlocks(); + mBlocksUsedDelta += sizeDelta; + if((polder->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) != 0) + { + mBlocksInDeletedFilesDelta += sizeDelta; + } + if((polder->GetFlags() & BackupStoreDirectory::Entry::Flags_OldVersion) != 0) + { + mBlocksInOldFilesDelta += sizeDelta; + } + polder->SetSizeInBlocks(newSize); + } + + // pentry no longer valid + } + + // Delete it from the directory + rDirectory.DeleteEntry(ObjectID); + + // Save directory back to disc + // BLOCK + int64_t dirRevisedSize = 0; + { + RaidFileWrite writeDir(mStoreDiscSet, rDirectoryFilename); + writeDir.Open(true /* allow overwriting */); + rDirectory.WriteToStream(writeDir); + + // get the disc usage (must do this before commiting it) + dirRevisedSize = writeDir.GetDiscUsageInBlocks(); + + // Commit directory + writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY); + + // adjust usage counts for this directory + if(dirRevisedSize > 0) + { + int64_t adjust = dirRevisedSize - OriginalDirSizeInBlocks; + mBlocksUsedDelta += adjust; + mBlocksInDirectoriesDelta += adjust; + } + } + + // Commit any new adjusted entry + if(padjustedEntry.get() != 0) + { + padjustedEntry->Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY); + padjustedEntry.reset(); // delete it now + } + + // Drop reference count by one. If it reaches zero, delete the file. + if(--mNewRefCounts[ObjectID] == 0) + { + BOX_TRACE("Removing unreferenced object " << + BOX_FORMAT_OBJECTID(ObjectID)); + std::string objFilename; + MakeObjectFilename(ObjectID, objFilename); + RaidFileWrite del(mStoreDiscSet, objFilename); + del.Delete(); + } + else + { + BOX_TRACE("Preserving object " << + BOX_FORMAT_OBJECTID(ObjectID) << " with " << + mNewRefCounts[ObjectID] << " references"); + } + + // Adjust counts for the file + ++mFilesDeleted; + mBlocksUsedDelta -= deletedFileSizeInBlocks; + if(wasDeleted) mBlocksInDeletedFilesDelta -= deletedFileSizeInBlocks; + if(wasOldVersion) mBlocksInOldFilesDelta -= deletedFileSizeInBlocks; + + // Delete the directory? + // Do this if... dir has zero entries, and is marked as deleted in it's containing directory + if(rDirectory.GetNumberOfEntries() == 0) + { + // Candidate for deletion + mEmptyDirectories.push_back(InDirectory); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: HousekeepStoreAccount::DeleteEmptyDirectories() +// Purpose: Remove any empty directories which are also marked as deleted in their containing directory, +// returning true if the opertaion was interrupted +// Created: 15/12/03 +// +// -------------------------------------------------------------------------- +bool HousekeepStoreAccount::DeleteEmptyDirectories() +{ + while(mEmptyDirectories.size() > 0) + { + std::vector toExamine; + + // Go through list + for(std::vector::const_iterator i(mEmptyDirectories.begin()); i != mEmptyDirectories.end(); ++i) + { +#ifndef WIN32 + if((--mCountUntilNextInterprocessMsgCheck) <= 0) + { + mCountUntilNextInterprocessMsgCheck = POLL_INTERPROCESS_MSG_CHECK_FREQUENCY; + // Check for having to stop + if(mpHousekeepingCallback && mpHousekeepingCallback->CheckForInterProcessMsg(mAccountID)) // include account ID here as the specified account is now locked + { + // Need to abort now + return true; + } + } +#endif + + // Do not delete the root directory + if(*i == BACKUPSTORE_ROOT_DIRECTORY_ID) + { + continue; + } + + DeleteEmptyDirectory(*i, toExamine); + } + + // Remove contents of empty directories + mEmptyDirectories.clear(); + // Swap in new, so it's examined next time round + mEmptyDirectories.swap(toExamine); + } + + // Not interrupted + return false; +} + +void HousekeepStoreAccount::DeleteEmptyDirectory(int64_t dirId, + std::vector& rToExamine) +{ + // Load up the directory to potentially delete + std::string dirFilename; + BackupStoreDirectory dir; + int64_t dirSizeInBlocks = 0; + + // 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 dirStream( + RaidFileRead::Open(mStoreDiscSet, dirFilename)); + dirSizeInBlocks = dirStream->GetDiscUsageInBlocks(); + dir.ReadFromStream(*dirStream, IOStream::TimeOutInfinite); + } + + // Make sure this directory is actually empty + if(dir.GetNumberOfEntries() != 0) + { + // Not actually empty, try next one + return; + } + + // Candidate for deletion... open containing directory + std::string containingDirFilename; + BackupStoreDirectory containingDir; + int64_t containingDirSizeInBlocksOrig = 0; + { + MakeObjectFilename(dir.GetContainerID(), containingDirFilename); + std::auto_ptr containingDirStream( + RaidFileRead::Open(mStoreDiscSet, + containingDirFilename)); + containingDirSizeInBlocksOrig = + containingDirStream->GetDiscUsageInBlocks(); + containingDir.ReadFromStream(*containingDirStream, + IOStream::TimeOutInfinite); + } + + // 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()); + + // Is the containing dir now a candidate for deletion? + if(containingDir.GetNumberOfEntries() == 0) + { + rToExamine.push_back(containingDir.GetObjectID()); + } + + // Write revised parent directory + RaidFileWrite writeDir(mStoreDiscSet, containingDirFilename); + writeDir.Open(true /* allow overwriting */); + containingDir.WriteToStream(writeDir); + + // get the disc usage (must do this before commiting it) + int64_t dirSize = writeDir.GetDiscUsageInBlocks(); + + // Commit directory + writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY); + + // adjust usage counts for this directory + if(dirSize > 0) + { + int64_t adjust = dirSize - containingDirSizeInBlocksOrig; + mBlocksUsedDelta += adjust; + mBlocksInDirectoriesDelta += adjust; + } + + // 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 new file mode 100644 index 00000000..1dd6d79c --- /dev/null +++ b/bin/bbstored/HousekeepStoreAccount.h @@ -0,0 +1,111 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: HousekeepStoreAccount.h +// Purpose: Action class to perform housekeeping on a store account +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- + +#ifndef HOUSEKEEPSTOREACCOUNT__H +#define HOUSEKEEPSTOREACCOUNT__H + +#include +#include +#include + +class BackupStoreDaemon; +class BackupStoreDirectory; + +class HousekeepingCallback +{ + public: + virtual ~HousekeepingCallback() {} + virtual bool CheckForInterProcessMsg(int AccountNum = 0, int MaximumWaitTime = 0) = 0; +}; + +// -------------------------------------------------------------------------- +// +// Class +// Name: HousekeepStoreAccount +// Purpose: Action class to perform housekeeping on a store account +// Created: 11/12/03 +// +// -------------------------------------------------------------------------- +class HousekeepStoreAccount +{ +public: + HousekeepStoreAccount(int AccountID, const std::string &rStoreRoot, + int StoreDiscSet, HousekeepingCallback* pHousekeepingCallback); + ~HousekeepStoreAccount(); + + void DoHousekeeping(bool KeepTryingForever = false); + + +private: + // utility functions + void MakeObjectFilename(int64_t ObjectID, std::string &rFilenameOut); + + bool ScanDirectory(int64_t ObjectID); + bool DeleteFiles(); + bool DeleteEmptyDirectories(); + void DeleteEmptyDirectory(int64_t dirId, + std::vector& rToExamine); + void DeleteFile(int64_t InDirectory, int64_t ObjectID, BackupStoreDirectory &rDirectory, const std::string &rDirectoryFilename, int64_t OriginalDirSizeInBlocks); + +private: + typedef struct + { + int64_t mObjectID; + int64_t mInDirectory; + 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 + { + bool operator()(const DelEn &x, const DelEn &y); + }; + + int mAccountID; + std::string mStoreRoot; + int mStoreDiscSet; + HousekeepingCallback* mpHousekeepingCallback; + + int64_t mDeletionSizeTarget; + + std::set mPotentialDeletions; + int64_t mPotentialDeletionsTotalSize; + int64_t mMaxSizeInPotentialDeletions; + + // List of directories which are empty, and might be good for deleting + std::vector mEmptyDirectories; + + // The re-calculated blocks used stats + int64_t mBlocksUsed; + int64_t mBlocksInOldFiles; + int64_t mBlocksInDeletedFiles; + int64_t mBlocksInDirectories; + + // Deltas from deletion + int64_t mBlocksUsedDelta; + int64_t mBlocksInOldFilesDelta; + int64_t mBlocksInDeletedFilesDelta; + int64_t mBlocksInDirectoriesDelta; + + // Deletion count + int64_t mFilesDeleted; + int64_t mEmptyDirectoriesDeleted; + + // New reference count list + std::vector mNewRefCounts; + bool mSuppressRefCountChangeWarnings; + + // Poll frequency + int mCountUntilNextInterprocessMsgCheck; +}; + +#endif // HOUSEKEEPSTOREACCOUNT__H + diff --git a/bin/bbstored/Makefile.extra b/bin/bbstored/Makefile.extra new file mode 100644 index 00000000..6562647d --- /dev/null +++ b/bin/bbstored/Makefile.extra @@ -0,0 +1,9 @@ + +MAKEPROTOCOL = ../../lib/server/makeprotocol.pl + +GEN_CMD_SRV = $(MAKEPROTOCOL) Server backupprotocol.txt + +# AUTOGEN SEEDING +autogen_BackupProtocolServer.cpp autogen_BackupProtocolServer.h: $(MAKEPROTOCOL) backupprotocol.txt + $(_PERL) $(GEN_CMD_SRV) + diff --git a/bin/bbstored/backupprotocol.txt b/bin/bbstored/backupprotocol.txt new file mode 100644 index 00000000..3eca514a --- /dev/null +++ b/bin/bbstored/backupprotocol.txt @@ -0,0 +1,234 @@ +# +# backup protocol definition +# + +Name Backup +IdentString Box-Backup:v=C +ServerContextClass BackupStoreContext BackupStoreContext.h + +ClientType Filename BackupStoreFilenameClear BackupStoreFilenameClear.h +ServerType Filename BackupStoreFilename BackupStoreFilename.h + +ImplementLog Server syslog +ImplementLog Client syslog +ImplementLog Client file + +LogTypeToText Client Filename \"%s\" VAR.GetClearFilename().c_str() + +BEGIN_OBJECTS + +# ------------------------------------------------------------------------------------- +# Session commands +# ------------------------------------------------------------------------------------- + +Error 0 IsError(Type,SubType) Reply + int32 Type + int32 SubType + CONSTANT ErrorType 1000 + CONSTANT Err_WrongVersion 1 + CONSTANT Err_NotInRightProtocolPhase 2 + CONSTANT Err_BadLogin 3 + CONSTANT Err_CannotLockStoreForWriting 4 + CONSTANT Err_SessionReadOnly 5 + CONSTANT Err_FileDoesNotVerify 6 + CONSTANT Err_DoesNotExist 7 + CONSTANT Err_DirectoryAlreadyExists 8 + CONSTANT Err_CannotDeleteRoot 9 + CONSTANT Err_TargetNameExists 10 + CONSTANT Err_StorageLimitExceeded 11 + CONSTANT Err_DiffFromFileDoesNotExist 12 + CONSTANT Err_DoesNotExistInDirectory 13 + CONSTANT Err_PatchConsistencyError 14 + +Version 1 Command(Version) Reply + int32 Version + + +Login 2 Command(LoginConfirmed) + int32 ClientID + int32 Flags + CONSTANT Flags_ReadOnly 1 + + +LoginConfirmed 3 Reply + int64 ClientStoreMarker + int64 BlocksUsed + int64 BlocksSoftLimit + int64 BlocksHardLimit + + +Finished 4 Command(Finished) Reply EndsConversation + + +# generic success object +Success 5 Reply + int64 ObjectID + + +SetClientStoreMarker 6 Command(Success) + int64 ClientStoreMarker + + +# ------------------------------------------------------------------------------------- +# Generic object commands +# ------------------------------------------------------------------------------------- + +GetObject 10 Command(Success) + int64 ObjectID + CONSTANT NoObject 0 + # reply has stream following, if ObjectID != NoObject + + +MoveObject 11 Command(Success) + int64 ObjectID + int64 MoveFromDirectory + int64 MoveToDirectory + int32 Flags + Filename NewFilename + + CONSTANT Flags_MoveAllWithSameName 1 + CONSTANT Flags_AllowMoveOverDeletedObject 2 + +# consider this an object command as, although it deals with directory entries, +# it's not specific to either a file or a directory + + +GetObjectName 12 Command(ObjectName) + int64 ObjectID + int64 ContainingDirectoryID + CONSTANT ObjectID_DirectoryOnly 0 + + # set ObjectID to ObjectID_DirectoryOnly to only get info on the directory + + +ObjectName 13 Reply + int32 NumNameElements + int64 ModificationTime + int64 AttributesHash + int16 Flags + # NumNameElements is zero if the object doesn't exist + CONSTANT NumNameElements_ObjectDoesntExist 0 + # a stream of Filename objects follows, if and only if NumNameElements > 0 + + +# ------------------------------------------------------------------------------------- +# Directory commands +# ------------------------------------------------------------------------------------- + +CreateDirectory 20 Command(Success) StreamWithCommand + int64 ContainingDirectoryID + int64 AttributesModTime + Filename DirectoryName + # stream following containing attributes + + +ListDirectory 21 Command(Success) + int64 ObjectID + int16 FlagsMustBeSet + int16 FlagsNotToBeSet + bool SendAttributes + # make sure these flags are synced with those in BackupStoreDirectory + CONSTANT Flags_INCLUDE_EVERYTHING -1 + CONSTANT Flags_EXCLUDE_NOTHING 0 + CONSTANT Flags_EXCLUDE_EVERYTHING 15 + CONSTANT Flags_File 1 + CONSTANT Flags_Dir 2 + CONSTANT Flags_Deleted 4 + CONSTANT Flags_OldVersion 8 + # make sure this is the same as in BackupStoreConstants.h + CONSTANT RootDirectory 1 + + # reply has stream following Success object, containing a stored BackupStoreDirectory + + +ChangeDirAttributes 22 Command(Success) StreamWithCommand + int64 ObjectID + int64 AttributesModTime + # stream following containing attributes + + +DeleteDirectory 23 Command(Success) + int64 ObjectID + +UndeleteDirectory 24 Command(Success) + int64 ObjectID + # may not have exactly the desired effect if files within in have been deleted before the directory was deleted. + + +# ------------------------------------------------------------------------------------- +# File commands +# ------------------------------------------------------------------------------------- + +StoreFile 30 Command(Success) StreamWithCommand + int64 DirectoryObjectID + int64 ModificationTime + int64 AttributesHash + int64 DiffFromFileID # 0 if the file is not a diff + Filename Filename + # then send a stream containing the encoded file + + +GetFile 31 Command(Success) + int64 InDirectory + int64 ObjectID + # error returned if not a file, or does not exist + # reply has stream following, containing an encoded file IN STREAM ORDER + # (use GetObject to get it in file order) + + +SetReplacementFileAttributes 32 Command(Success) StreamWithCommand + int64 InDirectory + int64 AttributesHash + Filename Filename + # stream follows containing attributes + + +DeleteFile 33 Command(Success) + int64 InDirectory + Filename Filename + # will return 0 if the object couldn't be found in the specified directory + + +GetBlockIndexByID 34 Command(Success) + int64 ObjectID + + # stream of the block index follows the reply + # returns an error if the object didn't exist + + +GetBlockIndexByName 35 Command(Success) + int64 InDirectory + Filename Filename + + # Success object contains the found ID -- or 0 if the entry wasn't found in the directory + # 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 +# ------------------------------------------------------------------------------------- + +GetAccountUsage 40 Command(AccountUsage) + # no data members + +AccountUsage 41 Reply + int64 BlocksUsed + int64 BlocksInOldFiles + int64 BlocksInDeletedFiles + int64 BlocksInDirectories + int64 BlocksSoftLimit + int64 BlocksHardLimit + int32 BlockSize + +GetIsAlive 42 Command(IsAlive) + # no data members + +IsAlive 43 Reply + # no data members + diff --git a/bin/bbstored/bbstored-certs.in b/bin/bbstored/bbstored-certs.in new file mode 100755 index 00000000..85560748 --- /dev/null +++ b/bin/bbstored/bbstored-certs.in @@ -0,0 +1,319 @@ +#!@PERL@ +use strict; + +# validity period for root certificates -- default is 2038, the best we can do for now +my $root_sign_period = int(((1<<31) - time()) / 86400); + +# but less so for client certificates +my $sign_period = '5000'; + +# check and get command line parameters +if($#ARGV < 1) +{ + print <<__E; + +bbstored certificates utility. + +Bad command line parameters. +Usage: + bbstored-certs certs-dir command [arguments] + +certs-dir is the directory holding the root keys and certificates for the backup system +command is the action to perform, taking parameters. + +Commands are + + init + -- generate initial root certificates (certs-dir must not already exist) + sign certificate-name + -- sign a client certificate + sign-server certificate-name + -- sign a server certificate + +Signing requires confirmation that the certificate is correct and should be signed. + +__E + exit(1); +} + +# check for OPENSSL_CONF environment var being set +if(exists $ENV{'OPENSSL_CONF'}) +{ + print <<__E; + +--------------------------------------- + +WARNING: + You have the OPENSSL_CONF environment variable set. + Use of non-standard openssl configs may cause problems. + +--------------------------------------- + +__E +} + +# directory structure: +# +# roots/ +# clientCA.pem -- root certificate for client (used on server) +# serverCA.pem -- root certificate for servers (used on clients) +# keys/ +# clientRootKey.pem -- root key for clients +# serverRootKey.pem -- root key for servers +# servers/ +# hostname.pem -- certificate for server 'hostname' +# clients/ +# account.pem -- certficiate for account 'account' (ID in hex) +# + + +# check parameters +my ($cert_dir,$command,@args) = @ARGV; + +# check directory exists +if($command ne 'init') +{ + if(!-d $cert_dir) + { + die "$cert_dir does not exist"; + } +} + +# run command +if($command eq 'init') {&cmd_init;} +elsif($command eq 'sign') {&cmd_sign;} +elsif($command eq 'sign-server') {&cmd_sign_server;} +else +{ + die "Unknown command $command" +} + +sub cmd_init +{ + # create directories + unless(mkdir($cert_dir,0700) + && mkdir($cert_dir.'/roots',0700) + && mkdir($cert_dir.'/keys',0700) + && mkdir($cert_dir.'/servers',0700) + && mkdir($cert_dir.'/clients',0700)) + { + die "Failed to create directory structure" + } + + # create root keys and certrs + cmd_init_create_root('client'); + cmd_init_create_root('server'); +} + +sub cmd_init_create_root +{ + my $entity = $_[0]; + + my $cert = "$cert_dir/roots/".$entity.'CA.pem'; + my $serial = "$cert_dir/roots/".$entity.'CA.srl'; + my $key = "$cert_dir/keys/".$entity.'RootKey.pem'; + my $csr = "$cert_dir/keys/".$entity.'RootCSR.pem'; + + # generate key + if(system("openssl genrsa -out $key 2048") != 0) + { + die "Couldn't generate private key." + } + + # make CSR + die "Couldn't run openssl for CSR generation" unless + open(CSR,"|openssl req -new -key $key -sha1 -out $csr"); + print CSR <<__E; +. +. +. +. +. +Backup system $entity root +. +. +. + +__E + close CSR; + print "\n\n"; + die "Certificate request wasn't created.\n" unless -f $csr; + + # sign it to make a self-signed root CA key + if(system("openssl x509 -req -in $csr -sha1 -extensions v3_ca -signkey $key -out $cert -days $root_sign_period") != 0) + { + die "Couldn't generate root certificate." + } + + # write the initial serial number + open SERIAL,">$serial" or die "Can't open $serial for writing"; + print SERIAL "00\n"; + close SERIAL; +} + +sub cmd_sign +{ + my $csr = $args[0]; + + if(!-f $csr) + { + die "$csr does not exist"; + } + + # get the common name specified in this certificate + my $common_name = get_csr_common_name($csr); + + # look OK? + unless($common_name =~ m/\ABACKUP-([A-Fa-f0-9]+)\Z/) + { + die "The certificate presented does not appear to be a backup client certificate" + } + + my $acc = $1; + + # check against filename + if(!($csr =~ m/(\A|\/)([A-Fa-f0-9]+)-/) || $2 ne $acc) + { + die "Certificate request filename does not match name in certificate ($common_name)" + } + + print <<__E; + +This certificate is for backup account + + $acc + +Ensure this matches the account number you are expecting. The filename is + + $csr + +which should include this account number, and additionally, you should check +that you received it from the right person. + +Signing the wrong certificate compromises the security of your backup system. + +Would you like to sign this certificate? (type 'yes' to confirm) +__E + + return unless get_confirmation(); + + # out certificate + my $out_cert = "$cert_dir/clients/$acc"."-cert.pem"; + + # sign it! + if(system("openssl x509 -req -in $csr -sha1 -extensions usr_crt -CA $cert_dir/roots/clientCA.pem -CAkey $cert_dir/keys/clientRootKey.pem -out $out_cert -days $sign_period") != 0) + { + die "Signing failed" + } + + # tell user what to do next + print <<__E; + + +Certificate signed. + +Send the files + + $out_cert + $cert_dir/roots/serverCA.pem + +to the client. + +__E +} + +sub cmd_sign_server +{ + my $csr = $args[0]; + + if(!-f $csr) + { + die "$csr does not exist"; + } + + # get the common name specified in this certificate + my $common_name = get_csr_common_name($csr); + + # look OK? + if($common_name !~ m/\A[-a-zA-Z0-9.]+\Z/) + { + die "Invalid server name" + } + + print <<__E; + +This certificate is for backup server + + $common_name + +Signing the wrong certificate compromises the security of your backup system. + +Would you like to sign this certificate? (type 'yes' to confirm) +__E + + return unless get_confirmation(); + + # out certificate + my $out_cert = "$cert_dir/servers/$common_name"."-cert.pem"; + + # sign it! + if(system("openssl x509 -req -in $csr -sha1 -extensions usr_crt -CA $cert_dir/roots/serverCA.pem -CAkey $cert_dir/keys/serverRootKey.pem -out $out_cert -days $sign_period") != 0) + { + die "Signing failed" + } + + # tell user what to do next + print <<__E; + + +Certificate signed. + +Install the files + + $out_cert + $cert_dir/roots/clientCA.pem + +on the server. + +__E +} + + +sub get_csr_common_name +{ + my $csr = $_[0]; + + open CSRTEXT,"openssl req -text -in $csr |" or die "Can't open openssl for reading"; + + my $subject; + while() + { + $subject = $1 if m/Subject:.+?CN=([-\.\w]+)/ + } + close CSRTEXT; + + if($subject eq '') + { + die "No subject found in CSR $csr" + } + + return $subject +} + +sub get_confirmation() +{ + my $line = ; + chomp $line; + if(lc $line ne 'yes') + { + print "CANCELLED\n"; + return 0; + } + + return 1; +} + + + + + diff --git a/bin/bbstored/bbstored-config.in b/bin/bbstored/bbstored-config.in new file mode 100755 index 00000000..83305c4f --- /dev/null +++ b/bin/bbstored/bbstored-config.in @@ -0,0 +1,245 @@ +#!@PERL@ +use strict; + +# should be running as root +if($> != 0) +{ + printf "\nWARNING: this should be run as root\n\n" +} + +# check and get command line parameters +if($#ARGV < 2) +{ + print <<__E; + +Setup bbstored config utility. + +Bad command line parameters. +Usage: + bbstored-config config-dir server-hostname username [raidfile-config] + +Parameters: + config-dir is usually @sysconfdir_expanded@/boxbackup + server-hostname is the hostname that clients will use to connect to + this server + username is the user to run the server under + raidfile-config is optional. Use if you have a non-standard + raidfile.conf file. + +__E + exit(1); +} + +# check for OPENSSL_CONF environment var being set +if(exists $ENV{'OPENSSL_CONF'}) +{ + print <<__E; + +--------------------------------------- + +WARNING: + You have the OPENSSL_CONF environment variable set. + Use of non-standard openssl configs may cause problems. + +--------------------------------------- + +__E +} + +# default locations +my $default_config_location = '@sysconfdir_expanded@/boxbackup/bbstored.conf'; + +# command line parameters +my ($config_dir,$server,$username,$raidfile_config) = @ARGV; + +$raidfile_config = $config_dir . '/raidfile.conf' unless $raidfile_config ne ''; + +# check server exists, but don't bother checking that it's actually this machine. +{ + my @r = gethostbyname($server); + if($#r < 0) + { + die "Server '$server' not found. (check server name, test DNS lookup failed.)" + } +} + +# check this exists +if(!-f $raidfile_config) +{ + print "The RaidFile configuration file $raidfile_config doesn't exist.\nYou may need to create it with raidfile-config.\nWon't configure bbstored without it.\n"; + exit(1); +} + +# check that the user exists +die "You shouldn't run bbstored as root" if $username eq 'root'; +my $user_uid = 0; +(undef,undef,$user_uid) = getpwnam($username); +if($user_uid == 0) +{ + die "User $username doesn't exist\n"; +} + +# check that directories are writeable +open RAIDCONF,$raidfile_config or die "Can't open $raidfile_config"; +{ + my %done = (); + while() + { + next unless m/Dir\d\s*=\s*(.+)/; + my $d = $1; + $d = $d.'/backup' if -e $d.'/backup'; + print "Checking permissions on $d\n"; + my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($d); + my $req_perms = ($uid == $user_uid)?0700:0007; + if(($mode & $req_perms) != $req_perms) + { + print "$username doesn't appear to have the necessary permissions on $d\n"; + print "Either adjust permissions, or create a directory 'backup' inside the\n"; + print "directory specified in raidfile.conf which is writable.\n"; + exit(1); + } + } +} +close RAIDCONF; + +# ssl stuff +my $private_key = "$config_dir/bbstored/$server-key.pem"; +my $certificate_request = "$config_dir/bbstored/$server-csr.pem"; +my $certificate = "$config_dir/bbstored/$server-cert.pem"; +my $ca_root_cert = "$config_dir/bbstored/clientCA.pem"; + +# other files +my $config_file = "$config_dir/bbstored.conf"; +my $accounts_file = "$config_dir/bbstored/accounts.txt"; + +# summarise configuration + +print <<__E; + +Setup bbstored config utility. + +Configuration: + Writing configuration file: $config_file + Writing empty accounts file: $accounts_file + Server hostname: $server + RaidFile config: $raidfile_config + +__E + +# create directories +if(!-d $config_dir) +{ + print "Creating $config_dir...\n"; + mkdir $config_dir,0755 or die "Can't create $config_dir"; +} + +if(!-d "$config_dir/bbstored") +{ + print "Creating $config_dir/bbstored\n"; + mkdir "$config_dir/bbstored",0755 or die "Can't create $config_dir/bbstored"; +} + +# create blank accounts file +if(!-f $accounts_file) +{ + print "Creating blank accounts file\n"; + open ACC,">$accounts_file"; + close ACC; +} + +# generate the private key for the server +if(!-f $private_key) +{ + print "Generating private key...\n"; + if(system("openssl genrsa -out $private_key 2048") != 0) + { + die "Couldn't generate private key." + } +} + +# generate a certificate request +if(!-f $certificate_request) +{ + die "Couldn't run openssl for CSR generation" unless + open(CSR,"|openssl req -new -key $private_key -sha1 -out $certificate_request"); + print CSR <<__E; +. +. +. +. +. +$server +. +. +. + +__E + close CSR; + print "\n\n"; + die "Certificate request wasn't created.\n" unless -f $certificate_request +} + +# write the configuration file +print "Writing configuration file $config_file\n"; +open CONFIG,">$config_file" or die "Can't open config file for writing"; +print CONFIG <<__E; + +RaidFileConf = $raidfile_config +AccountDatabase = $accounts_file + +# Uncomment this line to see exactly what commands are being received from clients. +# ExtendedLogging = yes + +# scan all accounts for files which need deleting every 15 minutes. + +TimeBetweenHousekeeping = 900 + +Server +{ + PidFile = @localstatedir_expanded@/run/bbstored.pid + User = $username + ListenAddresses = inet:$server + CertificateFile = $certificate + PrivateKeyFile = $private_key + TrustedCAsFile = $ca_root_cert +} + + +__E + +close CONFIG; + +# explain to the user what they need to do next +my $daemon_args = ($config_file eq $default_config_location)?'':" $config_file"; + +print <<__E; + +=================================================================== + +bbstored basic configuration complete. + +What you need to do now... + +1) Sign $certificate_request + using the bbstored-certs utility. + +2) Install the server certificate and root CA certificate as + $certificate + $ca_root_cert + +3) You may wish to read the configuration file + $config_file + and adjust as appropraite. + +4) Create accounts with bbstoreaccounts + +5) Start the backup store daemon with the command + @sbindir_expanded@/bbstored$daemon_args + in /etc/rc.local, or your local equivalent. + +=================================================================== + +__E + + + diff --git a/bin/bbstored/bbstored.cpp b/bin/bbstored/bbstored.cpp new file mode 100644 index 00000000..21a9e5f1 --- /dev/null +++ b/bin/bbstored/bbstored.cpp @@ -0,0 +1,37 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: bbstored.cpp +// Purpose: main file for backup store daemon +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "BackupStoreDaemon.h" +#include "MainHelper.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +int main(int argc, const char *argv[]) +{ + MAINHELPER_START + + Logging::SetProgramName("bbstored"); + Logging::ToConsole(true); + Logging::ToSyslog (true); + + BackupStoreDaemon daemon; + + #ifdef WIN32 + return daemon.Main(BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE, + argc, argv); + #else + return daemon.Main(BOX_FILE_BBSTORED_DEFAULT_CONFIG, + argc, argv); + #endif + + MAINHELPER_END +} + diff --git a/bin/s3simulator/s3simulator.cpp b/bin/s3simulator/s3simulator.cpp new file mode 100644 index 00000000..9a10635c --- /dev/null +++ b/bin/s3simulator/s3simulator.cpp @@ -0,0 +1,32 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: s3simulator.cpp +// Purpose: main file for S3 simulator daemon +// Created: 2003/10/11 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "S3Simulator.h" +#include "MainHelper.h" + +#include "MemLeakFindOn.h" + +int main(int argc, const char *argv[]) +{ + int ExitCode = 0; + + MAINHELPER_START + + Logging::SetProgramName("s3simulator"); + Logging::ToConsole(true); + Logging::ToSyslog (true); + + S3Simulator daemon; + ExitCode = daemon.Main("s3simulator.conf", argc, argv); + + MAINHELPER_END + + return ExitCode; +} diff --git a/bootstrap b/bootstrap new file mode 100755 index 00000000..14fc19e3 --- /dev/null +++ b/bootstrap @@ -0,0 +1,5 @@ +#!/bin/sh + +aclocal -I infrastructure/m4 +autoheader +autoconf diff --git a/cleanupforcvs.pl b/cleanupforcvs.pl new file mode 100755 index 00000000..3379a7ad --- /dev/null +++ b/cleanupforcvs.pl @@ -0,0 +1,196 @@ +#!/usr/bin/perl +use strict; + +my @del_macos_files; +my @bad_cpp; +my @test_main; +my @makefiles; +my @autogen_cpp; +my $cleaned = 1; +my $dist_archives_exist = 0; +my @bad_h; + +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); + +while() +{ + chomp; + next if -d; + if(m~/autogen_\w+\.(h|cpp)~) + { + push @autogen_cpp,$_ + } + if(m~/\._[^/]+\Z~ || m~/\.DS_Store\Z~) + { + # mac OS files we don't want + push @del_macos_files,$_ + } + elsif(m/\/(\w+\.cpp)/) + { + my $leafname = $1; + # check that Box.h is first include + open CPP,$_ or die "Can't open $_ for reading"; + + my $box_found = 0; + my $last_was_memteston = 0; + my $ok = 1; + + while(my $l = ) + { + if($l =~ m/#include\s+["<](.+?)[">]/) + { + my $inc_name = $1; + if($inc_name eq 'Box.h') + { + $box_found = 1; + } + else + { + # Box.h must be first include file in every cpp file + $ok = 0 unless $box_found; + } + # is it the mem test on thing? (ignoring the wire packing .h files) + if($inc_name ne 'BeginStructPackForWire.h' && $inc_name ne 'EndStructPackForWire.h') + { + $last_was_memteston = ($inc_name eq 'MemLeakFindOn.h'); + } + } + } + if(!exists $exclude_from_memtest_checks{$leafname}) + { + $ok = 0 unless $last_was_memteston; + } + push @bad_cpp,$_ unless $ok; + + close CPP; + } + elsif(m/\/(\w+\.h)/) + { + my $leafname = $1; + + open H,$_ or die "Can't open $_ for reading"; + + my $ok = 1; + my $memteston = 0; + + while(my $l = ) + { + if($l =~ m/#include\s+["<](.+?)[">]/) + { + if($1 eq 'MemLeakFindOn.h') + { + $memteston = 1; + } + elsif($1 eq 'MemLeakFindOff.h') + { + $memteston = 0; + } + else + { + # don't allow #include within mem test on + $ok = 0 unless !$memteston; + } + } + else + { + # strip comments + my $lsc = $l; + $lsc =~ s~//.+$~~; + if($lsc =~ m/\b(new|delete|malloc|free|realloc)\b/) + { + # only allow this if memory checking is ON + $ok = 0 unless $memteston; + } + } + } + # mem test must be off at the end of this .h file + $ok = 0 if $memteston; + + if($_ !~ /testfiles/ && !exists $exclude_from_memtest_checks{$leafname}) + { + push @bad_h,$_ unless $ok; + } + close H; + } + elsif(m~/Makefile\Z~) + { + push @makefiles,$_ + } + + if(m~/_(main\.cpp|t|t-gdb)\Z~) + { + push @test_main,$_ + } + + if(m~\./boxbackup~) + { + $dist_archives_exist = 1; + } +} + +close EVERYTHING; + +ask_about_delete(\@del_macos_files, "supurious MacOS X files"); +ask_about_delete(\@makefiles, "automatically generated Makefiles"); +ask_about_delete(\@test_main, "automatically generated test files"); +ask_about_delete(\@autogen_cpp, "automatically generated source files"); + +if($#bad_cpp >= 0) +{ + print "\n"; + print $_,"\n" for @bad_cpp; + print "There are some .cpp file where Box.h is not the first included file or MemLeakFindOn.h is not the last .h file included\n"; + $cleaned = 0; +} +if($#bad_h >= 0) +{ + print "\n"; + print $_,"\n" for @bad_h; + print "There are some .h files which use memory functions without memory leak finding on, or leave memory leak finding on at end\n"; + $cleaned = 0; +} + +if(-d 'debug') {print "debug directory exists\n"; $cleaned = 0;} +if(-d 'release') {print "release directory exists\n"; $cleaned = 0;} +if(-d 'parcels') {print "parcels directory exists\n"; $cleaned = 0;} +if($dist_archives_exist) {print "boxbackup* files/dirs exist\n"; $cleaned = 0;} + +if(!$cleaned) +{ + print <<__E; + +======================================================== + NOT CLEANED! +======================================================== +__E +} + + +sub ask_about_delete +{ + my ($del_r, $name) = @_; + return if $#$del_r < 0; + + print "\n"; + for(@$del_r) + { + print $_,"\n"; + } + print "Delete these ",$#$del_r + 1, " $name? "; + my $in = ; + chomp $in; + if($in eq 'yes') + { + print "Deleting...\n"; + unlink $_ for @$del_r + } + else + { + $cleaned = 0; + } +} + + + diff --git a/config.guess b/config.guess new file mode 100755 index 00000000..0773d0f6 --- /dev/null +++ b/config.guess @@ -0,0 +1,1456 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2004-03-03' + +# This file 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + amd64:OpenBSD:*:*) + echo x86_64-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + cats:OpenBSD:*:*) + echo arm-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pegasos:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mipseb-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit 0 ;; + macppc:MirBSD:*:*) + echo powerppc-unknown-mirbsd${UNAME_RELEASE} + exit 0 ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit 0 ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha*:OpenVMS:*:*) + echo alpha-hp-vms + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit 0 ;; + DRS?6000:UNIX_SV:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7 && exit 0 ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c \ + && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && exit 0 + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + # avoid double evaluation of $set_cc_for_build + test -n "$CC_FOR_BUILD" || eval $set_cc_for_build + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + *:UNICOS/mp:*:*) + echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + # Determine whether the default compiler uses glibc. + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #if __GLIBC__ >= 2 + LIBC=gnu + #else + LIBC= + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + # GNU/KFreeBSD systems have a "k" prefix to indicate we are using + # FreeBSD's kernel, but not the complete OS. + case ${LIBC} in gnu) kernel_only='k' ;; esac + echo ${UNAME_MACHINE}-unknown-${kernel_only}freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC} + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + x86:Interix*:[34]*) + echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' + exit 0 ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit 0 ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + case `uname -p` in + *86) UNAME_PROCESSOR=i686 ;; + powerpc) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit 0 ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.sub b/config.sub new file mode 100755 index 00000000..264f820a --- /dev/null +++ b/config.sub @@ -0,0 +1,1549 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2004-02-23' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ + kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32r-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | msp430-* \ + | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \ + | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + cr16c) + basic_machine=cr16c-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nv1) + basic_machine=nv1-cray + os=-unicosmp + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..48fb510f --- /dev/null +++ b/configure.ac @@ -0,0 +1,420 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([Box Backup], 0.11, [boxbackup@boxbackup.org]) +AC_CONFIG_SRCDIR([lib/common/Box.h]) +AC_CONFIG_HEADERS([lib/common/BoxConfig.h]) + +touch install-sh +AC_CANONICAL_SYSTEM +test -s install-sh || rm install-sh + +### Checks for programs. + +AC_LANG([C++]) +AC_PROG_CXX +AC_CXX_EXCEPTIONS +AC_CXX_NAMESPACES +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, 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]])]) + +case $target_os in +mingw*) + TARGET_PERL=perl + ;; +*) + TARGET_PERL=$PERL + ;; +esac + +AC_SUBST([TARGET_PERL]) +AC_DEFINE_UNQUOTED([PERL_EXECUTABLE], ["$TARGET_PERL"], + [Location of the perl executable]) + +AC_CHECK_TOOL([AR], [ar], + [AC_MSG_ERROR([[cannot find ar executable]])]) +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 +mingw32*) ;; +winnt) ;; +*) + AC_SEARCH_LIBS([nanosleep], [rt], [ac_have_nanosleep=yes], + [AC_MSG_ERROR([[cannot find a short sleep function (nanosleep)]])]) + ;; +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]) + +## Check for Berkely DB. Restrict to certain versions +AX_PATH_BDB([1.x or 4.1], [ + LIBS="$BDB_LIBS $LIBS" + LDFLAGS="$BDB_LDFLAGS $LDFLAGS" + CPPFLAGS="$CPPFLAGS $BDB_CPPFLAGS" + + AX_COMPARE_VERSION([$BDB_VERSION],[ge],[4.1],, + [AX_COMPARE_VERSION([$BDB_VERSION],[lt],[2],, + [AC_MSG_ERROR([[only Berkely DB versions 1.x or at least 4.1 are currently supported]])] + )] + ) + AX_SPLIT_VERSION([BDB_VERSION], [$BDB_VERSION]) +]) + +## Check for Open SSL, use old versions only if explicitly requested +AC_SEARCH_LIBS([gethostbyname], [nsl socket resolv]) +AC_SEARCH_LIBS([shutdown], [nsl socket resolv]) +AX_CHECK_SSL(, [AC_MSG_ERROR([[OpenSSL is not installed but is required]])]) +AC_ARG_ENABLE( + [old-ssl], + [AC_HELP_STRING([--enable-old-ssl], + [Allow use of pre-0.9.7 Open SSL - NOT RECOMMENDED, read the documentation])]) +AC_CHECK_LIB( + [crypto], + [EVP_CipherInit_ex],, [ + if test "x$enable_old_ssl" = "xyes"; then + AC_DEFINE([HAVE_OLD_SSL], 1, [Define to 1 if SSL is pre-0.9.7]) + else + AC_MSG_ERROR([[found an old (pre 0.9.7) version of SSL. +Upgrade or read the documentation for alternatives]]) + fi + ]) + + +### Checks for header files. + +case $target_os in +mingw32*) ;; +winnt*) ;; +*) + AC_HEADER_DIRENT + ;; +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 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]) +AC_CHECK_HEADERS([bsd/unistd.h]) + +AC_CHECK_HEADER([regex.h], [have_regex_h=yes]) + +if test "$have_regex_h" = "yes"; then + AC_DEFINE([HAVE_REGEX_H], [1], [Define to 1 if regex.h is available]) +else + AC_CHECK_HEADER([pcreposix.h], [have_pcreposix_h=yes]) +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 + +if test "$have_pcreposix_h" = "yes"; then + AC_DEFINE([HAVE_PCREPOSIX_H], [1], [Define to 1 if pcreposix.h is available]) +fi + +if test "$have_regex_h" = "yes" -o "$have_pcreposix_h" = "yes"; then + have_regex_support=yes + AC_DEFINE([HAVE_REGEX_SUPPORT], [1], [Define to 1 if regular expressions are supported]) +else + have_regex_support=no +fi + +AC_SEARCH_LIBS([dlsym], ["dl"]) + +### Checks for typedefs, structures, and compiler characteristics. + +AC_CHECK_TYPES([u_int8_t, u_int16_t, u_int32_t, u_int64_t]) +AC_CHECK_TYPES([uint8_t, uint16_t, uint32_t, uint64_t]) + +AC_HEADER_STDBOOL +AC_C_CONST +AC_C_BIGENDIAN +AC_TYPE_UID_T +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_PID_T +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 + #include + ]]) +AC_CHECK_MEMBERS([DIR.d_fd],,, [[#include ]]) +AC_CHECK_MEMBERS([DIR.dd_fd],,, [[#include ]]) + +AC_CHECK_DECLS([INFTIM],,, [[#include ]]) +AC_CHECK_DECLS([SO_PEERCRED],,, [[#include ]]) +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 ]]) +AC_CHECK_DECLS([dirfd],,, + [[ + #include + #include + ]]) + +AC_HEADER_TIME +AC_STRUCT_TM +AX_CHECK_DIRENT_D_TYPE +AC_SYS_LARGEFILE +AX_CHECK_DEFINE_PRAGMA +if test "x$ac_cv_c_bigendian" != "xyes"; then + AX_BSWAP64 +fi + +case $target_os in +mingw32*) ;; +winnt*) ;; +*) + AX_RANDOM_DEVICE + AX_CHECK_MOUNT_POINT(,[ + AC_MSG_ERROR([[cannot work out how to discover mount points on your platform]]) + ]) + AC_CHECK_MEMBERS([struct dirent.d_ino],,, [[#include ]]) +;; +esac + +AX_CHECK_MALLOC_WORKAROUND + + +### Checks for library functions. + +AC_FUNC_CLOSEDIR_VOID +AC_FUNC_ERROR_AT_LINE +AC_TYPE_SIGNAL +AC_FUNC_STAT +AC_CHECK_FUNCS([getpeereid lchown setproctitle getpid gettimeofday waitpid]) +AC_SEARCH_LIBS([setproctitle], ["bsd"]) + +# 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]) +AC_CHECK_DECLS([XATTR_NOFOLLOW],,, [[#include ]]) + + +### Miscellaneous complicated feature checks + +## 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], + [box_cv_have_large_file_support], + [AC_TRY_RUN([ + $ac_includes_default + int main() + { + return sizeof(off_t)==4; + } + ], + [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$box_cv_have_large_file_support" = "xyes"; then + AC_DEFINE([HAVE_LARGE_FILE_SUPPORT], [1], + [Define to 1 if large files are supported]) +fi + +## Find out how to do file locking +AC_CHECK_FUNCS([flock]) +AC_CHECK_DECLS([O_EXLOCK],,, [[#include ]]) +AC_CHECK_DECLS([F_SETLK],,, [[#include ]]) + +case $target_os in +mingw32*) ;; +winnt*) ;; +*) +if test "x$ac_cv_func_flock" != "xyes" && \ + test "x$ac_cv_have_decl_O_EXLOCK" != "xyes" && \ + test "x$ac_cv_have_decl_F_SETLK" != "xyes" +then + AC_MSG_ERROR([[cannot work out how to do file locking on your platform]]) +fi +;; +esac + +## Get tmpdir +temp_directory_name="/tmp" +AC_ARG_WITH( + [tmp-dir], + [AC_HELP_STRING([--with-tmp-dir=DIR], [Directory for temporary files [/tmp]])], + [temp_directory_name="$withval"]) +AC_DEFINE_UNQUOTED([TEMP_DIRECTORY_NAME], ["$temp_directory_name"], [TMP directory name]) + +## Allow linking binaries with static libraries +AC_ARG_ENABLE( + [static-bin], + [AC_HELP_STRING([--enable-static-bin], [Link binaries with static libraries])]) +if test "x$enable_static_bin" = "xyes"; then + AC_CHECK_LIB([ssl],[SSL_read],,, [crypto]) + 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}' +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]) +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 + 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 + bin/bbstored/bbstored-config + contrib/debian/bbackupd + contrib/debian/bbstored + contrib/redhat/bbackupd + contrib/redhat/bbstored + contrib/suse/bbackupd + contrib/suse/bbstored + 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/makeexception.pl + lib/raidfile/raidfile-config + lib/server/makeprotocol.pl + runtest.pl + test/backupstorefix/testfiles/testbackupstorefix.pl + test/bbackupd/testfiles/extcheck1.pl + test/bbackupd/testfiles/extcheck2.pl + test/bbackupd/testfiles/notifyscript.pl + test/bbackupd/testfiles/syncallowscript.pl]) +# TODO: Need to do contrib/cygwin/install-cygwin-service.pl but location varies +AC_OUTPUT + +# Configure the Box build system +echo +if ! $PERL ./infrastructure/makebuildenv.pl \ +|| ! $PERL ./infrastructure/makeparcels.pl; then + echo "Making infrastructure failed!" + exit 1 +fi + +# Write summary of important info +tee config.log.features <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 = ) + { + 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 =~ /^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 " . + "BACKUP-$new_acc_no but found " . + "$cn."); + } + + my $out_cert_dir = "$ca_dir/clients"; + unless (-w $out_cert_dir) + { + return error("Cannot write to certificate directory " . + "$out_cert_dir as user " . + "" . getpwuid($UID) . "."); + } + + 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 $out_cert " . + "exists and is not writable as user " . + "$out_cert_dir as user " . + "" . getpwuid($UID) . "."); + } + + my $client_ca_cert_file = "$ca_dir/roots/clientCA.pem"; + unless (-r $client_ca_cert_file) + { + return error("The client CA certificate file " . + "$client_ca_cert_file " . + "is not readable by user " . + "" . getpwuid($UID) . "."); + } + + my $client_ca_key_file = "$ca_dir/keys/clientRootKey.pem"; + unless (-r $client_ca_key_file) + { + return error("The client CA key file " . + "$client_ca_key_file " . + "is not readable by user " . + "" . getpwuid($UID) . "."); + } + + my $serial_file = "$ca_dir/roots/clientCA.srl"; + unless (-w $serial_file) + { + return error("The certificate serial number file " . + "$serial_file " . + "is not writable by user " . + "" . getpwuid($UID) . "."); + } + + 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("", ); + 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 = ) + { + 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 + + + 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 + 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. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. 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 +# +# 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 . +# + +# 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/README.txt b/contrib/debian/README.txt new file mode 100644 index 00000000..ebe5fdf7 --- /dev/null +++ b/contrib/debian/README.txt @@ -0,0 +1,9 @@ +These start scripts are for Debian GNU/Linux. If installed manually they should +be placed in /etc/init.d. To create the symbolic links for the appropriate run +levels execute the following commands: + +update-rc.d bbackupd defaults 90 +update-rc.d bbstored defaults 80 + +James Stark + diff --git a/contrib/debian/bbackupd.in b/contrib/debian/bbackupd.in new file mode 100644 index 00000000..c340939a --- /dev/null +++ b/contrib/debian/bbackupd.in @@ -0,0 +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 + +NAME=bbackupd +LONGNAME="Box Backup Client daemon" +BINARY=@sbindir_expanded@/$NAME +CONFIG=@sysconfdir_expanded@/boxbackup/$NAME.conf +PIDFILE=@localstatedir_expanded@/bbackupd/$NAME.pid + +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 $LONGNAME: $NAME" + start_stop_verbose --start + ;; + + stop) + echo -n "Stopping $LONGNAME: $NAME" + start_stop_verbose --stop + ;; + + reload|force-reload) + echo -n "Reloading $LONGNAME configuration" + start_stop_verbose --stop --signal 1 + ;; + + restart) + echo -n "Restarting $LONGNAME: $NAME" + if start_stop --stop --retry 5 && start_stop --start; then + echo "." + else + echo " failed!" + exit 1 + fi + ;; + + *) + echo "Usage: $0 {start|stop|reload|force-reload|restart}" +esac + +exit 0 diff --git a/contrib/debian/bbstored.in b/contrib/debian/bbstored.in new file mode 100644 index 00000000..c9214537 --- /dev/null +++ b/contrib/debian/bbstored.in @@ -0,0 +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 + +NAME=bbstored +LONGNAME="Box Backup Server daemon" +BINARY=@sbindir_expanded@/$NAME +CONFIG=@sysconfdir_expanded@/boxbackup/$NAME.conf +PIDFILE=@localstatedir_expanded@/run/$NAME.pid + +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 $LONGNAME: $NAME" + start_stop_verbose --start + ;; + + stop) + echo -n "Stopping $LONGNAME: $NAME" + start_stop_verbose --stop + ;; + + reload|force-reload) + echo -n "Reloading $LONGNAME configuration" + start_stop_verbose --stop --signal 1 + ;; + + restart) + echo -n "Restarting $LONGNAME: $NAME" + if start_stop --stop --retry 5 && start_stop --start; then + echo "." + else + echo " failed!" + exit 1 + fi + ;; + + *) + 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 @@ + + + + + Label + org.boxbackup.bbackupd + RunAtLoad + + ProgramArguments + + @prefix@/sbin/bbackupd + -F + @prefix@/etc/boxbackup/bbackupd.conf + + LowPriorityIO + + Nice + 1 + + 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 @@ + + + + + Label + org.boxbackup.bbstored + RunAtLoad + + ProgramArguments + + @prefix@/sbin/bbstored + -F + @prefix@/etc/boxbackup/bbackupd.conf + + + LowPriorityIO + + Nice + 1 + + diff --git a/contrib/redhat/README.txt b/contrib/redhat/README.txt new file mode 100644 index 00000000..cfc8d968 --- /dev/null +++ b/contrib/redhat/README.txt @@ -0,0 +1,7 @@ +These start scripts are for Fedora Core or RedHat Enterprise Linux. If +installed manually they should be placed in /etc/rc.d/init.d. + +They may also work for Mandrake. + +Martin Ebourne +martin@zepler.org diff --git a/contrib/redhat/bbackupd.in b/contrib/redhat/bbackupd.in new file mode 100644 index 00000000..2f51137a --- /dev/null +++ b/contrib/redhat/bbackupd.in @@ -0,0 +1,83 @@ +#! /bin/bash +# +# bbackupd Start/Stop the box backup client daemon. +# +# chkconfig: 345 93 07 +# description: bbackupd is the client side deamon for Box Backup, \ +# a completely automatic on-line backup system. +# processname: bbackupd +# config: @sysconfdir_expanded@/box +# pidfile: @localstatedir_expanded@/bbackupd.pid + +# Source function library. +. /etc/init.d/functions + +RETVAL=0 + +# See how we were called. + +prog="bbackupd" + +# Check that configuration exists. +[ -f @sysconfdir_expanded@/box/$prog.conf ] || exit 0 + +start() { + echo -n $"Starting $prog: " + daemon @sbindir_expanded@/$prog + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog + return $RETVAL +} + +stop() { + echo -n $"Stopping $prog: " + killproc @sbindir_expanded@/$prog + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog + return $RETVAL +} + +rhstatus() { + status @sbindir_expanded@/$prog +} + +restart() { + stop + start +} + +reload() { + echo -n $"Reloading $prog configuration: " + killproc @sbindir_expanded@/$prog -HUP + retval=$? + echo + return $RETVAL +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + restart + ;; + reload) + reload + ;; + status) + rhstatus + ;; + condrestart) + [ -f /var/lock/subsys/$prog ] && restart || : + ;; + *) + echo $"Usage: $0 {start|stop|status|reload|restart|condrestart}" + exit 1 +esac + +exit $? diff --git a/contrib/redhat/bbstored.in b/contrib/redhat/bbstored.in new file mode 100644 index 00000000..755a8569 --- /dev/null +++ b/contrib/redhat/bbstored.in @@ -0,0 +1,83 @@ +#! /bin/bash +# +# bbstored Start/Stop the box backup server daemon. +# +# chkconfig: 345 93 07 +# description: bbstored is the server side daemon for Box Backup, \ +# a completely automatic on-line backup system. +# processname: bbstored +# config: @sysconfdir_expanded@/box +# pidfile: @localstatedir_expanded@/bbstored.pid + +# Source function library. +. /etc/init.d/functions + +RETVAL=0 + +# See how we were called. + +prog="bbstored" + +# Check that configuration exists. +[ -f @sysconfdir_expanded@/box/$prog.conf ] || exit 0 + +start() { + echo -n $"Starting $prog: " + daemon @sbindir_expanded@/$prog + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog + return $RETVAL +} + +stop() { + echo -n $"Stopping $prog: " + killproc @sbindir_expanded@/$prog + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog + return $RETVAL +} + +rhstatus() { + status @sbindir_expanded@/$prog +} + +restart() { + stop + start +} + +reload() { + echo -n $"Reloading $prog configuration: " + killproc @sbindir_expanded@/$prog -HUP + retval=$? + echo + return $RETVAL +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + restart + ;; + reload) + reload + ;; + status) + rhstatus + ;; + condrestart) + [ -f /var/lock/subsys/$prog ] && restart || : + ;; + *) + echo $"Usage: $0 {start|stop|status|reload|restart|condrestart}" + exit 1 +esac + +exit $? diff --git a/contrib/rpm/README.txt b/contrib/rpm/README.txt new file mode 100644 index 00000000..290a5252 --- /dev/null +++ b/contrib/rpm/README.txt @@ -0,0 +1,16 @@ +BUILDING AN RPM + +The easy way is to: + +rpmbuild -ta + +where is the archive you downloaded of Box Backup. + +This RPM should work on RedHat Enterprise, Fedora Core, Mandrake, SUSE, and +any similar distributions. It has been developed and tested on Fedora Core. + +Changes for SUSE Linux were provided by Chris Smith +(chris.smith@nothingbutnet.co.nz). + +Martin Ebourne +martin@zepler.org diff --git a/contrib/rpm/boxbackup.spec b/contrib/rpm/boxbackup.spec new file mode 100644 index 00000000..e782a6c3 --- /dev/null +++ b/contrib/rpm/boxbackup.spec @@ -0,0 +1,244 @@ +%define bb_user_id 171 +%define ident %{name}-%{version} + +# In official distribution tarballs, distribution files are copied to +# the base directory (where configure is), so distribution_dir should be empty. +# This is the default, overridden by the following block in non-distribution +# builds. +%define distribution_dir '' + +# BOX_PRIVATE_BEGIN +# In unofficial tarballs, made from svn, distribution files are still in +# distribution/boxbackup, so the following line overrides the default above: +# (this section will be removed automatically from distribution tarballs +# by infrastructure/makedistribution.pl) +%define distribution_dir distribution/boxbackup/ +# BOX_PRIVATE_END + +# 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@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) +%define is_suse %(test -e %{_sysconfdir}/SuSE-release && echo 1 || echo 0) + +%if %{is_suse} +%define init_dir %{_sysconfdir}/init.d +%define distribution suse +%define rc_start rc +%else +%define init_dir %{_sysconfdir}/rc.d/init.d +%define distribution redhat +%define rc_start "service " +%endif + +Summary: An automatic on-line backup system for UNIX. +Name: boxbackup +Version: ###DISTRIBUTION-VERSION-NUMBER### +Release: 1%{?dist} +License: BSD +Group: Applications/Archiving +Packager: boxbackup-dev@boxbackup.org +URL: http://www.boxbackup.org/ +Source0: %{ident}.tgz +Requires: openssl >= 0.9.7a +BuildRoot: %{_tmppath}/%{ident}-%{release}-root +BuildRequires: openssl >= 0.9.7a, openssl-devel + +%description +Box Backup is a completely automatic on-line backup system. Backed up files +are stored encrypted on a filesystem on a remote server, which does not need +to be trusted. The backup server runs as a daemon on the client copying only +the changes within files, and old versions and deleted files are retained. It +is designed to be easy and cheap to run a server and (optional) RAID is +implemented in userland for ease of use. + +%package client +Summary: An automatic on-line backup system for UNIX. +Group: Applications/Archiving + +%description client +Box Backup is a completely automatic on-line backup system. Backed up files +are stored encrypted on a filesystem on a remote server, which does not need +to be trusted. The backup server runs as a daemon on the client copying only +the changes within files, and old versions and deleted files are retained. It +is designed to be easy and cheap to run a server and (optional) RAID is +implemented in userland for ease of use. + +This package contains the client. + +%package server +Summary: An automatic on-line backup system for UNIX. +Group: System Environment/Daemons + +%description server +Box Backup is a completely automatic on-line backup system. Backed up files +are stored encrypted on a filesystem on a remote server, which does not need +to be trusted. The backup server runs as a daemon on the client copying only +the changes within files, and old versions and deleted files are retained. It +is designed to be easy and cheap to run a server and (optional) RAID is +implemented in userland for ease of use. + +This package contains the server. + +%prep +%setup -q + +%build +echo -e '%{version}\n%{name}' > VERSION.txt +test -e configure || ./bootstrap +%configure + +make + +%install +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} +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/box/bbackupd +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/box/bbstored +mkdir -p $RPM_BUILD_ROOT%{_var}/lib/box + +install -m 644 -t $RPM_BUILD_ROOT%{_docdir}/%{ident} \ + BUGS.txt \ + VERSION.txt \ + ExceptionCodes.txt \ + LICENSE-GPL.txt \ + LICENSE-DUAL.txt \ + %{distribution_dir}CONTACT.txt \ + %{distribution_dir}DOCUMENTATION.txt \ + %{distribution_dir}LINUX.txt \ + %{distribution_dir}THANKS.txt + +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/%{distribution}/bbackupd $RPM_BUILD_ROOT%{init_dir} +%if %{is_suse} +ln -s ../../%{init_dir}/bbackupd $RPM_BUILD_ROOT%{_sbindir}/rcbbackupd +%endif +%define client_dir parcels/%{ident}-backup-client-linux-gnu +install %{client_dir}/bbackupd $RPM_BUILD_ROOT%{_sbindir} +install %{client_dir}/bbackupquery $RPM_BUILD_ROOT%{_sbindir} +install %{client_dir}/bbackupctl $RPM_BUILD_ROOT%{_sbindir} +install %{client_dir}/bbackupd-config $RPM_BUILD_ROOT%{_sbindir} + +# Server +touch $RPM_BUILD_ROOT%{_sysconfdir}/box/bbstored.conf +touch $RPM_BUILD_ROOT%{_sysconfdir}/box/raidfile.conf +install -m 755 contrib/%{distribution}/bbstored $RPM_BUILD_ROOT%{init_dir} +%if %{is_suse} +ln -s ../../%{init_dir}/bbstored $RPM_BUILD_ROOT%{_sbindir}/rcbbstored +%endif +%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%{_sbindir} +install %{server_dir}/bbstored-config $RPM_BUILD_ROOT%{_sbindir} +install %{server_dir}/raidfile-config $RPM_BUILD_ROOT%{_sbindir} + +%pre server +%{_sbindir}/useradd -c "Box Backup" -u %{bb_user_id} \ + -s /sbin/nologin -r -d / box 2> /dev/null || : + +%post client +/sbin/chkconfig --add bbackupd +if [ ! -f %{_sysconfdir}/box/bbackupd.conf ]; then + echo "You should run the following to configure the client:" + echo "bbackupd-config %{_sysconfdir}/box lazy " \ + "%{_var}/lib/box " + echo "Then follow the instructions. Use this to start the client:" + echo "%{rc_start}bbackupd start" +fi + +%post server +/sbin/chkconfig --add bbstored +if [ ! -f %{_sysconfdir}/box/bbstored.conf ]; then + echo "You should run the following to configure the server:" + echo "raidfile-config %{_sysconfdir}/box 2048 []" + echo "bbstored-config %{_sysconfdir}/box" `hostname` box + echo "Then follow the instructions. Use this to start the server:" + echo "%{rc_start}bbstored start" +fi + +%preun client +if [ $1 = 0 ]; then + %{init_dir}/bbackupd stop > /dev/null 2>&1 + /sbin/chkconfig --del bbackupd +fi + +%preun server +if [ $1 = 0 ]; then + %{init_dir}/bbstored stop > /dev/null 2>&1 + /sbin/chkconfig --del bbstored +fi + + +%clean +rm -rf $RPM_BUILD_ROOT + +%files client +%defattr(-,root,root,-) +%dir %attr(700,root,root) %{_sysconfdir}/box/bbackupd +%dir %attr(755,root,root) %{_var}/lib/box +%doc %{_docdir}/%{ident}/*.txt +%config %{init_dir}/bbackupd +%if %{is_suse} +%{_sbindir}/rcbbackupd +%endif +%config %ghost %{_sysconfdir}/box/bbackupd.conf +%{_sbindir}/bbackupd +%{_sbindir}/bbackupquery +%{_sbindir}/bbackupctl +%{_sbindir}/bbackupd-config + +%files server +%defattr(-,root,root,-) +%dir %attr(700,box,root) %{_sysconfdir}/box/bbstored +%config %{init_dir}/bbstored +%if %{is_suse} +%{_sbindir}/rcbbstored +%endif +%config %ghost %{_sysconfdir}/box/bbstored.conf +%config %ghost %{_sysconfdir}/box/raidfile.conf +%{_sbindir}/bbstored +%{_sbindir}/bbstoreaccounts +%{_sbindir}/bbstored-certs +%{_sbindir}/bbstored-config +%{_sbindir}/raidfile-config +%doc %{_docdir}/%{ident}/bbreporter + +%changelog +* Thu Apr 23 2009 Martin Ebourne +- Use dist tag in version + +* Thu May 29 2008 Martin Ebourne +- Fix paths to bbreporter files + +* Sat Jan 13 2006 Chris Wilson +- Support building from an unofficial tarball (from svn) by changing + %{distribution_dir} at the top. +- Write our RPM version number into VERSION.txt and hence compile it in + +* Wed Dec 28 2005 Martin Ebourne +- Box now uses autoconf so use configure macro + +* Fri Oct 1 2004 Martin Ebourne - 0.08-3 +- Moved most of the exes to /usr/sbin +- SUSE updates from Chris Smith + +* Fri Sep 24 2004 Martin Ebourne - 0.08-2 +- Added support for other distros +- Changes for SUSE provided by Chris Smith + +* Mon Sep 16 2004 Martin Ebourne - 0.07-1 +- Initial build diff --git a/contrib/solaris/bbackupd-manifest.xml.in b/contrib/solaris/bbackupd-manifest.xml.in new file mode 100644 index 00000000..ab30a78e --- /dev/null +++ b/contrib/solaris/bbackupd-manifest.xml.in @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/solaris/bbackupd-smf-method.in b/contrib/solaris/bbackupd-smf-method.in new file mode 100755 index 00000000..0ff610bf --- /dev/null +++ b/contrib/solaris/bbackupd-smf-method.in @@ -0,0 +1,24 @@ + +PIDFILE=@localstatedir_expanded@/bbackupd.pid + +case $1 in + + # SMF arguments (start and restart [really "refresh"]) +'start') + @sbindir_expanded@/bbackupd + ;; + +'restart') + if [ -f "$PIDFILE" ]; then + /usr/bin/kill -HUP `/usr/bin/cat $PIDFILE` + fi + ;; + +*) + echo "Usage: $0 { start | restart }" + exit 1 + ;; +esac + +exit $? + diff --git a/contrib/solaris/bbstored-manifest.xml.in b/contrib/solaris/bbstored-manifest.xml.in new file mode 100644 index 00000000..f7133677 --- /dev/null +++ b/contrib/solaris/bbstored-manifest.xml.in @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/solaris/bbstored-smf-method.in b/contrib/solaris/bbstored-smf-method.in new file mode 100755 index 00000000..dbdb3e69 --- /dev/null +++ b/contrib/solaris/bbstored-smf-method.in @@ -0,0 +1,23 @@ +PIDFILE=@localstatedir_expanded@/bbstored.pid + +case $1 in + + # SMF arguments (start and restart [really "refresh"]) +'start') + @sbindir_expanded@/bbstored + ;; + +'restart') + if [ -f "$PIDFILE" ]; then + /usr/bin/kill -HUP `/usr/bin/cat $PIDFILE` + fi + ;; + +*) + echo "Usage: $0 { start | restart }" + exit 1 + ;; +esac + +exit $? + diff --git a/contrib/suse/README.txt b/contrib/suse/README.txt new file mode 100644 index 00000000..0f260b7a --- /dev/null +++ b/contrib/suse/README.txt @@ -0,0 +1,5 @@ +These start scripts are for SUSE Linux. If installed manually they should be +placed in /etc/init.d. + +Copyright (c)2004, Nothing But Net Limited + diff --git a/contrib/suse/bbackupd.in b/contrib/suse/bbackupd.in new file mode 100644 index 00000000..77fd40ba --- /dev/null +++ b/contrib/suse/bbackupd.in @@ -0,0 +1,103 @@ +#!/bin/sh +# +# Copyright (c)2004, Nothing But Net Limited +# +# +###################################################################### +# RELEASED AND PROVIDED TO YOU UNDER THE SAME LICENCE AS THE BOXBACKUP +# SUITE OF PROGRAMS. LICENCE MAY BE VIEWED HERE: +# +# http://www.boxbackup.org/license.html +###################################################################### +# +# /etc/init.d/bbackupd +# and its symbolic link +# /(usr/)sbin/rcbbackupd +# +### BEGIN INIT INFO +# Provides: bbackupd +# Required-Start: $named $network $local_fs $syslog +# X-UnitedLinux-Should-Start: $time ypbind sendmail +# Required-Stop: $named $network $localfs $syslog +# X-UnitedLinux-Should-Stop: $time ypbind sendmail +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Short-Description: BoxBackup client side daemon +# Description: Client daemon for the BoxBackup software +# that allows you to communicate with a bbstored server. +### END INIT INFO + +# Check for missing binaries (stale symlinks should not happen) +BBACKUPD_BIN=@sbindir_expanded@/bbackupd +if [ ! -x $BBACKUPD_BIN ] ; then + echo "$BBACKUPD_BIN not installed" + exit 5 +fi + +. /etc/rc.status + +# Reset status of this service +rc_reset + +case "$1" in + start) + echo -n "Starting bbackupd " + startproc $BBACKUPD_BIN + rc_status -v + ;; + + stop) + echo -n "Shutting down bbackupd " + killproc -TERM $BBACKUPD_BIN + rc_status -v + ;; + + try-restart|condrestart) + if test "$1" = "condrestart"; then + echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" + fi + $0 status + if test $? = 0; then + $0 restart + else + rc_reset # Not running is not a failure. + fi + rc_status + ;; + + restart) + $0 stop + $0 start + rc_status + ;; + + force-reload) + echo -n "Reload service bbackupd " + killproc -HUP $BBACKUPD_BIN + rc_status -v + ;; + + reload) + echo -n "Reload service bbackupd " + killproc -HUP $BBACKUPD_BIN + rc_status -v + ;; + + status) + echo -n "Checking for service bbackupd " + checkproc $BBACKUPD_BIN + rc_status -v + ;; + + probe) + test @sysconfdir_expanded@/box/bbackupd.conf \ + -nt @localstatedir_expanded@/bbackupd/bbackupd.pid \ + && echo reload + ;; + + *) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" + exit 1 + +esac +rc_exit diff --git a/contrib/suse/bbstored.in b/contrib/suse/bbstored.in new file mode 100644 index 00000000..e84edfd1 --- /dev/null +++ b/contrib/suse/bbstored.in @@ -0,0 +1,104 @@ +#!/bin/sh +# +# Copyright (c)2004, Nothing But Net Limited +# +# +###################################################################### +# RELEASED AND PROVIDED TO YOU UNDER THE SAME LICENCE AS THE BOXBACKUP +# SUITE OF PROGRAMS. LICENCE MAY BE VIEWED HERE: +# +# http://www.boxbackup.org/license.html +###################################################################### +# +# /etc/init.d/bbstored +# and its symbolic link +# /(usr/)sbin/rcbbstored +# +### BEGIN INIT INFO +# Provides: bbstored +# Required-Start: $named $network $local_fs $syslog +# X-UnitedLinux-Should-Start: $time ypbind sendmail +# Required-Stop: $named $network $localfs $syslog +# X-UnitedLinux-Should-Stop: $time ypbind sendmail +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Short-Description: BoxBackup server side daemon +# Description: Server daemon for the BoxBackup software, +# to which bbackupd clients connect. +### END INIT INFO +# + +# Check for missing binaries (stale symlinks should not happen) +BBSTORED_BIN=@sbindir_expanded@/bbstored +if [ ! -x $BBSTORED_BIN ] ; then + echo "$BBSTORED_BIN not installed" + exit 5 +fi + +. /etc/rc.status + +# Reset status of this service +rc_reset + +case "$1" in + start) + echo -n "Starting bbstored " + startproc $BBSTORED_BIN + rc_status -v + ;; + + stop) + echo -n "Shutting down bbstored " + killproc -TERM $BBSTORED_BIN + rc_status -v + ;; + + try-restart|condrestart) + if test "$1" = "condrestart"; then + echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" + fi + $0 status + if test $? = 0; then + $0 restart + else + rc_reset # Not running is not a failure. + fi + rc_status + ;; + + restart) + $0 stop + $0 start + rc_status + ;; + + force-reload) + echo -n "Reload service bbstored " + killproc -HUP $BBSTORED_BIN + rc_status -v + ;; + + reload) + echo -n "Reload service bbstored " + killproc -HUP $BBSTORED_BIN + rc_status -v + ;; + + status) + echo -n "Checking for service bbstored " + checkproc $BBSTORED_BIN + rc_status -v + ;; + + probe) + test @sysconfdir_expanded@/box/bbstored.conf \ + -nt @localstatedir_expanded@/run/bbstored.pid && echo reload + ;; + + *) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" + exit 1 + ;; + +esac +rc_exit diff --git a/contrib/windows/installer/boxbackup.mpi.in b/contrib/windows/installer/boxbackup.mpi.in new file mode 100755 index 00000000..12f6d62d --- /dev/null +++ b/contrib/windows/installer/boxbackup.mpi.in @@ -0,0 +1,3392 @@ +array set info { +AccountNo +10005005 + +AllowLanguageSelection +No + +AppName +<%BrandName%> + +ApplicationID +E10C6FD9-E524-28BD-B0AB3588F16C + +ApplicationURL +http://www.boxbackup.org/ + +AutoFileGroups +No + +AutoRefreshFiles +Yes + +BBVersionNo +@box_version@ + +BrandName +{Box Backup} + +BuildFailureAction +{Fail (recommended)} + +CancelledInstallAction +{Rollback and Stop} + +CleanupCancelledInstall +Yes + +CommandLineFailureAction +{Fail (recommended)} + +Company +{Tebuco, Inc. and Ben Summers and Contributors} + +CompressionLevel +6 + +CompressionMethod +zlib + +ConfigFileName +{<%InstallDir%>\bbackupd.conf} + +ConfigFileTemplate +{<%InstallDir%>\templates\template.conf} + +Copyright +{2003-2008 Tebuco, Inc. and Ben Summers and Contributors} + +CreateDesktopShortcut +No + +CreateQuickLaunchShortcut +No + +DefaultDirectoryLocation +{} + +DefaultLanguage +English + +EncryptedKeyFilePassword +Enter_EncryptedKeys_Password_Here + +Ext +.exe + +ExtractSolidArchivesOnStartup +No + +Icon +{} + +Image +@build_dir@/docs/html/images/bblogo.png + +IncludeDebugging +Yes + +InstallDirSuffix +<%ShortAppName%> + +InstallPassword +{} + +InstallVersion +@box_version@ + +Language,de +No + +Language,en +Yes + +Language,es +No + +Language,fr +No + +Language,hu +Yes + +Language,it +Yes + +Language,nl +Yes + +Language,pl +No + +Language,pt_br +No + +Language,ru +Yes + +LaunchApplication +No + +PackageDescription +{<%BrandName%> Backup Service} + +PackageLicense +{} + +PackageMaintainer +{Tebuco, Inc. and Ben Summers and Contributors} + +PackageName +<%ShortAppName%> + +PackagePackager +{Tebuco, Inc. and Ben Summers and Contributors} + +PackageRelease +<%PatchVersion%> + +PackageSummary +{} + +PackageVersion +<%MajorVersion%>.<%MinorVersion%> + +PreserveFileAttributes +Yes + +PreserveFilePermissions +Yes + +ProjectID +140B9882-3327-FEA8-13415A62FBB2 + +ProjectVersion +1.2.9.0 + +SaveOnlyToplevelDirs +No + +ScriptExt +.bat + +ServiceExeName +bbackupd.exe + +ServiceName +<%BrandName%> + +ShortAppName +<%BrandName%> + +SkipUnusedFileGroups +Yes + +SystemLanguage +en_us + +Theme +Modern_Wizard + +ThemeDir +Modern_Wizard + +ThemeVersion +1 + +UpgradeApplicationID +{} + +UserInfoAcctNo +<%AccountNo%> + +UserInfoCompany +{} + +UserInfoEmail +{} + +UserInfoName +{} + +UserInfoPhone +{} + +Version +@box_version@ + +ViewReadme +No + +WizardHeight +365 + +WizardWidth +500 + +} + +array set ::InstallJammer::InstallCommandLineOptions { +D +{{} Prefix No No {} {set the value of an option in the installer}} + +S +{InstallMode Switch No No Silent {run the installer in silent mode}} + +T +{Testing Switch Yes No {} {run installer without installing any files}} + +Y +{InstallMode Switch No No Default {accept all defaults and run the installer}} + +debug +{Debugging Switch Yes No {} {run installer in debug mode}} + +debugconsole +{ShowConsole Switch Yes No {} {run installer with a debug console open}} + +mode +{InstallMode Choice No No {Console Default Silent Standard} {set the mode to run the installer in}} + +prefix +{InstallDir String No No {} {set the installation directory}} + +test +{Testing Switch Yes No {} {run installer without installing any files}} + +} +array set ::InstallJammer::UninstallCommandLineOptions { +S +{InstallMode Switch No No Silent {run the uninstaller in silent mode}} + +Y +{InstallMode Switch No No Default {accept all defaults and run the uninstaller}} + +debugconsole +{ShowConsole Switch Yes No {} {run uninstaller with a debug console open}} + +mode +{UninstallMode Choice No No {Console Silent Standard} {set the mode to run the uninstaller in}} + +test +{Testing Switch Yes No {} {run uninstaller without uninstalling any files}} + +} +FileGroup ::481451CC-F49C-D389-8645076F595B -setup Install -active Yes -platforms {Windows MacOS-X} -name {Program Files} -parent FileGroups +File ::B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 -filemethod {Update files with more recent dates} -type dir -directory <%InstallDir%> -name /home/petjal/doc/teb/cli/bu/installer/win/2.2 -location @client_parcel_dir@ -parent 481451CC-F49C-D389-8645076F595B +File ::CDDED10B-2747-DD07-5F9D-42A7FD7BB7E6 -name LICENSE.txt -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::D6E262BC-8A84-B6DB-794B-8FDC8AECB079 -name mgwz.dll -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::E56A0360-7D7F-D99E-E9A4-3C20BC4C2B99 -name mingwm10.dll -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::47602DF7-8463-AB89-E13F-11983610CAA2 -type dir -name tools -location @build_dir@/contrib/windows/installer/tools -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::F7F61231-C340-5CD5-686B-01F521994B0C -name InstallService.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::68DAE474-165D-81FE-1396-FDD2E6081B41 -name KillBackupProcess.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::2436C940-3332-13AA-7613-8EE67C35CE9B -name ReloadConfig.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::336DDAC3-F3BA-1117-73D4-11DFEF9E98AB -name RemoveService.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::0C15AE46-0FF3-3B7F-FC55-D91EF279DBD3 -name RestartService.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::58D97EDE-58F2-15D7-7113-BEE3047F0782 -name StartService.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::BE7CDB16-D3FE-30FA-2153-7C0509CD5E78 -name StopService.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::73BD5859-FB38-71F8-24BD-BDCF871F9FD3 -name Sync.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::67B3838F-4EF7-2C1C-2E86-78DB8ADD6682 -name ShowUsage.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::2646A97C-C0D9-A29C-E145-C5C371F44938 -name QueryOutputAll.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::2F41E3D2-DA4D-2FCB-B3D5-F04032D17A63 -name QueryOutputCurrent.bat -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::C6430BDD-8A07-B80E-FC0C-426C16EB4187 -name RemoteControl.exe -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::5EADAB59-F559-44CB-A3EE-9A56D4EF17C8 -type dir -name .svn -active 0 -parent 47602DF7-8463-AB89-E13F-11983610CAA2 +File ::31429CC4-525E-4E30-9328-4774AFA9F619 -name entries -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8 +File ::A27BEFB6-1421-4030-8F11-F04316BCE57C -name format -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8 +File ::C99700CE-1035-498C-9A96-B60835652077 -type dir -name prop-base -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8 +File ::6FF9DFDE-4BB7-4319-AC85-BF1E88731C1C -name InstallService.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::6AAE8600-5CFC-4240-A47F-5CFCF4E571C6 -name KillBackupProcess.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::834C33D3-4B53-4B2D-9380-A05420AEE75F -name QueryOutputAll.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::854AD23A-5DDE-44C4-900C-34F5845E6747 -name QueryOutputCurrent.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::9A587DE7-B17C-4CDF-B92C-0D267E30E361 -name ReloadConfig.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::197AAEFA-C62E-4E79-890F-C2E4C8440239 -name RemoteControl.exe.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::912E0F50-53DD-4483-A4F4-CA69944A5959 -name RemoveService.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::BDD695BF-9C7E-4F06-BBCE-EC89536DCF27 -name RestartService.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::61615EE4-BF66-40E7-B89A-E6A50B92AF93 -name ShowUsage.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::A6F7C6B7-9759-4B86-9388-4A42E6F7C5C3 -name StartService.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::45D3E7F5-277B-4E52-81BA-ED6D2BB441D7 -name StopService.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::58966972-8387-4D14-A06E-15AA176633A3 -name Sync.bat.svn-base -active 0 -parent C99700CE-1035-498C-9A96-B60835652077 +File ::2A77AF5B-4761-45B5-A543-6328A7F0F39B -type dir -name props -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8 +File ::BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 -type dir -name text-base -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8 +File ::D972D6B2-40E5-40B3-BC06-66B8B7F51B04 -name InstallService.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::2F14E4F3-5331-4AC5-93F7-C4748970C7F4 -name KillBackupProcess.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::9F3663B2-8BAA-4EAE-B606-53D5C922E703 -name QueryOutputAll.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::BE9BD4FB-DF44-4F4B-BB55-15285A8566BA -name QueryOutputCurrent.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::3B4E0BF4-7FDD-4903-8D43-76C43F38C6C4 -name ReloadConfig.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::038CEEA0-3F21-48F6-B109-BBE1EF7D3E96 -name RemoteControl.exe.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::0C177E04-DF2D-43AB-8A42-9E7A389AB1D1 -name RemoveService.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::9BE16F40-9D1D-4C84-843D-955A44069040 -name RestartService.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::4E503A3C-EB42-4870-9849-D508A3D9BAB7 -name ShowUsage.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::131B7D8B-1BEB-456C-8F05-386C2EAFBEAE -name StartService.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::D3D0AFC1-CB6C-42D4-8C05-21898505DA40 -name StopService.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::C89F78B2-25A7-432B-9D92-DC2AB636CFB5 -name Sync.bat.svn-base -active 0 -parent BF74F2C1-3CE7-4875-9B52-CD0F527E01C7 +File ::90695C82-0000-4F6A-8FE7-0ABDEAA17CAE -type dir -name tmp -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8 +File ::7DFF0EE4-7298-4C8C-A5BC-56BBDD81CFC8 -type dir -name prop-base -active 0 -parent 90695C82-0000-4F6A-8FE7-0ABDEAA17CAE +File ::4C60E473-119E-4B0B-9B01-56240F24D9D5 -type dir -name props -active 0 -parent 90695C82-0000-4F6A-8FE7-0ABDEAA17CAE +File ::E1E25ACC-487B-4191-B8CF-9E7C8C88EA09 -type dir -name text-base -active 0 -parent 90695C82-0000-4F6A-8FE7-0ABDEAA17CAE +File ::E7258732-3D21-4E89-AA41-24AA8B8EBF29 -name all-wcprops -active 0 -parent 5EADAB59-F559-44CB-A3EE-9A56D4EF17C8 +File ::9CEBA2A0-C68B-48BA-944E-2E8EE9A35D97 -name bbackupctl.exe -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::497483A6-7264-4361-86F2-F2703F719908 -name bbackupd-config -active 0 -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::204358FC-610F-47DF-8928-1D0E39921700 -targetfilename templates/original.conf -name bbackupd.conf -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::98C376E2-A6C3-4B6C-BBD4-F70CAC2E6A7B -name bbackupd.exe -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::5C3EAB34-7CD4-4DF3-9DEB-0FC23A6F5812 -name bbackupquery.exe -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::7633DBC3-EACA-4F9B-9A87-AD3AF0EC298E -name installer.iss -active 0 -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::D3CF86E1-CAFF-4342-8730-463F96EACC39 -targetfilename templates/NotifySysAdmin.original.vbs -name NotifySysAdmin.vbs -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +File ::A702625F-29C7-4CA0-A8F8-E50DBF5C541B -name uninstall.exe -active 0 -parent B9F58CFC-EE7A-BEE4-62CB-2C10665095A2 +Component ::4A9C852B-647E-EED5-5482FFBCC2AF -setup Install -active Yes -platforms {Windows MacOS-X} -name {Default Component} -parent Components +SetupType ::8202CECC-54A0-9B6C-D24D111BA52E -setup Install -active Yes -platforms {Windows MacOS-X} -name Typical -parent SetupTypes + +InstallComponent AE3BD5B4-35DE-4240-B79914D43E56 -setup Install -type pane -title {Welcome Screen} -component Welcome -active No -parent StandardInstall +InstallComponent 2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8 -setup Install -type pane -conditions 4EE35849-FAD7-170B-0E45-FA30636467B1 -title {Install Password} -component InstallPassword -command insert -active No -parent StandardInstall +Condition 4EE35849-FAD7-170B-0E45-FA30636467B1 -active Yes -parent 2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8 -title {Password Test Condition} -component PasswordTestCondition -TreeObject::id 4EE35849-FAD7-170B-0E45-FA30636467B1 +InstallComponent B3B99E2D-C368-A921-B7BC-A71EBDE3AD4D -setup Install -type action -title {Set Install Password} -component SetInstallPassword -active Yes -parent 2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8 +InstallComponent 1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E -setup Install -type pane -title {User Information} -component UserInformation -active Yes -parent StandardInstall +InstallComponent 9013E862-8E81-5290-64F9-D8BCD13EC7E5 -setup Install -type pane -title {User Information Phone Email} -component UserInformation -active Yes -parent StandardInstall +InstallComponent F8FD4BD6-F1DF-3F8D-B857-98310E4B1143 -setup Install -type pane -title {User Information Account No} -component UserInformation -active Yes -parent StandardInstall +InstallComponent 58E1119F-639E-17C9-5D3898F385AA -setup Install -type pane -conditions 84DA7F05-9FB7-CC36-9EC98F8A6826 -title {Select Destination} -component SelectDestination -command insert -active Yes -parent StandardInstall +Condition 84DA7F05-9FB7-CC36-9EC98F8A6826 -active Yes -parent 58E1119F-639E-17C9-5D3898F385AA -title {File Permission Condition} -component FilePermissionCondition -TreeObject::id 84DA7F05-9FB7-CC36-9EC98F8A6826 +InstallComponent 0FDBA082-90AB-808C-478A-A13E7C525336 -setup Install -type action -title BackupLocationNumber -component ExecuteScript -active Yes -parent 58E1119F-639E-17C9-5D3898F385AA +InstallComponent 0047FF40-0139-2A59-AAC0-A44D46D6F5CC -setup Install -type action -title BackupLocationName -component ExecuteScript -active No -parent 58E1119F-639E-17C9-5D3898F385AA +InstallComponent 2BB06B72-DE53-2319-B1B8-351CDCBA2008 -setup Install -type action -title AddBackupLocation -component ExecuteScript -active Yes -parent 58E1119F-639E-17C9-5D3898F385AA +InstallComponent B506E7DA-E7C4-4D42-8C03-FD27BA16D078 -setup Install -type pane -title {License Agreement} -component License -active Yes -parent StandardInstall +InstallComponent B93D2216-1DDB-484C-A9AC-D6C18ED7DE23 -setup Install -type action -conditions {6D9D1ABC-7146-443F-9EE9-205D5CA6C830 79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C} -title {Modify Widget} -component ModifyWidget -command insert -active Yes -parent B506E7DA-E7C4-4D42-8C03-FD27BA16D078 +Condition 6D9D1ABC-7146-443F-9EE9-205D5CA6C830 -active Yes -parent B93D2216-1DDB-484C-A9AC-D6C18ED7DE23 -title {String Is Condition} -component StringIsCondition -TreeObject::id 6D9D1ABC-7146-443F-9EE9-205D5CA6C830 +Condition 79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C -active Yes -parent B93D2216-1DDB-484C-A9AC-D6C18ED7DE23 -title {String Is Condition} -component StringIsCondition -TreeObject::id 79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C +InstallComponent 37E627F2-E04B-AEF2-D566C017A4D6 -setup Install -type pane -title {Copying Files} -component CopyFiles -active Yes -parent StandardInstall +InstallComponent 3CFFF099-6122-46DD-9CE4-F5819434AC53 -setup Install -type action -title {Stop running service} -component ExecuteExternalProgram -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent FB697A88-2842-468E-9776-85E84B009340 -setup Install -type action -title {Remove installed service} -component ExecuteExternalProgram -active No -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent 41CDE776-2667-5CEB-312A-FC4C33A83E7F -setup Install -type action -title {Backup File} -component BackupFile -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent 0D93323D-779D-44A8-1E0614E5285D -setup Install -type action -title {Disable Buttons} -component ModifyWidget -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent 5CA3EA16-E37C-AABE-E576C4636EB0 -setup Install -type action -title {Execute Action} -component ExecuteAction -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent F5F21749-8B3A-49C6-9138-9C4D6D703D26 -setup Install -type action -title {Unpack Keys} -component ExecuteExternalProgram -active No -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent FDF68FD6-BEA8-4A74-867D-5139F4D9E793 -setup Install -type action -title Wait -component Wait -active No -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent E56ADFF4-C15E-AEDB-A599-C468AF72C4BB -setup Install -type action -title {Copy File NotifySysAdmin} -component CopyFile -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent D9F88AC1-3D2D-F6DB-871E-3A0E016770B1 -setup Install -type action -title {Copy File config} -component CopyFile -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent 5F2C1F1C-B9F7-1642-59D9-A18318C1D70B -setup Install -type action -title {Replace Text In File} -component ReplaceTextInFile -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent 2EC82FBD-8294-A3E4-7F39-1CBA0582FA64 -setup Install -type action -title {Write Text To File} -component WriteTextToFile -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent 28E76C8B-2605-4739-9FFE-9C2880C17E59 -setup Install -type action -title {Edit config file} -component ExecuteExternalProgram -active No -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent 52F0A238-57E1-A578-2CE4DA177B32 -setup Install -type action -title {Move Forward} -component MoveForward -active Yes -parent 37E627F2-E04B-AEF2-D566C017A4D6 +InstallComponent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 -setup Install -type pane -title SetBackupLocations -component CustomBlankPane2 -command reorder -active Yes -parent StandardInstall +InstallComponent 614C45B2-7515-780C-E444-7F165CF02DD7 -setup Install -type action -title {Execute Script} -component ExecuteScript -active No -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent A5B32DA1-B2FE-C1FA-6057-FBC3059EF076 -setup Install -type action -title {Execute Script} -component ExecuteScript -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent F9E38720-6ABA-8B99-2471-496902E4CBC2 -setup Install -type action -title {Execute Script} -component ExecuteScript -active No -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent 362B6D6A-11BC-83CE-AFF6-410D8FBCF54D -setup Install -type action -title {Execute Script} -component ExecuteScript -active No -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent 2E2963BD-DDBD-738D-A910-B7F3F04946F9 -setup Install -type action -title ShowAddAnotherValue -component AddWidget -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent 93AA298C-B64E-5683-14D2-7B86F7DEFD2C -setup Install -type action -title BackupLocationName -component ExecuteScript -active No -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent 3FDB57ED-598D-8A4E-CEF7-D90833305558 -setup Install -type action -title {Backup Directory} -component AddWidget -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A -setup Install -type action -title BackupLocationShortName -component AddWidget -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent 855DE408-060E-3D35-08B5-1D9AB05C2865 -setup Install -type action -title Exclusions -component AddWidget -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent 9892B25C-689B-5B8F-F0C9-B14FF6ACC40C -setup Install -type action -title {Execute Script} -component ExecuteScript -active No -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent 8419AAAD-5860-F73E-8D11-4D1BDA4D7D37 -setup Install -type action -title AddAnother -component AddWidget -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent C7762473-273F-E3CA-17E3-65789B14CDB0 -setup Install -type action -title {Write Text To File} -component WriteTextToFile -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent D7FBBEBB-2186-5674-BA87-BB7151859D4E -setup Install -type action -title BackupLocationNumber -component ExecuteScript -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +InstallComponent 49E80443-62DB-1C10-392D-1091AEA5ED88 -setup Install -type action -conditions EB532611-5F30-3C24-66EB-F3826D9054FD -title {Move to Pane} -component MoveToPane -command insert -active Yes -parent 3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 +Condition EB532611-5F30-3C24-66EB-F3826D9054FD -active Yes -parent 49E80443-62DB-1C10-392D-1091AEA5ED88 -title {String Is Condition} -component StringIsCondition -TreeObject::id EB532611-5F30-3C24-66EB-F3826D9054FD +InstallComponent 9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266 -setup Install -type pane -title {Click Next to Continue} -component CustomBlankPane2 -active Yes -parent StandardInstall +InstallComponent DDBBD8A9-13D7-9509-9202-419E989F60A9 -setup Install -type action -title {Add Widget} -component AddWidget -active No -parent 9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266 +InstallComponent 8E095096-F018-A880-429D-A2177A9B70EA -setup Install -type action -title {Add Widget} -component AddWidget -active No -parent 9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266 +InstallComponent 88A50FD5-480F-19A5-DA74-C915EB0A9765 -setup Install -type action -conditions 5EE78EF7-37CA-D440-3DB5-09136CD566B3 -title {Move to Pane} -component MoveToPane -command insert -active No -parent 9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266 +Condition 5EE78EF7-37CA-D440-3DB5-09136CD566B3 -active Yes -parent 88A50FD5-480F-19A5-DA74-C915EB0A9765 -title {String Is Condition} -component StringIsCondition -TreeObject::id 5EE78EF7-37CA-D440-3DB5-09136CD566B3 +InstallComponent 908CE221-5A3D-0A78-24A1-E7C91EBE38D4 -setup Install -type pane -title {Next-Build Config} -component CustomBlankPane2 -active No -parent StandardInstall +InstallComponent DA33B826-E633-A845-4646-76DFA78B907B -setup Install -type pane -title {Custom Blank Pane 2} -component CustomBlankPane2 -active Yes -parent StandardInstall +InstallComponent 6FEE2889-0338-1D49-60BF-1471F465AB26 -setup Install -type action -title {Write Text To File} -component WriteTextToFile -active Yes -parent DA33B826-E633-A845-4646-76DFA78B907B +InstallComponent 73DD4D07-B1DC-BA38-2B12-07EB24A7F0C8 -setup Install -type action -title {Copy File} -component CopyFile -active Yes -parent DA33B826-E633-A845-4646-76DFA78B907B +InstallComponent D23DD94C-E517-7F34-FD59-802CB18AB887 -setup Install -type action -title {Adjust Line Feeds} -component AdjustLineFeeds -active Yes -parent DA33B826-E633-A845-4646-76DFA78B907B +InstallComponent 7D8E1902-2BC4-80D8-2C18771E7C22 -setup Install -type action -title {Installing service} -component ExecuteExternalProgram -active Yes -parent DA33B826-E633-A845-4646-76DFA78B907B +InstallComponent 1C14291C-0971-4283-92E9-3808401303F5 -setup Install -type action -title {Starting service} -component ExecuteExternalProgram -active No -parent DA33B826-E633-A845-4646-76DFA78B907B +InstallComponent 6C323815-B9AB-FA94-4F5D152EBC51 -setup Install -type pane -title {Setup Complete} -component SetupComplete -active Yes -parent StandardInstall +InstallComponent 574198A7-7322-2F5E-02EF185D965C -setup Install -type pane -title {Copying Files} -component CopyFiles -active Yes -parent DefaultInstall +InstallComponent 8A761DBD-0640-D98C-9B3AD7672A8F -setup Install -type action -title {Disable Buttons} -component ModifyWidget -active Yes -parent 574198A7-7322-2F5E-02EF185D965C +InstallComponent 6E70FB1F-6A43-6C23-3242E965A0D0 -setup Install -type action -title {Execute Action} -component ExecuteAction -active Yes -parent 574198A7-7322-2F5E-02EF185D965C +InstallComponent 8E1A5944-5AF5-5906-16D395E386D8 -setup Install -type action -title {Move Forward} -component MoveForward -active Yes -parent 574198A7-7322-2F5E-02EF185D965C +InstallComponent 1F0926EE-6884-1330-B4A1DB11C1BF -setup Install -type pane -title {Setup Complete} -component SetupComplete -active Yes -parent DefaultInstall +InstallComponent 3B6E2E7C-1A26-27F1-D578E383B128 -setup Install -type action -conditions {13BD88FE-CD71-5AC7-E99C10B6CB28 E02368C5-95B5-03A7-3282740037B0} -title {View Readme Checkbutton} -component AddWidget -command insert -active Yes -parent 1F0926EE-6884-1330-B4A1DB11C1BF +Condition 13BD88FE-CD71-5AC7-E99C10B6CB28 -active Yes -parent 3B6E2E7C-1A26-27F1-D578E383B128 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 13BD88FE-CD71-5AC7-E99C10B6CB28 +Condition E02368C5-95B5-03A7-3282740037B0 -active Yes -parent 3B6E2E7C-1A26-27F1-D578E383B128 -title {String Is Condition} -component StringIsCondition -TreeObject::id E02368C5-95B5-03A7-3282740037B0 +InstallComponent CFFA27AF-A641-E41C-B4A0E3BB3CBB -setup Install -type action -conditions {592F46AE-8CEE-01F3-0BA7EBDCA4F4 793D8178-0F51-7F07-BC5886586D3C} -title {Launch Application Checkbutton} -component AddWidget -command insert -active Yes -parent 1F0926EE-6884-1330-B4A1DB11C1BF +Condition 592F46AE-8CEE-01F3-0BA7EBDCA4F4 -active Yes -parent CFFA27AF-A641-E41C-B4A0E3BB3CBB -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 592F46AE-8CEE-01F3-0BA7EBDCA4F4 +Condition 793D8178-0F51-7F07-BC5886586D3C -active Yes -parent CFFA27AF-A641-E41C-B4A0E3BB3CBB -title {String Is Condition} -component StringIsCondition -TreeObject::id 793D8178-0F51-7F07-BC5886586D3C +InstallComponent 16D53E40-546B-54C3-088B1B5E3BBB -setup Install -type action -conditions {4E643D8A-CA31-018D-57D7053C2CE8 B39C0455-D1B6-7DDC-E2717F83463E} -title {Desktop Shortcut Checkbutton} -component AddWidget -command insert -active Yes -parent 1F0926EE-6884-1330-B4A1DB11C1BF +Condition 4E643D8A-CA31-018D-57D7053C2CE8 -active Yes -parent 16D53E40-546B-54C3-088B1B5E3BBB -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 4E643D8A-CA31-018D-57D7053C2CE8 +Condition B39C0455-D1B6-7DDC-E2717F83463E -active Yes -parent 16D53E40-546B-54C3-088B1B5E3BBB -title {String Is Condition} -component StringIsCondition -TreeObject::id B39C0455-D1B6-7DDC-E2717F83463E +InstallComponent 937C3FDD-FB28-98BD-3DAB276E59ED -setup Install -type action -conditions {6B966959-05D9-DB32-8D9C4AD2A3DF 748D673B-DFE6-5F74-329903ACE4DB 3379F80B-36D6-73DC-6FC1D6223A26} -title {Quick Launch Shortcut Checkbutton} -component AddWidget -command insert -active Yes -parent 1F0926EE-6884-1330-B4A1DB11C1BF +Condition 6B966959-05D9-DB32-8D9C4AD2A3DF -active Yes -parent 937C3FDD-FB28-98BD-3DAB276E59ED -title {Platform Condition} -component PlatformCondition -TreeObject::id 6B966959-05D9-DB32-8D9C4AD2A3DF +Condition 748D673B-DFE6-5F74-329903ACE4DB -active Yes -parent 937C3FDD-FB28-98BD-3DAB276E59ED -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 748D673B-DFE6-5F74-329903ACE4DB +Condition 3379F80B-36D6-73DC-6FC1D6223A26 -active Yes -parent 937C3FDD-FB28-98BD-3DAB276E59ED -title {String Is Condition} -component StringIsCondition -TreeObject::id 3379F80B-36D6-73DC-6FC1D6223A26 +InstallComponent 3FE82C17-A3E2-4A57-A563-F80818B00B81 -setup Install -type action -title {Console Ask Yes Or No} -component ConsoleAskYesOrNo -active Yes -parent ConsoleInstall +InstallComponent 56EE5149-6AA2-4E0C-8841-F66A2EF9276E -setup Install -type action -conditions 241BBFCE-4EB1-432F-94DD-69D444DDB6C0 -title Exit -component Exit -command insert -active Yes -parent ConsoleInstall +Condition 241BBFCE-4EB1-432F-94DD-69D444DDB6C0 -active Yes -parent 56EE5149-6AA2-4E0C-8841-F66A2EF9276E -title {String Is Condition} -component StringIsCondition -TreeObject::id 241BBFCE-4EB1-432F-94DD-69D444DDB6C0 +InstallComponent 0C12D2D3-AEBC-42FE-A73A-0815EFB10DA5 -setup Install -type action -conditions BC4EA5FD-50BD-4D6E-953F-5E3EDB957360 -title {Console Get User Input} -component ConsoleGetUserInput -command insert -active Yes -parent ConsoleInstall +Condition BC4EA5FD-50BD-4D6E-953F-5E3EDB957360 -active Yes -parent 0C12D2D3-AEBC-42FE-A73A-0815EFB10DA5 -title {File Permission Condition} -component FilePermissionCondition -TreeObject::id BC4EA5FD-50BD-4D6E-953F-5E3EDB957360 +InstallComponent B002A311-F8E7-41DE-B039-521391924E5B -setup Install -type action -title {Console Message} -component ConsoleMessage -active Yes -parent ConsoleInstall +InstallComponent D4FC6EB5-DDEE-4E4A-B8E1-D4B588A7928B -setup Install -type action -title {Execute Action} -component ExecuteAction -active Yes -parent ConsoleInstall +InstallComponent 2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6 -setup Install -type action -title {Console Message} -component ConsoleMessage -active Yes -parent ConsoleInstall +InstallComponent 6B4CB3C2-4799-4C9F-BA8E-1EE47C4606E1 -setup Install -type action -title Exit -component Exit -active Yes -parent ConsoleInstall +InstallComponent D8F0AA0F-AD79-C566-15CC508F503B -setup Install -type action -title {Execute Action} -component ExecuteAction -active Yes -parent SilentInstall +InstallComponent 175CBE81-9EBE-1E21-A91479BEEFAE -setup Install -type action -title Exit -component Exit -active Yes -parent SilentInstall +InstallComponent A1DD1DC2-85D7-9BC6-998AC3D4A3A9 -setup Install -type actiongroup -title {Startup Actions} -active Yes -parent ActionGroupsInstall +InstallComponent 1F9E8CB8-02C1-0416-1F7445B4147F -setup Install -type action -conditions {3D0D1898-4C65-3E66-F82F56581E87 32F5B0AF-EB83-7A03-D8FAE1ECE473} -title Exit -component Exit -command insert -active Yes -parent A1DD1DC2-85D7-9BC6-998AC3D4A3A9 +Condition 3D0D1898-4C65-3E66-F82F56581E87 -active Yes -parent 1F9E8CB8-02C1-0416-1F7445B4147F -title {String Is Condition} -component StringIsCondition -TreeObject::id 3D0D1898-4C65-3E66-F82F56581E87 +Condition 32F5B0AF-EB83-7A03-D8FAE1ECE473 -active Yes -parent 1F9E8CB8-02C1-0416-1F7445B4147F -title {Ask Yes or No} -component AskYesOrNo -TreeObject::id 32F5B0AF-EB83-7A03-D8FAE1ECE473 +InstallComponent 32DC8FB1-A04B-71AA-EC18496D4BD0 -setup Install -type action -title {Create Install Panes} -component CreateInstallPanes -active Yes -parent A1DD1DC2-85D7-9BC6-998AC3D4A3A9 +InstallComponent 198905FB-9FAC-23DE-7422D25B8ECA -setup Install -type actiongroup -title {Install Actions} -active Yes -parent ActionGroupsInstall +InstallComponent 4D4A7BF0-7CCE-46E6-BDE5222F82D7 -setup Install -type action -title {Install Selected Files} -component InstallSelectedFiles -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent 53588803-6B41-D9FC-A385906A5106 -setup Install -type action -title {Install Uninstaller} -component InstallUninstaller -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent 73EA65C1-3BE3-B190-55C3E99F6269 -setup Install -type action -conditions 4EF787E3-0643-DE46-15E64BAF0816 -title {Windows Uninstall Registry} -component AddWindowsUninstallEntry -command insert -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +Condition 4EF787E3-0643-DE46-15E64BAF0816 -active Yes -parent 73EA65C1-3BE3-B190-55C3E99F6269 -title {Platform Condition} -component PlatformCondition -TreeObject::id 4EF787E3-0643-DE46-15E64BAF0816 +InstallComponent 39B2B666-78D8-75E6-6EA071594D34 -setup Install -type action -conditions 18C00430-D6B1-151F-307762B3A045 -title {Uninstall Shortcut} -component InstallWindowsShortcut -command insert -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +Condition 18C00430-D6B1-151F-307762B3A045 -active Yes -parent 39B2B666-78D8-75E6-6EA071594D34 -title {Platform Condition} -component PlatformCondition -TreeObject::id 18C00430-D6B1-151F-307762B3A045 +InstallComponent 6652193C-5D4B-44B6-ABC6-D6E96D89E5DC -setup Install -type action -title {Install Program Folder Shortcut} -component InstallProgramFolderShortcut -active No -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent 9D101299-B80C-441B-8685-6E3AC61808E8 -setup Install -type action -title {RemoteControl Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E -setup Install -type action -title {stopservice Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7 -setup Install -type action -title {Install Backup Service} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent 25AA533E-02FC-47D9-9273-25266B8FA1F9 -setup Install -type action -title {Remove Backup Service} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent CDD84DE3-C970-458F-9162-1A3CE0AA716B -setup Install -type action -title {startservice Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent B5DFEC63-92A9-4686-909E-0CE78A7069D6 -setup Install -type action -title {restartservice Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent C0452595-F3EB-43AD-BCA2-661437584636 -setup Install -type action -title {editconfig Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent 1AF5CD58-65C0-49CB-9A9D-994816CF414E -setup Install -type action -title {QueryUpload Shortcut} -component InstallProgramFolderShortcut -active No -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent 1681CF85-A5D2-4D73-A3FC-52B2A6A1847D -setup Install -type action -title {killbackupprocess Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968 -setup Install -type action -title {reloadconfig Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent 6F61CDA8-30C9-454F-82A3-9987E1203079 -setup Install -type action -title {sync Shortcut} -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent 9A663209-495B-ED16-09BE-457B61148022 -setup Install -type action -title QueryCurrent -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent C0AF7C05-A31A-8376-BCB9-BA8B3A666252 -setup Install -type action -title SafeQueryAll -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent 32B08FB1-99DF-234E-8BAF-333E80AAC9F5 -setup Install -type action -title Usage -component InstallProgramFolderShortcut -active Yes -parent 198905FB-9FAC-23DE-7422D25B8ECA +InstallComponent FEFD090D-C133-BC95-B3564F693CD3 -setup Install -type actiongroup -title {Finish Actions} -active Yes -parent ActionGroupsInstall +InstallComponent DECC120D-6904-7F17-45A49184A5A3 -setup Install -type action -conditions {E44CFF46-6302-C518-B9C30D2E43F7 B0AA6839-AAB6-A602-C0E4ECA2E4FF} -title {Install Desktop Shortcut} -component InstallDesktopShortcut -command insert -active No -parent FEFD090D-C133-BC95-B3564F693CD3 +Condition E44CFF46-6302-C518-B9C30D2E43F7 -active Yes -parent DECC120D-6904-7F17-45A49184A5A3 -title {String Is Condition} -component StringIsCondition -TreeObject::id E44CFF46-6302-C518-B9C30D2E43F7 +Condition B0AA6839-AAB6-A602-C0E4ECA2E4FF -active Yes -parent DECC120D-6904-7F17-45A49184A5A3 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id B0AA6839-AAB6-A602-C0E4ECA2E4FF +InstallComponent 7B770A07-A785-5215-956FA82CF14E -setup Install -type action -conditions {6F94698F-0839-3ABF-0CF2DF05A4C8 738DD098-7E3B-BC89-875CDB93CBE2 8C866252-8760-9B08-FE569C25B60D} -title {Install Quick Launch Shortcut} -component InstallWindowsShortcut -command insert -active No -parent FEFD090D-C133-BC95-B3564F693CD3 +Condition 6F94698F-0839-3ABF-0CF2DF05A4C8 -active Yes -parent 7B770A07-A785-5215-956FA82CF14E -title {String Is Condition} -component StringIsCondition -TreeObject::id 6F94698F-0839-3ABF-0CF2DF05A4C8 +Condition 738DD098-7E3B-BC89-875CDB93CBE2 -active Yes -parent 7B770A07-A785-5215-956FA82CF14E -title {Platform Condition} -component PlatformCondition -TreeObject::id 738DD098-7E3B-BC89-875CDB93CBE2 +Condition 8C866252-8760-9B08-FE569C25B60D -active Yes -parent 7B770A07-A785-5215-956FA82CF14E -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 8C866252-8760-9B08-FE569C25B60D +InstallComponent C105AAAE-7C16-2C9E-769FE4535B60 -setup Install -type action -conditions {2583A547-11DE-1C27-B6D04B023CC0 A6E1B027-A1B4-5848-4F868D028D00 0357FAE9-FCFD-26D8-6541D810CD61} -title {View Readme Window} -component TextWindow -command insert -active No -parent FEFD090D-C133-BC95-B3564F693CD3 +Condition 2583A547-11DE-1C27-B6D04B023CC0 -active Yes -parent C105AAAE-7C16-2C9E-769FE4535B60 -title {String Is Condition} -component StringIsCondition -TreeObject::id 2583A547-11DE-1C27-B6D04B023CC0 +Condition A6E1B027-A1B4-5848-4F868D028D00 -active Yes -parent C105AAAE-7C16-2C9E-769FE4535B60 -title {String Is Condition} -component StringIsCondition -TreeObject::id A6E1B027-A1B4-5848-4F868D028D00 +Condition 0357FAE9-FCFD-26D8-6541D810CD61 -active Yes -parent C105AAAE-7C16-2C9E-769FE4535B60 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 0357FAE9-FCFD-26D8-6541D810CD61 +InstallComponent C33D74B2-26FA-16F5-433A10C6A747 -setup Install -type action -conditions {CC4337CC-F3B5-757C-DFCF5D1D365A 795EE61F-6C0D-4A8B-93E02AA3894A 1528F4F0-145C-A48D-A8526DBB6289} -title {Launch Application} -component ExecuteExternalProgram -command insert -active No -parent FEFD090D-C133-BC95-B3564F693CD3 +Condition CC4337CC-F3B5-757C-DFCF5D1D365A -active Yes -parent C33D74B2-26FA-16F5-433A10C6A747 -title {String Is Condition} -component StringIsCondition -TreeObject::id CC4337CC-F3B5-757C-DFCF5D1D365A +Condition 795EE61F-6C0D-4A8B-93E02AA3894A -active Yes -parent C33D74B2-26FA-16F5-433A10C6A747 -title {String Is Condition} -component StringIsCondition -TreeObject::id 795EE61F-6C0D-4A8B-93E02AA3894A +Condition 1528F4F0-145C-A48D-A8526DBB6289 -active Yes -parent C33D74B2-26FA-16F5-433A10C6A747 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 1528F4F0-145C-A48D-A8526DBB6289 +InstallComponent E23AC50D-7CFB-800E-A99C6F4068F8 -setup Install -type actiongroup -title {Cancel Actions} -active Yes -parent ActionGroupsInstall +InstallComponent 3B8CDC8E-1239-D2E9-DF4CA6B1756D -setup Uninstall -type pane -title Uninstall -component Uninstall -active Yes -parent StandardUninstall +InstallComponent 19ADBDDB-1690-4A57-913E32A026C4 -setup Uninstall -type action -title {Modify Widget} -component ModifyWidget -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D +InstallComponent 7A983CD8-302C-4942-BE59-525C5B5FA2F2 -setup Uninstall -type action -title {Stop Backup Process} -component ExecuteExternalProgram -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D +InstallComponent E4DEA723-FC78-45D7-BAB1-A3E4C4C96EA1 -setup Uninstall -type action -title {Stop Service} -component ExecuteExternalProgram -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D +InstallComponent B4D31D1E-ADB1-DE8F-18EB7294DDA8 -setup Uninstall -type action -title {Remove Service} -component ExecuteExternalProgram -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D +InstallComponent D55BA4AF-E73B-60D1-E26F79175227 -setup Uninstall -type action -title {Execute Action} -component ExecuteAction -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D +InstallComponent 69FD7409-5E2A-143B-DABD1C3B1E67 -setup Uninstall -type action -conditions {96A68CAC-9ED7-806C-086B104720FD E161F216-E597-B340-C1A71C476E2C} -title {Uninstall Leftover Files} -component UninstallLeftoverFiles -command insert -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D +Condition 96A68CAC-9ED7-806C-086B104720FD -active Yes -parent 69FD7409-5E2A-143B-DABD1C3B1E67 -title {String Is Condition} -component StringIsCondition -TreeObject::id 96A68CAC-9ED7-806C-086B104720FD +Condition E161F216-E597-B340-C1A71C476E2C -active Yes -parent 69FD7409-5E2A-143B-DABD1C3B1E67 -title {Ask Yes or No} -component AskYesOrNo -TreeObject::id E161F216-E597-B340-C1A71C476E2C +InstallComponent 05060263-E852-87AB-8D0F2954CAA6 -setup Uninstall -type action -title {Move Forward} -component MoveForward -active Yes -parent 3B8CDC8E-1239-D2E9-DF4CA6B1756D +InstallComponent 41D3E165-C263-5F80-0FEEC0AEE47A -setup Uninstall -type pane -conditions EB2B31A1-C111-3582-0C8A5656692A -title {Uninstall Details} -component UninstallDetails -command insert -active Yes -parent StandardUninstall +Condition EB2B31A1-C111-3582-0C8A5656692A -active Yes -parent 41D3E165-C263-5F80-0FEEC0AEE47A -title {String Is Condition} -component StringIsCondition -TreeObject::id EB2B31A1-C111-3582-0C8A5656692A +InstallComponent 3D33AA8C-0037-204B-39A339FD38BD -setup Uninstall -type pane -title {Uninstall Complete} -component UninstallComplete -active Yes -parent StandardUninstall +InstallComponent 49E59F91-27F7-46D1-A1C1-19865C2392D3 -setup Uninstall -type action -title {Console Ask Yes Or No} -component ConsoleAskYesOrNo -active Yes -parent ConsoleUninstall +InstallComponent ADA6EB2F-8820-4366-BBEF-ED1335B7F828 -setup Uninstall -type action -conditions 87DE6D78-81E1-495B-A214-B3FF3E7E5614 -title Exit -component Exit -command insert -active Yes -parent ConsoleUninstall +Condition 87DE6D78-81E1-495B-A214-B3FF3E7E5614 -active Yes -parent ADA6EB2F-8820-4366-BBEF-ED1335B7F828 -title {String Is Condition} -component StringIsCondition -TreeObject::id 87DE6D78-81E1-495B-A214-B3FF3E7E5614 +InstallComponent B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174 -setup Uninstall -type action -title {Console Message} -component ConsoleMessage -active Yes -parent ConsoleUninstall +InstallComponent 3C7130B3-3206-403D-B09E-59D4A758FBAD -setup Uninstall -type action -title {Execute Action} -component ExecuteAction -active Yes -parent ConsoleUninstall +InstallComponent 20CBDBEA-2217-457B-8D98-D692C4F591E9 -setup Uninstall -type action -title {Console Message} -component ConsoleMessage -active Yes -parent ConsoleUninstall +InstallComponent 7F85263E-CAE2-46BA-AAC0-6B89D20FD2DE -setup Uninstall -type action -title Exit -component Exit -active Yes -parent ConsoleUninstall +InstallComponent 17D8BA8E-5992-AA5C-F5ECB73A3433 -setup Uninstall -type action -title {Execute Action} -component ExecuteAction -active Yes -parent SilentUninstall +InstallComponent D3D73C76-D9D3-07DA-63D4163A44BE -setup Uninstall -type action -title Exit -component Exit -active Yes -parent SilentUninstall +InstallComponent 848844B5-6103-9343-8B731B0BE4E0 -setup Uninstall -type actiongroup -title {Startup Actions} -active Yes -parent ActionGroupsUninstall +InstallComponent 97ACF525-C075-8635-E019202A83D8 -setup Uninstall -type action -conditions {DFFF91A9-2CA5-6ABE-8474D814AF88 4ACB0B47-42B3-2B3A-BFE9AA4EC707} -title Exit -component Exit -command insert -active Yes -parent 848844B5-6103-9343-8B731B0BE4E0 +Condition DFFF91A9-2CA5-6ABE-8474D814AF88 -active Yes -parent 97ACF525-C075-8635-E019202A83D8 -title {String Is Condition} -component StringIsCondition -TreeObject::id DFFF91A9-2CA5-6ABE-8474D814AF88 +Condition 4ACB0B47-42B3-2B3A-BFE9AA4EC707 -active Yes -parent 97ACF525-C075-8635-E019202A83D8 -title {Ask Yes or No} -component AskYesOrNo -TreeObject::id 4ACB0B47-42B3-2B3A-BFE9AA4EC707 +InstallComponent F4024A3E-9A6D-2726-5E0CFFA93054 -setup Uninstall -type actiongroup -title {Uninstall Actions} -active Yes -parent ActionGroupsUninstall +InstallComponent 39D7394E-04E9-CA70-0034DB830BFE -setup Uninstall -type action -title {Uninstall Selected Files} -component UninstallSelectedFiles -active Yes -parent F4024A3E-9A6D-2726-5E0CFFA93054 +InstallComponent 39270FD8-932E-6132-7EF795ED9B93 -setup Uninstall -type actiongroup -title {Finish Actions} -active Yes -parent ActionGroupsUninstall +InstallComponent 905DA2E9-988C-2F27-BB1F5F274AC9 -setup Uninstall -type actiongroup -title {Cancel Actions} -active Yes -parent ActionGroupsUninstall + +array set Properties { +0047FF40-0139-2A59-AAC0-A44D46D6F5CC,Active +No + +0047FF40-0139-2A59-AAC0-A44D46D6F5CC,Comment +{set BackupLocationName "BackupLocation_${BackupLocationNumber}"} + +0047FF40-0139-2A59-AAC0-A44D46D6F5CC,Conditions +{0 conditions} + +0047FF40-0139-2A59-AAC0-A44D46D6F5CC,ExecuteAction +{Before Next Pane is Displayed} + +0047FF40-0139-2A59-AAC0-A44D46D6F5CC,ResultVirtualText +BackupLocationName + +0047FF40-0139-2A59-AAC0-A44D46D6F5CC,TclScript +{set BackupLocationName "BackupLocation_${BackupLocationNumber}"} + +0357FAE9-FCFD-26D8-6541D810CD61,CheckCondition +{Before Action is Executed} + +0357FAE9-FCFD-26D8-6541D810CD61,Filename +<%ProgramReadme%> + +05060263-E852-87AB-8D0F2954CAA6,Conditions +{0 conditions} + +0C12D2D3-AEBC-42FE-A73A-0815EFB10DA5,Prompt +<%ConsoleSelectDestinationText%> + +0C12D2D3-AEBC-42FE-A73A-0815EFB10DA5,VirtualText +InstallDir + +0D93323D-779D-44A8-1E0614E5285D,Conditions +{0 conditions} + +0D93323D-779D-44A8-1E0614E5285D,State +disabled + +0D93323D-779D-44A8-1E0614E5285D,Widget +{Back Button;Next Button} + +0FDBA082-90AB-808C-478A-A13E7C525336,Conditions +{0 conditions} + +0FDBA082-90AB-808C-478A-A13E7C525336,ExecuteAction +{Before Next Pane is Displayed} + +0FDBA082-90AB-808C-478A-A13E7C525336,ResultVirtualText +BackupLocationNumber + +0FDBA082-90AB-808C-478A-A13E7C525336,TclScript +{set BackupLocationNumber 1} + +13BD88FE-CD71-5AC7-E99C10B6CB28,CheckCondition +{Before Action is Executed} + +13BD88FE-CD71-5AC7-E99C10B6CB28,Filename +<%ProgramReadme%> + +1528F4F0-145C-A48D-A8526DBB6289,CheckCondition +{Before Action is Executed} + +1528F4F0-145C-A48D-A8526DBB6289,Filename +<%ProgramExecutable%> + +1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,Alias +{Stop Backup Windows Process} + +1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,Conditions +{0 conditions} + +1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,FileName +<%ShortAppName%>-program-killbackupprocess + +1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,ShortcutName +{Stop backup process} + +1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,TargetFileName +<%InstallDir%>/tools/KillBackupProcess.bat + +1681CF85-A5D2-4D73-A3FC-52B2A6A1847D,WorkingDirectory +<%InstallDir%> + +16D53E40-546B-54C3-088B1B5E3BBB,Background +white + +16D53E40-546B-54C3-088B1B5E3BBB,Conditions +{2 conditions} + +16D53E40-546B-54C3-088B1B5E3BBB,Text,subst +1 + +16D53E40-546B-54C3-088B1B5E3BBB,Type +checkbutton + +16D53E40-546B-54C3-088B1B5E3BBB,VirtualText +CreateDesktopShortcut + +16D53E40-546B-54C3-088B1B5E3BBB,X +185 + +16D53E40-546B-54C3-088B1B5E3BBB,Y +180 + +175CBE81-9EBE-1E21-A91479BEEFAE,ExitType +Finish + +17D8BA8E-5992-AA5C-F5ECB73A3433,Action +{Uninstall Actions} + +17D8BA8E-5992-AA5C-F5ECB73A3433,Conditions +{0 conditions} + +18C00430-D6B1-151F-307762B3A045,CheckCondition +{Before Action is Executed} + +18C00430-D6B1-151F-307762B3A045,Platform +Windows + +198905FB-9FAC-23DE-7422D25B8ECA,Alias +{Install Actions} + +198905FB-9FAC-23DE-7422D25B8ECA,Conditions +{0 conditions} + +19ADBDDB-1690-4A57-913E32A026C4,Conditions +{0 conditions} + +19ADBDDB-1690-4A57-913E32A026C4,State +disabled + +19ADBDDB-1690-4A57-913E32A026C4,Widget +{NextButton; CancelButton} + +1AF5CD58-65C0-49CB-9A9D-994816CF414E,Active +No + +1AF5CD58-65C0-49CB-9A9D-994816CF414E,Alias +{Upload File Listing} + +1AF5CD58-65C0-49CB-9A9D-994816CF414E,Comment +{Upload list of backed up files for Tech Support purposes. May be blocked by firewalls.} + +1AF5CD58-65C0-49CB-9A9D-994816CF414E,Conditions +{0 conditions} + +1AF5CD58-65C0-49CB-9A9D-994816CF414E,FileName +<%ShortAppName%>-program-TebucoSafeQuerypload + +1AF5CD58-65C0-49CB-9A9D-994816CF414E,ShortcutName +{Upload Filelisting to TebucoSafe for review} + +1AF5CD58-65C0-49CB-9A9D-994816CF414E,TargetFileName +<%InstallDir%>/tools/TebucoSafeQueryUpload.bat + +1AF5CD58-65C0-49CB-9A9D-994816CF414E,WorkingDirectory +<%InstallDir%> + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Active +Yes + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,BackButton,subst +1 + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,CancelButton,subst +1 + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Caption,subst +1 + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,CompanyLabel,subst +0 + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Conditions +{0 conditions} + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Message,subst +1 + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,NextButton,subst +1 + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Subtitle,subst +1 + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,Title,subst +1 + +1BEFB82C-C073-73D4-CFCE-F5DE7A674D9E,UserNameLabel,subst +0 + +1C14291C-0971-4283-92E9-3808401303F5,Active +No + +1C14291C-0971-4283-92E9-3808401303F5,Comment +{Don't start it yet, need to install keys by hand.} + +1C14291C-0971-4283-92E9-3808401303F5,Conditions +{0 conditions} + +1C14291C-0971-4283-92E9-3808401303F5,ProgramCommandLine +{net start <%ServiceName%>} + +1C14291C-0971-4283-92E9-3808401303F5,WorkingDirectory +<%InstallDir%> + +1F0926EE-6884-1330-B4A1DB11C1BF,BackButton,subst +1 + +1F0926EE-6884-1330-B4A1DB11C1BF,CancelButton,subst +1 + +1F0926EE-6884-1330-B4A1DB11C1BF,Caption,subst +1 + +1F0926EE-6884-1330-B4A1DB11C1BF,Message,subst +1 + +1F0926EE-6884-1330-B4A1DB11C1BF,NextButton,subst +1 + +1F9E8CB8-02C1-0416-1F7445B4147F,Comment +{Ask the user if they want to proceed with the install.} + +1F9E8CB8-02C1-0416-1F7445B4147F,Conditions +{2 conditions} + +20CBDBEA-2217-457B-8D98-D692C4F591E9,Message,subst +1 + +241BBFCE-4EB1-432F-94DD-69D444DDB6C0,CheckCondition +{Before Action is Executed} + +241BBFCE-4EB1-432F-94DD-69D444DDB6C0,Operator +false + +241BBFCE-4EB1-432F-94DD-69D444DDB6C0,String +<%Answer%> + +2583A547-11DE-1C27-B6D04B023CC0,CheckCondition +{Before Action is Executed} + +2583A547-11DE-1C27-B6D04B023CC0,Operator +false + +2583A547-11DE-1C27-B6D04B023CC0,String +<%SilentMode%> + +25AA533E-02FC-47D9-9273-25266B8FA1F9,Alias +{Remove Backup Service} + +25AA533E-02FC-47D9-9273-25266B8FA1F9,Comment +{Remove the Backup Windows Service} + +25AA533E-02FC-47D9-9273-25266B8FA1F9,Conditions +{0 conditions} + +25AA533E-02FC-47D9-9273-25266B8FA1F9,FileName +<%ShortAppName%>-program-removeService + +25AA533E-02FC-47D9-9273-25266B8FA1F9,ShortcutName +{Remove Service} + +25AA533E-02FC-47D9-9273-25266B8FA1F9,TargetFileName +<%InstallDir%>/tools/RemoveService.bat + +25AA533E-02FC-47D9-9273-25266B8FA1F9,WorkingDirectory +<%InstallDir%> + +28E76C8B-2605-4739-9FFE-9C2880C17E59,Active +No + +28E76C8B-2605-4739-9FFE-9C2880C17E59,Conditions +{0 conditions} + +28E76C8B-2605-4739-9FFE-9C2880C17E59,ProgramCommandLine +{notepad <%ConfigFileName%>} + +28E76C8B-2605-4739-9FFE-9C2880C17E59,WorkingDirectory +<%InstallDir%> + +2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,BackButton,subst +1 + +2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,CancelButton,subst +1 + +2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,Caption,subst +1 + +2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,Conditions +{1 condition} + +2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,Message,subst +1 + +2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,NextButton,subst +1 + +2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,Subtitle,subst +1 + +2AC89879-6E9D-3D4E-F28E-5985EEBFAAA8,Title,subst +1 + +2BB06B72-DE53-2319-B1B8-351CDCBA2008,Conditions +{0 conditions} + +2BB06B72-DE53-2319-B1B8-351CDCBA2008,ExecuteAction +{Before Next Pane is Displayed} + +2BB06B72-DE53-2319-B1B8-351CDCBA2008,ResultVirtualText +AddBackupLocation + +2BB06B72-DE53-2319-B1B8-351CDCBA2008,TclScript +{set AddBackupLocation no} + +2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6,Message,subst +1 + +2E2963BD-DDBD-738D-A910-B7F3F04946F9,Conditions +{0 conditions} + +2E2963BD-DDBD-738D-A910-B7F3F04946F9,Text,subst +1 + +2E2963BD-DDBD-738D-A910-B7F3F04946F9,Value +<%AddBackupLocation%> + +2E2963BD-DDBD-738D-A910-B7F3F04946F9,X +400 + +2E2963BD-DDBD-738D-A910-B7F3F04946F9,Y +70 + +2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,AppendNewline +No + +2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,Comment +{.conf doesn't exist yet} + +2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,Conditions +{0 conditions} + +2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,FileOpenAction +{Append to file} + +2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,Files +<%ConfigFileTemplate%> + +2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,TextToWrite,subst +1 + +32B08FB1-99DF-234E-8BAF-333E80AAC9F5,Conditions +{0 conditions} + +32B08FB1-99DF-234E-8BAF-333E80AAC9F5,FileName +<%ShortAppName%>-program-Usage + +32B08FB1-99DF-234E-8BAF-333E80AAC9F5,ShortcutName +Usage + +32B08FB1-99DF-234E-8BAF-333E80AAC9F5,TargetFileName +<%InstallDir%>/tools/ShowUsage.bat + +32B08FB1-99DF-234E-8BAF-333E80AAC9F5,WorkingDirectory +<%InstallDir%> + +32DC8FB1-A04B-71AA-EC18496D4BD0,Conditions +{0 conditions} + +32F5B0AF-EB83-7A03-D8FAE1ECE473,CheckCondition +{Before Action is Executed} + +32F5B0AF-EB83-7A03-D8FAE1ECE473,Message,subst +1 + +32F5B0AF-EB83-7A03-D8FAE1ECE473,Title,subst +1 + +32F5B0AF-EB83-7A03-D8FAE1ECE473,TrueValue +No + +3379F80B-36D6-73DC-6FC1D6223A26,CheckCondition +{Before Action is Executed} + +3379F80B-36D6-73DC-6FC1D6223A26,Operator +false + +3379F80B-36D6-73DC-6FC1D6223A26,String +<%InstallStopped%> + +362B6D6A-11BC-83CE-AFF6-410D8FBCF54D,Active +No + +362B6D6A-11BC-83CE-AFF6-410D8FBCF54D,Conditions +{0 conditions} + +362B6D6A-11BC-83CE-AFF6-410D8FBCF54D,ResultVirtualText +BackupLocationExclusions + +362B6D6A-11BC-83CE-AFF6-410D8FBCF54D,TclScript +{set BackupLocationExclusions ""} + +37E627F2-E04B-AEF2-D566C017A4D6,BackButton,subst +1 + +37E627F2-E04B-AEF2-D566C017A4D6,CancelButton,subst +1 + +37E627F2-E04B-AEF2-D566C017A4D6,Caption,subst +1 + +37E627F2-E04B-AEF2-D566C017A4D6,Conditions +{0 conditions} + +37E627F2-E04B-AEF2-D566C017A4D6,FileLabel,subst +1 + +37E627F2-E04B-AEF2-D566C017A4D6,Message,subst +1 + +37E627F2-E04B-AEF2-D566C017A4D6,NextButton,subst +1 + +37E627F2-E04B-AEF2-D566C017A4D6,ProgressValue,subst +1 + +37E627F2-E04B-AEF2-D566C017A4D6,Subtitle,subst +1 + +37E627F2-E04B-AEF2-D566C017A4D6,Title,subst +1 + +39270FD8-932E-6132-7EF795ED9B93,Alias +{Finish Actions} + +39270FD8-932E-6132-7EF795ED9B93,Conditions +{0 conditions} + +39B2B666-78D8-75E6-6EA071594D34,Conditions +{1 condition} + +39B2B666-78D8-75E6-6EA071594D34,ShortcutName +{Uninstall <%BrandName%>} + +39B2B666-78D8-75E6-6EA071594D34,TargetFileName +<%Uninstaller%> + +39B2B666-78D8-75E6-6EA071594D34,WorkingDirectory +<%InstallDir%> + +39D7394E-04E9-CA70-0034DB830BFE,Conditions +{0 conditions} + +3B6E2E7C-1A26-27F1-D578E383B128,Background +white + +3B6E2E7C-1A26-27F1-D578E383B128,Conditions +{2 conditions} + +3B6E2E7C-1A26-27F1-D578E383B128,Text,subst +1 + +3B6E2E7C-1A26-27F1-D578E383B128,Type +checkbutton + +3B6E2E7C-1A26-27F1-D578E383B128,VirtualText +ViewReadme + +3B6E2E7C-1A26-27F1-D578E383B128,X +185 + +3B6E2E7C-1A26-27F1-D578E383B128,Y +140 + +3B8CDC8E-1239-D2E9-DF4CA6B1756D,BackButton,subst +1 + +3B8CDC8E-1239-D2E9-DF4CA6B1756D,CancelButton,subst +1 + +3B8CDC8E-1239-D2E9-DF4CA6B1756D,Caption,subst +1 + +3B8CDC8E-1239-D2E9-DF4CA6B1756D,Conditions +{0 conditions} + +3B8CDC8E-1239-D2E9-DF4CA6B1756D,FileValue,subst +1 + +3B8CDC8E-1239-D2E9-DF4CA6B1756D,Message,subst +1 + +3B8CDC8E-1239-D2E9-DF4CA6B1756D,NextButton,subst +1 + +3B8CDC8E-1239-D2E9-DF4CA6B1756D,ProgressValue,subst +1 + +3B8CDC8E-1239-D2E9-DF4CA6B1756D,Subtitle,subst +1 + +3B8CDC8E-1239-D2E9-DF4CA6B1756D,Title,subst +1 + +3C7130B3-3206-403D-B09E-59D4A758FBAD,Action +{Uninstall Actions} + +3CFFF099-6122-46DD-9CE4-F5819434AC53,Conditions +{0 conditions} + +3CFFF099-6122-46DD-9CE4-F5819434AC53,IgnoreErrors +Yes + +3CFFF099-6122-46DD-9CE4-F5819434AC53,ProgramCommandLine +{net stop <%ServiceName%>} + +3CFFF099-6122-46DD-9CE4-F5819434AC53,ProgressiveOutputWidget +Message + +3CFFF099-6122-46DD-9CE4-F5819434AC53,WorkingDirectory +<%Temp%> + +3D0D1898-4C65-3E66-F82F56581E87,CheckCondition +{Before Action is Executed} + +3D0D1898-4C65-3E66-F82F56581E87,Operator +false + +3D0D1898-4C65-3E66-F82F56581E87,String +<%SilentMode%> + +3D33AA8C-0037-204B-39A339FD38BD,BackButton,subst +1 + +3D33AA8C-0037-204B-39A339FD38BD,CancelButton,subst +1 + +3D33AA8C-0037-204B-39A339FD38BD,Caption,subst +1 + +3D33AA8C-0037-204B-39A339FD38BD,Conditions +{0 conditions} + +3D33AA8C-0037-204B-39A339FD38BD,Message,subst +1 + +3D33AA8C-0037-204B-39A339FD38BD,NextButton,subst +1 + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Active +Yes + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Alias +SetBackupLocations + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,BackButton,subst +1 + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,CancelButton,subst +1 + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Caption,subst +1 + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Conditions +{0 conditions} + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Message,subst +1 + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,NextButton,subst +1 + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Subtitle,subst +1 + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Title,subst +1 + +3FDB57ED-598D-8A4E-CEF7-D90833305558,Conditions +{0 conditions} + +3FDB57ED-598D-8A4E-CEF7-D90833305558,LabelSide +left + +3FDB57ED-598D-8A4E-CEF7-D90833305558,Text,subst +1 + +3FDB57ED-598D-8A4E-CEF7-D90833305558,Type +{browse entry} + +3FDB57ED-598D-8A4E-CEF7-D90833305558,VirtualText +BackupLocationPath + +3FDB57ED-598D-8A4E-CEF7-D90833305558,Y +70 + +3FE82C17-A3E2-4A57-A563-F80818B00B81,Default +Yes + +3FE82C17-A3E2-4A57-A563-F80818B00B81,Prompt +<%InstallStartupText%> + +41CDE776-2667-5CEB-312A-FC4C33A83E7F,Conditions +{0 conditions} + +41CDE776-2667-5CEB-312A-FC4C33A83E7F,Files +{*/*.conf;*/*.txt;*/*.pem;*/*.raw;*/*.exe;*/*.bat;*/*.dll} + +41CDE776-2667-5CEB-312A-FC4C33A83E7F,RenameFiles +Yes + +41D3E165-C263-5F80-0FEEC0AEE47A,BackButton,subst +1 + +41D3E165-C263-5F80-0FEEC0AEE47A,CancelButton,subst +1 + +41D3E165-C263-5F80-0FEEC0AEE47A,Caption,subst +1 + +41D3E165-C263-5F80-0FEEC0AEE47A,Conditions +{1 condition} + +41D3E165-C263-5F80-0FEEC0AEE47A,Message,subst +1 + +41D3E165-C263-5F80-0FEEC0AEE47A,NextButton,subst +1 + +41D3E165-C263-5F80-0FEEC0AEE47A,Subtitle,subst +1 + +41D3E165-C263-5F80-0FEEC0AEE47A,Text,subst +1 + +41D3E165-C263-5F80-0FEEC0AEE47A,Title,subst +1 + +481451CC-F49C-D389-8645076F595B,Destination +<%InstallDir%> + +481451CC-F49C-D389-8645076F595B,FileSize +15288767 + +481451CC-F49C-D389-8645076F595B,Name +{Program Files} + +49E59F91-27F7-46D1-A1C1-19865C2392D3,Default +Yes + +49E59F91-27F7-46D1-A1C1-19865C2392D3,Prompt +<%UninstallStartupText%> + +49E80443-62DB-1C10-392D-1091AEA5ED88,Conditions +{1 condition} + +49E80443-62DB-1C10-392D-1091AEA5ED88,ExecuteAction +{Before Next Pane is Displayed} + +49E80443-62DB-1C10-392D-1091AEA5ED88,Pane +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 + +4A9C852B-647E-EED5-5482FFBCC2AF,Description,subst +1 + +4A9C852B-647E-EED5-5482FFBCC2AF,DisplayName,subst +1 + +4A9C852B-647E-EED5-5482FFBCC2AF,FileGroups +481451CC-F49C-D389-8645076F595B + +4A9C852B-647E-EED5-5482FFBCC2AF,Name +{Default Component} + +4A9C852B-647E-EED5-5482FFBCC2AF,RequiredComponent +Yes + +4ACB0B47-42B3-2B3A-BFE9AA4EC707,CheckCondition +{Before Action is Executed} + +4ACB0B47-42B3-2B3A-BFE9AA4EC707,Message,subst +1 + +4ACB0B47-42B3-2B3A-BFE9AA4EC707,Title,subst +1 + +4ACB0B47-42B3-2B3A-BFE9AA4EC707,TrueValue +No + +4D4A7BF0-7CCE-46E6-BDE5222F82D7,Conditions +{0 conditions} + +4D4A7BF0-7CCE-46E6-BDE5222F82D7,UpdateFilePercentage +Yes + +4D4A7BF0-7CCE-46E6-BDE5222F82D7,UpdateFileText +Yes + +4E643D8A-CA31-018D-57D7053C2CE8,CheckCondition +{Before Action is Executed} + +4E643D8A-CA31-018D-57D7053C2CE8,Filename +<%ProgramExecutable%> + +4EE35849-FAD7-170B-0E45-FA30636467B1,CheckCondition +{Before Next Pane is Displayed} + +4EE35849-FAD7-170B-0E45-FA30636467B1,EncryptedPassword +<%InstallPasswordEncrypted%> + +4EE35849-FAD7-170B-0E45-FA30636467B1,FailureFocus +{Password Entry} + +4EE35849-FAD7-170B-0E45-FA30636467B1,FailureMessage +<%PasswordIncorrectText%> + +4EE35849-FAD7-170B-0E45-FA30636467B1,UnencryptedPassword +<%InstallPassword%> + +4EF787E3-0643-DE46-15E64BAF0816,CheckCondition +{Before Action is Executed} + +4EF787E3-0643-DE46-15E64BAF0816,Platform +Windows + +52F0A238-57E1-A578-2CE4DA177B32,Conditions +{0 conditions} + +53588803-6B41-D9FC-A385906A5106,Conditions +{0 conditions} + +574198A7-7322-2F5E-02EF185D965C,BackButton,subst +1 + +574198A7-7322-2F5E-02EF185D965C,CancelButton,subst +1 + +574198A7-7322-2F5E-02EF185D965C,Caption,subst +1 + +574198A7-7322-2F5E-02EF185D965C,Conditions +{0 conditions} + +574198A7-7322-2F5E-02EF185D965C,FileLabel,subst +1 + +574198A7-7322-2F5E-02EF185D965C,Message,subst +1 + +574198A7-7322-2F5E-02EF185D965C,NextButton,subst +1 + +574198A7-7322-2F5E-02EF185D965C,ProgressValue,subst +1 + +574198A7-7322-2F5E-02EF185D965C,Subtitle,subst +1 + +574198A7-7322-2F5E-02EF185D965C,Title,subst +1 + +58E1119F-639E-17C9-5D3898F385AA,BackButton,subst +1 + +58E1119F-639E-17C9-5D3898F385AA,BrowseButton,subst +1 + +58E1119F-639E-17C9-5D3898F385AA,BrowseText,subst +1 + +58E1119F-639E-17C9-5D3898F385AA,CancelButton,subst +1 + +58E1119F-639E-17C9-5D3898F385AA,Caption,subst +1 + +58E1119F-639E-17C9-5D3898F385AA,Conditions +{1 condition} + +58E1119F-639E-17C9-5D3898F385AA,DestinationLabel,subst +0 + +58E1119F-639E-17C9-5D3898F385AA,Message,subst +1 + +58E1119F-639E-17C9-5D3898F385AA,NextButton,subst +1 + +58E1119F-639E-17C9-5D3898F385AA,Subtitle,subst +1 + +58E1119F-639E-17C9-5D3898F385AA,Title,subst +1 + +592F46AE-8CEE-01F3-0BA7EBDCA4F4,CheckCondition +{Before Action is Executed} + +592F46AE-8CEE-01F3-0BA7EBDCA4F4,Filename +<%ProgramExecutable%> + +5CA3EA16-E37C-AABE-E576C4636EB0,Action +{Install Actions} + +5CA3EA16-E37C-AABE-E576C4636EB0,Conditions +{0 conditions} + +5EE78EF7-37CA-D440-3DB5-09136CD566B3,CheckCondition +{Before Action is Executed} + +5EE78EF7-37CA-D440-3DB5-09136CD566B3,String +<%AddBackupLocation%> + +5F2C1F1C-B9F7-1642-59D9-A18318C1D70B,Conditions +{0 conditions} + +5F2C1F1C-B9F7-1642-59D9-A18318C1D70B,Files +{<%ConfigFileTemplate%>;*/*.bat;<%InstallDir%>/*.vbs} + +5F2C1F1C-B9F7-1642-59D9-A18318C1D70B,LineFeed +Windows + +5F2C1F1C-B9F7-1642-59D9-A18318C1D70B,StringMap +{"@@CUSTOMERCOMPANY@@" <%UserInfoCompany%> +"@@BRANDNAME@@" <%BrandName%> +"@@SERVICENAME@@" <%ServiceName%> +"@@CUSTOMERUSERNAME@@" <%UserInfoName%> +"@@CUSTOMERUSERPHONE@@" <%UserInfoPhone%> +"@@CUSTOMERUSEREMAIL@@" <%UserInfoEmail%> +"@@ACCTNO@@" <%UserInfoAcctNo%> +"@@INSTALLDIR@@" <%InstallDir%>} + +614C45B2-7515-780C-E444-7F165CF02DD7,Active +No + +614C45B2-7515-780C-E444-7F165CF02DD7,Conditions +{0 conditions} + +614C45B2-7515-780C-E444-7F165CF02DD7,ResultVirtualText +BackupLocationShortName + +614C45B2-7515-780C-E444-7F165CF02DD7,TclScript +{set BackupLocationShortName ""} + +6652193C-5D4B-44B6-ABC6-D6E96D89E5DC,Active +No + +6652193C-5D4B-44B6-ABC6-D6E96D89E5DC,Comment +{PJ removed. Is this the one at the top leve?} + +6652193C-5D4B-44B6-ABC6-D6E96D89E5DC,Conditions +{0 conditions} + +69FD7409-5E2A-143B-DABD1C3B1E67,Conditions +{2 conditions} + +6B4CB3C2-4799-4C9F-BA8E-1EE47C4606E1,ExitType +Finish + +6B966959-05D9-DB32-8D9C4AD2A3DF,CheckCondition +{Before Action is Executed} + +6B966959-05D9-DB32-8D9C4AD2A3DF,Platform +Windows + +6C323815-B9AB-FA94-4F5D152EBC51,BackButton,subst +1 + +6C323815-B9AB-FA94-4F5D152EBC51,CancelButton,subst +1 + +6C323815-B9AB-FA94-4F5D152EBC51,Caption,subst +1 + +6C323815-B9AB-FA94-4F5D152EBC51,Conditions +{0 conditions} + +6C323815-B9AB-FA94-4F5D152EBC51,Message,subst +1 + +6C323815-B9AB-FA94-4F5D152EBC51,NextButton,subst +1 + +6D9D1ABC-7146-443F-9EE9-205D5CA6C830,CheckCondition +{Before Action is Executed} + +6D9D1ABC-7146-443F-9EE9-205D5CA6C830,String +{<%Property <%CurrentPane%> UserMustAcceptLicense%>} + +6E70FB1F-6A43-6C23-3242E965A0D0,Action +{Install Actions} + +6E70FB1F-6A43-6C23-3242E965A0D0,Conditions +{0 conditions} + +6F61CDA8-30C9-454F-82A3-9987E1203079,Alias +{Start a sync now.} + +6F61CDA8-30C9-454F-82A3-9987E1203079,Conditions +{0 conditions} + +6F61CDA8-30C9-454F-82A3-9987E1203079,FileName +<%ShortAppName%>-program-sync + +6F61CDA8-30C9-454F-82A3-9987E1203079,ShortcutName +{Sync now} + +6F61CDA8-30C9-454F-82A3-9987E1203079,TargetFileName +<%InstallDir%>/tools/Sync.bat + +6F61CDA8-30C9-454F-82A3-9987E1203079,WorkingDirectory +<%InstallDir%> + +6F94698F-0839-3ABF-0CF2DF05A4C8,CheckCondition +{Before Action is Executed} + +6F94698F-0839-3ABF-0CF2DF05A4C8,String +<%CreateQuickLaunchShortcut%> + +6FEE2889-0338-1D49-60BF-1471F465AB26,AppendNewline +No + +6FEE2889-0338-1D49-60BF-1471F465AB26,Comment +{Closing final BackupLocations bracket} + +6FEE2889-0338-1D49-60BF-1471F465AB26,Conditions +{0 conditions} + +6FEE2889-0338-1D49-60BF-1471F465AB26,FileOpenAction +{Append to file} + +6FEE2889-0338-1D49-60BF-1471F465AB26,Files +<%ConfigFileTemplate%> + +6FEE2889-0338-1D49-60BF-1471F465AB26,LineFeed +Windows + +6FEE2889-0338-1D49-60BF-1471F465AB26,TextToWrite,subst +1 + +738DD098-7E3B-BC89-875CDB93CBE2,CheckCondition +{Before Action is Executed} + +738DD098-7E3B-BC89-875CDB93CBE2,Platform +Windows + +73DD4D07-B1DC-BA38-2B12-07EB24A7F0C8,Conditions +{0 conditions} + +73DD4D07-B1DC-BA38-2B12-07EB24A7F0C8,Destination +<%ConfigFileName%> + +73DD4D07-B1DC-BA38-2B12-07EB24A7F0C8,Source +<%ConfigFileTemplate%> + +73EA65C1-3BE3-B190-55C3E99F6269,Conditions +{1 condition} + +748D673B-DFE6-5F74-329903ACE4DB,CheckCondition +{Before Action is Executed} + +748D673B-DFE6-5F74-329903ACE4DB,Filename +<%ProgramExecutable%> + +793D8178-0F51-7F07-BC5886586D3C,CheckCondition +{Before Action is Executed} + +793D8178-0F51-7F07-BC5886586D3C,Operator +false + +793D8178-0F51-7F07-BC5886586D3C,String +<%InstallStopped%> + +795EE61F-6C0D-4A8B-93E02AA3894A,CheckCondition +{Before Action is Executed} + +795EE61F-6C0D-4A8B-93E02AA3894A,String +<%LaunchApplication%> + +79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C,CheckCondition +{Before Action is Executed} + +79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C,Operator +false + +79DAC913-A33D-4ED6-9BAE-B3A2053C0F2C,String +<%LicenseAccepted%> + +7A983CD8-302C-4942-BE59-525C5B5FA2F2,Conditions +{0 conditions} + +7A983CD8-302C-4942-BE59-525C5B5FA2F2,ProgramCommandLine +{ServiceControl terminate} + +7A983CD8-302C-4942-BE59-525C5B5FA2F2,WorkingDirectory +<%InstallDir%> + +7B770A07-A785-5215-956FA82CF14E,Active +No + +7B770A07-A785-5215-956FA82CF14E,Conditions +{3 conditions} + +7B770A07-A785-5215-956FA82CF14E,ShortcutDirectory +<%QUICK_LAUNCH%> + +7B770A07-A785-5215-956FA82CF14E,ShortcutName +<%BrandName%> + +7B770A07-A785-5215-956FA82CF14E,TargetFileName +<%ProgramExecutable%> + +7B770A07-A785-5215-956FA82CF14E,WorkingDirectory +<%InstallDir%> + +7D8E1902-2BC4-80D8-2C18771E7C22,Conditions +{0 conditions} + +7D8E1902-2BC4-80D8-2C18771E7C22,ProgramCommandLine +{<%ServiceExeName%> -i -S <%ServiceName%> -c "<%ConfigFileName%>"} + +7D8E1902-2BC4-80D8-2C18771E7C22,ProgressiveOutputWidget +Message + +7D8E1902-2BC4-80D8-2C18771E7C22,ShowProgressiveOutput +Yes + +7D8E1902-2BC4-80D8-2C18771E7C22,WorkingDirectory +<%InstallDir%> + +7F85263E-CAE2-46BA-AAC0-6B89D20FD2DE,ExitType +Finish + +8202CECC-54A0-9B6C-D24D111BA52E,Components +4A9C852B-647E-EED5-5482FFBCC2AF + +8202CECC-54A0-9B6C-D24D111BA52E,Description,subst +1 + +8202CECC-54A0-9B6C-D24D111BA52E,DisplayName,subst +1 + +8202CECC-54A0-9B6C-D24D111BA52E,Name +Typical + +8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Checked +No + +8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Conditions +{0 conditions} + +8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Text,subst +1 + +8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Type +checkbutton + +8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Value +Yes + +8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,VirtualText +AddBackupLocation + +8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Y +250 + +848844B5-6103-9343-8B731B0BE4E0,Alias +{Startup Actions} + +848844B5-6103-9343-8B731B0BE4E0,Conditions +{0 conditions} + +84DA7F05-9FB7-CC36-9EC98F8A6826,CheckCondition +{Before Next Pane is Displayed} + +84DA7F05-9FB7-CC36-9EC98F8A6826,FailureMessage +<%DirectoryPermissionText%> + +84DA7F05-9FB7-CC36-9EC98F8A6826,Filename +<%InstallDir%> + +84DA7F05-9FB7-CC36-9EC98F8A6826,Permission +{can create} + +855DE408-060E-3D35-08B5-1D9AB05C2865,Conditions +{0 conditions} + +855DE408-060E-3D35-08B5-1D9AB05C2865,Height +100 + +855DE408-060E-3D35-08B5-1D9AB05C2865,Text,subst +1 + +855DE408-060E-3D35-08B5-1D9AB05C2865,Type +text + +855DE408-060E-3D35-08B5-1D9AB05C2865,VirtualText +BackupLocationExclusions + +855DE408-060E-3D35-08B5-1D9AB05C2865,Y +130 + +87DE6D78-81E1-495B-A214-B3FF3E7E5614,CheckCondition +{Before Action is Executed} + +87DE6D78-81E1-495B-A214-B3FF3E7E5614,Operator +false + +87DE6D78-81E1-495B-A214-B3FF3E7E5614,String +<%Answer%> + +88A50FD5-480F-19A5-DA74-C915EB0A9765,Active +No + +88A50FD5-480F-19A5-DA74-C915EB0A9765,Conditions +{1 condition} + +88A50FD5-480F-19A5-DA74-C915EB0A9765,ExecuteAction +{After Pane is Finished} + +88A50FD5-480F-19A5-DA74-C915EB0A9765,Pane +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7 + +8A761DBD-0640-D98C-9B3AD7672A8F,Conditions +{0 conditions} + +8A761DBD-0640-D98C-9B3AD7672A8F,State +disabled + +8A761DBD-0640-D98C-9B3AD7672A8F,Widget +{Back Button;Next Button} + +8C866252-8760-9B08-FE569C25B60D,CheckCondition +{Before Action is Executed} + +8C866252-8760-9B08-FE569C25B60D,Filename +<%ProgramExecutable%> + +8E095096-F018-A880-429D-A2177A9B70EA,Active +No + +8E095096-F018-A880-429D-A2177A9B70EA,Conditions +{0 conditions} + +8E095096-F018-A880-429D-A2177A9B70EA,Text,subst +1 + +8E095096-F018-A880-429D-A2177A9B70EA,X +50 + +8E095096-F018-A880-429D-A2177A9B70EA,Y +150 + +8E1A5944-5AF5-5906-16D395E386D8,Conditions +{0 conditions} + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,Active +Yes + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,BackButton,subst +1 + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,CancelButton,subst +1 + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,Caption,subst +1 + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,CompanyLabel,subst +1 + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,Conditions +{0 conditions} + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,Message,subst +1 + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,NextButton,subst +1 + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,Subtitle,subst +1 + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,Title,subst +1 + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,UserNameLabel,subst +1 + +905DA2E9-988C-2F27-BB1F5F274AC9,Alias +{Cancel Actions} + +905DA2E9-988C-2F27-BB1F5F274AC9,Conditions +{0 conditions} + +908CE221-5A3D-0A78-24A1-E7C91EBE38D4,BackButton,subst +1 + +908CE221-5A3D-0A78-24A1-E7C91EBE38D4,CancelButton,subst +1 + +908CE221-5A3D-0A78-24A1-E7C91EBE38D4,Caption,subst +1 + +908CE221-5A3D-0A78-24A1-E7C91EBE38D4,Conditions +{0 conditions} + +908CE221-5A3D-0A78-24A1-E7C91EBE38D4,Message,subst +1 + +908CE221-5A3D-0A78-24A1-E7C91EBE38D4,NextButton,subst +1 + +908CE221-5A3D-0A78-24A1-E7C91EBE38D4,Subtitle,subst +1 + +908CE221-5A3D-0A78-24A1-E7C91EBE38D4,Title,subst +1 + +937C3FDD-FB28-98BD-3DAB276E59ED,Background +white + +937C3FDD-FB28-98BD-3DAB276E59ED,Conditions +{3 conditions} + +937C3FDD-FB28-98BD-3DAB276E59ED,Text,subst +1 + +937C3FDD-FB28-98BD-3DAB276E59ED,Type +checkbutton + +937C3FDD-FB28-98BD-3DAB276E59ED,VirtualText +CreateQuickLaunchShortcut + +937C3FDD-FB28-98BD-3DAB276E59ED,X +185 + +937C3FDD-FB28-98BD-3DAB276E59ED,Y +200 + +93AA298C-B64E-5683-14D2-7B86F7DEFD2C,Active +No + +93AA298C-B64E-5683-14D2-7B86F7DEFD2C,Comment +{set BackupLocationName "BackupLocation_${BackupLocationNumber}"} + +93AA298C-B64E-5683-14D2-7B86F7DEFD2C,Conditions +{0 conditions} + +93AA298C-B64E-5683-14D2-7B86F7DEFD2C,ResultVirtualText +BackupLocationName + +93AA298C-B64E-5683-14D2-7B86F7DEFD2C,TclScript +{set BackupLocationName "BackupLocation_${BackupLocationNumber}"} + +96A68CAC-9ED7-806C-086B104720FD,CheckCondition +{Before Action is Executed} + +96A68CAC-9ED7-806C-086B104720FD,String +<%ErrorsOccurred%> + +97ACF525-C075-8635-E019202A83D8,Comment +{Ask the user if they want to proceed with the uninstall.} + +9892B25C-689B-5B8F-F0C9-B14FF6ACC40C,Active +No + +9892B25C-689B-5B8F-F0C9-B14FF6ACC40C,Conditions +{0 conditions} + +9892B25C-689B-5B8F-F0C9-B14FF6ACC40C,ResultVirtualText +AddBackupLocation + +9892B25C-689B-5B8F-F0C9-B14FF6ACC40C,TclScript +{set AddBackupLocation no} + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Active +Yes + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,BackButton,subst +1 + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,CancelButton,subst +1 + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Caption,subst +1 + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Conditions +{0 conditions} + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Message,subst +1 + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,NextButton,subst +1 + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Subtitle,subst +1 + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Title,subst +1 + +9A663209-495B-ED16-09BE-457B61148022,Conditions +{0 conditions} + +9A663209-495B-ED16-09BE-457B61148022,FileName +<%ShortAppName%>-program-QueryCurrent + +9A663209-495B-ED16-09BE-457B61148022,ShortcutName +{Query Current Only} + +9A663209-495B-ED16-09BE-457B61148022,TargetFileName +<%InstallDir%>/tools/QueryOutputCurrent.bat + +9A663209-495B-ED16-09BE-457B61148022,WorkingDirectory +<%InstallDir%> + +9D101299-B80C-441B-8685-6E3AC61808E8,Alias +{Remote Control} + +9D101299-B80C-441B-8685-6E3AC61808E8,Comment +{Get tech support via remote control} + +9D101299-B80C-441B-8685-6E3AC61808E8,Conditions +{0 conditions} + +9D101299-B80C-441B-8685-6E3AC61808E8,FileName +<%ShortAppName%>-program-RemoteControl + +9D101299-B80C-441B-8685-6E3AC61808E8,ShortcutName +RemoteControl + +9D101299-B80C-441B-8685-6E3AC61808E8,TargetFileName +<%InstallDir%>/tools/RemoteControl.exe + +9D101299-B80C-441B-8685-6E3AC61808E8,WorkingDirectory +<%InstallDir%> + +A1DD1DC2-85D7-9BC6-998AC3D4A3A9,Alias +{Startup Actions} + +A1DD1DC2-85D7-9BC6-998AC3D4A3A9,Conditions +{0 conditions} + +A5B32DA1-B2FE-C1FA-6057-FBC3059EF076,Conditions +{0 conditions} + +A5B32DA1-B2FE-C1FA-6057-FBC3059EF076,ResultVirtualText +AddBackupLocation + +A5B32DA1-B2FE-C1FA-6057-FBC3059EF076,TclScript +{set AddBackupLocation no } + +A6E1B027-A1B4-5848-4F868D028D00,CheckCondition +{Before Action is Executed} + +A6E1B027-A1B4-5848-4F868D028D00,String +<%ViewReadme%> + +ADA6EB2F-8820-4366-BBEF-ED1335B7F828,Conditions +{1 condition} + +AE3BD5B4-35DE-4240-B79914D43E56,Active +No + +AE3BD5B4-35DE-4240-B79914D43E56,BackButton,subst +1 + +AE3BD5B4-35DE-4240-B79914D43E56,CancelButton,subst +1 + +AE3BD5B4-35DE-4240-B79914D43E56,Caption,subst +1 + +AE3BD5B4-35DE-4240-B79914D43E56,Conditions +{0 conditions} + +AE3BD5B4-35DE-4240-B79914D43E56,Message,subst +1 + +AE3BD5B4-35DE-4240-B79914D43E56,NextButton,subst +1 + +AIX-ppc,Active +No + +AIX-ppc,DefaultDirectoryPermission +0755 + +AIX-ppc,DefaultFilePermission +0755 + +AIX-ppc,Executable +<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%> + +AIX-ppc,FallBackToConsole +Yes + +AIX-ppc,InstallDir +<%Home%>/<%ShortAppName%> + +AIX-ppc,InstallMode +Standard + +AIX-ppc,InstallType +Typical + +AIX-ppc,ProgramExecutable +{} + +AIX-ppc,ProgramFolderAllUsers +No + +AIX-ppc,ProgramFolderName +<%AppName%> + +AIX-ppc,ProgramLicense +<%InstallDir%>/LICENSE.txt + +AIX-ppc,ProgramName +{} + +AIX-ppc,ProgramReadme +<%InstallDir%>/README.txt + +AIX-ppc,PromptForRoot +Yes + +AIX-ppc,RequireRoot +No + +AIX-ppc,RootInstallDir +/usr/local/<%ShortAppName%> + +B002A311-F8E7-41DE-B039-521391924E5B,Message,subst +1 + +B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,Alias +{Stop Backup Service} + +B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,Comment +{Stop the Backup Windows Service} + +B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,Conditions +{0 conditions} + +B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,FileName +<%ShortAppName%>-program-stopservice + +B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,ShortcutName +{Stop Service} + +B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,TargetFileName +<%InstallDir%>/tools/StopService.bat + +B01CBBB2-6A78-CA53-9ED9-C3C4CFC9239E,WorkingDirectory +<%InstallDir%> + +B0AA6839-AAB6-A602-C0E4ECA2E4FF,CheckCondition +{Before Action is Executed} + +B0AA6839-AAB6-A602-C0E4ECA2E4FF,Filename +<%ProgramExecutable%> + +B39C0455-D1B6-7DDC-E2717F83463E,CheckCondition +{Before Action is Executed} + +B39C0455-D1B6-7DDC-E2717F83463E,Operator +false + +B39C0455-D1B6-7DDC-E2717F83463E,String +<%InstallStopped%> + +B3B99E2D-C368-A921-B7BC-A71EBDE3AD4D,Conditions +{0 conditions} + +B3B99E2D-C368-A921-B7BC-A71EBDE3AD4D,ExecuteAction +{Before Next Pane is Displayed} + +B3B99E2D-C368-A921-B7BC-A71EBDE3AD4D,Password +<%InstallPassword%> + +B4D31D1E-ADB1-DE8F-18EB7294DDA8,Conditions +{0 conditions} + +B4D31D1E-ADB1-DE8F-18EB7294DDA8,ProgramCommandLine +{<%ServiceExeName%> -r -S <%ServiceName%>} + +B4D31D1E-ADB1-DE8F-18EB7294DDA8,WorkingDirectory +<%InstallDir%> + +B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174,Message,subst +1 + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,AcceptRadiobutton,subst +0 + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Active +Yes + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,BackButton,subst +1 + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,CancelButton,subst +1 + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Caption,subst +1 + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Conditions +{0 conditions} + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,DeclineRadiobutton,subst +0 + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Message,subst +1 + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,NextButton,subst +1 + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Subtitle,subst +1 + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Text,subst +1 + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Title,subst +1 + +B5DFEC63-92A9-4686-909E-0CE78A7069D6,Alias +{Restart Backup Service} + +B5DFEC63-92A9-4686-909E-0CE78A7069D6,Comment +{Stop and restart the Backup Windows Service} + +B5DFEC63-92A9-4686-909E-0CE78A7069D6,Conditions +{0 conditions} + +B5DFEC63-92A9-4686-909E-0CE78A7069D6,FileName +<%ShortAppName%>-program-restartservice + +B5DFEC63-92A9-4686-909E-0CE78A7069D6,ShortcutName +{Restart Service} + +B5DFEC63-92A9-4686-909E-0CE78A7069D6,TargetFileName +{<%InstallDir%>\tools\RestartService.bat} + +B5DFEC63-92A9-4686-909E-0CE78A7069D6,WorkingDirectory +<%InstallDir%> + +B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,Conditions +{0 conditions} + +B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,LabelSide +left + +B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,Text,subst +1 + +B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,Type +entry + +B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,VirtualText +BackupLocationShortName + +B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,Y +100 + +B93D2216-1DDB-484C-A9AC-D6C18ED7DE23,Conditions +{2 conditions} + +B93D2216-1DDB-484C-A9AC-D6C18ED7DE23,State +disabled + +B93D2216-1DDB-484C-A9AC-D6C18ED7DE23,Widget +NextButton + +BC4EA5FD-50BD-4D6E-953F-5E3EDB957360,CheckCondition +{Before Next Action is Executed} + +BC4EA5FD-50BD-4D6E-953F-5E3EDB957360,FailureMessage +<%DirectoryPermissionText%> + +BC4EA5FD-50BD-4D6E-953F-5E3EDB957360,Filename +<%InstallDir%> + +BC4EA5FD-50BD-4D6E-953F-5E3EDB957360,Permission +{can create} + +C0452595-F3EB-43AD-BCA2-661437584636,Alias +{Modify Backup Configuration} + +C0452595-F3EB-43AD-BCA2-661437584636,Comment +{Modify your Backup Configuration} + +C0452595-F3EB-43AD-BCA2-661437584636,Conditions +{0 conditions} + +C0452595-F3EB-43AD-BCA2-661437584636,FileName +<%ShortAppName%>-program-editconfig + +C0452595-F3EB-43AD-BCA2-661437584636,ShortcutName +{Edit Config File} + +C0452595-F3EB-43AD-BCA2-661437584636,TargetFileName +<%InstallDir%>/tools/EditConfig.bat + +C0452595-F3EB-43AD-BCA2-661437584636,WorkingDirectory +<%InstallDir%> + +C0AF7C05-A31A-8376-BCB9-BA8B3A666252,Conditions +{0 conditions} + +C0AF7C05-A31A-8376-BCB9-BA8B3A666252,FileName +<%ShortAppName%>-program-QueryAll + +C0AF7C05-A31A-8376-BCB9-BA8B3A666252,ShortcutName +{Query All} + +C0AF7C05-A31A-8376-BCB9-BA8B3A666252,TargetFileName +<%InstallDir%>/tools/QueryOutputAll.bat + +C0AF7C05-A31A-8376-BCB9-BA8B3A666252,WorkingDirectory +<%InstallDir%> + +C105AAAE-7C16-2C9E-769FE4535B60,Active +No + +C105AAAE-7C16-2C9E-769FE4535B60,Caption,subst +1 + +C105AAAE-7C16-2C9E-769FE4535B60,CloseButton,subst +1 + +C105AAAE-7C16-2C9E-769FE4535B60,Conditions +{3 conditions} + +C105AAAE-7C16-2C9E-769FE4535B60,Message,subst +1 + +C105AAAE-7C16-2C9E-769FE4535B60,TextFile +<%ProgramReadme%> + +C105AAAE-7C16-2C9E-769FE4535B60,Title,subst +1 + +C33D74B2-26FA-16F5-433A10C6A747,Active +No + +C33D74B2-26FA-16F5-433A10C6A747,Conditions +{3 conditions} + +C33D74B2-26FA-16F5-433A10C6A747,ProgramCommandLine +<%ProgramExecutable%> + +C33D74B2-26FA-16F5-433A10C6A747,WaitForProgram +No + +C33D74B2-26FA-16F5-433A10C6A747,WorkingDirectory +<%InstallDir%> + +C7762473-273F-E3CA-17E3-65789B14CDB0,Conditions +{0 conditions} + +C7762473-273F-E3CA-17E3-65789B14CDB0,ExecuteAction +{Before Next Pane is Displayed} + +C7762473-273F-E3CA-17E3-65789B14CDB0,FileOpenAction +{Append to file} + +C7762473-273F-E3CA-17E3-65789B14CDB0,Files +<%ConfigFileTemplate%> + +C7762473-273F-E3CA-17E3-65789B14CDB0,TextToWrite,subst +1 + +CC4337CC-F3B5-757C-DFCF5D1D365A,CheckCondition +{Before Action is Executed} + +CC4337CC-F3B5-757C-DFCF5D1D365A,Operator +false + +CC4337CC-F3B5-757C-DFCF5D1D365A,String +<%SilentMode%> + +CDD84DE3-C970-458F-9162-1A3CE0AA716B,Alias +{Start Backup Service} + +CDD84DE3-C970-458F-9162-1A3CE0AA716B,Comment +{Start Backup Windows Service} + +CDD84DE3-C970-458F-9162-1A3CE0AA716B,Conditions +{0 conditions} + +CDD84DE3-C970-458F-9162-1A3CE0AA716B,FileName +<%ShortAppName%>-program-startservice + +CDD84DE3-C970-458F-9162-1A3CE0AA716B,ShortcutName +{Start Service} + +CDD84DE3-C970-458F-9162-1A3CE0AA716B,TargetFileName +{<%InstallDir%>\tools\StartService.bat} + +CDD84DE3-C970-458F-9162-1A3CE0AA716B,WorkingDirectory +<%InstallDir%> + +CFFA27AF-A641-E41C-B4A0E3BB3CBB,Background +white + +CFFA27AF-A641-E41C-B4A0E3BB3CBB,Conditions +{2 conditions} + +CFFA27AF-A641-E41C-B4A0E3BB3CBB,Text,subst +1 + +CFFA27AF-A641-E41C-B4A0E3BB3CBB,Type +checkbutton + +CFFA27AF-A641-E41C-B4A0E3BB3CBB,VirtualText +LaunchApplication + +CFFA27AF-A641-E41C-B4A0E3BB3CBB,X +185 + +CFFA27AF-A641-E41C-B4A0E3BB3CBB,Y +160 + +D23DD94C-E517-7F34-FD59-802CB18AB887,Comment +{Need to do before starting anything else.} + +D23DD94C-E517-7F34-FD59-802CB18AB887,Conditions +{0 conditions} + +D23DD94C-E517-7F34-FD59-802CB18AB887,Files +{*/*.conf;*/*.txt;*/*.bat} + +D23DD94C-E517-7F34-FD59-802CB18AB887,LineFeed +Windows + +D3D73C76-D9D3-07DA-63D4163A44BE,Conditions +{0 conditions} + +D3D73C76-D9D3-07DA-63D4163A44BE,ExitType +Finish + +D4FC6EB5-DDEE-4E4A-B8E1-D4B588A7928B,Action +{Install Actions} + +D55BA4AF-E73B-60D1-E26F79175227,Action +{Uninstall Actions} + +D55BA4AF-E73B-60D1-E26F79175227,Conditions +{0 conditions} + +D7FBBEBB-2186-5674-BA87-BB7151859D4E,Conditions +{0 conditions} + +D7FBBEBB-2186-5674-BA87-BB7151859D4E,ResultVirtualText +BackupLocationNumber + +D7FBBEBB-2186-5674-BA87-BB7151859D4E,TclScript +{incr BackupLocationNumber} + +D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,Alias +{Reload Configuration File, after editing it.} + +D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,Conditions +{0 conditions} + +D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,FileName +<%ShortAppName%>-program-reloadconfig + +D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,ShortcutName +{Reload configuration file} + +D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,TargetFileName +<%InstallDir%>/tools/ReloadConfig.bat + +D8B8A9BF-5F2E-4236-A63E-5A8C5FFA8968,WorkingDirectory +<%InstallDir%> + +D8F0AA0F-AD79-C566-15CC508F503B,Action +{Install Actions} + +D8F0AA0F-AD79-C566-15CC508F503B,Conditions +{0 conditions} + +D9F88AC1-3D2D-F6DB-871E-3A0E016770B1,Conditions +{0 conditions} + +D9F88AC1-3D2D-F6DB-871E-3A0E016770B1,Destination +<%ConfigFileTemplate%> + +D9F88AC1-3D2D-F6DB-871E-3A0E016770B1,Source +<%InstallDir%>/templates/original.conf + +DA33B826-E633-A845-4646-76DFA78B907B,Active +Yes + +DA33B826-E633-A845-4646-76DFA78B907B,BackButton,subst +1 + +DA33B826-E633-A845-4646-76DFA78B907B,CancelButton,subst +1 + +DA33B826-E633-A845-4646-76DFA78B907B,Caption,subst +1 + +DA33B826-E633-A845-4646-76DFA78B907B,Conditions +{0 conditions} + +DA33B826-E633-A845-4646-76DFA78B907B,Message,subst +1 + +DA33B826-E633-A845-4646-76DFA78B907B,NextButton,subst +1 + +DA33B826-E633-A845-4646-76DFA78B907B,Subtitle,subst +1 + +DA33B826-E633-A845-4646-76DFA78B907B,Title,subst +1 + +DDBBD8A9-13D7-9509-9202-419E989F60A9,Active +No + +DDBBD8A9-13D7-9509-9202-419E989F60A9,Checked +No + +DDBBD8A9-13D7-9509-9202-419E989F60A9,Conditions +{0 conditions} + +DDBBD8A9-13D7-9509-9202-419E989F60A9,Text,subst +1 + +DDBBD8A9-13D7-9509-9202-419E989F60A9,Type +checkbutton + +DDBBD8A9-13D7-9509-9202-419E989F60A9,VirtualText +AddBackupLocation + +DDBBD8A9-13D7-9509-9202-419E989F60A9,X +50 + +DDBBD8A9-13D7-9509-9202-419E989F60A9,Y +200 + +DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,Alias +{Install Backup Service} + +DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,Comment +{Install the Backup Windows Service} + +DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,Conditions +{0 conditions} + +DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,FileName +<%ShortAppName%>-program-installService + +DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,ShortcutName +{Install Service} + +DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,TargetFileName +<%InstallDir%>/tools/InstallService.bat + +DE800F1C-CB1A-E1CE-AEB8-B0A6DB4818E7,WorkingDirectory +<%InstallDir%> + +DECC120D-6904-7F17-45A49184A5A3,Active +No + +DECC120D-6904-7F17-45A49184A5A3,Conditions +{2 conditions} + +DECC120D-6904-7F17-45A49184A5A3,ShortcutName +<%AppName%> + +DECC120D-6904-7F17-45A49184A5A3,TargetFileName +<%ProgramExecutable%> + +DECC120D-6904-7F17-45A49184A5A3,WorkingDirectory +<%InstallDir%> + +DFFF91A9-2CA5-6ABE-8474D814AF88,CheckCondition +{Before Action is Executed} + +DFFF91A9-2CA5-6ABE-8474D814AF88,Operator +false + +DFFF91A9-2CA5-6ABE-8474D814AF88,String +<%SilentMode%> + +E02368C5-95B5-03A7-3282740037B0,CheckCondition +{Before Action is Executed} + +E02368C5-95B5-03A7-3282740037B0,Operator +false + +E02368C5-95B5-03A7-3282740037B0,String +<%InstallStopped%> + +E161F216-E597-B340-C1A71C476E2C,CheckCondition +{Before Action is Executed} + +E161F216-E597-B340-C1A71C476E2C,Message,subst +1 + +E161F216-E597-B340-C1A71C476E2C,Title,subst +1 + +E23AC50D-7CFB-800E-A99C6F4068F8,Alias +{Cancel Actions} + +E23AC50D-7CFB-800E-A99C6F4068F8,Conditions +{0 conditions} + +E44CFF46-6302-C518-B9C30D2E43F7,CheckCondition +{Before Action is Executed} + +E44CFF46-6302-C518-B9C30D2E43F7,String +<%CreateDesktopShortcut%> + +E4DEA723-FC78-45D7-BAB1-A3E4C4C96EA1,Conditions +{0 conditions} + +E4DEA723-FC78-45D7-BAB1-A3E4C4C96EA1,ProgramCommandLine +{net stop <%ServiceName%>} + +E56ADFF4-C15E-AEDB-A599-C468AF72C4BB,Conditions +{0 conditions} + +E56ADFF4-C15E-AEDB-A599-C468AF72C4BB,Destination +{<%InstallDir%>\templates\NotifySysAdmin.original.vbs} + +E56ADFF4-C15E-AEDB-A599-C468AF72C4BB,Source +{<%InstallDir%>\templates\NotifySysAdmin.template.vbs} + +EB2B31A1-C111-3582-0C8A5656692A,String +<%ErrorsOccurred%> + +EB532611-5F30-3C24-66EB-F3826D9054FD,CheckCondition +{Before Action is Executed} + +EB532611-5F30-3C24-66EB-F3826D9054FD,String +<%AddBackupLocation%> + +F4024A3E-9A6D-2726-5E0CFFA93054,Alias +{Uninstall Actions} + +F4024A3E-9A6D-2726-5E0CFFA93054,Conditions +{0 conditions} + +F5F21749-8B3A-49C6-9138-9C4D6D703D26,Active +No + +F5F21749-8B3A-49C6-9138-9C4D6D703D26,Conditions +{0 conditions} + +F5F21749-8B3A-49C6-9138-9C4D6D703D26,ProgramCommandLine +{cmd /k tools/7za.exe encrypted_keys.exe -p<%InstallPassword%>} + +F5F21749-8B3A-49C6-9138-9C4D6D703D26,ProgressiveOutputWidget +Message + +F5F21749-8B3A-49C6-9138-9C4D6D703D26,ShowProgressiveOutput +Yes + +F5F21749-8B3A-49C6-9138-9C4D6D703D26,WorkingDirectory +<%InstallDir%> + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Active +Yes + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,BackButton,subst +1 + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,CancelButton,subst +1 + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Caption,subst +1 + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,CompanyLabel,subst +0 + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Conditions +{0 conditions} + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Message,subst +1 + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,NextButton,subst +1 + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Subtitle,subst +1 + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Title,subst +1 + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,UserNameLabel,subst +1 + +F9E38720-6ABA-8B99-2471-496902E4CBC2,Active +No + +F9E38720-6ABA-8B99-2471-496902E4CBC2,Conditions +{0 conditions} + +F9E38720-6ABA-8B99-2471-496902E4CBC2,ResultVirtualText +BackupLocationPath + +F9E38720-6ABA-8B99-2471-496902E4CBC2,TclScript +{set BackupLocationPath "" } + +FB697A88-2842-468E-9776-85E84B009340,Active +No + +FB697A88-2842-468E-9776-85E84B009340,Conditions +{0 conditions} + +FB697A88-2842-468E-9776-85E84B009340,IgnoreErrors +Yes + +FB697A88-2842-468E-9776-85E84B009340,ProgramCommandLine +{"<%InstallDir%>\<%ServiceExeName%> -r -S <%ServiceName%>} + +FB697A88-2842-468E-9776-85E84B009340,WorkingDirectory +<%Temp%> + +FDF68FD6-BEA8-4A74-867D-5139F4D9E793,Active +No + +FDF68FD6-BEA8-4A74-867D-5139F4D9E793,Conditions +{0 conditions} + +FDF68FD6-BEA8-4A74-867D-5139F4D9E793,WaitTime +2000 + +FEFD090D-C133-BC95-B3564F693CD3,Alias +{Finish Actions} + +FEFD090D-C133-BC95-B3564F693CD3,Conditions +{0 conditions} + +FreeBSD-4-x86,Active +No + +FreeBSD-4-x86,DefaultDirectoryPermission +0755 + +FreeBSD-4-x86,DefaultFilePermission +0755 + +FreeBSD-4-x86,Executable +<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%> + +FreeBSD-4-x86,FallBackToConsole +Yes + +FreeBSD-4-x86,InstallDir +<%Home%>/<%ShortAppName%> + +FreeBSD-4-x86,InstallMode +Standard + +FreeBSD-4-x86,InstallType +Typical + +FreeBSD-4-x86,ProgramExecutable +{} + +FreeBSD-4-x86,ProgramFolderAllUsers +No + +FreeBSD-4-x86,ProgramFolderName +<%AppName%> + +FreeBSD-4-x86,ProgramLicense +<%InstallDir%>/LICENSE.txt + +FreeBSD-4-x86,ProgramName +{} + +FreeBSD-4-x86,ProgramReadme +<%InstallDir%>/README.txt + +FreeBSD-4-x86,PromptForRoot +Yes + +FreeBSD-4-x86,RequireRoot +No + +FreeBSD-4-x86,RootInstallDir +/usr/local/<%ShortAppName%> + +FreeBSD-x86,Active +No + +FreeBSD-x86,DefaultDirectoryPermission +0755 + +FreeBSD-x86,DefaultFilePermission +0755 + +FreeBSD-x86,Executable +<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%> + +FreeBSD-x86,FallBackToConsole +Yes + +FreeBSD-x86,InstallDir +<%Home%>/<%ShortAppName%> + +FreeBSD-x86,InstallMode +Standard + +FreeBSD-x86,InstallType +Typical + +FreeBSD-x86,ProgramExecutable +{} + +FreeBSD-x86,ProgramFolderAllUsers +No + +FreeBSD-x86,ProgramFolderName +<%AppName%> + +FreeBSD-x86,ProgramLicense +<%InstallDir%>/LICENSE.txt + +FreeBSD-x86,ProgramName +{} + +FreeBSD-x86,ProgramReadme +<%InstallDir%>/README.txt + +FreeBSD-x86,PromptForRoot +Yes + +FreeBSD-x86,RequireRoot +No + +FreeBSD-x86,RootInstallDir +/usr/local/<%ShortAppName%> + +HPUX-hppa,Active +No + +HPUX-hppa,DefaultDirectoryPermission +0755 + +HPUX-hppa,DefaultFilePermission +0755 + +HPUX-hppa,Executable +<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%> + +HPUX-hppa,FallBackToConsole +Yes + +HPUX-hppa,InstallDir +<%Home%>/<%ShortAppName%> + +HPUX-hppa,InstallMode +Standard + +HPUX-hppa,InstallType +Typical + +HPUX-hppa,ProgramExecutable +{} + +HPUX-hppa,ProgramFolderAllUsers +No + +HPUX-hppa,ProgramFolderName +<%AppName%> + +HPUX-hppa,ProgramLicense +<%InstallDir%>/LICENSE.txt + +HPUX-hppa,ProgramName +{} + +HPUX-hppa,ProgramReadme +<%InstallDir%>/README.txt + +HPUX-hppa,PromptForRoot +Yes + +HPUX-hppa,RequireRoot +No + +HPUX-hppa,RootInstallDir +/usr/local/<%ShortAppName%> + +Linux-x86,Active +No + +Linux-x86,BuildType +dynamic + +Linux-x86,DefaultDirectoryPermission +00755 + +Linux-x86,DefaultFilePermission +00755 + +Linux-x86,Executable +<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%> + +Linux-x86,FallBackToConsole +Yes + +Linux-x86,InstallDir +<%Home%>/<%ShortAppName%> + +Linux-x86,InstallMode +Standard + +Linux-x86,InstallType +Typical + +Linux-x86,ProgramExecutable +<%InstallDir%>/TebucoSafe + +Linux-x86,ProgramFolderAllUsers +No + +Linux-x86,ProgramFolderName +<%AppName%> + +Linux-x86,ProgramLicense +<%InstallDir%>/LICENSE.txt + +Linux-x86,ProgramName +{} + +Linux-x86,ProgramReadme +<%InstallDir%>/README.txt + +Linux-x86,PromptForRoot +Yes + +Linux-x86,RequireRoot +No + +Linux-x86,RootInstallDir +/usr/local/<%ShortAppName%> + +Solaris-sparc,Active +No + +Solaris-sparc,DefaultDirectoryPermission +0755 + +Solaris-sparc,DefaultFilePermission +0755 + +Solaris-sparc,Executable +<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%> + +Solaris-sparc,FallBackToConsole +Yes + +Solaris-sparc,InstallDir +<%Home%>/<%ShortAppName%> + +Solaris-sparc,InstallMode +Standard + +Solaris-sparc,InstallType +Typical + +Solaris-sparc,ProgramExecutable +{} + +Solaris-sparc,ProgramFolderAllUsers +No + +Solaris-sparc,ProgramFolderName +<%AppName%> + +Solaris-sparc,ProgramLicense +<%InstallDir%>/LICENSE.txt + +Solaris-sparc,ProgramName +{} + +Solaris-sparc,ProgramReadme +<%InstallDir%>/README.txt + +Solaris-sparc,PromptForRoot +Yes + +Solaris-sparc,RequireRoot +No + +Solaris-sparc,RootInstallDir +/usr/local/<%ShortAppName%> + +TarArchive,Active +No + +TarArchive,CompressionLevel +6 + +TarArchive,DefaultDirectoryPermission +0755 + +TarArchive,DefaultFilePermission +0755 + +TarArchive,Executable +<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%> + +TarArchive,FallBackToConsole +Yes + +TarArchive,InstallDir +<%Home%>/<%ShortAppName%> + +TarArchive,InstallMode +Standard + +TarArchive,InstallType +Typical + +TarArchive,OutputFileName +<%ShortAppName%>-<%Version%>.tar.gz + +TarArchive,ProgramExecutable +{} + +TarArchive,ProgramFolderAllUsers +No + +TarArchive,ProgramFolderName +<%AppName%> + +TarArchive,ProgramLicense +<%InstallDir%>/LICENSE.txt + +TarArchive,ProgramName +{} + +TarArchive,ProgramReadme +<%InstallDir%>/README.txt + +TarArchive,PromptForRoot +Yes + +TarArchive,RequireRoot +No + +TarArchive,RootInstallDir +/usr/local/<%ShortAppName%> + +TarArchive,VirtualTextMap +{<%InstallDir%> <%ShortAppName%>} + +Windows,Active +Yes + +Windows,BuildType +{} + +Windows,Executable +installer.exe + +Windows,IncludeTWAPI +No + +Windows,InstallDir +{C:\Program Files\<%BrandName%>} + +Windows,InstallMode +Standard + +Windows,InstallType +Typical + +Windows,ProgramExecutable +{} + +Windows,ProgramFolderAllUsers +No + +Windows,ProgramFolderName +<%BrandName%> + +Windows,ProgramLicense +{<%InstallDir%>\LICENSE.txt} + +Windows,ProgramName +{} + +Windows,ProgramReadme +{} + +Windows,WindowsIcon +{} + +ZipArchive,Active +No + +ZipArchive,CompressionLevel +6 + +ZipArchive,DefaultDirectoryPermission +0755 + +ZipArchive,DefaultFilePermission +0755 + +ZipArchive,Executable +<%AppName%>-<%Version%>-<%Platform%>-Install<%Ext%> + +ZipArchive,FallBackToConsole +Yes + +ZipArchive,InstallDir +<%Home%>/<%ShortAppName%> + +ZipArchive,InstallMode +Standard + +ZipArchive,InstallType +Typical + +ZipArchive,OutputFileName +<%ShortAppName%>-<%Version%>.zip + +ZipArchive,ProgramExecutable +{} + +ZipArchive,ProgramFolderAllUsers +No + +ZipArchive,ProgramFolderName +<%AppName%> + +ZipArchive,ProgramLicense +<%InstallDir%>/LICENSE.txt + +ZipArchive,ProgramName +{} + +ZipArchive,ProgramReadme +<%InstallDir%>/README.txt + +ZipArchive,PromptForRoot +Yes + +ZipArchive,RequireRoot +No + +ZipArchive,RootInstallDir +/usr/local/<%ShortAppName%> + +ZipArchive,VirtualTextMap +{<%InstallDir%> <%ShortAppName%>} + +} + +::msgcat::mcmset de { +20CBDBEA-2217-457B-8D98-D692C4F591E9,Message +<%UninstallCompleteText%> + +2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6,Message +<%InstallationCompleteText%> + +B002A311-F8E7-41DE-B039-521391924E5B,Message +<%InstallingApplicationText%> + +B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174,Message +<%UninstallingApplicationText%> + +} +::msgcat::mcmset en { +16D53E40-546B-54C3-088B1B5E3BBB,Text +<%CreateDesktopShortcutText%> + +20CBDBEA-2217-457B-8D98-D692C4F591E9,Message +<%UninstallCompleteText%> + +2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6,Message +<%InstallationCompleteText%> + +2E2963BD-DDBD-738D-A910-B7F3F04946F9,Text +{No:<%BackupLocationNumber%> } + +2EC82FBD-8294-A3E4-7F39-1CBA0582FA64,TextToWrite +BackupLocations\n\{\n + +32F5B0AF-EB83-7A03-D8FAE1ECE473,Message +<%InstallStartupText%> + +32F5B0AF-EB83-7A03-D8FAE1ECE473,Title +<%InstallApplicationText%> + +36FF8915-8148-0F1F-27D7239CBFA1,Text +<%ViewReadmeText%> + +3B6E2E7C-1A26-27F1-D578E383B128,Text +<%ViewReadmeText%> + +3D33AA8C-0037-204B-39A339FD38BD,Message +{<%BrandName%> has been removed from your system. Thank you for using the <%BrandName%> Backup Service. If you need further assistance, please contact us at http://<%BrandName%>.com or support@<%BrandName%>.com.} + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Caption +{} + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Message +{} + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Subtitle +{Add a directory to backup, nickname it, and add some exclusions.} + +3FD9BFF3-2F6E-E4FC-2FAE-98F2017916A7,Title +{Backup Locations} + +3FDB57ED-598D-8A4E-CEF7-D90833305558,Text +{Backup Directory} + +4A9C852B-647E-EED5-5482FFBCC2AF,Description +<%ProgramFilesDescription%> + +4ACB0B47-42B3-2B3A-BFE9AA4EC707,Message +<%UninstallStartupText%> + +4ACB0B47-42B3-2B3A-BFE9AA4EC707,Title +<%UninstallApplicationText%> + +58E1119F-639E-17C9-5D3898F385AA,Caption +{Setup will install <%ShortAppName%> in the following folder. + +To install to this folder, click Next. To install to a different folder, click Browse and select another folder.} + +58E1119F-639E-17C9-5D3898F385AA,Subtitle +{Where should <%ShortAppName%> be installed?} + +59395A3B-6116-F93A-84E1-5E079C2CD44B,Caption +{Backup Exclusions} + +59395A3B-6116-F93A-84E1-5E079C2CD44B,Message +{} + +59395A3B-6116-F93A-84E1-5E079C2CD44B,Subtitle +{Enter your backup exclusions for this Backup Location <%BackupLocationName_ShortName%> here.} + +59395A3B-6116-F93A-84E1-5E079C2CD44B,Text +{} + +59395A3B-6116-F93A-84E1-5E079C2CD44B,Title +{Backup Exclusions} + +640DA2B2-6CF3-0873-D7AE-ABCDDE39EFCF,Text +{Put your custom text here. +<%AddBackupLocation%> +<%BackupLocationNumber%> +<%BackupLocationName%>} + +6C323815-B9AB-FA94-4F5D152EBC51,Caption +{Installation Wizard Complete} + +6C323815-B9AB-FA94-4F5D152EBC51,Message +{The Installation Wizard has successfully installed the <%BrandName%> Backup Service. Click Finish to exit the wizard.} + +6CFBBE13-6B70-4B7C-B5EF-0677752D95A8,Caption +{Installing encryption keys...} + +6CFBBE13-6B70-4B7C-B5EF-0677752D95A8,Message +{Click next to install encrypted keys and configuration file (bbackupd.conf)... + +You will be presented with the current configuration file so that you may make any last minute changes...} + +6FEE2889-0338-1D49-60BF-1471F465AB26,TextToWrite +\}\n + +8202CECC-54A0-9B6C-D24D111BA52E,Description +<%TypicalInstallDescription%> + +8419AAAD-5860-F73E-8D11-4D1BDA4D7D37,Text +{Add another backup location?} + +855DE408-060E-3D35-08B5-1D9AB05C2865,Text +Exclusions + +8E095096-F018-A880-429D-A2177A9B70EA,Text +{AddAnother: <%AddBackupLocation%>} + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,Caption +{Please enter your phone number and email address.} + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,CompanyLabel +Email: + +9013E862-8E81-5290-64F9-D8BCD13EC7E5,UserNameLabel +Phone: + +937C3FDD-FB28-98BD-3DAB276E59ED,Text +<%CreateQuickLaunchShortcutText%> + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Caption +{Click Next to continue...} + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Message +{Building configuration file...} + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Subtitle +{} + +9A23D3ED-4D9D-9C57-C2A7-71DE0FFF0266,Title +Continue + +9BAB328D-414B-D351-CA8D-824DF94B9DCA,Text +{Add another backup location after this one?} + +A18C2977-1409-C1FB-892415711F72,Text +<%LaunchApplicationText%> + +AAF2142A-9FC9-4664-DFF2-13B9EB7BA0E1,CompanyLabel +Company: + +AE3BD5B4-35DE-4240-B79914D43E56,Caption +{Welcome to the Installation Wizard for <%BrandName%> Backup Service!} + +AE3BD5B4-35DE-4240-B79914D43E56,Message +{Thank you for installing the <%BrandName%>(SM) Backup Service. + +If you need any assistance, please contact us at http://<%BrandName%>.com or support@<%BrandName%>.com. + +This will install <%BrandName%> version <%Version%> on your computer. + +It is recommended that you close all other applications before continuing. + +Click Next to continue or Cancel to exit Setup. +} + +B002A311-F8E7-41DE-B039-521391924E5B,Message +<%InstallingApplicationText%> + +B4404713-AF4F-4F4B-670F3115517F,Description +<%CustomInstallDescription%> + +B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174,Message +<%UninstallingApplicationText%> + +B506E7DA-E7C4-4D42-8C03-FD27BA16D078,Text +{Box Backup, http://www.fluffy.co.uk/boxbackup +Copyright (c) 2003-2007 Ben Summers and contributors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. All use of this software and associated advertising materials must display the following acknowledgement: + This product includes software developed by Ben Summers and contributors. + +4. The names of the Authors may not be used to endorse or promote products derived from this software without specific prior written permission. + +[Where legally impermissible the Authors do not disclaim liability for direct physical injury or death caused solely by defects in the software unless it is modified by a third party.] + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE} + +B57F8C91-2439-CFD3-7EB5-57D4EA48D3C6,Caption +{<%BrandName%> will backup the following folder. + +To backup this folder, click Next. To backup a different folder, click Browse and select another folder.} + +B57F8C91-2439-CFD3-7EB5-57D4EA48D3C6,DestinationLabel +{Backup This Folder} + +B57F8C91-2439-CFD3-7EB5-57D4EA48D3C6,Message +{} + +B57F8C91-2439-CFD3-7EB5-57D4EA48D3C6,Subtitle +{What directory should <%BrandName%> backup?} + +B57F8C91-2439-CFD3-7EB5-57D4EA48D3C6,Title +{Choose Backup Location} + +B927A5AF-4DFE-82A3-DCA8-35FA4D91EC5A,Text +{Short Name} + +B9B85EF1-1D76-4BF5-ABB9-092A8DB35851,Caption +{Please enter the agreed-upon password for your encrypted key file...} + +B9B85EF1-1D76-4BF5-ABB9-092A8DB35851,CompanyLabel +Password: + +B9B85EF1-1D76-4BF5-ABB9-092A8DB35851,Subtitle +{Please enter your encrypted key file password.} + +C105AAAE-7C16-2C9E-769FE4535B60,Caption +<%ApplicationReadmeText%> + +C105AAAE-7C16-2C9E-769FE4535B60,Message +{} + +C105AAAE-7C16-2C9E-769FE4535B60,Title +<%ApplicationReadmeText%> + +C7762473-273F-E3CA-17E3-65789B14CDB0,TextToWrite +{<%BackupLocationShortName%> +{ +Path = <%BackupLocationPath%> +<%BackupLocationExclusions%> +} +} + +CB058DBA-C3B7-2F48-D985-BE2F7107A76D,BrowseText +{To continue, click Next. If you would like to select a folder to backup, click Browse.} + +CB058DBA-C3B7-2F48-D985-BE2F7107A76D,Caption +{} + +CB058DBA-C3B7-2F48-D985-BE2F7107A76D,DestinationLabel +{Backup This Folder} + +CB058DBA-C3B7-2F48-D985-BE2F7107A76D,Subtitle +{What directories should <%BrandName%> backup?} + +CB058DBA-C3B7-2F48-D985-BE2F7107A76D,Title +{Choose Backup Location} + +CFFA27AF-A641-E41C-B4A0E3BB3CBB,Text +<%LaunchApplicationText%> + +D4625CA6-9864-D8EF-F252D7B7DC87,Text +<%CreateDesktopShortcutText%> + +D47BE952-79F2-844E-D2E5-8F22044E7A9D,Text +{Account Number:} + +DA33B826-E633-A845-4646-76DFA78B907B,Caption +{Click Next to continue...} + +DA33B826-E633-A845-4646-76DFA78B907B,Message +{Completing configuration file...} + +DA33B826-E633-A845-4646-76DFA78B907B,Subtitle +{} + +DA33B826-E633-A845-4646-76DFA78B907B,Title +Continue + +DDBBD8A9-13D7-9509-9202-419E989F60A9,Text +{Add another Backup Location?} + +E0CADC4E-08A6-E429-3B49-BB8CFB7B097F,Text +{Simple name for this Backup Location (short, no spaces or special characters)} + +E161F216-E597-B340-C1A71C476E2C,Message +<%UninstallLeftoverText%> + +E161F216-E597-B340-C1A71C476E2C,Title +{Uninstall <%BrandName%>} + +EA2C57E8-CCFB-CF3D-92CA-83369EFF1B08,Text +{Add (another) Backup Location after this one?} + +EDE364D6-22C7-5108-D398-26FC24E0A55A,Text +{Enter your Backup Exclusions for this Backup Location...} + +F59AF47D-4136-64F8-82C7-4506BD4327FD,Text +{Add another Backup Location after this one?} + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,Caption +{Please enter your Account Number.} + +F8FD4BD6-F1DF-3F8D-B857-98310E4B1143,UserNameLabel +{Account Number (like 10005004):} + +F98784B1-1965-0F42-6BB0542AE1A9,Caption +{Installing and starting the TebucoSafe Backup Service...} + +F98784B1-1965-0F42-6BB0542AE1A9,Message +{Click Next to install the TebucoSafe Backup Service as an operating system service on your computer (see services.msc), and start up that service. } + +FC678E76-6823-2E55-204CA01C35EF,Text +<%CreateQuickLaunchShortcutText%> + +FF4F6EEA-F4CC-428E-AF33-EB0E88E2147E,Text +{ +Copyright (c) 2003 - 2006 + Ben Summers and contributors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All use of this software and associated advertising materials must + display the following acknowledgment: + This product includes software developed by Ben Summers. +4. The names of the Authors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +Where legally impermissible the Authors do not disclaim liability for +direct physical injury or death caused solely by defects in the software +unless it is modified by a third party.] + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + + } + +} +::msgcat::mcmset es { +20CBDBEA-2217-457B-8D98-D692C4F591E9,Message +<%UninstallCompleteText%> + +2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6,Message +<%InstallationCompleteText%> + +B002A311-F8E7-41DE-B039-521391924E5B,Message +<%InstallingApplicationText%> + +B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174,Message +<%UninstallingApplicationText%> + +} +::msgcat::mcmset fr { +20CBDBEA-2217-457B-8D98-D692C4F591E9,Message +<%UninstallCompleteText%> + +2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6,Message +<%InstallationCompleteText%> + +B002A311-F8E7-41DE-B039-521391924E5B,Message +<%InstallingApplicationText%> + +B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174,Message +<%UninstallingApplicationText%> + +} +::msgcat::mcmset pl { +20CBDBEA-2217-457B-8D98-D692C4F591E9,Message +<%UninstallCompleteText%> + +2BF07B5A-9B06-4C1E-810D-5B5E9303D2C6,Message +<%InstallationCompleteText%> + +B002A311-F8E7-41DE-B039-521391924E5B,Message +<%InstallingApplicationText%> + +B4ED4636-22D8-41DC-9E3D-BD1E1CAD2174,Message +<%UninstallingApplicationText%> + +} +::msgcat::mcmset pt_br { +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%> + +} + diff --git a/contrib/windows/installer/tools/InstallService.bat b/contrib/windows/installer/tools/InstallService.bat new file mode 100755 index 00000000..80a342eb --- /dev/null +++ b/contrib/windows/installer/tools/InstallService.bat @@ -0,0 +1,3 @@ +service.exe -i -S GigaLock +echo off +ping 192.168.254.254 -n 5 -w 1000 > nul diff --git a/contrib/windows/installer/tools/KillBackupProcess.bat b/contrib/windows/installer/tools/KillBackupProcess.bat new file mode 100755 index 00000000..416d4a79 --- /dev/null +++ b/contrib/windows/installer/tools/KillBackupProcess.bat @@ -0,0 +1,3 @@ +control.exe terminate +echo off +ping 192.168.254.254 -n 5 -w 1000 > nul diff --git a/contrib/windows/installer/tools/QueryOutputAll.bat b/contrib/windows/installer/tools/QueryOutputAll.bat new file mode 100755 index 00000000..2ab30a72 --- /dev/null +++ b/contrib/windows/installer/tools/QueryOutputAll.bat @@ -0,0 +1,5 @@ +@ECHO OFF +: o=old, d=deleted, s=size info, t=timestamp, r=recursive +set Queryopts=-odstr +::set Queryopts=-str +query.exe "list %Queryopts%" quit > QueryOutputAllResults.txt diff --git a/contrib/windows/installer/tools/QueryOutputCurrent.bat b/contrib/windows/installer/tools/QueryOutputCurrent.bat new file mode 100755 index 00000000..d59ddbf1 --- /dev/null +++ b/contrib/windows/installer/tools/QueryOutputCurrent.bat @@ -0,0 +1,5 @@ +@ECHO OFF +: o=old, d=deleted, s=size info, t=timestamp, r=recursive +::set Queryopts=-odstr +set Queryopts=-str +query.exe "list %Queryopts%" quit > QueryOutputCurrentResults.txt diff --git a/contrib/windows/installer/tools/ReloadConfig.bat b/contrib/windows/installer/tools/ReloadConfig.bat new file mode 100755 index 00000000..5fd44e83 --- /dev/null +++ b/contrib/windows/installer/tools/ReloadConfig.bat @@ -0,0 +1,3 @@ +control.exe reload +echo off +ping 192.168.254.254 -n 8 -w 1000 > nul diff --git a/contrib/windows/installer/tools/RemoteControl.exe b/contrib/windows/installer/tools/RemoteControl.exe new file mode 100755 index 00000000..f7667421 Binary files /dev/null and b/contrib/windows/installer/tools/RemoteControl.exe differ diff --git a/contrib/windows/installer/tools/RemoveService.bat b/contrib/windows/installer/tools/RemoveService.bat new file mode 100755 index 00000000..881ec5b1 --- /dev/null +++ b/contrib/windows/installer/tools/RemoveService.bat @@ -0,0 +1,3 @@ +@@SERVICEEXENAME@ -r -S GigaLock +echo off +ping 192.168.254.254 -n 5 -w 1000 > nul diff --git a/contrib/windows/installer/tools/RestartService.bat b/contrib/windows/installer/tools/RestartService.bat new file mode 100755 index 00000000..77092a69 --- /dev/null +++ b/contrib/windows/installer/tools/RestartService.bat @@ -0,0 +1,5 @@ +net stop GigaLock +ping 192.168.254.254 -n 2 -w 1000 > nul +net start GigaLock +echo off +ping 192.168.254.254 -n 5 -w 1000 > nul diff --git a/contrib/windows/installer/tools/ShowUsage.bat b/contrib/windows/installer/tools/ShowUsage.bat new file mode 100755 index 00000000..e6f69e9f --- /dev/null +++ b/contrib/windows/installer/tools/ShowUsage.bat @@ -0,0 +1,3 @@ +query.exe usage quit +ping 192.168.254.254 -n 10 -w 1000 > nul + diff --git a/contrib/windows/installer/tools/StartService.bat b/contrib/windows/installer/tools/StartService.bat new file mode 100755 index 00000000..1771238f --- /dev/null +++ b/contrib/windows/installer/tools/StartService.bat @@ -0,0 +1,3 @@ +net start GigaLock +echo off +ping 192.168.254.254 -n 5 -w 1000 > nul diff --git a/contrib/windows/installer/tools/StopService.bat b/contrib/windows/installer/tools/StopService.bat new file mode 100755 index 00000000..8e70d68d --- /dev/null +++ b/contrib/windows/installer/tools/StopService.bat @@ -0,0 +1,3 @@ +net stop GigaLock +echo off +ping 192.168.254.254 -n 5 -w 1000 > nul diff --git a/contrib/windows/installer/tools/Sync.bat b/contrib/windows/installer/tools/Sync.bat new file mode 100755 index 00000000..30a04bec --- /dev/null +++ b/contrib/windows/installer/tools/Sync.bat @@ -0,0 +1,3 @@ +control.exe sync +echo off +ping 192.168.254.254 -n 5 -w 1000 > nul diff --git a/distribution/COMMON-MANIFEST.txt b/distribution/COMMON-MANIFEST.txt new file mode 100644 index 00000000..6753c886 --- /dev/null +++ b/distribution/COMMON-MANIFEST.txt @@ -0,0 +1,47 @@ +LICENSE-DUAL.txt + +LICENSE DUAL + +RUN ./bootstrap +RUN cd docs; make + +lib/common +lib/crypto +lib/server +lib/compress +lib/win32 +test/common +test/common/testfiles +test/basicserver +test/basicserver/testfiles +test/crypto +test/compress +test/win32 +docs/api-notes +docs/api-notes/common +docs/api-notes/common/lib_common +docs/api-notes/common/lib_common.txt +docs/api-notes/common/lib_compress +docs/api-notes/common/lib_crypto +docs/api-notes/common/lib_server +MKDIR infrastructure +infrastructure/buildenv-testmain-template.cpp +infrastructure/makebuildenv.pl.in +infrastructure/makedistribution.pl.in +infrastructure/makeparcels.pl.in +infrastructure/parcelpath.pl +infrastructure/printversion.pl +infrastructure/BoxPlatform.pm.in +infrastructure/mingw +infrastructure/msvc +bootstrap +configure.ac +configure +parcels.txt +runtest.pl.in +COPYING.txt + +LICENSE none +config.sub +config.guess +infrastructure/m4 diff --git a/distribution/boxbackup/CONTACT.txt b/distribution/boxbackup/CONTACT.txt new file mode 100644 index 00000000..9f12e435 --- /dev/null +++ b/distribution/boxbackup/CONTACT.txt @@ -0,0 +1,6 @@ + +http://www.boxbackup.org/ + +Ben Summers & contributors +boxbackup@boxbackup.org + diff --git a/distribution/boxbackup/DISTRIBUTION-MANIFEST.txt b/distribution/boxbackup/DISTRIBUTION-MANIFEST.txt new file mode 100644 index 00000000..3677d376 --- /dev/null +++ b/distribution/boxbackup/DISTRIBUTION-MANIFEST.txt @@ -0,0 +1,95 @@ +LICENSE-GPL.txt + +LICENSE DUAL +lib/intercept +lib/raidfile +lib/httpserver +test/raidfile +test/raidfile/testfiles +test/httpserver + +LICENSE GPL +lib/backupclient +lib/backupstore +bin/bbstored +bin/bbstoreaccounts +bin/bbackupd +bin/bbackupd/win32 +bin/bbackupquery +bin/bbackupctl +bin/bbackupobjdump +bin/s3simulator +test/backupstore +test/backupstore/testfiles +test/backupstorefix +test/backupstorefix/testfiles +test/backupstorepatch +test/bbackupd +test/bbackupd/testfiles +test/backupdiff +test/httpserver/testfiles +test/httpserver/testfiles/photos +docs/Makefile +docs/tools + +LICENSE DUAL +docs/api-notes +docs/api-notes/raidfile +docs/api-notes/raidfile/lib_raidfile.txt + +LICENSE GPL +docs/api-notes/backup + +docs/images +docs/htmlguide +docs/htmlguide/adminguide +docs/htmlguide/images +docs/htmlguide/instguide +docs/htmlguide/man-html +docs/man +MKDIR docs/docbook +docs/docbook/ExceptionCodes.xml +docs/docbook/adminguide.xml +docs/docbook/bb-book.xsl +docs/docbook/bb-man.xsl +docs/docbook/bb-nochunk-book.xsl +docs/docbook/bbackupctl.xml +docs/docbook/bbackupd-config.xml +docs/docbook/bbackupd.conf.xml +docs/docbook/bbackupd.xml +docs/docbook/bbackupquery.xml +docs/docbook/bbstoreaccounts.xml +docs/docbook/bbstored-certs.xml +docs/docbook/bbstored-config.xml +docs/docbook/bbstored.conf.xml +docs/docbook/bbstored.xml +docs/docbook/instguide.xml +docs/docbook/raidfile-config.xml +docs/docbook/raidfile.conf.xml +docs/docbook/html +docs/docbook/html/images +docs/xsl-generic +docs/xsl-generic/manpages +docs/xsl-generic/lib +docs/xsl-generic/common +docs/xsl-generic/html +docs/xsl-generic/highlighting +BUGS.txt +contrib + +contrib/bbadmin +contrib/bbreporter +contrib/debian +contrib/mac_osx +contrib/redhat +contrib/rpm +REPLACE-VERSION-IN contrib/rpm/boxbackup.spec +contrib/solaris +contrib/suse +contrib/windows +contrib/windows/installer +contrib/windows/installer/tools + +infrastructure/msvc +infrastructure/msvc/2003 +infrastructure/msvc/2005 diff --git a/distribution/boxbackup/DOCUMENTATION.txt b/distribution/boxbackup/DOCUMENTATION.txt new file mode 100644 index 00000000..f45f61d5 --- /dev/null +++ b/distribution/boxbackup/DOCUMENTATION.txt @@ -0,0 +1,6 @@ + +For compilation and installation instructions, see the web site at + + http://www.boxbackup.org/ + + diff --git a/distribution/boxbackup/LINUX.txt b/distribution/boxbackup/LINUX.txt new file mode 100644 index 00000000..d7481811 --- /dev/null +++ b/distribution/boxbackup/LINUX.txt @@ -0,0 +1,29 @@ + +For instructions on building an RPM of Box Backup, see the contrib/rpm +directory. This is primarily for RedHat style systems, but notes are provided +on what needs to be modified for SUSE. + + +Requirements: + + OpenSSL 0.9.7 + +Require zlib and openssl headers for compilation -- may not be included when +installing the packages. (libssl-dev + libz-dev packages under debian) + +Bekerley DB v1 or v4 support is required. The configure script should find an +appropriate db package -- and if not, use an in-memory version of the code. +However, the in-memory version is not desirable as it will lose information +over restarts of the daemon. + +Ideally, use libeditline as a readline replacement. If not available then use +GNU readline (libreadline4-dev, probably) and pass --enable-gnu-readline to +./configure. + + + +(OpenSSL 0.9.7 is required as it implements new interfaces which make encryption +using the same key faster by avoiding key setup each time around. Configure it with +./config shared , then copy the libs and headers into the required places.) + + diff --git a/distribution/boxbackup/NETBSD.txt b/distribution/boxbackup/NETBSD.txt new file mode 100644 index 00000000..03000791 --- /dev/null +++ b/distribution/boxbackup/NETBSD.txt @@ -0,0 +1,8 @@ + +Install perl + +Install OpenSSL 0.9.7 or later. + +Not a completely working port -- symlinks don't get backed up or restored properly. +(run test/bbackupd) + diff --git a/distribution/boxbackup/THANKS.txt b/distribution/boxbackup/THANKS.txt new file mode 100644 index 00000000..09ad60e2 --- /dev/null +++ b/distribution/boxbackup/THANKS.txt @@ -0,0 +1,69 @@ + +The following developers contributed code to version 0.10: + +Nick Knight + - ported Box Backup to Windows (properly, not using Cygwin) + +Gary Niemcewicz + - added client/server (SSL) keepalives to keep the connection to the + server alive during a long diff, and saving the daemon's state across restarts + +Martin Ebourne + - ported to Solaris; wrote extended attribute support (xattr); + converted to use autoconf for automatic compiler configuration. + +Chris Wilson + - updated Nick's and Gary's work to fit in with the new trunk, + fixed some issues pointed out by Ben and Martin, made it compile + on Windows with the free MinGW compiler. + +Jonathan Morton + - vastly improved the performance and efficiency of the file-diffing code, and + obtained a free G5 PowerMac from IBM as his reward. + + +---- + +Many individuals have helped with the development of Box Backup by testing, reporting experiences, and making suggestions. In particular, thanks are due to + +Charles Lecklider + - Helped with the finer details of Win32 programming + +Pascal Lalonde + - Comprehensive and accurate bug reports, and constructive feedback + +Paul Arch + - Cygwin client port + +Ben Lovett + - Help with odd architectures, suggesting small changes + +Martin Ebourne + - RPM specification for RedHat based Linux distributions + - Patch to fix problems on 64 bit architectures + - Patch to fix compilation after RedHat Fedora's latest changes + +Per Thomsen + - Cygwin Windows service install scripts and build notes + - Answering queries on the boxbackup mailing list + +Tim Fletcher +David Harris +Richard Eigenmann + - Testing many attempts at clean compiles on various Linux distributions + +Eduardo Alvarenga + - Valuable feedback and persuasion to include new features + +Joe Gillespie + - Web site design + +JŽr™me Schell + - Fixes to build+config problems on Linux + +John Pybus + - Ideas and feature requests + - Useful little patches to code + +Stefan Norlin + - Help with testing and fixes on lots of different Solaris platforms diff --git a/distribution/boxbackup/VERSION.txt b/distribution/boxbackup/VERSION.txt new file mode 100644 index 00000000..129b41fa --- /dev/null +++ b/distribution/boxbackup/VERSION.txt @@ -0,0 +1,2 @@ +0.11.1 +boxbackup diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..c4a63671 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,183 @@ +# Process DocBook to HTML + +# This makefile is a bit obfuscated so that it works correctly on both +# BSD and GNU make. Some parts apply to one version of make and not the +# other; these are marked by comments. + +# The "all" target shouldn't be up here, but the trickery below defines +# what looks like a rule to GNU make, and so we need to define the actual +# default target before it. + +all: docs + +DBPROC_COMMAND = xsltproc +MKDIR_COMMAND = mkdir +CP_COMMAND = cp +PERL_COMMAND = perl +RM_COMMAND = rm -f +TAR_COMMAND = tar +GZIP_COMMAND = gzip -f +GENERATE_SCRIPT = tools/generate_except_xml.pl + +DBPROC = $(DBPROC_COMMAND) +MKDIR = $(MKDIR_COMMAND) +CP = $(CP_COMMAND) +GENERATE = $(PERL_COMMAND) $(GENERATE_SCRIPT) +RM_QUIET = $(RM_COMMAND) +TAR = $(TAR_COMMAND) +GZIP = $(GZIP_COMMAND) +PROGRESS = @ true + +# use a GNU make "define" command, that looks like a harmless dummy rule +# to BSD make, to hide parts of the Makefile from GNU make. +define IGNORED_BY_GNU_MAKE: +.if 0 +endef + + # seen by GNU make, not by BSD make + ifeq ($(V),) + DBPROC = @ echo " [XLSTPROC]" $^ && $(DBPROC_COMMAND) 2>/dev/null + GENERATE = @ echo " [GENERATE]" $@ && $(PERL_COMMAND) $(GENERATE_SCRIPT) + TAR = @ echo " [TAR] " $@ && $(TAR_COMMAND) + GZIP = @ echo " [GZIP] " $< && $(GZIP_COMMAND) + RM_QUIET = @ $(RM_COMMAND) + PROGRESS = @ echo + endif + +define IGNORED_BY_GNU_MAKE: +.endif + +.ifndef V + # seen by BSD make, not by GNU make + DBPROC = @ echo " [XSLTPROC]" $(.ALLSRC) && $(DBPROC_COMMAND) 2>/dev/null + GENERATE = @ echo " [GENERATE]" $(.TARGET) && $(PERL_COMMAND) $(GENERATE_SCRIPT) + TAR = @ echo " [TAR] " $(.TARGET) && $(TAR_COMMAND) + GZIP = @ echo " [GZIP] " $(.TARGET:.gz=) && $(GZIP_COMMAND) + RM_QUIET = @ $(RM_COMMAND) + PROGRESS = @ echo +.endif + +# neither .endif nor endef can be followed by a colon; each creates +# warnings or errors in one or other version of make. we need some +# magic to make them both work. Luckily, .endfor ignores the colon. + +.for DUMMY in $(NO_SUCH_VARIABLE) +endef +.endfor : + +PROGRESS_RM = $(PROGRESS) " [RM] " + +DOCBOOK_DIR = docbook +HTML_DIR = htmlguide +MAN_DIR = man + +BOOKXSL = $(DOCBOOK_DIR)/bb-book.xsl +NOCHUNKBOOKXSL = $(DOCBOOK_DIR)/bb-nochunk-book.xsl +MANXSL = $(DOCBOOK_DIR)/bb-man.xsl + +VPATH = $(DOCBOOK_DIR) +.SUFFIXES: .html .xml .gz .1 .5 .8 + +docs: instguide adminguide manpages + @mkdir -p $(HTML_DIR)/images + @cp $(DOCBOOK_DIR)/html/images/*.png $(HTML_DIR)/images/. + @cp $(DOCBOOK_DIR)/html/*.css $(HTML_DIR)/. + @cp $(DOCBOOK_DIR)/html/*.ico $(HTML_DIR)/. + +adminguide: $(DOCBOOK_DIR)/ExceptionCodes.xml $(HTML_DIR)/adminguide/index.html + +# $^ gives all sources on GNU make, and nothing on BSD make +# $> gives all sources on BSD make, and nothing on GNU make +$(HTML_DIR)/adminguide/index.html: $(BOOKXSL) $(DOCBOOK_DIR)/adminguide.xml + $(DBPROC) -o $(HTML_DIR)/adminguide/ $^ $> + +instguide: $(HTML_DIR)/instguide/index.html + +$(HTML_DIR)/instguide/index.html: $(BOOKXSL) $(DOCBOOK_DIR)/instguide.xml + $(DBPROC) -o $(HTML_DIR)/instguide/ $^ $> + +# On BSD make, $> contains all sources and $^ is empty +# On GNU make, $^ contains all sources and $> is empty +$(DOCBOOK_DIR)/ExceptionCodes.xml: ../ExceptionCodes.txt + $(GENERATE) $> $^ $@ + +manpages: man-dirs man-nroff man-html + +man-dirs: man/.there $(HTML_DIR)/man-html/.there + +$(HTML_DIR)/man-html/.there: + mkdir -p $(HTML_DIR)/man-html + touch $(HTML_DIR)/man-html/.there + +man/.there: + mkdir -p man + touch man/.there + +NROFF_PAGES = bbackupd.8 bbackupd-config.8 bbackupctl.8 bbackupquery.8 \ + bbstored.8 bbstored-config.8 bbstoreaccounts.8 bbstored-certs.8 \ + raidfile-config.8 \ + bbackupd.conf.5 bbstored.conf.5 raidfile.conf.5 + +NROFF_FILES = $(NROFF_PAGES:%=$(MAN_DIR)/%.gz) + +man-nroff: $(NROFF_FILES) + +HTML_FILES_1 = $(NROFF_PAGES:%.5=%.html) +HTML_FILES_2 = $(HTML_FILES_1:%.8=%.html) +HTML_FILES = $(HTML_FILES_2:%=$(HTML_DIR)/man-html/%) + +man-html: $(HTML_FILES) + +# $^ gives all sources on GNU make, and nothing on BSD make + +# GNU make +$(HTML_DIR)/man-html/%.html: $(NOCHUNKBOOKXSL) $(DOCBOOK_DIR)/%.xml + $(DBPROC) -o $@ $^ + +# GNU make +$(MAN_DIR)/%.8: $(MANXSL) $(DOCBOOK_DIR)/%.xml + $(DBPROC) -o $@ $^ + +# GNU make +$(MAN_DIR)/%.8.gz: $(MAN_DIR)/%.8 + $(GZIP) $< + +# GNU make +$(MAN_DIR)/%.5: $(MANXSL) $(DOCBOOK_DIR)/%.xml $(MANXSL) + $(DBPROC) -o $@ $^ + +# GNU make +$(MAN_DIR)/%.5.gz: $(MAN_DIR)/%.5 + $(GZIP) $< + +# BSD make: the final colon (:) is required to make the .for and .endfor +# lines valid in GNU make. It creates (different) dummy rules in GNU and +# BSD make. Both dummy rules are harmless. + +.for MAN_PAGE in $(NROFF_PAGES) : +$(MAN_DIR)/$(MAN_PAGE).gz: $(MANXSL) $(DOCBOOK_DIR)/$(MAN_PAGE:R).xml + $(DBPROC) -o $(.TARGET:.gz=) $(.ALLSRC) + $(GZIP) $(.TARGET:.gz=) + +$(HTML_DIR)/man-html/$(MAN_PAGE:R).html: $(NOCHUNKBOOKXSL) \ +$(DOCBOOK_DIR)/$(MAN_PAGE:R).xml + $(DBPROC) -o $(.TARGET) $(.ALLSRC) +.endfor : + +dockit: clean docs documentation-kit-0.10.tar.gz + +documentation-kit-0.10.tar.gz: + $(TAR) zcf documentation-kit-0.10.tar.gz $(HTML_DIR)/ + +clean: + $(PROGRESS_RM) "$(HTML_DIR)/man-html/*.html" + $(RM_QUIET) $(HTML_FILES) + + $(PROGRESS_RM) "$(MAN_DIR)/*.[58].gz" + $(RM_QUIET) $(NROFF_FILES) + + $(PROGRESS_RM) "$(DOCBOOK_DIR)/ExceptionCodes.xml" + $(RM_QUIET) $(DOCBOOK_DIR)/ExceptionCodes.xml + + $(PROGRESS_RM) "documentation-kit-0.10.tar.gz" + $(RM_QUIET) documentation-kit-0.10.tar.gz diff --git a/docs/api-notes/INDEX.txt b/docs/api-notes/INDEX.txt new file mode 100644 index 00000000..f333ac3b --- /dev/null +++ b/docs/api-notes/INDEX.txt @@ -0,0 +1,61 @@ +TITLE Programmers Notes for Box Backup + +This directory contains the programmers notes for the Box Backup system. They will be of interest only if you want to review or modify the code. + +These notes are intended to be run through a script to produce HTML at some later stage, hence the marks such as 'TITLE'. + + +SUBTITLE Organisation + +The project is split up into several modules. The modules within 'lib' are building blocks for creation of the actual executable programs within 'bin'. 'test' contains unit tests for lib and bin modules. + +The file modules.txt lists the modules which are to be built, and their dependencies. It also allows for platform differences. + + +SUBTITLE Documentation Organisation + +In this directory, the files correspond to modules or areas of interest. Sub directories of the same name contain files documenting specific classes within that module. + + +SUBTITLE Suggested reading order + +* common/lib_common.txt +* common/lib_server.txt +* bin_bbackupd.txt +* backup_encryption.txt +* bin_bbstored.txt +* raidfile/lib_raidfile.txt + +and refer to other sections as required. + + +SUBTITLE Building + +The makefiles are generated by makebuildenv.pl. (The top level makefile is generated by makeparcels.pl, but this is for the end user to use, not a programmer.) + +To build a module, cd to it and type make. If the -DRELEASE option is specified (RELEASE=1 with GNU make) the release version will be built. The object files and exes are placed in a directory structure under 'release' or 'debug'. + +It is intended that a test will be written for everything, so in general make commands will be issued only within the test/* modules. Once it has been built, cd to debug/test/ and run the test with ./t . + + +SUBTITLE Programming style + +The code is written to be easy to write. Ease of programming is the primary concern, as this should lead to fewer bugs. Efficiency improvements can be made later when the system as a whole works. + +Much use is made of the STL. + +There is no common base class. + +All errors are reported using exceptions. + +Some of the boring code is generated by perl scripts from description files. + +There are a lot of modules and classes which can easily be used to build other projects in the future -- there is a lot of "framework" code. + + +SUBTITLE Lots more documentation + +The files are extensively commented. Consider this notes as an overview, and then read the source files for detailed and definitive information. + +Each function and class has a very brief decsription of it's purpose in a standard header, and extensive efforts have been maed to comment the code itself. + diff --git a/docs/api-notes/Win32_Clients.txt b/docs/api-notes/Win32_Clients.txt new file mode 100644 index 00000000..fad6c4af --- /dev/null +++ b/docs/api-notes/Win32_Clients.txt @@ -0,0 +1,13 @@ +The basic client tools now run on Win32 natively. +The port was done by nick@omniis.com. + +* bbackupd +* bbackupquery +* bbackupctl + +Have been ported. bbackupd runs as a NT style service. + +Known limitations: + +* File attributes and permissions are not backed up. + diff --git a/docs/api-notes/backup_encryption.txt b/docs/api-notes/backup_encryption.txt new file mode 100644 index 00000000..36580581 --- /dev/null +++ b/docs/api-notes/backup_encryption.txt @@ -0,0 +1,109 @@ +TITLE Encryption in the backup system + +This document explains how everything is encrypted in the backup system, and points to the various functions which need reviewing to ensure they do actually follow this scheme. + + +SUBTITLE Security objectives + +The crpyto system is designed to keep the following things secret from an attacker who has full access to the server. + +* The names of the files and directories +* The contents of files and directories +* The exact size of files + +Things which are not secret are + +* Directory heirarchy and number of files in each directory +* How the files change over time +* Approximate size of files + + +SUBTITLE Keys + +There are four separate keys used: + +* Filename +* File attributes +* File block index +* File data + +and an additional secret for file attribute hashes. + +The Cipher is Blowfish in CBC mode in most cases, except for the file data. All keys are maximum length 448 bit keys, since the key size only affects the setup time and this is done very infrequently. + +The file data is encrypted with AES in CBC mode, with a 256 bit key (max length). Blowfish is used elsewhere because the larger block size of AES, while more secure, would be terribly space inefficient. Note that Blowfish may also be used when older versions of OpenSSL are in use, and for backwards compatibility with older versions. + +The keys are generated using "openssl rand", and a 1k file of key material is stored in /etc/box/bbackupd. The configuration scripts make this readable only by root. + +Code for review: BackupClientCryptoKeys_Setup() +in lib/backupclient/BackupClientCryptoKeys.cpp + + +SUBTITLE Filenames + +Filenames need to be secret from the attacker, but they need to be compared on the server so it can determine whether or not is it a new version of an old file. + +So, the same Initialisation Vector is used for every single filename, so the same filename encrypted twice will have the same binary representation. + +Filenames use standard PKCS padding implemented by OpenSSL. They are proceeded by two bytes of header which describe the length, and the encoding. + +Code for review: BackupStoreFilenameClear::EncryptClear() +in lib/backupclient/BackupStoreFilenameClear.cpp + + +SUBTITLE File attributes + +These are kept secret as well, since they reveal information. Especially as they contain the target name of symbolic links. + +To encrypt, a random Initialisation Vector is choosen. This is stored first, followed by the attribute data encrypted with PKCS padding. + +Code for review: BackupClientFileAttributes::EncryptAttr() +in lib/backupclient/BackupClientFileAttributes.cpp + + +SUBTITLE File attribute hashes + +To detect and update file attributes efficiently, the file status change time is not used, as this would give suprious results and result in unnecessary updates to the server. Instead, a hash of user id, group id, and mode is used. + +To avoid revealing details about attributes + +1) The filename is added to the hash, so that an attacker cannot determine whether or not two files have identical attributes + +2) A secret is added to the hash, so that an attacker cannot compare attributes between accounts. + +The hash used is the first 64 bits of an MD5 hash. + + +SUBTITLE File block index + +Files are encoded in blocks, so that the rsync algorithm can be used on them. The data is compressed first before encryption. These small blocks don't give the best possible compression, but there is no alternative because the server can't see their contents. + +The file contains a number of blocks, which contain among other things + +* Size of the block when it's not compressed +* MD5 checksum of the block +* RollingChecksum of the block + +We don't want the attacker to know the size, so the first is bad. (Because of compression and padding, there's uncertainty on the size.) + +When the block is only a few bytes long, the latter two reveal it's contents with only a moderate amount of work. So these need to be encrypted. + +In the header of the index, a 64 bit number is chosen. The sensitive parts of the block are then encrypted, without padding, with an Initialisation Vector of this 64 bit number + the block index. + +If a block from an previous file is included in a new version of a file, the same checksum data will be encrypted again, but with a different IV. An eavesdropper will be able to easily find out which data has been re-encrypted, but the plaintext is not revealed. + +Code for review: BackupStoreFileEncodeStream::Read() (IV base choosen about half-way through) +BackupStoreFileEncodeStream::EncodeCurrentBlock() (encrypt index entry) +in lib/backupclient/BackupStoreFileEncodeStream.cpp + + +SUBTITLE File data + +As above, the first is split into chunks and compressed. + +Then, a random initialisation vector is chosen, stored first, followed by the compressed file data encrypted using PKCS padding. + +Code for review: BackupStoreFileEncodeStream::EncodeCurrentBlock() +in lib/backupclient/BackupStoreFileEncodeStream.cpp + + diff --git a/docs/api-notes/bin_bbackupd.txt b/docs/api-notes/bin_bbackupd.txt new file mode 100644 index 00000000..67ad9267 --- /dev/null +++ b/docs/api-notes/bin_bbackupd.txt @@ -0,0 +1,88 @@ +TITLE bin/bbackupd + +The backup client daemon. + +This aims to maintain as little information as possible to record which files have been uploaded to the server, while minimising the amount of queries which have to be made to the server. + + +SUBTITLE Scanning + +The daemon is given a length of time, t, which files over this age should be uploaded to the server. This is to stop recently updated files being uploaded immediately to avoid uploading something repeatedly (on the assumption that if a file has been written, it is likely to be modified again shortly). + +It will scan the files at a configured interval, and connect to the server if it needs to upload files or make queries about files and directories. + +The scan interval is actually varied slightly between each run by adding a random number up to a 64th of the configured time. This is to reduce cyclic patterns of load on the backup servers -- otherwise if all the boxes are turned on at about 9am, every morning at 9am there will be a huge spike in load on the server. + +Each scan chooses a time interval, which ends at the current time - t. This will be from 0 to current time - t on the first run, then the next run takes the start time as the end time of the previous run. The scan is only performed if the difference between the start and end times is greater or equal to t. + +For each configured location, the client scans the directories on disc recursively. + +For each directory + +* If the directory has never been scanned before (in this invocation of the daemon) or the modified time on the directory is not that recorded, the listing on the server is downloaded. + +* For each file, if it's modified time is within the time period, it is uploaded. If the directory has been downloaded, it is compared against that, and only uploaded if it's changed. + +* Find all the new files, and upload them if they lie within the time interval. + +* Recurse to sub directories, creating them on the server if necessary. + +Hence, the first time it runs, it will download and compare the entries on the disc to those on the server, but in future runs it will use the file and directory modification times to work out if there is anything which needs uploading. + +If there aren't any changes, it won't even need to connect to the server. + +There are some extra details which allow this to work reliably, but they are documented in the source. + + +SUBTITLE File attributes + +The backup client will update the file attributes on files as soon as it notices they are changed. It records most of the details from stat(), but only a few can be restored. Attributes will only be considered changed if the user id, group id or mode is changed. Detection is by a 64 bit hash, so detection is strictly speaking probablistic. + + +SUBTITLE Encryption + +All the user data is encrypted. There is a separate file, backup_encryption.txt which describes this, and where in the code to look to verify it works as described. + + +SUBTITLE Tracking files and directories + +Renaming files is a difficult problem under this minimal data scanning scheme, because you don't really know whether a file has been renamed, or another file deleted and new one created. + +The solution is to keep (on disc) a map of inode numbers to server object IDs for all directories and files over a certain user configurable threshold. Then, when a new file is discovered, it is first checked to see if it's in this map. If so, a rename is considered, which will take place if the local object corresponding to the name of the tracked object doesn't exist any more. + +Because of the renaming requirement, deletions of objects from the server are recorded and delayed until the end of the scan. + + +SUBTITLE Running out of space + +If the store server indicates on login to the backup client, it will scan, but not upload anything nor adjust it's internal stored details of the local objects. However, deletions and renames happen. + +This is to allow deletions to still work and reduce the amount of storage space used on the server, in the hope that in the future there will be enough space. + +Just not doing anything would mean that one big file created and then deleted at the wrong time would stall the whole backup process. + + +SUBTITLE BackupDaemon + +This is the daemon class for the backup daemon. It handles setting up of all the objects, and implements calulcation of the time intervals for the scanning. + + +SUBTITLE BackupClientContext + +State information for the scans, including maintaining a connection to the store server if required. + + +SUBTITLE BackupClientDirectoryRecord + +A record of state of a directory on the local filesystem. Containing the recursive scanning function, which is long and entertaining, but very necessary. It contains lots of comments which explain the exact details of what's going on. + + +SUBTITLE BackupClientInodeToIDMap + +A implementation of a map of inode number to object ID on the server. If Berkeley DB is available on the platform, it is stored on disc, otherwise there is an in memory version which isn't so good. + + + + + + diff --git a/docs/api-notes/bin_bbstored.txt b/docs/api-notes/bin_bbstored.txt new file mode 100644 index 00000000..c9c4e229 --- /dev/null +++ b/docs/api-notes/bin_bbstored.txt @@ -0,0 +1,54 @@ +TITLE bin/bbstored + +The backup store daemon. + +Maintains a store of encrypted files, and every so often goes through deleting unnecessary data. + +Uses an implementation of Protocol to communicate with the backup client daemon. See bin/bbstored/backupprotocol.txt for details. + + +SUBTITLE Data storage + +The data is arranged as a set of objects within a RaidFile disc set. Each object has a 64 bit object ID, which is turned into a filename in a mildly complex manner which ensure that directories don't have too many objects in them, but there is a minimal number of nested directories. See StoreStructure::MakeObjectFilename in lib/backupstore/StoreStructure.cpp for more details. + +An object can be a directory or a file. Directories contain files and other directories. + +Files in directories are supersceded by new versions when uploaded, but the old versions are flagged as such. A new version has a different object ID to the old version. + +Every so often, a housekeeping process works out what can be deleted, and deletes unnecessary files to take them below the storage limits set in the store info file. + + +SUBTITLE Note about file storage and downloading + +There's one slight entertainment to file storage, in that the format of the file streamed depends on whether it's being downloaded or uploaded. + +The problem is that it contains an index of all the blocks. For efficiency in managing these blocks, they all need to be in the same place. + +Files are encoded and decoded as they are streamed to and from the server. With encoding, the index is only completely known at the end of the process, so it's sent last, and lives in the filesystem last. + +When it's downloaded, it can't be decoded without knowing the index. So the index is sent first, followed by the data. + + +SUBTITLE BackupContext + +The context of the current connection, and the object which modifies the store. + +Maintains a cache of directories, to avoid reading them continuously, and keeps a track of a BackupStoreInfo object which is written back periodiocally. + + +SUBTITLE BackupStoreDaemon + +A ServerTLS daemon which forks off a separate housekeeping process as it starts up. + +Handling connections is delegated to a Protocol implementation. + + +SUBTITLE BackupCommands.cpp + +Implementation of all the commands. Work which requires writing is handled in the context, read only commands mainly in this file. + + +SUBTITLE HousekeepStoreAccount + +A class which performs housekeeping on a single account. + diff --git a/docs/api-notes/common/lib_common.txt b/docs/api-notes/common/lib_common.txt new file mode 100644 index 00000000..11d7b02d --- /dev/null +++ b/docs/api-notes/common/lib_common.txt @@ -0,0 +1,52 @@ +TITLE lib/common + +This module is the basic building block of the project. It is included implicitly as a dependency of all other modules. + +It provides basic services such as file and stream functionality, platform information, and various bits of utility code which doesn't fit naturally anywhere else but is useful to many other applications. + + +SUBTITLE Box.h + +The main include file for the project. It should be the first file included in every cpp file, followed by any system headers, then other project headers. This order has been chosen so that eventually it can be used as a target for pre-compiled headers. (This will be important for the Windows port) + + +SUBTITLE BoxPlatform.h + +This contains various bits of information on platform differences. The build scripts include a compile command line definition like PLATFORM_OPENBSD, which is then used to select the required options. + +Some of the stuff is not particularly pleasant, but it aims to produce a consistent compilation environment, and to have a abstracted way of setting other defines to tell the other files what things are available on this platform. + +GNU configure is not used, as it would be just another dependency. Although something does have to be done about compilation on Linux, which is just too different on each distribution to be caught by a common configuration here. + + +SUBTITLE Streams + +Much use is made of streams -- files, connections, buffers, all sorts of things are implemented using streams. + +The abstract base class IOStream defines the interface, see the class file for more details. + +Streams are lib/common's basic contribution to the project. A standard interface for handling data, and a number of utility classes which make performing common stream functions easy. + + +SUBTITLE Serialisation + +Note there is no "serialisable" class. Instead, objects which need to be serialised into Streams simply have two functions, ReadFromStream and WriteToStream. These perform as expected. + +As it turns out, there's no real need for a specific serialisation base class. If you do need to be able to serialise arbitary objects, there are two approaches. + +1) If you're serialising an arbitary object, you're going to know roughly what type it is. So it's likely to be a subclass of a known base class... in which case, you simply use virtual functions. + +2) If it really is an arbitary type, then you just write your function which accepts any type of object to serialise as a template. + + +SUBTITLE BoxException + +The exception base class for the project. All exceptions thrown by this code are dervied from BoxException. In general, each module which has unique errors has it's own exception class. + +CommonException errors are thrown throughout the project. + + +SUBTITLE Other bits + +There are some other extra useful bits which are documented only in the source files. + diff --git a/docs/api-notes/common/lib_common/BoxTime.txt b/docs/api-notes/common/lib_common/BoxTime.txt new file mode 100644 index 00000000..18bef5a5 --- /dev/null +++ b/docs/api-notes/common/lib_common/BoxTime.txt @@ -0,0 +1,7 @@ +TITLE BoxTime + +Not strictly a class, but time is presented in the project as 64 bit integers as microseconds since the UNIX Epoch. + +There are a number of utility functions in the BoxTime*.h files, which are useful. + +To get BoxTime values from a struct stat, use the FileModificationTime.h functions. \ No newline at end of file diff --git a/docs/api-notes/common/lib_common/CollectInBufferStream.txt b/docs/api-notes/common/lib_common/CollectInBufferStream.txt new file mode 100644 index 00000000..5f9556a3 --- /dev/null +++ b/docs/api-notes/common/lib_common/CollectInBufferStream.txt @@ -0,0 +1,26 @@ +CLASS CollectInBufferStream + +This class is essentially a buffer. Data is written to it, and stored in memory. Then when all data is written which will be written, it can be read out again. + +Useful for building streams in memory. + + +FUNCTION CollectInBufferStream::SetForReading() + +After you've written all the data, call this to set it to read mode. + + +FUNCTION CollectInBufferStream::Reset() + +Reset the stream to the state where is has no data, and is about to have data written. + + +FUNCTION CollectInBufferStream::GetSize() + +Get the size of the buffered data -- the entire data. Use the interface function BytesLeftToRead() in most cases. + + +FUNCTION CollectInBufferStream::GetBuffer() + +Get the buffer, so you can do bad things with it if you're feeling naughty. + diff --git a/docs/api-notes/common/lib_common/Configuration.txt b/docs/api-notes/common/lib_common/Configuration.txt new file mode 100644 index 00000000..ef5f38f6 --- /dev/null +++ b/docs/api-notes/common/lib_common/Configuration.txt @@ -0,0 +1,102 @@ +CLASS Configuration + +Implements the reading of multi-level configuration files. This is intended to be a generic way of representing configuration implementation. + +Basic validation is performed on files as they are read, specified by a data structure. + +test/common has some examples of usage. + + +SUBTITLE Configuration file format + +The format is simple, a list of Key = Value pairs. For example + +Key1 = Value1 +Key2 = Value2 + +Optionally, multiple values for the same key are allowed. + +Lists of configurations can be nested to any level. These are called Sub-configurations: + +SubConfig +{ + SubKey1 = ValueX + SubKey2 = ValueY +} + + +SUBTITLE Verification + +The verification structure specifies what keys are required, what are optional, and what sub-configurations are expected. Default values can be specified. + +See Configuration.h for the structures. + +RaidFileController::Initialise has a good example of a simple verification layout. + +Wildcards can be used for SubConfigurations, by specifying a name of "*". This allows you to use multiple sections to configure any number of items. + +Each item has a number of flags, which are combined to say whether an item is required, should be an integer or boolean, and rather importantly, whether it's the last item in the list. + +Verification is limited, so you may wish to do more verification the file. There are unimplemented hooks for custom verification functions to be included in the verification definitions. Should be done at some point. + +Boolean keys have possible values "true", "yes", "false", "no" (case insensitive). + + +FUNCTION Configuration::LoadAndVerify() + +Loads the configuration from disc, and verifies it. If there are problems with the verification, it returns some text which can be used to tell the user the problems with the file. These are fairly basic error messages, but do say what should be done to get it to parse properly. + +This returns a Configuration object, which can then be queries for Keys and Values. Sub-configurations are implemented through an interface which returns a reference to another Configuration object. + + + +FUNCTION Configuration::KeyExists() + +Does a specified key exist? + +Use this for optional key values only -- specify them in the verification structure if they are required. + + +FUNCTION Configuration::GetKeyValue() + +Get the value of a key as a string. + +If ConfigTest_MultiValueAllowed is set in the relevant entry in the verify structure, this string may contain multiple values, separated by a single byte with value 0x01 (use Configuration::MultiValueSeparator in code). Use SplitString() defined in Utils.h to split it into components. + +This representation was chosen as multi-values are probably rare, and unlikely to justify writing a nicer (but more memory intensive and complex) solution. + + +FUNCTION Configuration::GetKeyValueInt() + +Get the value of a key as an integer. Make sure the AsInt property is requried in the verification structure. + + +FUNCTION Configuration::GetKeyValueBool() + +Get the value of a key as a boolean value. Make sure the AsBool property is requried in the verification structure. + +Default to "false", should this verification not be performed and an unknown value is specified. + + +FUNCTION Configuration::GetKeyNames() + +Return a list of all the keys in this configuration. + + +FUNCTION Configuration::SubConfigurationExists() + +Does a specified sub-configuration exist? + + +FUNCTION Configuration::GetSubConfiguration() + +Return another Configuration object representing the sub section. + + +FUNCTION Configuration::GetSubConfigurationNames() + +Get a list of all the sub configurations. + +As there isn't a particularly neat way that configurations can be iterated over, mSubConfigurations is public. (BAD!) + + diff --git a/docs/api-notes/common/lib_common/Conversion.txt b/docs/api-notes/common/lib_common/Conversion.txt new file mode 100644 index 00000000..62d1967a --- /dev/null +++ b/docs/api-notes/common/lib_common/Conversion.txt @@ -0,0 +1,14 @@ +TITLE Generic type conversion + +Conversion.h provides generic type conversion. Within the BoxConvert namespace, it defines the templated function + + ToType Convert(FromType From) + +which converts the data type as expected. In general, from and to types have to be specified explicitly. + +Templates rather than overloaded functions are used, firstly to be absolutely explicit about the conversion required, and secondly because overloaded functions can't have differing return types for the same argument type. + +The function is specialised for various types, and the generic version uses C++ type conversion. + +Exceptions may be thrown if the conversion is not possible. These are all of the ConversionException type. + diff --git a/docs/api-notes/common/lib_common/ExcludeList.txt b/docs/api-notes/common/lib_common/ExcludeList.txt new file mode 100644 index 00000000..8a5bf36c --- /dev/null +++ b/docs/api-notes/common/lib_common/ExcludeList.txt @@ -0,0 +1,21 @@ +TITLE ExcludeList + +A class implementing a list of strings which are to be excluded in some way. Entries can be added as strings to be matched exactly, or as regular expressions. + +Multiple entries can be added in a single function call in a manner designed to work with the multi-value entries store in a Configuation object. + + +FUNCTION ExcludeList::AddDefiniteEntries() + +Definite entries are exact strings to match. + + +FUNCTION ExcludeList::AddRegexEntries() + +Regular expressions as defined by POSIX, and supported by the host platform. Will throw an exception if regular expressions are not supported by the platform. + + +FUNCTION ExcludeList::IsExcluded() + +Test a string for being excluded by definite or regex entries. + diff --git a/docs/api-notes/common/lib_common/FdGetLine.txt b/docs/api-notes/common/lib_common/FdGetLine.txt new file mode 100644 index 00000000..d92ff94d --- /dev/null +++ b/docs/api-notes/common/lib_common/FdGetLine.txt @@ -0,0 +1,11 @@ +CLASS FdGetLine + +See IOStreamGetLine, difference is that it works on basic UNIX file descriptors rather than full blown streams. Follows basic interface, except... + + +FUNCTION FdGetLine::GetLine + +Returns a string containing the optionally preprocessed line. + +Do not call if IsEOF if true. + diff --git a/docs/api-notes/common/lib_common/Guards.txt b/docs/api-notes/common/lib_common/Guards.txt new file mode 100644 index 00000000..6174fea6 --- /dev/null +++ b/docs/api-notes/common/lib_common/Guards.txt @@ -0,0 +1,5 @@ +TITLE Guards.h + +This file contains a couple of classes for using file and memory. When the class goes out of scope, the underlying object is closed or freed, respectively. + + diff --git a/docs/api-notes/common/lib_common/IOStream.txt b/docs/api-notes/common/lib_common/IOStream.txt new file mode 100644 index 00000000..09460656 --- /dev/null +++ b/docs/api-notes/common/lib_common/IOStream.txt @@ -0,0 +1,89 @@ +CLASS IOStream + +The base class for streams of data. See IOStream.h for specific details on functions, but the interface is described here. + +Most streams only implement one direction, but some both. + + +SUBTITLE Reading + + +FUNCTION IOStream::Read() + +Reads data from the stream. Returns 0 if there is no data available within the timeout requested. + +Unlike the UNIX API, a return of 0 does not mean that there is no more data to read. Use IOStream::StreamDataLeft() to find this out. + + +FUNCTION IOStream::ReadFullBuffer() + +If you want to read a specific sized block of data from a stream, it is annoying ot use the Read function, because it might return less, so you have to loop. + +This implements that loop. Note that the timeout is the timeout for each individual read -- it might take a lot longer to complete. + +You must check the return value, which is whether or not it managed to get all that data. + + +FUNCTION IOStream::BytesLeftToRead() + +How many bytes are left to read in the stream treated as a whole. May return IOStream::SizeOfStreamUnknown if it is something like a socket which doesn't know how much data is in the stream. + + +FUNCTION IOStream::StreamDataLeft() + +Returns true if more data can be read. Or more specifically, if more data might be able to be read some time in the future. + + +SUBTITLE Writing + + +FUNCTION IOStream::Write() + +Write data to a stream. Writes the entire block, or exceptions. + + +FUNCTION IOStream::WriteAllBuffered() + +Writes any buffered data to the underlying object. + +Call at the end of a series of writes to make sure the data is actually written. (Buffering is not yet implemented anywhere, but it should be soon.) + + +FUNCTION IOStream::StreamClosed() + +Is the stream closed, and writing no longer possible? + + +FUNCTION IOStream::CopyStreamTo() + +A utility function to copy the contents of one stream to another, until the reading stream has no data left. + + +SUBTITLE Other interfaces + +These are slightly nasty interfaces, because they have to match the UNIX API to some extent. + + +FUNCTION IOStream::Close() + +Close the stream -- intended to indicate that nothing more will be written. + +However, also closes reading in most cases. Stream dependent. Probably best just to delete the object and let it sort itself out. + + +FUNCTION IOStream::Seek() + +Seeks within the stream -- mainly for using files within a stream interface, although some of the utility stream classes support it too. + +This is actually a bad interface, because it does not specify whether it applies to reading or writing. This matches the interface provided by files with a single file pointer, but does not map well onto other stream types. + +Should it be changed? Perhaps. But then it means that files would either have to have an inconsitent interface, or the class keep track of two separate file pointers. Which isn't nice. + +So use carefully, and remember whether you're using a file stream, a reading stream, or a writing stream. + + +FUNCTION IOStream::GetPosition() + +Get the current file pointer. Subject to same problems as Seek with regard to semantics. + + diff --git a/docs/api-notes/common/lib_common/IOStreamGetLine.txt b/docs/api-notes/common/lib_common/IOStreamGetLine.txt new file mode 100644 index 00000000..04c56b57 --- /dev/null +++ b/docs/api-notes/common/lib_common/IOStreamGetLine.txt @@ -0,0 +1,29 @@ +CLASS IOStreamGetLine + +This class provides a convenient way to read text from a file, line by line. It also can preprocess the line to remove leading and trailing whitespace and comments. Comments are started by the character # and run to the end of the line. + +Create an instance by passing a reference to a stream into the constructor. + +Note the class does internal buffering, so you can only detach it later if the stream supports seeking backwards. + + +FUNCTION IOStreamGetLine::GetLine() + +Returns true if a line could be retreieved without a read timing out. + + +FUNCTION IOStreamGetLine::IsEOF() + +Whether the end of the stream has been reached. Do not call GetLine if this is true. + + +FUNCTION IOStreamGetLine::GetLineNumber() + +Returns the line number. + + +FUNCTION IOStreamGetLine::DetachFile() + +Detaches the stream from the GetLine class. Will seek backwards to "replace" data it's buffered back in the stream. + + diff --git a/docs/api-notes/common/lib_common/MainHelper.txt b/docs/api-notes/common/lib_common/MainHelper.txt new file mode 100644 index 00000000..eb5b07f0 --- /dev/null +++ b/docs/api-notes/common/lib_common/MainHelper.txt @@ -0,0 +1,4 @@ +TITLE MainHelper.h + +This header contains a couple of macros which add exception handling and reporting to main() functions for executable programs. + diff --git a/docs/api-notes/common/lib_common/WaitForEvent.txt b/docs/api-notes/common/lib_common/WaitForEvent.txt new file mode 100644 index 00000000..0bc55726 --- /dev/null +++ b/docs/api-notes/common/lib_common/WaitForEvent.txt @@ -0,0 +1,16 @@ +CLASS WaitForEvent + +This class implements a way to efficiently wait on one or more system objects, for example sockets and file descriptors. Where kqueue() is available, this is used, otherwise poll(). The poll() implementation is less comprehensive and rather more limited. + +To add a compatible object, call Add(). To remove it, call Remove(). To wait for an object to become ready, call Wait(), which returns a pointer to the first ready object, or 0 for a timeout. + +The timeout is set either in the constructor, or using the SetTimout() method. It is specified in milliseconds. + +The kqueue based implementation will automatically remove objects when they are closed (actually, the OS does this), but the poll implementation requires that Remove() be called. + +The flags passed to Add() or Remove() are passed to the object, and are ignored by this class. + +For an object to be able to be added to the list, it should implement FillInKEvent() and FillInPoll() for kqueue and poll implementations respectively. Use the PLATFORM_KQUEUE_NOT_SUPPORTED define to test which is necessary for the platform. + +It does not have to be derived off any particular class, as it uses templates to be compatible with any class. + diff --git a/docs/api-notes/common/lib_common/xStream.txt b/docs/api-notes/common/lib_common/xStream.txt new file mode 100644 index 00000000..91e9c0ea --- /dev/null +++ b/docs/api-notes/common/lib_common/xStream.txt @@ -0,0 +1,40 @@ +TITLE Various stream types + +Some useful streams are implemented in lib/common. + + +SUBTITLE CollectInBufferStream + +Described in it's own page. + + +SUBTITLE FileStream + +Implements a stream from a file, allowing both reading and writing. + + +SUBTITLE MemBlockStream + +Turns a memory block into a readable stream. + +Can also be constructed using StreamableMemBlock, CollectInBufferStream and MemBlockStream as sources of the buffer. + + +SUBTITLE PartialReadStream + +Create a readable stream which will read a set number of bytes from another stream, and then declare itself as closed. + +Useful for extracting a small chunk of another stream to present to a function which expects to consume all of a stream. + + +SUBTITLE ReadGatherStream + +Create a readable stream out of blocks from many streams -- so various sections of any number of streams are composed into a single stream. + +To use, register each stream with AddComponent, then use the returned 'handle' with AddBlock to add blocks to the composed stream. + +Optionally, the object will take ownership of the streams added, and delete them when itself is deleted. + +See the comments in the function blocks in the cpp file for more info. + + diff --git a/docs/api-notes/common/lib_compress.txt b/docs/api-notes/common/lib_compress.txt new file mode 100644 index 00000000..f3e26f20 --- /dev/null +++ b/docs/api-notes/common/lib_compress.txt @@ -0,0 +1,8 @@ +TITLE lib/compress + +This is a horrid C++ interface to zlib. It is written this way to avoid having to buffer any data, and so be unnecessarily inefficient. But this does end up just being a wrapper to the zlib interface. + +See test/compress for an example of how to use it. But it should be very familiar. + +For an easier interface, use CompressStream. + diff --git a/docs/api-notes/common/lib_compress/CompressStream.txt b/docs/api-notes/common/lib_compress/CompressStream.txt new file mode 100644 index 00000000..eca43eb6 --- /dev/null +++ b/docs/api-notes/common/lib_compress/CompressStream.txt @@ -0,0 +1,27 @@ +TITLE CompressStream + +This implements a compressing stream class, which compresses and decompresses data sent to an underlying stream object. Data is likely to be buffered. + + +WARNING: Use WriteAllBuffered() (after objects which need to be sent in their entirity) and Close() (after you have finished writing to the stream) otherwise the compression buffering will lose the last bit of data for you. + + +The class works as a filter to an existing stream. Ownership can optionally be taken so that the source stream is deleted when this object is deleted. + +It can operate in one or two directions at any time. Data written to the stream is compressed, data read from the stream is decompressed. If a direction is not being (de)compressed, then it can act as a pass-through without touching the data. + +The constructor specifies the actions to be taken: + +CompressStream(IOStream *pStream, bool TakeOwnership, + bool DecompressRead, bool CompressWrite, + bool PassThroughWhenNotCompressed = false); + +pStream - stream to filter +TakeOwnership - if true, delete the stream when this object is deleted. +DecompressRead - reads pass through a decompress filter +CompressWrite - writes pass through a compression filter +PassThroughWhenNotCompressed - if not filtering a direction, pass through the data unaltered, otherwise exception if an attempt is made on that direction. + + +Note that the buffering and compression will result in different behaviour than the underlying stream: data may not be written in one go, and data being read may come out in different sized chunks. + diff --git a/docs/api-notes/common/lib_crypto.txt b/docs/api-notes/common/lib_crypto.txt new file mode 100644 index 00000000..9ddafe69 --- /dev/null +++ b/docs/api-notes/common/lib_crypto.txt @@ -0,0 +1,28 @@ +TITLE lib/crypto + +Provides cryptographic primatives using the OpenSSL implementation. + + +SUBTITLE CipherContexts and CipherDescriptions + +A CipherContext is the interface to encryption and decryption, providing a generic interface. + +It is constructed with a decrypt or encrypt flag, and a CipherDescription. The CipherDescription specifies which cipher is to be used, the key, and all other cipher specific paramteres. + +The CipherContext has support for padding and initialisation vectors. + + +SUBTITLE Random numbers + +An interface to the OpenSSL psuedo random number generater is provided. + + +SUBTITLE Digests + +An interface to the MD5 digest is provided. + + +SUBTITLE RollingChecksum + +The rsync rolling checksum is implemented. The interface is a little tricky to be efficient as the caller must track the position and provide relevant bytes to advance the checksum. + diff --git a/docs/api-notes/common/lib_crypto/CipherContext.txt b/docs/api-notes/common/lib_crypto/CipherContext.txt new file mode 100644 index 00000000..30fb4608 --- /dev/null +++ b/docs/api-notes/common/lib_crypto/CipherContext.txt @@ -0,0 +1,28 @@ +CLASS CipherContext + +Encryption and decryption using OpenSSL EVP interface. + +See the cpp file for more documentation in the function headers, and test/crypto for examples. + +General notes below. + + +SUBTITLE Construction + +Construct with the encryption direction, and a CipherDescription of the cipher and key required. + + +SUBTITLE Encrypting or decrypting + +Begin() and Transform() allow piece by piece transformation of a block. + +TransformBlock() transforms an entire block. + + +SUBTITLE Buffering + +All transforms expect to have enough space in the buffers for their entire output. Because of block boundaries and padding, it is necessary that the output buffer should be bigger than the input buffer. The amount of space depends on the cipher. + +InSizeForOutBufferSize() and MaxOutSizeForInBufferSize() perform these space calculations, returning the maximuim in size for a specified out size, and the reverse, respectively. + + diff --git a/docs/api-notes/common/lib_crypto/RollingChecksum.txt b/docs/api-notes/common/lib_crypto/RollingChecksum.txt new file mode 100644 index 00000000..d871b3f2 --- /dev/null +++ b/docs/api-notes/common/lib_crypto/RollingChecksum.txt @@ -0,0 +1,36 @@ +CLASS RollingChecksum + +Implementing the rsync rolling checksum algorithm. Read it's description first: + +http://samba.anu.edu.au/rsync/tech_report/node3.html + + +SUBTITLE Construction and initial checksum calculation + +The constructor takes a pointer to a block of data and a size, and calculates the checksum of this block. It can now be "rolled forward" to find the checksum of the block of the same size, one byte forward, with minimal calculation. + + +FUNCTION RollingChecksum::GetChecksum() + +Returns the checksum for the current block. + + +FUNCTION RollingChecksum::RollForward() + +This function takes the byte at the start of the current block, and the last byte of the block it's rolling forward to, and moves the checksum on. + +If the block is + + char *pBlock = ; + +with size s, then it should be called with + + RollForward(pBlock[0], pBlock[s]) + +and now GetChecksum will return the checksum of the block (pBlock+1) of size s. + + +FUNCTION RollingChecksum::RollForwardSeveral() + +Similar to RollForward(), but is more efficient for skipping several bytes at once. Takes pointers to the data buffer rather than the actual data values. + diff --git a/docs/api-notes/common/lib_server.txt b/docs/api-notes/common/lib_server.txt new file mode 100644 index 00000000..392f331d --- /dev/null +++ b/docs/api-notes/common/lib_server.txt @@ -0,0 +1,9 @@ +TITLE lib/server + +This provides various classes for implementing client/server applications (and UNIX daemons). + +The stars of the show are ServerTLS and Protocol, which allow client server applications which communicate using TLS (SSL successor) to be built with surprisingly few lines of code. + +All details in the respective class files. + +Look at test/basicserver for examples. diff --git a/docs/api-notes/common/lib_server/Daemon.txt b/docs/api-notes/common/lib_server/Daemon.txt new file mode 100644 index 00000000..6956ec2b --- /dev/null +++ b/docs/api-notes/common/lib_server/Daemon.txt @@ -0,0 +1,96 @@ +CLASS Daemon + +Implement a UNIX daemon which + +* Daemonises (detach from console, etc) +* Sets up signal handlers, and does useful things with them +* Reads configuration files +* Writes PID file +* Opens syslog + +all the usual UNIX basic daemon behaviour. + +The daemon exe takes optional arguments: The first is a configuration file filename which overrides the default. If the second argument is 'SINGLEPROCESS' the daemon will not daemonise. + +The configuration file must have a section like this + +Server +{ + PidFile = /var/run/daemon.pid + User = username +} + +Use DAEMON_VERIFY_SERVER_KEYS (defined in Daemon.h) to include the necessary keys in your configuration file's verify structure. + +The "User" line is optional, and if it is present the Daemon will change user to this username just before it daemonises. Note that unless the directory the PID file is written to has write permission for this user, the PID file will not be deleted on exit. + + +To implement a daemon, derive a class from Daemon, and override Run(). + +Then in your main file, do something like + +int main(int argc, const char *argv[]) +{ + MAINHELPER_START + + BackupDaemon daemon; + return daemon.Main(BOX_FILE_BBACKUPD_DEFAULT_CONFIG, argc, argv); + + MAINHELPER_END +} + +and that's it. Obviously it's worth doing a few more things as well, but that's it. + + +FUNCTION Daemon::Run() + +Override with the main daemon code. It should behave like this + +void SomethingDaemon::Run() +{ + // Read configuration file + + // Do lots of work until StopRun() returns true + while(!StopRun()) + { + ::sleep(10); + } + + // Clean up nicely +} + +which allows the base class to implement the standard start, terminate and -HUP behaviours correctly. + + +FUNCTION Daemon::DaemonName() + +Returns the name of the daemon, for use in syslog. + + +FUNCTION Daemon::DaemonBanner() + +Returns the banner to be displayed on startup, or 0 for no banner. + + +FUNCTION Daemon::GetConfigVerify() + +Returns a configuration verify structure for verifying the config file. Note that this configuration structure should include a sub-configuration called "Server, and have entries defined by DAEMON_VERIFY_SERVER_KEYS. See one of the bin/bbackupd for an example. + + +FUNCTION Daemon::StopRun() + +Returns true when the daemon needs to be terminated or restarted. Use IsReloadConfigWanted() and IsTerminateWanted() to find out which one, if you need to know. + + +FUNCTION Daemon::SetupInInitialProcess() + +Override to perform additional functions in the initial process, before forking and detachment happens. + + +FUNCTION Daemon::EnterChild() + +Called when a child is entered. If you override it, remember to call the base class. + + + + diff --git a/docs/api-notes/common/lib_server/Protocol.txt b/docs/api-notes/common/lib_server/Protocol.txt new file mode 100644 index 00000000..09d3c1f1 --- /dev/null +++ b/docs/api-notes/common/lib_server/Protocol.txt @@ -0,0 +1,120 @@ +CLASS Protocol + +Protocol + +* serialises and deserialises data objects +* sends arbitary streams + +through a bi-directional IOStream object, usually a socket. + +These data objects are auto-generated by a perl script, along with the logic to implement a simple protocol where one end is a client, and the other a server. The client sends a command object (and optional streams) and the server returns a reply object (and optional streams). + +The perl script uses a description file which specifies the data inside these objects (which can be extended to include any type which implements the standard serialisation functions), the required responses to the commands, and whether streams are involved. + +It then implements a server object, which given a stream and a user defined context object, waits for commands, processes them, and sends the results back. All you need to do is implement a DoCommand() function for each command object you define. + +On the client side, a Query() function is implemented which takes objects as parameters, and returns the right type of reply object (or throws an exception if it doesn't get it.) Short cut Query() functions are also implemented which don't require objects to be created manually. + +Thus, implementing a server is as simple as deriving a daemon off ServerStream or ServerTLS, and implementing the Connection() function as + + void TestProtocolServer::Connection(SocketStream &rStream) + { + TestProtocolServer server(rStream); + TestContext context; + server.DoServer(context); + } + +and that's it. TestContext is a user defined class which keeps track of all the state of the connection, and is passed to all the DoCommand() functions, which look like this: + + std::auto_ptr + TestProtocolServerSimple::DoCommand(TestProtocolServer &rProtocol, + TestContext &rContext) + { + return std::auto_ptr + (new TestProtocolServerSimpleReply(mValue+1)); + } + +(taken from test/basicserver) + +The client code looks like this + + SocketStream conn; + conn.Open(Socket::TypeUNIX, "testfiles/srv4.sock"); + + TestProtocolClient protocol(conn); + + // Query + { + std::auto_ptr + reply(protocol.QuerySimple(41)); + TEST_THAT(reply->GetValuePlusOne() == 42); + } + + +Finally, debug logging can be generated which allows a list of all commands and their parameters to be logged to syslog or a file. + + +SUBTITLE Protocol Description File + +This file is passed to the lib/server/makeprotocol.pl script, which generates a h and cpp file to implement the protocol. + +It is in two sections, separated by a 'BEGIN_OBJECTS' on a line of it's own. + +In the top half, the following statements must be made. + +Name + The name of the protocol, used in naming classes. + +IdentString + The idenfitifaction string sent over the IOStream to confirm it it + is talking to another Protocol object speaking the same Protocol. + +ServerContextClass + The user defined context class used for the server, and the header + file it is defined in. + +Additionally, the following optional commands can be made. + +ClientType +ServerType (similarly) + Extends the types used in the objects below. Server and client + can use different types for the same object type. + +ImplementLog (Client|Server) (syslog|file) + Implement command logging for client or server into syslog or a file. + +LogTypeToText (Client|Server) + + For extended types, optionally define how to convert them into printf + elements and the code to run to get the argument. Within the evaluate + parameter, VAR is replaced by the name of the variable to display. + If this is not specified for a given type, OPAQUE is output instead. + + +In the object section, an object is defined by a line + + + +followed by lines beginning with whitespace defining the data transmitted in the object. The type may be list, which specifies a list (implemented as a std::vector) of entries of that type. + +The attributes specify exactly how that object is used in the defined protocol. + +Reply + The object is a reply object, sent from the server to the client. + +Command(Reply-Type) + The object is a command, send from the client to the server, and the server + will send back an object of type Reply-Type. + +IsError(Type-Field,SubType-Field) + The command is an error object, and the two files specify the data member + which describes the error type and sub type. + +EndsConversation + When this command is received, the connection is to be terminated. + (ie a logout command) + +StreamWithCommand + When this command is sent as a command, a stream follows it. + + diff --git a/docs/api-notes/common/lib_server/ServerStream.txt b/docs/api-notes/common/lib_server/ServerStream.txt new file mode 100644 index 00000000..6c5932a0 --- /dev/null +++ b/docs/api-notes/common/lib_server/ServerStream.txt @@ -0,0 +1,29 @@ +CLASS ServerStream + +ServerStream implementes a Daemon which accepts stream connections over sockets, and forks into a child process to handle them. + +To implement a daemon, derive from + + ServerStream + +The type SocketStream specifies that incoming connections should be treated as normal sockets, and SERVER_LISTEN_PORT is the port to listen to (if it's a inet socket, and if not overridden in the config file). The actual addresses (or names) to bind to are specified in the configuration file by the user. + +Make sure SERVERSTREAM_VERIFY_SERVER_KEYS(0) is included in the configuration verification structure in the "Server" sub configuration. 0 could be replaced with a default address, for example "unix:/var/run/server.sock" to specific a default UNIX socket in the filesystem. + +See test/basicserver for a simple example. + +The ListenAddresses key in the Server subconfiguration is a comma separated list of addresses, specified as family:name. Internet sockets are family 'inet', for example 'inet:localhost' (or 'inet:localhost:1080' to specify a port number as well), and unix domain sockets are 'unix', example above. + + +Override Connection to handle the connection. + +Remember to override Daemon functions like the server name, and start it up, just like a generic Daemon. + + +FUNCTION ServerStream::Connection + +This function takes a connected stream as it's argument. It should then proceed to do whatever it needs to do to talk to the client. + +Using IOStreamGetLine or a Protocol class to communicate may be quick ways of implementing this functionality. + + diff --git a/docs/api-notes/common/lib_server/ServerTLS.txt b/docs/api-notes/common/lib_server/ServerTLS.txt new file mode 100644 index 00000000..dbde500f --- /dev/null +++ b/docs/api-notes/common/lib_server/ServerTLS.txt @@ -0,0 +1,6 @@ +CLASS ServerTLS + +Implements a server which uses TLS (SSL) to encrypt and authenticate connections. + +Very similar to ServerStream, except it reads the certificate files for the TLSContext out of the Server sub-configuration to set up a TLSContext ("CertificateFile", "PrivateKeyFile" and "TrustedCAsFile"). Otherwise works exactly the same. + diff --git a/docs/api-notes/common/lib_server/SocketStream.txt b/docs/api-notes/common/lib_server/SocketStream.txt new file mode 100644 index 00000000..82813279 --- /dev/null +++ b/docs/api-notes/common/lib_server/SocketStream.txt @@ -0,0 +1,8 @@ +CLASS SocketStream + +A implementation of IOStream which wraps a socket connection. + +It can either be created by attaching to an existing object, or use the Open() function to open a connection to a named host on a specific port (or a local UNIX socket in the filesystem). + +Follows stream interface. + diff --git a/docs/api-notes/common/lib_server/SocketStreamTLS.txt b/docs/api-notes/common/lib_server/SocketStreamTLS.txt new file mode 100644 index 00000000..ebb3f233 --- /dev/null +++ b/docs/api-notes/common/lib_server/SocketStreamTLS.txt @@ -0,0 +1,11 @@ +CLASS SocketStreamTLS + +An implementation of IOStream which wraps a TLS (SSL) connection over a socket. + +The Open function takes a TLSContext reference which specifies the parameters for the connection. + + +FUNCTION GetPeerCommonName() + +Returns the common name of the certificate presented by the remote end. + diff --git a/docs/api-notes/common/lib_server/TLSContext.txt b/docs/api-notes/common/lib_server/TLSContext.txt new file mode 100644 index 00000000..ff50d3e6 --- /dev/null +++ b/docs/api-notes/common/lib_server/TLSContext.txt @@ -0,0 +1,16 @@ +CLASS TLSContext + +A wrapper over the OpenSSL context object. + +Note: you need to call SSLLib::Initialise at the beginning of your program to use these functions. + + +SUBTITLE Construction + +The constuctor takes the following parameters + +* Boolean for whether this is acting as a server or a client +* The .pem file containing the certificate which will be used +* The .pem file containing the private key for this certificate +* The .pem file containing the certificates which will certify the other end of the connection. + diff --git a/docs/api-notes/common/memory_leaks.txt b/docs/api-notes/common/memory_leaks.txt new file mode 100644 index 00000000..9a9764ea --- /dev/null +++ b/docs/api-notes/common/memory_leaks.txt @@ -0,0 +1,44 @@ +TITLE Memory leak detection + +The build system contains a primative memory leak detection system in debug builds. + +It works by using #defines to replace calls to malloc, free, realloc, new and delete with debug versions which record their use. When a process ends, it dumps a list of all the blocks or objects which were allocated by not freed, and the file and line of the source where they are originally allocated. + +It's not perfect, but should catch most things and work on most platforms. + +If it doesn't work on your platform, define PLATFORM_DISABLE_MEM_LEAK_TESTING in BoxPlatform.h within the relevant section. + + +SUBTITLE Requirements in source + +It does require some extra lines in the source file. The last included file in each .cpp file must be + + #include "MemLeakFindOn.h" + +and if a .h file uses any of these functions, the contents of the .h file should be bounded with + + #include "MemLeakFindOn.h" + + // ... some code, but absolutely no #includes + + #include "MemLeakFindOff.h" + +The cleanupforcvs.pl script checks for correct usage. + + +SUBTITLE Use in tests + +Tests will automatica