pax_global_header00006660000000000000000000000064140434132470014514gustar00rootroot0000000000000052 comment=23efb0f573966693af170356353711ddd201caaa burp-2.4.0/000077500000000000000000000000001404341324700124675ustar00rootroot00000000000000burp-2.4.0/.gitignore000066400000000000000000000014051404341324700144570ustar00rootroot00000000000000*.o *~ *.swp *.swo *.gcno *.gcda .deps/ .dirstamp /aclocal.m4 /autoconf/Make.common /autoconf/Make.common2 /autoconf/compile /autoconf/config.* /autoconf/depcomp /autoconf/install-sh /autoconf/ltmain.sh /autoconf/missing /autoconf/mkinstalldirs /autoconf/test-driver /autom4te.cache /backup_tool_script /burp /burp_ca /burp-coverage/ /burp-coverage.info /burp-coverage-clean.info /burp-cross-tools /burp-depkgs /config.h.in /config.log /config.out /config.status /configure /libtool /Makefile /Makefile.in /manpages/bedup.8 /manpages/bsigs.8 /manpages/bsparse.8 /manpages/burp.8 /manpages/burp_ca.8 /manpages/vss_strip.8 /runner /runner.log /runner.trs /src/config.h /src/config.h.in /src/server/protocol1/vss_strip /src/stamp-h1 /summary_script /test-suite.log /vss_strip burp-2.4.0/.travis-script.sh000077500000000000000000000004521404341324700157170ustar00rootroot00000000000000#!/bin/bash -ex autoreconf -vif if [ "$TRAVIS_OS_NAME" = "linux" ] ; then ./configure make -j3 distcheck fi if [ "$TRAVIS_OS_NAME" = "osx" ] ; then ./configure --with-openssl=/usr/local/opt/openssl make -j3 runner DISTCHECK_CONFIGURE_FLAGS="--with-openssl=/usr/local/opt/openssl" ./runner fi burp-2.4.0/.travis.yml000066400000000000000000000027021404341324700146010ustar00rootroot00000000000000sudo: required dist: trusty language: c git: depth: 3 compiler: - gcc - clang os: - linux - osx osx_image: xcode7.2 deploy: skip_cleanup: true before_script: - git describe --always --dirty=+ before_install: - test "$TRAVIS_OS_NAME" = "linux" && sudo apt-get -qq update || true - test "$TRAVIS_OS_NAME" = "osx" && sudo wget -q https://raw.githubusercontent.com/troydhanson/uthash/master/src/uthash.h -O /usr/local/include/uthash.h || true - test "$TRAVIS_OS_NAME" = "osx" && brew update || true install: - test "$TRAVIS_OS_NAME" = "linux" && sudo apt-get -qq install check || true - test "$TRAVIS_OS_NAME" = "linux" && sudo apt-get -qq install libacl1-dev || true - test "$TRAVIS_OS_NAME" = "linux" && sudo apt-get -qq install libncurses5-dev || true - test "$TRAVIS_OS_NAME" = "linux" && sudo apt-get -qq install librsync-dev || true - test "$TRAVIS_OS_NAME" = "linux" && sudo apt-get -qq install uthash-dev || true - test "$TRAVIS_OS_NAME" = "osx" && brew install check || true - test "$TRAVIS_OS_NAME" = "osx" && brew install librsync || true - test "$TRAVIS_OS_NAME" = "osx" && brew install openssl || true - test "$TRAVIS_OS_NAME" = "osx" && brew uninstall libtool || true - test "$TRAVIS_OS_NAME" = "osx" && brew install libtool || true script: - ./.travis-script.sh after_failure: - tail burp-*/_build/sub/test-suite.log - sleep 2 deploy: provider: script script: test/travis-deploy.sh on: branch: master burp-2.4.0/CHANGELOG000066400000000000000000002377761404341324700137270ustar00rootroot000000000000002021-05-02 burp-2.4.0: * New stable version, based on 2.3.38. 2020-11-07 Changes in burp-2.3.38: * From Demid R: - Added network access control at the burp level. * From svalouch: - Don't rewrite config if not neccessary in certificate exchange. * Cleaned up network access control config items, fixed monitor access. 2020-10-01 Changes in burp-2.3.36: * 211: Choose case sensitivity on list/verify/restore, with '-r/-R'. 2020-09-02 Changes in burp-2.3.34: * 18: Option to look at the timer script regularly. * From Demid R.: - Add max_parallel_backups feature. 2020-08-01 Changes in burp-2.3.32: * 686: Better email message when resuming is not possible. - Add a configuration item to allow you to notify failure when a working directory is deleted. * Protect against hard-link attacks on restore by unlinking first. * Bug fix for resuming when the previous backup is empty. 2020-07-03 Changes in burp-2.3.30: * 847: bfile should always obey set_attribs_on_close. Fixes running the Windows client from a fat32 partition. 2020-06-01 Changes in burp-2.3.28: * 864: Separate files-only release for windows. * 861: Try to make hostname calls in burp_ca more robust. * 854: Fix "Directory: '(null)'" log message. * 855: Talk about the -d option in docs/restoring.txt. 2020-05-01 Changes in burp-2.3.26: * From Dan Tihelka: - Initial systemd socket activation support proof-of-concept. * 467: Sherlock221B's suggestion for accelerating protocol2 rabin: Skip min block size minus window size. * Socket activation fixes and cleanup. * 860: Fix incorrect cron summary example. 2020-03-01 Changes in burp-2.3.24: * 848: Protocol 2 - warn and skip on verify/restore of unsupported file types. * From Dan Tihelka: Improve systemd services a bit - Use type=simple and foreground mode in the server's service file. The forking is not necessary under systemd and it also nicely catches all the stdout/stderr messages when running in "foreground" - Create client service and timer units. The client is triggered every 30 minutes by the timer unit. * Move systemd files to a generic systemd directory. - Remove unmaintained distro-specific init scripts and specfiles. 2020-02-01 Changes in burp-2.3.22: * 845: Fix possible segfault in server-side listing. * 841: Fix protocol2 bug where cname was overwritten. * Man page and README tweaks. 2020-01-01 Changes in burp-2.3.20: * 597: Protocol 2 - prune the sparse index. * 800: Correct counters after resume. * 837: Fix bug where user= config causes exit. * Tidy up README. * From Orsiris de Jong: - Add Centos 8 building instructions to README. * From Bob Ryan: - Add smb2 magic id. 2019-12-01 Changes in burp-2.3.18: * 30: Add option to take a list of paths for restore. * Add '-a p' option, for outputing parseable file lists. * 832: Try to be more generic about IO_REPARSE tags. * From Vitalio: - Implement support of keeping readall capabilities after UID/GID switch. 2019-10-06 Changes in burp-2.3.16: * 832: Avoid 'could not stat' on OneDrive reparse points. * Upgrade Windows depkgs to: - openssl-1.1.1d. - pcre-8.43. 2019-09-01 Changes in burp-2.3.14: * 823: Fix server-initiated restore without a restoreprefix causing the client side's restoreprefix to be doubled. * From Orsiris de Jong: - Backup tool script 0.5.0 for burp 2019-08-01 Changes in burp-2.3.12: * Fix Windows memory breakage when restoring long paths. * Add long directories to tests. * Simplify windows mempool code. * Ability to choose to attempt to strip VSS on Windows restore or not, as well as disabling the Windows restore api, with '-x' and '-X' and options. 2019-07-01 Changes in burp-2.3.10: * Upgrade Windows depkgs to openssl-1.1.1c. * 739: Log each client command in the server storage directory. * 817: Bug fix - send winattr field in phase1. * 816: Bug fix - vss trailing data readonly problem on restore. 2019-06-01 Changes in burp-2.3.8: * 653: Seed functionality. * 815: .nobackup/.exclude does not stop the directory itself from being in the backup, only the contents. 2019-05-01 Changes in burp-2.3.6: * 812: Fix counters missing received/sent bytes broken in 2.3.4. * 808: Fix for ssl_peer_cn when server:port syntax is used. * From Orsiris de Jong: - 809: Updates to Windows upgrade script: + Better path fallback and optional conf file update + Removed duplicate error handling + Comments and smaller improvements + 813: Disable config file update by default * From Bob Ryan: - 811: Add ceph (cephfs) fs magic alias. 2019-04-01 Changes in burp-2.3.4: * Feature sponsored by Orsiris de Jong: - 782: Server failover feature. You may specify multiple server:port combinations in the client burp.conf. They will each be tried in turn until a connection is made. Orsiris's use case is that he has clients that may or not be on restricted networks where ports are blocked. You may also configure the client to try the next server:port if a backup actually failed. * 800: Create backup_stats before phase4, so that it exists if a backup is interrupted during phase4 and resumed. * 803: Man page notes about file encoding and encryption_password. * 806: Error out on protocol 2 with encryption_password set. * From melak: - Manpage clarification about compression/encryption and protocol version. 2019-03-03 Changes in burp-2.3.2: * 796: Fix not compressing log on phase3/4 resume. * 797: Fix wide-character restores on Windows. * 794: Fix burp not starting with only one listen address. * 735: restore_client client_can_X behaviour. - Obey the original client permissions. - Add super_client option, which behaves as restore_client used to. - Also add client_can_monitor permission. * Correct 'listen' section in the manpage. - There is no longer a default or compile-time options. * Run utests with valgrind in automatic testing suite. * Fix valgrind errors. * Fix minor memory leaks in timer code. * Fix potential buffer overruns when decoding attribs. * 769: ca_name and ca_server_name cannot be the same, so error early, to avoid certificate problems. * 774: Fix incorrect action names passed to server_post_script. * 775: Fix empty action in server_post_script. * 790: On restore, mention target directory in the client output. * Use librsync's rs_strerror() for better error messages. * Update freebsd test instance to freebsd 12. - Fix compiler warnings. * Remove LT_INIT, it seems to cause more problems than it solves. * Fix 'PACKAGE_URL undeclared' for older versions of autoconf. * From Orsiris de Jong: - Backup tool script minor fixes 2019-02-23 Changes in burp-2.2.18: * 797: Fix wide-character restores on Windows. * 794: Fix burp not starting with only one listen address. 2019-02-02 Changes in burp-2.3.0: * 777: Make include_glob able to detect windows drive letters. * From Olivier Cherrier: - NetBSD/OpenBSD fix for burp_ca. * 780: More logging for VSS. * Remove non-widechar Windows functions. * From Calogero Lo Leggio: - Remove debian packaging files from upstream repository * From Orsiris de Jong: - Backup tool script 0.4.6. 2019-01-01 Changes in burp-2.2.16: * 786: Don't run relative attack detection on CMD_FINGERPRINT. * 785: Fix incorrect xattr buffer size. * 784: Fix compiler warning d->d_name always evaluates to true. * From Michael Hanselmann: - Remove unused sbuf_print_alloc_stats prototype 2018-12-12 Changes in burp-2.2.14: * Fix restoring with compressed/encrypted trailing vss data. * Protect the client against restoring over symlinks that point outside of the desired destination directory. Giving a restore directory is now mandatory. Make '-d /' work on Windows. * 753: More logging for VSS. * Protect against malicious relative paths in sbuf parsing. * 766: Don't run xattr tests if the filesystem doesn't support them. * Check lengths in xattr/extrameta/acl. * Avoid path attacks via cname. * Fix 'configure --enable-xattr=no'. * Buffer safety check in asfd::extract_buf(). * Clean up burp_ca and make it more secure. * Fix buffer overrun in client-side long list. * Fix buffer overrun in asfd_do_read_ssl(). * 766: Correct gzip os type in utest/server/test_restore.c. * From Michael Hanselmann: - Add option to verify client certificate early. - Use "O_NOFOLLOW" as appropriate. - Sanitise autoupgrade os name. - Random delay on password mismatch. - Sleep before giving auth failure message. - 772: Ensure O_NOATIME is passed to system calls. - 770: In extra_comms: Avoid calling unlink(2) with NULL. - 768: In run_script: Limit number of arguments to avoid stack overflow. - 767: Avoid interactive question on CentOS when overwriting binary. 2018-10-01 Changes in burp-2.2.12: * From ziirish: - 253: 'logic' rules parser with new exclude_logic option. * 753: Send error to server when there is a VSS problem. * 754: Fixes for find_logic code. * 756: Man page correction about number of notify args. Also rename 'warnings' to 'warning_count' in notify_script. * 675: Stdout command line options. '-v' now overrides stdout=0 in the config file, to be 1. '-Q' now overrides stdout=1 in the config file, to be 0, and turns off the progress counter. '-v' used to be the version option - that is now '-V'. * 743: Fix broken status monitor command parsing. * 759: Log backup start/resume/complete/failure in the main log. * 760: Fix and prevent possibility of infinite loops on error in asfd code. * 758, 763: Read errors should interrupt backing up a file. 2018-09-01 Changes in burp-2.2.10: * 737, 738: Multiple listen addresses. * 747: Fix for ACLs not getting restored on directories, and fix metadata client side counters. * 744: Fix for truncated .created file. * 750: fail_on_warning config option. * From ziirish: - 742: Clarify backup_script_pre documentation. - 746: Add '-o' flag to override configuration options on the command line. - 3: Implement 'include_regex' . * From Orsiris de Jong and hakong: - 740: Updates for backup tool script, including calendar. * From pablodav: - Fix Windows upgrade script getting stuck after execution. * Add j:response-markers-on/off monitor commands. * Fix utests for travis mac builds, they have a new gzip version. 2018-08-01 Changes in burp-2.2.8: * Do not try to log remotely with client side post_scripts. * 693: Fix RS_BLAKE2_SIG_MAGIC detection. * 734: Fix typo in retention doc. * 728: On error in protocol2, print the last tried file. * 731: Minor documentation update from Iznohak: - Remove references to yajl. - Clarify usage of backup_tool_script on the burp server. - Remove unimplemented diff options. - Reorder man page examples and group them by action. 2018-06-22 Changes in burp-2.2.6: * 726: Fix 'burp -t' missing fields. * 720: Bug fix for falling back to server name when ssl_peer_cn is not set * 721: Take note of restore_interrupt return correctly. * 721: restore_interrupt should return error. * 723: Make iobuf_to_printable accept newlines. 2018-06-01 Changes in burp-2.2.4: * 718: '[b/Kb/Mb/Gb]' -> '[B/KB/MB/GB]' in man page. * 716: '0000000 never' instead of 'never' in status monitor. * 710: max_resume_attempts option - if exceeded, delete working dir. * 714: DST mktime fix. * 715: Edit backup_tool_script.txt for spelling and formatting, put on the website. * 700: Attempt to fix "stats file: had an exception". * Add more logging around manio_tell when resuming. * Fix/run code coverage. 2018-05-01 Changes in burp-2.2.2: * 645: Update burp-cross-tools Windows libraries to use docker (thanks to josvo for hints): - binutils-2.29.1 - gcc-7.3.0 - gmp-6.1.2 - isl-0.18 - mingw-w64-v5.0.3 - mpc-1.1.0 - mpfr-4.0.1 * From Orsiris de Jong: - 703: Add server port to Windows installer. - 693: Update Windows to librsync-2.0.2. * 693: Increase protocol1 strong length from 8 to 16. * 713: Fix for slow down after xattr warnings. * Add file descriptor number to asfd->desc, for better logging. 2018-04-01 Changes in burp-2.2.0: * 643: Protocol 2 restore load only up to the block that is needed. * 680: Strip trailing slashes from cross_filesystem option. * 689: Make list show working/finishing backups. * 707: Fix superfluous newlines on file vanished warnings. * From Orsiris de Jong: - 661: Add backup_tool_script housekeeping script. * All the fixes from burp-2.1.32. 2018-04-01 Changes in burp-2.1.32: * 696: Tests for some Windows installer command line switches, and use of hostname for cname. * 698: Fix for 'could not open current/incexc' warning on first backup. * 699: Remove use of strcasestr so that AIX is happy. * From Orsiris de Jong: - 701: Fix for broken Windows installer command line switches. - Man page tweak for rblk_memory_max. * From jirib: - spec file tweak for uthash. 2018-03-04 Changes in burp-2.1.30: * Revert "reopen syslog for child processes after fork". * Use iobuf_to_printable everywhere that iobufs are printed. * Don't run ncurses utests if you don't HAVE_NCURSES. * Bug fix for some utests accidentally closing stdin. * Protocol 2: Hash table for rblk lookups on restore/verify. Add rblk_memory_max option for configuring its size. * From Orsiris de Jong: - Replaced bits (b) with bytes (B) for quotas in man page. * From John Stoffel: - configure check for pkg-config. 2018-02-01 Changes in burp-2.1.28: * Set Windows creation time on restore. * From gfa-: - Fix for bedup junk files. * Unit test for bedup maximum hardlink code. * librsync_max_size option - only use librsync on files less than the given size. * Reopen syslog for child processes after fork. * Add a docs/restoring.txt file. * Tweak ratelimit man page descriptions. 2018-01-01 Changes in burp-2.1.26: * On non-Windows, strip vss on restore with the '-x' option. * Make 'burp -a m' work on a Windows console. * Monitor detect from storage when server side protocol unset. * Tweak man page 'backup_script_pre' section. * Fix all utests for openbsd 6.2. * Fewer warnings about the last backup on restore/verify. * Different setsockopt parameters for bulk packets with ipv6. * Log the return code of SSL_get_verify_result when it fails, so that we have a chance of knowing why "Certificate doesn't verify". * Make it a fatal error when bedup's do_hardlink() fails. Unlink the temporary path when bedup's do_rename() fails. This should stop bedup generating lots of mess when these things happen. * From Pablo Estigarribia: - Add additional entries to fstypes. * From BobRyanConsulting: - Add cifs entry to fstypes. * Note in docs/timer_script.txt about the internal timer_script. * Add Windows build tools to its own repo, and make the scripts download the sources: https://github.com/grke/burp-cross-tools * Make burp work with the new Windows build tools. 2017-12-01 Changes in burp-2.1.24: * Internalise the timer script. * Protocol2: Log a warning when a block cannot be retrieved during restore, and attempt to continue. * Fix for protocol 2 warnings not appearing on server side. * Fix encryption_password key length issue reintroduced in 2.1.18. Backups made by 2.1.18 to 2.1.22 using encryption_password should be considered broken. * Don't log version warning twice. * From Sergey Moskovkin: - Bug fix for rewriting the pidfile after forking - truncate first. * From Josef: - Include btrfs in fstype list. * From josvo: - Allow 'always' as an option to the timer script. * From DerGatt: - Small man page correction. 2017-11-01 Changes in burp-2.1.22: * On certificate exchange, do not rewrite the client config file if there are no changes. * Limit 'set file owner' warnings on restore when not root * On restore, don't send Windows VSS directory data to non-Windows clients. * On restore, don't send to split VSS data to non-Windows. * Fix travis for osx, where brew is now installing librsync 2.0.1. * Add 'monitor_exe' client option, in order to set where to look for the burp executable when forking a monitor client. This might be needed on systems that don't have any sensible way to self-determine a process' own path, such as openbsd. * From josvo: - Use ComputerName from Windows registry as suggested cname. * From Orsiris de Jong: - Added /nopowermode option to the Windows installer, with checkbox. This allows you to run timed backups when the laptop is not plugged in. - Update README and src/win32/README files. - Update Windows cross-tools and depkgs README file. * From trustchk: - Update README for AIX. * Upgrade Windows depkgs to librsync-1.0.1. * Upgrade Windows depkgs to pcre-8.41. * Upgrade Windows depkgs to zlib-1.2.11. 2017-10-01 Changes in burp-2.1.20: * On server daemonise(), rewrite the pid/prog to the lockfile without trying to open it a second time. This should avoid permission problems after chuser/chgrp. * Fix for 'success notification on failed backup'. * Check fileno() for errors in asfd.c. * List the mtime instead of the more recent of mtime/ctime. * Fix client_lockdir option. 2017-09-01 Changes in burp-2.1.18: * From qm2k: - Fix buffer overrun and slightly refactor xstrsplit in glob_windows.c - Add Content-Type to notification emails. - Windows exclude_fs functionality. * Add Content-Type to summary emails. * Fix missing newlines on WARNING/MESSAGEs logged by the server. * Make 'orig_client=' work OK. * Drop privileges after main pidfile creation (instead of before). * Be slightly more helpful with SSL error messages. 2017-08-10 Changes in burp-2.1.16: * Fix encryption_password key length issue introduced in 2.1.14. Backups made by 2.1.14 using encryption_password should be considered broken. * Follow AC_INIT defines throughout the codebase, including the Windows installer. * Log strerror(errno) when failing to write a lock. * From Orsiris de Jong: - Add systemd file for running Burp without root privileges. 2017-08-01 Changes in burp-2.1.14: * From Marek Szuba: - Use pkg-config to detect ncurses. * From Orsiris de Jong: - Fix burp service file for RHEL / CentOS 7 - should be 'forking'. * From Xaltonon: - Add proper key-derivation (and IV) for client encryption. * Make key derived encryption transparent, and add salt. * Upgrade Windows depkgs to openssl-1.1.0f. 2017-07-01 Changes in burp-2.1.12: * From moenoel: - Fix segfault when sb->link.buf is NULL. * Respond correctly to bfd->read returning an error - fixes client side segfault. * Fix for server side autoupgrade path corruption. * Add README link to the ansible wiki. * From ziirish: - Add the ability to reset strlists through the special ':=' syntax. - Fix missing globfree. * Fix a possible segfault in server protocol2 backup phase2. * Fix missing Windows log timestamps. Fix 'resumed' timestamps. * Minor typo in man page, for include_fs. * Make sure iobuf buffer is printable before trying to log it. Also, print the length. 2017-06-01 Changes in burp-2.1.10: * Features sponsored by VSHN (https://vshn.ch/): + Ability to listen on multiple port/status_port numbers, individually configurable with additional max_children/max_status_children. + Add the ability for the client to connect to different server ports according to whether it is doing backup/restore/verify/list/delete. + Allow restore during a backup. + JSON output/input supports multiple actions on the same client. * Fix for restore interrupt on transfer_gzfile_inl() failing. * More UTF-8 in paths, from Alexey Kovalenko. * Add offsets to timestamps. * Take note of warnings from the status server. * Log timestamps of resumes in a file called 'resumed' inside the backup directory that was resumed. * Write '.created' when client directory is initially created. * qm2k's patch to limit notify_script to 1000 lines of log. * Fix two more bugs in relinking deleted hardlink master records. * Add Dave Ludlow's comments to the Windows2008 bare metal file. 2017-05-01 Changes in burp-2.1.8: * Fix two bugs in relinking deleted hardlink master records. * Fix status monitor failing if client password is not set. * trustchk's AIX: Complete initial support. * Increase size of d_name on windows. * chrisv5179's fixes for error handling in burp_ca. * moenoel's include_fs option. * moenoel's partial fixes for strip_from_path(). * Fix for problems with truncating incexc file. 2017-04-01 Changes in burp-2.1.6: * Fix for bug treating too many files as new (a problem since 2.1.2). * Fix for changed/same counters being reversed in the monitor. * Set times on symlinks instead of dereferencing them. * Fix bug with backup_script_reserved_args = 0. * moenoel's burp_ca fix for SunOS. * trustchk's addition of ZFS to fstypes so that it can be excluded using exclude_fs. 2017-03-01 Changes in burp-2.1.4: * Protocol 1: When a file that is deleted on the client side was one that others hardlink to, do not treat the new first entry as a new file - link it in from the previous backup. * Protocol 2: It is OK if the cfiles directory already exists when trying to mkdir it. * Set windows d_name buffer to MAX_PATH. * Tests for run_action. 2017-02-01 Changes in burp-2.1.2: * Protocol 2 fixes: - Client side empty block memory leak - server side empty blocks lock up - server side limit number of paths loaded into memory - metadata protocol2 restore removing security capabilities * Don't try to set file times on Windows junction points. * Create 'bsparse' for recreating the sparse index. * Cleanup code in protocol1/backup_phase2.c. * Reinhard Biegel's fixes: - bad open call when ca_conf is not set - config check logic for ca_burp_ca 2017-01-01 Changes in burp-2.1.0: * Fix certificate and csr permissions on receive_a_file(). * Fix for metadata restore removing security capabilities. * Fix for 'burp -a S' not outputting all clients. * Compile and run utests on Windows. * Delete unused Windows build code and icons. * Update manpage for server_script_pre/server_script_post. * Upgrade Windows dpkgs to openssl-1.1.0c and pcre-8.39. 2017-01-01 Changes in burp-2.0.54: * Fix certificate and csr permissions on receive_a_file(). * Fix for metadata restore removing security capabilities. * Fix for 'burp -a S' not outputting all clients. 2016-11-07 Changes in burp-2.0.52: * Make compilation with openssl-1.1.0 work. * Fix for client side autoupgrade. * Rename 'enum page' so that compilation on Solaris 10 works. 2016-11-02 Changes in burp-2.0.50: * Bug fix for excluding directories directly inside /. * Fix for dindex regeneration concurrency accidently resulting in protocol2 data files getting deleted prematurely. 2016-10-01 Changes in burp-2.0.48: * Fix server-side segfault on re-encoding Windows attributes. * Stop server child hanging on verify/restore when gzipped file is corrupt. * If a file is missing on verify/restore, continue to verify/restore more files. * Make server initiated restores work in protocol 2. * Benjamin Sans' fixes for cname_ options: + manpage misses some returns + use globalcs while authenticating client + fix unit test + allow to change cname_fqdn and cname_lowercase on already setup servers * Add better docs for the cname_ options. * On the client side, make OPT_CNAME_FQDN and OPT_CNAME_LOWERCASE affect the cname retrieved from the ssl cert, as well as the cname when the certificate is generated in the first place. * Fix chuser/chgrp when switching with orig_client. * Fix server-initiated restores with orig_client. * Add end-to-end test for a server-initiated restore. * Avi Rozen's fixes: + for possible hardlink truncation on restore. + for burpfs support: - monitor/browse: link field added to file entries - monitor/browse: use "*" as browse directory in order to dump all files - fixed handling of NULL browse directory 2016-09-04 Changes in burp-2.0.46: * Fix server segfault in no-fork mode. * Counter fixes for protocol1. * Server to client counters via JSON. * Replace deprecated readdir_r() with readdir(). * Make asfds count received and sent bytes instead of trying to do it inside the backup/restore/verify code. * Don't count metadata in the phase1 estimate counts. * Add cname_lowercase to force lowercase cname on server-side (from Benjamin Sans). * Add cname_fqdn option to allow stripping FQDN (from Benjamin Sans). * Switch to pure C for non-Windows (from Ruben Kerkhof). * Make 'burp -a e' work again. * Remove restore_spool. * Make 'burp -a s/S' output the 'burp -a m' log lines on error. * Re-enable chuser_and_or_chgrp code, which got broken during the automake/autoconf refactoring a few versions ago. * Remove unused configs/install script. * Use /proc/%d/exec to figure out the path to run when forking a monitor 'burp -a m' process. * Fix for the debian init script so that it understands $BURP_USER (from Peter Maloney). 2016-07-30 Changes in burp-2.0.44: * Default to protocol 1. * Another attempt to remove network timeout from champ chooser main socket. * Fix building with explicitly added --enable-xattr. * Enable SE_SECURITY_NAME privilege on Windows. Should fix permission problems on attempting to back up: C:\Program Files\WindowsApps C:\Windows\InfusedApps 2016-06-30 Changes in burp-2.0.42: * Remove network timeout from champ chooser main socket. * Tests for monitor cache. * Deduplicate the current manifest locally in backup_phase2. * Some protocol 2 counter improvements. * More coverage for cmd.c. * Tests for src/server/extra_comms.c * Tests for src/client/extra_comms.c * Local time in the top corner of the monitor. * Fix C++11 -Wliteral-suffix warnings. * Make sure that places that are checking RS_DEFAULT_STRONG_LEN have included librsync.h. * Bug fix for restore_script_pre running instead of restore_script_post. * Correct mistakes in the manpage about the status monitor. * Bug fix for status client ncurses with -C showing last backup only. * Fallback to /usr/sbin/burp for the forked 'burp -a m' process. * A small tidy up in protocol1 phase4. * Fix pathcmp on raspberry pis. * Fix bug in rabin fingerprinting due to signedness on the window. * Make the server verify checksums during a protocol2 backup. * Fix bug that let clients overwrite existing protocol2 data files. * Correct rabin.h include in src/server/restore.c. * Rabin fingerprinting should be using unsigned char. * Rabin fingerprinting should reset window after every block. 2016-05-30 Changes in burp-2.0.40: * Tidy symlink/readlink functions. * Yopito's patches to help RHEL5 builds. * Fix 'burp -a S', which has been broken up to this point in burp-2.0.x. * Add more unit tests. * Fix for writing statuses down multiple server status child pipes. * Fix for loading initial running client status server data. * Fix bugs in sorting paths with characters with ASCII values higher than 127. Make my_alphasort() use pathcmp(), like burp-1 did. * Clean up network_timeout, ratelimit, streamtype and fdtype. * Tidy up setup_signals(). * Make path to CRL configurable. * Stéphane Berthelot's fix for too many lines for each client in the status monitor summary. * Fix segfault when using '-C -a s'. * Fix compilation without ncurses. * Upgrade Windows depkgs to openssl-1.0.2h. * Upgrade Windows depkgs to pcre-8.38. 2016-04-30 Changes in burp-2.0.38: * Add 'enabled' option (from Benjamin Sans). * Fix bugs in protocol2 delete_unused_data_files(). * Remove some hardcoded paths in scripts. * Add ecryptfs to the list of file types that can be excluded (from scosu). * Added ubuntu baremetal restore instructions (from Wybren Buijs). * Use an asfd for stdout in status_client_ncurses. * Make rubble cleanup with working_dir_recovery_method=delete delete unused data files. * Fix bno printf 32bit compile warnings. * Fix bug in server side restore regex parsing introduced in 2.0.36. * Don't try to set IPTOS_THROUGHPUT if it is not defined. * Fix and tidy up include_glob test. * Fix champ_chooser to try until max_tries instead of 10 seconds (from Stéphane Berthelot) * Add glob_after_script_pre option to evaaluate glob after script pre execution (from Benjamin Sans). * Adapt rpm spec file for 2.0.36, test on CentOS7 (from Marco Fretz). * Fix protocol2 backup deletion not obeying multiple 'keep' values. 2016-03-31 Changes in burp-2.0.36: * Support xattrs and acls in protocol2. * Fix acl+xattr bug introduced in 2.0.28. * Fix default acl bug that has existed forever. * Fix acl memory leak. * Some tests for status_client_ncurses.c. * Add server_can_override_includes option. * Fix Travis Darwin builds. * More test coverage for champ_chooser. 2016-02-29 Changes in burp-2.0.34: * Add support for glibc's additional algorithms in crypt(3)-style passwords ("passwd" configuration option). * Unit tests for check_passwd. * Bug fix for acls - use acl_free() instead of free_w(). * Make server accept 'restoreend_ok' and 'restoreend ok'. * Make client send 'restoreend ok', the same as burp 1.4.40. * Some work on protocol2 xattr support. * Implement labels being passed via status monitor. * Use localtime() instead of gmtime() in cntr_print(). * Better locking for burp_ca. * Make close_zp() not crash on OpenBSD on Z_RET_ERROR. * Add Travis file. * Turn on all tests for NetBSD. * Fix dindex filename in protocol 2 phase 4 regenerate client index. 2016-01-31 Changes in burp-2.0.32: * Tweak README install instructions. * Fix ncurses output option. * Fixes for ncurses support on OpenIndiana. * Header inclusion order fixes. * NetBSD be64toh and le64toh cleanup. * Make xattr code compile on NetBSD. * Attempt SSL_accept again on SSL_ERROR_WANT_READ. * Fix 'make distcheck'. * Add 'make install-config' and 'make install-all' commands. * Use AC_CHECK_TYPES to check for struct utimbuf. * Replace flock locking with bash file creation locking. * Portability fixes in test_main. * Make xattr.c depend more on detected functionality, less on OS. * Compiler warning fixes on various platforms. * Fixes so that burp runs on DragonFlyBSD. * Fix a protocol2 champsock problem for FreeBSD. * Use the BSD-style xattr functions on NetBSD. * FreeBSD now fully added to automatic test system. * Simplify checks for endian conversion functions. * Install bedup, bsigs, and vss_strip. * Don't try to create sockets with mknod. * Fix xattr test so that ordering doesn't matter. * Add keepalive flag to sockets. * Remove child status pipe from the connections that timeout. * Mark log functions as printf-like, clean up format warnings. * Allow for setting test verbosity from environment. * NetBSD fix - getaddrinfo: don't lookup http service. * Set timezone to UTC before calling mktime, otherwise Solaris uses the system timezone. * Use gmtime instead of localtime, to fix test failures on OpenIndiana and OmniOS. * Fix AM_PROC_CC_C_O warning with older automake versions. * Fix base64 decoding test on NetBSD. * Fix test_fzp for older zlib versions. * Updated yajl in burp-depgs in order to fix a Windows XP linking bug. 2016-01-03 Changes in burp-2.0.30: * Ruben Kerkhof has rewritten the automake/configure system. * Make CMD_INTERRUPT work in protocol 2. * Tests for restore. * Tests for backup phase2. * Tests for champ_server. * Tests for slist. * Fix minor memory leaks. * Bug fix for deleting the current backup. * Correct documentation about ca_crl_check. * Fix listing all backups and using '-d'. * Fix status monitor segfault on exit. 2015-11-30 Changes in burp-2.0.28: * Use the same maximum blocksize as rsync ((int32_t)1<<17). * Clean up client side librsync load_signature code. * Add freebsd to AWS automatic build (actual tests not working yet). * Parallel test runs. * Update Debian autoupgrade script. * A lot more unit test coverage. * Always flush asfd at the end of tasks. * Make bad regexes produce error messages. * Make bad restore regexes produce a message on the client side. * Bug fix to make bad strlist regexes not match everything. * Fix memory leaks in xattr.c. * Fix memory leaks in cntr.c and cstat.c. * Fix segfaults in cstat.c. * Get basic diff to work. * On restore, always open deltas via zlib, so it doesn't matter whether they have been compressed or not. * Make it possible to use '-b all' on restore/verify. Use '-b all' in tests. * Don't quit the restore if inflating or patching failed - move on to the next file. * Fix listing all backups. * Remove json list option. * Don't build separate .a files. Just use .o files. * Fix protocol 1 EFS restore breakage. * Add some EFS files to the Windows tests. * Fix winattr values not being preserved on changed files. * Fix for yajl sprintf_s. 2015-10-31 Changes in burp-2.0.26: * Paid for by an anonymous user: add a new screen to the Windows installer for choosing 'include' directories. * Upgrade Windows depkgs to openssl-1.0.2d. * Upgrade Windows depkgs to librsync-1.0.0. * Add internal CRL revocation feature based on a patch from Sebastien Couppey (as opposed to using the old ssl_extra_checks_script server pre script). * Fix protocol2 bug where rblk wasn't initialised. * Add linux i386 machine to automatic tests. * Use -Werror when build utests. * Delete unused protocol2 data files. * Eliminate compile warnings from the Windows build and add -Werror to the Windows CFLAGS. * Change dhfile.pem generation command to use -dsaparam and 2048 bits. * Fix memory leak in protocol2 restore. * Fix protocol2 problem where new candidates wouldn't be loaded during backups. * Make sure split/strip_vss don't break protocol 2. * Unit test client/auth.c by overriding asfd function pointers. * Fix iobuf_free() bug. * Code coverage report for all files. * Start unit testing client restore. * Preparation for fixing restore interrupt for protocol 2 2015-09-30 Changes in burp-2.0.24: * Another fix for sigchld handling. * Tests for delete algorithm. * Tests for bu_get. * Fix a bug where deleting with multiple keep settings and no hardlinked archives would result in too many backups getting removed in the oldest keep range. * During backup, read compression settings from the attribs buffer sent from the client rather than relying on the server config being correct. * Fix a bug where interrupting the server whilst it was deleting old backups would result in the next backup erroneously being given a backup number incremented from the timestamp in the 'deleteme' directory. * Fix the 'manual_delete' option so that it works on backups instead of just the rubble left behind by a librsync=1 backup. Add the option to the man page as it was missing. * Add acl=[0|1] and xattr=[0|1] options so that acls and xattrs can be turned on and off at runtime. * Ensure all logw() messages end with a newline. 2015-09-04 Changes in burp-2.0.22: * Make protocol2 resume work. Add unit tests for resume components. * Store manifest fingerprints and md5sums in binary instead of strings, plus related endian fixes. * Fix '-a l' regex so that it isn't split in two at the right-most colon. * Fix possible buffer overrun when encoding attribs. * Fix protocol 1 phase 2 resume bug introduced in 2.0.20. * Fix protocol 1 phase 2 bug where librsync was never used. * Fix possible protocol 2 phase 2 segfaults. * Make protocol 1 and 2 share the same code for phase 3. * Set default_md=sha256 in CA.cnf. * Use 'stat' for checking ssl files instead of 'lstat', and log if the ssl file was not found. * Fix path for server initiated backup via timer_script. * Use getpid() for random delay seed so that I can get rid of OS specific clock code and '-lrt' link flag. * Get xattr working on Mac OSX. This should mean that resource forks and finder info backup/restore is working. * Fix Windows protocol1 restore segfault when using split_vss. * Fix directories in Windows protocol1 strip_vss mode. * Fix from Patrick Van der Veken to correct typo in timer_script that meant month settings were broken. * Make listing a non-existent backup give an error. * Stop using scandir with a comparison function, in order to avoid problems with compilation on Macs. * Fix bug where Windows backups with split_vss was saving CMD_METADATA instead of CMD_VSS. * Prevent warnings when listing backups that contain metadata. * Fix for SIGHUP causing server to stop listening. * Use flock to stop simultaneous runs of burp_ca, hopefully preventing CA corruption. * Unit tests for src/client/find.c, enabling the removal of some of the time consuming end-to-end tests. * Fix 'nobackup' conf item. * Don't use absolute path to 'sort' in 'make test'. * Fix bug where fs_name_max could get set to an incorrect value on the server side, causing scandir to fail at the end of a backup. * Fix for a growing list of defunct child processes on busy servers, where overlapping sigchlds would get conflated into one, and waitpid would only get run once. * Set the server address in linux client burp.conf to "::1" if IPv6 is supported, and "127.0.0.1" otherwise. * Substitute 127.0.0.1 for the default burp.conf server address if the OS doesn't have IPv6. * Fix for server side restore file causing '-a m' to exit. * Fix large file support for 32 bit machines. * Let the champ chooser continue if candidate_add_fresh could not read a new candidate file. * On the server, log the client address and port for every connection. * Upgrade Windows depkgs to openssl-1.0.1p. 2015-07-01 Changes in burp-1.4.40: * Fix '-a l' regex so that it isn't split in two at the right-most colon. * Set default_md=sha256 in CA.cnf. * Correct logp format strings in quota.c. * Upgrade Windows depkgs to openssl-1.0.1o. 2015-05-30 Changes in burp-2.0.20: * Fix conf bug that was causing the server to segfault when the client used the -C option. Also add unit test for it. * Add documentation about retention and keep values. * Make status monitor print a warning in the JSON response if there was a problem with the request. * Add status monitor commands to turn pretty printing on and off. * Add documentation about adding and removing clients. * Use librsync hash blake2, if available on both server and client. * Remove 'working_dir_recovery_method = use'. * Stop bedup from trying to process protocol 1 directories. * Fix bedup segfaults. * Fix bug where setting the same include in both client burp.conf and server clientconfdir file could result in the item being used twice as a starting point and causing chaos. * Add 'include' directories that are not starting points to the 'cross_filesystem' list. * Add more protocol 1 phase 2 interrupt tests. * Fix some protocol 1 phase 2 resume bugs. 2015-05-30 Changes in burp-1.4.38: * Turn SO_REUSEADDR on instead of turning it off. 2015-05-03 Changes in burp-2.0.18 * Fix bug where an error generated by link() was obeyed, but not logged. * Allow compilation when openssl has no SSL_OP_NO_COMPRESSION option Log a message if an attempt to use the feature was made. * Fix problem where a client connecting while a backup was finalising would cause 'backup failed' emails to be sent. * Fix two possible segfaults discovered with efence. * Fix status monitor segfault due to recent conf system changes. * Use setlinebuf(stdout) with '-a m' so that reading processes get the output of the logp printfs straight away. * Add out-of-date report script from Pablo Daniel Estigarribia Davyt. * 'backup_script_reserved_args=0|1' and 'restore_script_reserved_args='0|1' client side options. * Fix protocol2 dpth incrementing bugs. * Fix Windows VSS output so that it prints the writer names at the start of backups. * More unit tests. * Add some documentation about working_dir_recovery_method. * Don't open the log when about to delete the working directory. * Stop server side 'auth ok' message logging a freed string. * Updates to out_of_date_report_script from Pablo Estigarribia. 2015-05-03 Changes in burp-1.4.36: * Fix bug where an error generated by link() was obeyed, but not logged. * Fix possible segfaults in asyncio.c. * Don't open a log file in a directory that we are about to delete. Fixes 'error in get_lock_and_clean()' that was happening with some network attached storages. 2015-02-28 Changes in burp-2.0.16: * Add ability to do 'burp -a l -b c' and list the files in the current backup. * Rewrite the conf system. Gives the ability to use 'burp -t' to show the current conf settings. Add '-c ' and '-C ' to get client/server conf settings, and clientconfdir settings. * More unit tests. * Add 'scan_problem_raises_error' option, which lets you treat phase1 scan warnings as hard errors. * Fix bug where 'burp -a S' was limited to 24 lines. * Make 'burp -a S' not truncate client names. * Implement glob '. /some/dir/*' conf syntax on non-Windows machines. * Changes to support librsync-1.0.0. * When listing a directory, include a '.' entry to indicate the directory itself. * When listing a directory, send an error if the directory was not found. * Fix for client getaddrinfo error message. * Fix resume bug when a backup is interrupted before phase1 has got under way. * Fix resume bug where an interruption in phase3 causes problems. * Fix compile when there is no ncurses. 2015-02-28 Changes in burp-1.4.34: * Fix bug where permissions would not be backed up correctly when only permissions have changed. * Support librsync-1.0.0. * Bug fix for client getaddrinfo error message. * Upgrade Windows depkgs to openssl-1.0.1m. 2015-03-02: Changes in burp-2.0.14: * Improve utest/Makefile. * Add unit test for base64.c. * Make from_base64() ignore invalid characters. * Make the path to pidfiles in the config files substitutable. * Fix some phase1 bugs related to min/max_file_size and hard links. * BSD compilation fixes from Stefan Paletta. * Make restoring a hard link without restoring the original file work. * Fix for a protocol 1 bug to do with the first hardlinked entry being deleted on the client. * Make verify work for protocol 2. 2015-02-06: Changes in burp-2.0.12: * Move /etc/burp scripts to /usr/share/burp/scripts. * Do some sed replacement when installing config files. * Start to add unit tests with 'check'. * Don't return code for restore warnings if there was an error. * RHEL updates from Andrew Niemantsverdriet: Added support for systemd and cleaned up the spec file according to rpmlint. * Allow syslog and stdout logging at the same time (was broken in 2.0.10). * Fix some file descriptors timing out due to the network timeout code, when they shouldn't be considered for that. * Use __STDC_FORMAT_MACROS to get rid of printf warnings on Debian 7.6 and also use them for Windows printf formatting, which simplifies some code. * Upgrade Windows depkgs to openssl-1.0.1l. 2015-02-05 Changes in burp-1.4.32: * Allow syslog and stdout logging at the same time (was broken in 1.4.30). * In the client, don't use the return code for restore warnings if an error code is already set. * Upgrade Windows depkgs to openssl-1.0.1l. 2015-01-03 Changes in burp-2.0.10: * There are many changes since the last burp-2 release, including: - Massive reworking of the status monitor system and ncurses client. - Massive reworking of async i/o. - Forward ports of all 1.4.x patches. * The main focus was improving the status monitor system in order to better support projects like burp-ui. There are still missing pieces to it, but most of the basics are now there. Be sure to read the UPGRADING file. Protocol 2 is still work in progress, so do not expect protocol 2 backups to work with future releases. 2015-01-03 Changes in burp-1.4.30: * Better SSL error logging. * Replace c_rehash with a line of shell script, from Markus Heberling. * Use lutimes to set times on symlinks. 2014-12-03 Changes in burp-1.4.28: * Help Mac compilation by removing calls to basename() in vss_strip.c and bedup.c. * Add soft/hard quota feature. 2014-10-28 Changes in burp-1.4.26: * Bug fix for incorrect handling of '' in check_browsedir(). * Bug fix for made up directory names in check_browsedir() not having the directory bit set. * Close unnecessary file descriptors after forking children. * Check for EAGAIN and EINTR when read on child status pipe fails in main server process. * Make status_wfd non-blocking and run a select() on it before the child tries to write to it. * Fix a possible segfault when reading from a status client. * Log a more accurate description of ssl_ciphers. * Make the Windows client return codes other than 0. * Set openssl options SSL_OP_NO_SSLv2 and SSL_OP_NO_SSLv3, to avoid the poodle vulnerability. * Slightly better logging when SSL fails in src/async:do_write(). * Bug fix for bedup not obeying clientconfdir configs. 2014-09-28 Changes in burp-1.4.24: * Make vss_strip.c include the configure output, so that vss_strip gets the large file options. * Make the server initiated restore not require an 'include' line. * Add the ability to list backup_stats/restore_stats/verify_stats via the status port. * Add 'client_is_windows=0|1' to the stats files. * Bug fix for resume bug not correctly truncating the phase2/unchanged manifests. * Make restore_clients override client_can_restore/list/verify/delete settings. 2014-08-31 Changes in burp-1.4.22: * Fix for xattrs with zero length values. * Fix for restore bug when a file cannot be opened. * Tweak to compile with burp-2 cross-tools/depkgs. 2014-07-26 Changes in burp-2.0.8: * There are many changes since the last burp-2 release, including: - Ability to configure the server listening addresses at run time. - Run the champion chooser as a separate process. - Option to remove the need for a client password and cname - use certs exclusively. - Warn if files are restored from outside a hardlinked_archive. - Fix unnecessary duplication and deletion with hardlinked_archive=1. - Fix backups failing with too long server side paths. - Add option to configure SSL compression. - Burp protocol 1 support looks good. - Many code improvements and bug fixes. * This release was focused on making the burp protocol 1 support good. Be sure to read the UPGRADING file. Protocol 2 is still work in progress, so do not expect protocol 2 backups to work with future releases. 2014-07-26 Changes in burp-1.4.20: * Fix for backing up fifos with xattrs. 2014-07-06 Changes in burp-1.4.18: * Fix some Mac compilation warnings. * Fix a bug where specifying an alternative restore directory with a trailing slash would not work. * Set REMOTE_ADDR and REMOTE_PORT environment variables. * Force the user spell out '-a delete', to avoid confusion with the future 'diff/long diff' options. Prevent old clients from deleting, to prevent potential accidents. * Fix a problem where interrupting the client during a list could leave the server child process hanging indefinitely. 2014-05-31 Changes in burp-1.4.16: * Bug fix for restoring permissions on empty directories. * Compile for systems that have no O_NOATIME and fdopendir() - ie, Mac OS X. * Bug fix - some readlink() returns were getting assigned to an unsigned variable, not a signed variable. * Removed references to old '-l' logfile option. * Added Jarkko Kniivilä's FreeBSD init script. * Added Jarkko Kniivilä's tweaks to make scripts run more easily on FreeBSD. * In bedup, detect if a rename failed after unlinking the target file, print a warning and stop processing. * Fix a case where an interrupted backup may not have been automatically recoverable. * Set IPTOS_THROUGHPUT bit on Windows, so that burp is doing the right thing even if Windows decides to remove it. 2014-04-29 Changes in burp-1.4.14: * Add an 'atime' option, which allows the user to choose whether to use O_NOATIME or not. * Fix function not returning anything when it should return an int in list_server.c. 2014-03-29 Changes in burp-2.0.4: * First release of burp-2. Use with extreme caution. 2014-03-29 Changes in burp-1.4.12: * Make 'make test' skip tests if no source of randomness seems to exist for openssl to use. * Bug fix for restoring correct file times on Windows clients. * Bug fix for false failure notification - differentiate between not getting a lock and getting an error when trying to get a lock. * Turn off excessive logging on some restores. * Debian packaging changes. * Rename configure.in to configure.ac to avoid Debian build warnings. * Make test script check for lock file instead of working/finishing symlinks when checking whether an operation has finished yet. * Fix encryption casting problem on 64 bit big endian platforms (eg, s390x, ppc64, sparc64). 2013-12-01 Changes in burp-1.4.10: * Bug fix for false failure notification when a client tries to connect again while a backup is still in progress. 2013-11-27 Changes in burp-1.4.8: * Be sure to close directory file descriptor if fdopendir() fails. * Extend the JSON list functionality so that covers all types of listing. * Allow the backup to carry on if a filename was too long for the server. * Regenerate server certificates if the CA directory exists and is empty. * Add machine-readable statistics files for restores and verifies in the backup directories. * Bug fix for server not exiting after one connection with '-n'. * Add 'manual_delete=' option. This makes the server move directories to be deleted to the specified directory, with the intention that they will be deleted manually (perhaps by a cron job) later. 2013-10-30 Changes in burp-1.4.6: * Add client option '-a T', which checks whether it is yet time for a backup without actually doing a backup. 2013-10-02 Changes in burp-1.4.4: * Bug fix for include_glob xmalloc success check on Windows. * Fix long list and status monitor output when a directory argument is given. * Fix restore problem with backups containing both compressed and uncompressed files. * Bug fix for an interrupted backup causing a resume when a restore_client connects. * Bug fix for defaulting 'resume partial' to off. 2013-09-02 Changes in burp-1.4.2 * Add a machine-readable statistics file to each backup directory. 2013-07-29 Changes in burp-1.4.0 * Add Abbas Khan's RHEL spec files and related things. * Add Benjamin Sans's include_glob feature. 2014-01-27 Changes in burp-1.3.48: * Bug fix for restoring correct file times on Windows clients. 2013-12-30 Changes in burp-1.3.46: * Another bug fix for false failure notification - differentiate between not getting a lock and getting an error when trying to get a lock. * Turn off excessive logging on some restores. 2013-12-01 Changes in burp-1.3.44: * Bug fix for false failure notification when a client tries to connect again while a backup is still in progress. 2013-11-27 Changes in burp-1.3.42: * In backup phase 4, a 'deleteme' directory may already exist if the server was previously interrupted whilst deleting it. If so, delete it before attempting to rename anything to it. 2013-10-30 Changes in burp-1.3.40: * Make the server request the whole file when ctime changes on Windows, otherwise changes to metadata when the file content is unchanged will not be noticed. * Add 'delete' to the client help output. * Bug fix for the include_glob code being run on the server side. * Bug fix for in the include_glob code not initialising the glob_t struct correctly. * Get 'include_glob=' in the clientconfdir server side file to work without also needing an 'include='. * When backing up, open files and directories with O_NOATIME flag so that the access time is not changed. * Bug fix for no failure notification being sent when the backup directory cannot be created. 2013-10-02 Changes in burp-1.3.38: * Fix long list and status monitor output when a directory argument is given. * Fix restore problem with backups containing both compressed and uncompressed files. * Bug fix for an interrupted backup causing a resume when a restore_client connects. * Bug fix for defaulting 'resume partial' to off. 2013-07-29 Changes in burp-1.3.36: * Fix truncation of timestamps in the live status monitor. * Default the 'resume partial' feature to being off, and add an option to turn it on. * Upgraded Windows cross tools to: + binutils-2.23.1 + gcc-4.7.3 + gmp-5.1.2 + mingw-w64-v2.0.8 + mpc-1.0.1 + mpfr-3.1.2 * Upgraded Windows depkgs to: + openssl-1.0.1e + zlib-1.2.8 + pcre-8.33 2013-06-29 Changes in burp-1.3.34: * Contributions from Avi Rozen: + Major autoconf cleanup. + Initial support for cross-building android targets. * On the server, indicate where logging is occurring. * Fix bedup segfault when using -m with no argument. 2013-05-31 Changes in burp-1.3.32: * Fix status monitor segfault. * Run timed backups with lower thread priority on Windows. * Add 'vss_drives' option, which gives the ability to specify which Windows drives get a VSS snapshot. 2013-04-02 Changes in burp-1.3.30: * Add a warning when run on Windows without admin privileges. * Perform fewer lstat()s on systems that support d_type (ie most Linux systems), in order to speed up certain operations. * Allow _FORTIFY_SOURCE to work. * Fix problem with burp_ca.bat and repeated field names in burp.conf. * Put registry keys back in the Windows installer. * Fix for SIGHUP reload causing the server to go into non-forking mode. * Indicate the backups that are deletable. * Add a client option for deleting deletable backups. * Add a 'client_can_delete' option on the server. * Fix for using the wrong lock directory when 'directory' is overridden for a client. 2013-03-30 Changes in burp-1.3.28: * Add the ability to resume partially transferred files. * Make sure 'timer conditions not met' message appears on the client. * Bug fix for turning off compression before resuming a backup. 2013-03-05 Changes in burp-1.3.26: * When a read_blockdev option matches a symlink, backup the destination as a raw block device. * Avi Rozen's valgrind cleanup. Includes a fix for a bug in restore_client.c where overwrite_ok checked the wrong stat struct when considering a FIFO. * Add '-v' option to bedup (output the names of duplicate files). * Add '-d' option to bedup (delete duplicates instead of hardlinking - not for use on burp storage directories). 2013-01-29 Changes in burp-1.3.24: * Bug fix for strip_vss/split_vss truncating backups of changed files on Windows. * Bug fix for autoupgrade failing on Windows if autoupgrade_dir has been modified. * Bug fix for listing backups created with split_vss=1. * Bug fix for restoring uncompressed encrypted backups created with split_vss=1. * Bug fix for making backups with min_file_size and split_vss=1. * Bug fix for Debian init do_stop() function from Peter Maloney. * Bug fix for resuming backups where a file to patch has already been hard linked into place. Also get the client to report a more helpful message if any similar problem happens. * Bug fix for signal handler race conditions. * Bug fix for the forkchild child not exiting when execv fails. * Changed the run_script() code to take an array. * Added server_script_pre_notify and server_script_post_notify options. * Rework the test script so that it can test Windows clients. * Attempt a Windows signal handler to try to shut VSS down nicely on certain signals, such as Ctrl-C. * Include an example server side offsite rsync backup script. This is not quite working properly, but it may still be useful. * Bug fix to stop Windows going to sleep during a backup, from Avi Rozen. 2013-01-02 Changes in burp-1.3.22: * Contributions from Avi Rozen: + Added a '-j' option to format the long file list as JSON. Intended for use with burpfs (https://github.com/ZungBang/burpfs). + Bug fix for list/verify/restore when regex is long and/or contains a colon. + Bug fix for split_vss backups being restored on a linux restore_client with the -f (force) option. Prevents overwrite of the destination file with the vss footer. * Contributions for burp_ca from Patrick Koppen: + Bug fix for the size option. + Use shell numeric comparison instead of string comparison. + Added subjectAltName patch (in case it is needed outside of burp). 2012-12-18 Changes in burp-1.3.20: * Bug fix for split/strip_vss = 1 on the client side always causing all files to be backed up. * Make configure report /usr as the default prefix. 2012-11-02 Changes in burp-1.3.18: * Turn --enable-ipv6 configure option into --disable-ipv6. * Print a carriage return when rewriting ssl_peer_cn on Windows. * Make all places that log 'out of memory' also log the function name. * Remove version_warn debug. * Tweaks to the test script. * Option to store Windows VSS headers separately to the actual file data. * Option to strip Windows VSS headers from the actual file data. * If no timebands are given to timer_script, default to not allowing a backup. * Add LC_TIME=C to LANG=C in the timer script. * Add 'vss_strip' program, which is a tool for extracting the file data from a file containing both VSS data and the file data. * Add Patrick Koppen's patches: - Add 'ssl_cipher' option. - Add support for signal USR2 to Debian init script. - Write pid first in pidfile. 2012-10-09 Changes in burp-1.3.16: * Important bug fix for exclude_comp. * Bug fix for read_blockdev. * Add 'version_warn=[0|1]' option. 2012-09-28 Changes in burp-1.3.14: * Remove '-l / logfile=' options because they didn't work properly and were adding too much complexity to the code. * Add 'stdout=[0|1]' option, which defines whether to log messages to stdout. * Add 'progress_counter=[0|1]' option for the client, which defines whether to print counters to stdout. * When syslogging, do not prepend a date to the log message. * Don't count phase 2 counters twice when resuming. * Ignore files in clientconfdir starting with '.' or ending with '~', as these are hidden or temporary files. * Fix bug where the client_can_* options were not being overridden in the client specific conf files. * Fix bug that stopped client_can_restore=0 working. * Scan the manifest before restore/verify so that counters and status monitor can show estimates. * Add verify/restore notifications. * Fix Windows batch file IF/ELSE syntax in Windows autoupgrade script. * Have the server send the client the counters when resuming. * Have the server send the client the counters when verifying/restoring, so that it knows what to expect. * Avi Rozen's bug fix for directory browsing on windows * Do not skip restores of sockets. * Add server_can_restore=[0|1] client side option, so that clients can refuse server initiated restores. * Allow server_can_restore and encryption_password to be set with the Windows installer. * Indicate to the server post script whether there was an error. * Indicate to the server post script whether a timed backup was attempted. * Add the ability to restore using a different client. See the 'restore_client' option in the man page. * Add the ability to exclude compression on files by extension. See the 'exclude_comp' option in the man page. * Make the functions that strcmp file extensions more efficient. * Fixed bug introduced in 1.3.12 that made the client crash when scanning files with very long names. 2012-08-28 Changes in burp-1.3.12: * Remove 'forward' timestamp references in backup directories, to allow the easier deletion of backup directories. * Add missing_return_in_non_void_function.patch from Suse. * Further break down find_files() into smaller functions. * Make sure behaviour is sensible when a client runs out of storage directories. * On Windows, use PROGRAMFILES environment variable(s) instead of C:/Program Files/Burp/. * Stop the Windows installer writing/reading stuff to/from the registry and the start menu. * Allow the Windows installer options to be set on the Windows command line. * Add password_check=[0|1] to options on the server. Defaults to on. Turning it off disables client password checks (although SSL certificates are still checked). * Count deleted files. * Remove unnecessary counter code. 2012-07-28 Changes in burp-1.3.10: * Added Tony Cheneau's IPv6 fixes. * Fixed bug to do with directory_tree=1 that would stop resume working. * Stop status monitor exiting on terminal resize. * Small man page corrections about overwriting existing files. * Don't use mingw64's utime() to set Windows file times, because it doesn't work on read-only files. * Tweak to timer_script to let it run with slightly older versions of 'date', plus the FreeBSD version of 'date'. * "burp -a l" should say that a backup is finalising. * Upgraded Windows to gcc-4.6.3. * Upgraded Windows to zlib-1.2.7. * Upgraded Windows to openssl-1.0.0j. * Added pcre-8.31 to Windows. * Added Gonéri Le Bouder's exclude_regex option. * Add a gentle shutdown server option - send signal 12 to the main process. * Make librsync errors in phase4 non-fatal. * Check for errors on gzclose() and fclose(). 2012-06-08 Changes in burp-1.3.8: * Fix bug introduced in the new storage directory structure stuff that had the potential to corrupt backups. * Force directory_tree = 0 for clients on less than version 1.3.6. * Escape '-' symbols in man pages. * Save received certs as temporary files and then rename them, to avoid accidently truncating the current ones. * If Windows VSS snapshot fails, do not continue. * 32bit mingw64 has difficulty printf-ing multiple %llu items and sometimes even segfaults, so split the counters into individual printfs. 2012-05-28 Changes in burp-1.3.6: * Store files in a directory structure like that of the original client. * Fix 'ERROR: Please use absolute include/exclude paths' when setting paths on the server side. * Add configure test for uthash.h. If it doesn't exist, use the uthash.h included with the burp source. * Make top level excludes not be a fatal error. * Make syslogging begin sooner. * Unhide warning messages originally hidden by bacula when having errors when setting things like file times. * Fix bug where file times were not getting set on Windows directories. * Remove openssl "can't open config file" warning when generating certificates on Windows. * Set IPTOS_THROUGHPUT bulk packet flag for linux backups. Windows/mingw64 appears not to be able to do this. 2012-04-27 Changes in burp-1.3.4: * Bug fix for Windows backups where it had stopped using the Windows backup API in 1.3.2. * Automatically convert backslashes to forward slashes for includes/excludes on Windows clients. * Bug fix: pass the path to the CA conf file whenever running burp_ca (fixes problems with the test scripts). * Remove whitespace that was causing the burp_ca '--crl' option not to work. * Have separate CA directories for the client and server. Make 'make install' create the directory /etc/burp/CA-client. * Make compile succeed when there is no libncurses library. * Add 'client_can_force_backup' option, defaulted to on. Turning it off means that only timed backups are allowed. Also add similar 'client_can_list', 'client_can_restore' and 'client_can_verify' options. * Remove getopt from burp_ca, allowing it to work on Macs. * Make an unknown exclude_fs a non-fatal error. * Add ability to trigger a backup on the server side by placing a file called 'backup' in the client's storage directory. * Fix bug where block device nodes were not backed up. Add 'read_blockdev' and 'read_all_blockdevs' options that work the same as the equivalent fifo options. * Add 'notify_success_changes_only' option, which means that success notifications only get sent if there were new or changed files. * Added Ruben Kerkhof's fixes to make run_script() work on OSX. * For each file, store the compression setting in the manifest. * Stop clients connecting twice when a timed backup is rejected. 2012-03-31 Changes in burp-1.3.2: * Add options for the client to automatically generate a certificate signing request, have the server sign and return it, and have the client use the new certificate. Remove old default SSL certificates because they should no longer be needed. See docs/burp_ca.txt for more information. * Add ability for conf files to point to more conf files to read. * Fix status server segfault when deleting files from clientconfdir. * Allow directories in clientconfdir. * Add the ability for the server to set the client include/excludes. * Simulate browsing backups with the use of "-a l/L -d path". * Add ability to set up a restore on the server side. * Add ability to specify child processes separately to status child process with new max_status_children option. * Status monitor - make keypresses more responsive. * Fix bug where old clients that didn't leave an incexc file on the server meant that resume would not start. * Add 'include_ext' option, which excludes all files unless their names end in the given extension. * Fall back to using the client or server name if ssl_peer_cn is unset on the server or client. * Don't try to chuser/chgrp in the child if the user/group settings are the same as the parent (fixes bug where setting user/group in the server wouldn't work, because it would try to chuser/chgrp twice). * Windows installer: add comment about poll interval '0' means that the scheduled job isn't installed. * Windows installer: don't install the scheduled task when doing an upgrade. * Unlock the client directory after server_script_post has run instead of before. * Fix bug where the wrong arg count was being given to the server post script. * Make sure status snapshot exits when there are no backup clients. * Ability to dump logs and paths in backups using '-a S' (useful for making a gui that browses backup contents). * Bug fixes to ssl_extra_checks_script. * Bug fix: initialise network_timeout properly. * Fix typos in docs/burp_ca.txt. * Add 'LANG=C' to the timer script, so that timer_arg days are always expected in English. * Add config file option to turn syslog logging on/off. * Bug fix for 'compression = gzip[0-9]'. * Pass the server version to the client. 2012-02-27 Changes in burp-1.3.1: * EFS support on Windows. * Move to using mingw-w64 for both 32bit and 64bit Windows builds. * Add 'exclude_fs =' option for Linux so that you can skip partitions types (for example, tmpfs) without generating warnings. * If recovery method 'resume' is set, but the includes/excludes change, switch to 'use', then start a new backup. * In the tests, check to see whether it looks like the server has finished the backup before moving on, rather than just waiting a set amount of time. * Add man pages for bedup and burp_ca. * Remove embedded uthash code. It will need to be installed as a dependency. For example, in Debian, you might run 'apt-get install uthash-dev'. * Add Bas van den Dikkenberg's tweaks for getting burp into official Debian and Ubuntu repositories. Burp is now in debian sid and ubuntu precise. * Make the server tell the client what extra_comms features it supports, so that, in future, old servers are more likely to work with new clients. * Improve 'make clean'. * Add option to strip off leading path components on restore. * Fix segfault that was happening on the server when SSL_accept() failed. * Move burp and bedup to /usr/sbin (you need to check that any cron jobs are pointing there too). * Moved list of things to do to https://github.com/grke/burp/issues 2012-01-27 Changes in burp-1.3.0: * Add a mechanism for automatically upgrading clients. * Fix bug where attributes on Windows directories were not getting read properly. * Fix rounding problem in remaining time estimation in the status monitor. * Don't backup the whole file if only ctime changed. * Include the client name in arguments to server scripts. * Do not log a message in the server SIGCHLD handler, as this appeared to be causing an occasional lock up on a Ubuntu server. * Upgrade Windows depkgs to latest versions of openssl. * Make Windows uninstall work. * Add 'allow autoupgrade' option to a second screen on the Windows installer. * Add 'max_storage_subdirs' option, and default it to 30000. 2011-12-30 Changes in burp-1.2.7: * Add an automated test script. * Fix bugs with uncompressed encrypted backup/restore. * Get verify all backups for a client (-a v -b a) to work. * Return non-zero if there were warnings on verify or restore. * Add 'min_file_size' option, fix 'max_file_size' option. * Add generic server-side pre/post scripts that can run on every connection after authentication and before disconnecting. * Set environment variables from SSL cert details so that the server-side scripts can see them. * Add Jason Haar's server script for doing extra SSL certificate checks. * Add 'max_hardlinks' option, which limits the number of times a single file is hardlinked in storage. Default is 10000. * Add '-i' option: print an index table of symbols that humans may see burp produce, and exit. * Let backups carry on when files cannot be opened. * Spot Windows EFS directories and files and warn about them. * Add an 'estimate' option (-a e), which does a scan of the file system to be backed up, and prints counters, without contacting the server. * Strip out 744 lines of unused Windows code. * Fix possible segfault when backup directory forward paths don't match. * Removed working_dir_recovery_method=merge. Use 'resume' instead. * Add a flexible way of passing new fields from the client to the server so that clients are less likely to have to upgrade. 2011-11-26 Changes in burp-1.2.5: * IMPORTANT: Fixed critical path comparison typo bug. If you have been using 1.2.4 either as a server or as a client, you need to upgrade immediately! Backups made with 1.2.4 should be treated as suspicious. * Add a 'max_file_size' option. * Store windows file attributes in a separate field to the standard unix ones. 2011-11-22 Changes in burp-1.2.4: * Add a file deduplication program, "bedup". * Add a network_timeout parameter. Default is 7200 seconds (2 hours). * Add option to send success notifications only if there were warnings. * Try to create the pidfile directory if it doesn't exist. * Restore times on Windows files. * Restore readonly/hidden attributes on Windows. * Fix bug in 'keep' mechanism where setting 'keep=2' would delete both backups. * Only install clientconfdir/testclient with 'make install' if clientconfdir didn't previously exist. * Fix possible segfault when resume mode triggers with nothing to resume. 2011-09-29 Changes in burp-1.2.2: * Multiple retention periods (e.g, keep 1 backup per day for 7 days, 1 backup per week for 4 weeks, 1 backup per 4 weeks for a year). See the 'keep' option in the man page. * Make sure the client version warning message appears in the backup log. * Network send rate-limiting feature (both client and server). * Change pidfile paths to /var/run/burp instead of /var/lock/burp. * Get pre backup scripts running reliably again. Also, send script messages on stderr to the server backup log. * Fix 'Warnings' line on the status monitor. * Fix resume mode bug to do with potentially partially written entries at the end of the phase2 file. 2011-09-09 Changes in burp-1.2.0: * Improvements to the counters and status monitor - remember the phase1 scan counters for later phases to use for things like estimated times. * Add 'working_dir_recovery_method = resume' option, which continues a backup from the point it was interrupted. * Add a useful message about options when run with '-h' or '-?'. * Get the client to pass its version to the server. The minimum version the client needs to be is now this release. * Get the server to warn when the client is on a different version. * Put the number of warnings in the subject of the notification emails. * Allow config file location to be changed via configure (Michael Roland's suggestions). * Implement main server process config reload functionality on SIGHUP. * Uncomment the reload lines in the Debian init script. * Add logrotate file for Debian. * Add ability to send a regular snapshot of the status summary screen in an email ("daily backup summary" feature). * Add simple 'exclude_ext =' feature. * Add Windows 7 bare metal restore instructions. 2011-07-26 Changes in burp-1.1.70: * FreeBSD ACL support. * FreeBSD xattr support. * Handle Windows junction points. * Add 'nobackup = [file name]' option. If this file system entry exists, the directory containing it will not be backed up. * Set librsync signature block size based on the old file size. * Small bug fixes to the status monitor. * Fixed working directory recovery bug where it couldn't find the manifest.gz file due to it being called manifest.gz.tmp. * Get rid of the stupidly complicated 'find_files' callback mechanism inherited from bacula. Just call all the functions directly. * Fix compiler warnings and '-ldl' linker error on FreeBSD 8.2. * When down to 'C:' in mkpath() on Windows restore, strip the ':'. * Run ERR_clear_error() before SSL_read/write. 2011-06-27 Changes in burp-1.1.65: * Add mechanism for backing up and restoring extra meta data. * Linux acl support. * Linux xattr support. * Add option to set the umask on the server. * Added option to specify the clientconfdir password in passwd format. * Allow user and group options to be placed in the server clientconfdir files, so that different client backups can be owned by different users. * Add Patrick's burp_ca patches and README.CA. * Sanitised magic characters by defining them in a new cmd.h file. * Fixed 'ret OK, but zstream not finished: 0' when refusing to restore over existing files. * Fixed bug that made clients disappear from the status monitor. * Bug fix the client_lockdir option. * On the server, be more cautious about creating directories that don't exist. * Remove link kludge in async_read_stat(). 2011-06-14 Changes in burp-1.1.57: * Add options to run as a different user and group. * Add Patrick Koppen's burp_ca script. * Get the client restore to use the sbuf functions, remove related kludges. * Fix possible client segfault when refusing to restore over existing files. * Make a failure to restore over a busy file not cause the whole restore to fail. * Print byte count as well as human readable byte count in the status summary screen. * Include backup numbers with the backup list in the status summary. * Don't close the server read fd when running in non-forking mode. * Add ssl_key options so that the cert and key can be in separate files. * Add ssl_key_password, a synonym for ssl_cert_password. 2011-05-13 Changes in burp-1.1.55: * Make the status monitor use ncurses. * Fixed two bugs that could drastically slow down the server if a file disappeared on a client after the initial scan (to get these two bug fixes, you need to upgrade your clients). * IPv6 patches. * Ability to specify the location of the client lockfiles on the server. * Make the server fork into the background by default. Use '-F' to stay in the foreground. * Add option to specify a log file. 2011-04-14 Changes in burp-1.1.51: * Added a script for backing up zfs snapshots with 'zfs send'. * Added the ability to set the level of compression. * Added the ability to turn off compression. * Added an option to define the poll interval in the Windows installer. * Added the ability to turn off librsync delta differencing. * Added the ability to give arguments to backup/restore/pre/post scripts. * Make configure/make work better on Solaris. 2011-04-02 Changes in burp-1.1.40: * Added the ability to run client pre/post backup/restore scripts. * Added the ability to read data from fifos (and restore to them). * Fixed a bug when restoring the first link in a directory. * Fixed a bug that prevented giving a backup number when using the verify option. 2011-03-27 Changes in burp-1.1.30: * First pass at having a live status monitor. * Fixed a bug that meant you couldn't restore links. * Fixed a bug when freeing structs at the end of a restore. 2011-03-18 Changes in burp-1.1.22: * Added scheduling. * Added email notifications. * Fixed autoconf. Programs like ccache should now work. * Strip whitespace from end of conf lines, ignore quotes around conf lines. * Updated the Windows build tools. 2011-03-10 Changes in burp-1.1.14: * Added client-side blowfish file encryption. See the man page for details. * Byte counters also give counts in KB/MB/GB/etc as appropriate. * Restore directories after their contents so that the access times are correct. * Updated the win32 build tools. 2011-03-02 Changes in burp-1.1.5: * Added a 'verify' command. See the man page for details. * Improved the include/exclude path functionality. * Added the ability to define mount points to cross. * Fixed a crash on 64bit Windows when exiting. Changes in burp-1.1.0: * Added openssl. * Added byte counters and checksums to the backup manifest files, in anticipation of a 'verify' command. IMPORTANT IF YOU ARE UPGRADING FROM AN OLDER VERSION OF BURP: a) Your previous backups will no longer work, due to changes in the manifest files. You will also need to move old backups aside in order to continue to make new backups. b) Your old config files will need some fields to do with SSL added to them. See the man page and the examples in the 'configs' directory for help. IMPORTANT FOR MY ENCOURAGEMENT AND THE FUTURE OF BURP: If you have tried burp, I really want to hear from you to find out what you like about it, what you don't like, what new features you would like, how you have been using it, whether it has helped you, or anything that you would like to tell me. I am currently working in the dark! Changes in burp-1.0.31: * Large scale rewrites to the main backup process to make it more efficient and easier to understand. * Much tidying up of code and splitting into smaller files. * Removed complicated/flaky compression code from the network stuff. Network compression will come free with SSL anyway (when it is implemented). Currently, whole new files are transmitted compressed, file information and deltas are not. Changes in burp-1.0.11: * Fixed Windows restore crash. Windows restores should now work better. Changes in burp-1.0.10: * Fixed three major bugs introduced in burp-1.0.9 to do with client/server communication when doing deltas. * Make "make install" and the Windows installers not overwrite an existing config file. * Support for backup and restore of fifos and nodes. * KNOWN ISSUE: There seems to be a problem with restoring multiple files at once to Windows. I am working on this. Changes in burp-1.0.9: * Added counters to summarise the files that were backed up or restored. * Added the ability to have warnings instead of errors always being fatal. * Prevent restores overwriting existing files, unless the new '-f' option is given. * Notice invalid characters in paths on Windows and replace them with '_'. This also means that you can now restore to alternative directories (the colon in the drive name no longer gets in the way). * Added instructions for building the Windows installer. burp-2.4.0/CONTRIBUTORS000066400000000000000000000016221404341324700143500ustar00rootroot00000000000000This is a list of people that have either submitted code, or otherwise contributed significantly to the project. List in alphabetical order by surname. Reinhard Biegel Pascal Bleser Gonéri Le Bouder Scott Brown Wybren Buijs Tony Cheneau Sebastien Couppey Michael Da Cova Pablo Daniel Estigarribia Davyt Bas van den Dikkenberg Bartosz Fenski Wade Fitzpatrick Saint Germain Jason Haar Markus Heberling Xavier Hernandez Orsiris de Jong Graham Keeling (main author) Ruben Kerkhof Patrick Koppen Peter Maloney Sébastien Marchal Hervé Mose Andrew Niemantsvedriet Stefan Paletta Wai Keong Phan Michael Roland Avi Rosen Maxime Rubino Benjamin Sans Patrick Van der Veken Also, thanks to all those that have provided feedback and testing of any sort. There are also numerous others who contributed to the original bacula project. See the bacula AUTHORS file for those names. The main author of bacula is Kern Sibbald. burp-2.4.0/DONATIONS000066400000000000000000000256261404341324700137630ustar00rootroot00000000000000Burp is open and free software. I work on it in my spare time. If you would like this work to continue, please consider making a small donation. A PayPal donation link and a flattr button can be found on the website http://burp.grke.net/, along with a bitcoin address. If you want to donate but don't like PayPal, flattr, or bitcoins, please email me via the website contact page and we can talk about alternatives. This is the list of donations received to date. Many thanks to all of you. Donations for 2021-04: * £20.00 Marcin W. * £10.00 Tobias T. * £8.00 iTalk * £5.00 Eckart K. * £5.00 John K. Donations for 2021-03: * £20.00 Marcin W. * £5.00 John K. * £5.00 Eckart K. Donations for 2021-02: * £20.00 Marcin W. * £5.00 John K. * £5.00 Eckart K. Donations for 2021-01: * £20.00 Marcin W. Donations for 2020-12: * £20.00 Marcin W. * £20.00 Niclas J. Donations for 2020-11: * £20.00 Marcin W. * £43.00 Jacky F. * £5.00 Eckart K. Donations for 2020-10: * £20.00 Marcin W. Donations for 2020-09: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2020-08: * £100.00 Romain L. * £20.00 Marcin W. * £5.00 Robert E. Donations for 2020-07: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2020-06: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2020-05: * £100.00 Romain L. * £20.00 Marcin W. * £20.00 Nigel R. Donations for 2020-04: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2020-03: * £100.00 Romain L. * £50.00 Simon G. * £20.00 Marcin W. * £5.00 Robert E. Donations for 2020-02: * £100.00 Romain L. * £100.00 Helix Telecom Inc. * £20.00 Marcin W. Donations for 2020-01: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2019-12: * £100.00 Romain L. * £20.00 Marcin W. * £10.00 Norbert H. Donations for 2019-11: * £100.00 Romain L. * £20.00 Marcin W. * £10.00 Gonéri L.B. Donations for 2019-10: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2019-09: * £100.00 Romain L. * £20.00 Marcin W. * £15.00 Mariusz M. Donations for 2019-08: * £100.00 Romain L. * £20.00 Marcin W. * £20.00 Thorben H. * £10.00 Jorge C. Donations for 2019-07: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2019-06: * £200.00 David L. * £100.00 Romain L. * £20.00 Marcin W. Donations for 2019-05: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2019-04: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2019-03: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2019-02: * £100.00 Romain L. * £20.00 Marcin W. * £10.00 Yves K. Donations for 2019-01: * £100.00 Romain L. * £100.00 Terki A. * £20.00 Marcin W. Donations for 2018-12: * £100.00 Romain L. * £20.00 Marcin W. * £20.00 Mariusz M. Donations for 2018-11: * £100.00 Romain L. * €100.00 Buchhaltung Blaschka KG * £20.00 Marcin W. Donations for 2018-10: * £100.00 Romain L. * £20.00 Marcin W. * £15.00 Nigel R. Donations for 2018-09: * £100.00 Romain L. * £20.00 Marcin W. * £10.00 Pablo D. * £10.00 Mariusz M. Donations for 2018-08: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2018-07: * £100.00 Romain L. * £20.00 Marcin W. * £11.00 Jared B. Donations for 2018-06: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2018-05: * £100.00 Romain L. * £20.00 Marcin W. * £10.00 Mariusz M. * £5.00 Alexander N. Donations for 2018-04: * £100.00 Romain L. * €100.00 Buchhaltung Blaschka KG * £20.00 Marcin W. Donations for 2018-03: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2018-02: * £100.00 Romain L. * £20.00 Marcin W. * £5.00 Karoly F. Donations for 2018-01: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2017-12: * £100.00 Romain L. * £50.00 Bram N. * £20.00 Marcin W. Donations for 2017-11: * £150.00 Josef V. * £100.00 Romain L. * £20.00 Marcin W. Donations for 2017-10: * £100.00 Romain L. * €100.00 Buchhaltung Blaschka KG * £50.00 Bram N. Donations for 2017-09: * £100.00 Romain L. * £40.00 Marcin W. * £30.00 Pablo D. Donations for 2017-08: * £100.00 Romain L. * £50.00 Holger B. * £20.00 Marcin W. * £15.00 Michael K. Donations for 2017-07: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2017-06: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2017-05: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2017-04: * £100.00 Romain L. * €100.00 Buchhaltung Blaschka KG * £20.00 Marcin W. * £20.00 Silke W. Donations for 2017-03: * £100.00 Romain L. * £20.00 Marcin W. * £4.00 Tamás S. Donations for 2017-02: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2017-01: * £100.00 Romain L. * £20.00 Marcin W. Donations for 2016-12: * £100.00 Romain L. * £15.00 Calogero L. * £10.00 Robert T. Donations for 2016-11: * £200.00 Francois L. * £100.00 Romain L. * £20.00 Marcin W. * £5.00 Johannes L. Donations for 2016-10: * £100.00 Romain L. * €100.00 Buchhaltung Blaschka KG * £40.00 Marcin W. * £20.00 Pablo D. Donations for 2016-09: * £100.00 Romain L. * £40.00 Marcin W. * £5.00 Alexander N. Donations for 2016-08: * £100.00 Romain L. * BTC0.06783 Anonymous * £25.00 Jonathan W. * £10.00 Lucas D. Donations for 2016-07: * £400.00 Johan K. * £100.00 Romain L. * £25.00 Damien H. * £15.00 Marcin W. Donations for 2016-06: * £100.00 Romain L. * £20.00 Anonymous * £15.00 Marcin W. * £10.00 Gautier F. * €0.70 Flattr Donations for 2016-05: * £100.00 Romain L. * £15.00 Marcin W. * €0.47 Flattr Donations for 2016-04: * £100.00 Romain L. * £27.00 Romain P. * £20.00 B. H. * £20.00 Christian N. * BTC0.02141 Anonymous * £15.00 Nigel R. * £10.00 Jason H. * £10.00 Marcin W. * £5.00 Olag S. * €0.19 Flattr Donations for 2016-03: * £250.00 VSHN AG * £100.00 Romain L. * £10.00 Jason H. * £10.00 Marcin W. * €3.19 Flattr Donations for 2016-02: * £100.00 Romain L. * £20.00 Adrian I. * £10.00 Jason H. * £10.00 Marcin W. * €2.70 Flattr Donations for 2016-01: * £100.00 Romain L. * £50.00 Christian N. * £20.00 Marcin W. * £10.00 Jason H. * £10.00 Petr G. * €6.00 Flattr Donations for 2015-12: * £100.00 Romain L. * £10.00 Jason H. * £10.00 Jaime B. * €2.05 Flattr Donations for 2015-11: * £100.00 Romain L. * £10.00 Jason H. * £10.00 Marcin W. * €1.98 Flattr Donations for 2015-10: * £100.00 Romain L. * £10.00 Jason H. * £10.00 Pablo D. * €1.98 Flattr Donations for 2015-09: * USD600.00 Aja Video Systems Inc. * £100.00 Romain L. * £10.00 Jason H. * €1.96 Flattr Donations for 2015-08: * £100.00 Romain L. * £10.00 Jason H. * £10.00 Florian L. * €0.16 Flattr Donations for 2015-07: * £100.00 Romain L. * £100.00 Patrick V. * £50.00 Damien H. * £40.00 Tobias R. * £10.00 Jason H. * €0.18 Flattr Donations for 2015-06: * £100.00 Romain L. * £10.00 Jason H. * €0.16 Flattr Donations for 2015-05: * £100.00 Romain L. * BTC0.02301 Anonymous * £10.00 Jason H. * £7.20 Antonio S. * €0.16 Flattr Donations for 2015-04: * £100.00 Romain L. * BTC0.47941 Saint G. * £50.00 Florent P. * £40.00 Stephen A. * £10.00 Jason H. * £10.00 Computer Architechs International Corporation * BTC0.01000 Anonymous * £7.20 Antonio S. * €0.15 Flattr Donations for 2015-03: * £300.00 Robert L. * £100.00 Romain L. * £15.00 Sjoerd S. * £10.00 Jason H. * £7.20 Antonio S. * €0.40 Flattr Donations for 2015-02: * £100.00 Romain L. * £25.00 Damien H. * £10.00 Jason H. * BTC0.034 Anonymous * €0.56 Flattr Donations for 2015-01: * £100.00 Romain L. * £15.00 Anonymous * €0.40 Flattr Donations for 2014-12: * USD15.00 Georges D. * €0.42 Flattr Donations for 2014-11: * £80.00 Giampaolo T. * BTC0.10 Anonymous * £10.00 Giuseppe C. * £5.00 Jorge C. * £5.00 Sven S. * €0.59 Flattr Donations for 2014-10: * £30.00 Hervé M. * €0.43 Flattr Donations for 2014-09: * £83.00 Giampaolo T. * £10.00 Michael W. * €0.45 Flattr Donations for 2014-08: * £10.00 Łukasz P. * £5.00 Giampaolo T. * €0.49 Flattr Donations for 2014-07: * £20.00 Morgan S. * £20.00 Hervé M. * €0.45 Flattr Donations for 2014-06: * £400.00 Naturalis Biodiversity Center, ICT. * £8.00 Dominique F. * €0.37 Flattr Donations for 2014-05: * £20.00 Benjamin S. * BTC0.001 Anonymous * €0.45 Flattr Donations for 2014-04: * £82.93 Orsiris de J. * £30.00 Anthony B. * £20.00 Morgan S. * €0.45 Flattr Donations for 2014-03: * £10.00 Thomas O. * €0.45 Flattr Donations for 2014-01: * £15.00 Calogero L. Donations for 2013-10: * €100.00 Saint G. * £10.00 Florian W. * £15.00 Nigel R. Donations for 2013-09: * £43.00 Sebastien C. Donations for 2013-08: * £43.00 Sebastien C. Donations for 2013-07: * £10.00 Anonymous. Donations for 2013-06: * £25.00 Louwris W. * £20.00 Morgan S. Donations for 2013-05: * £30.00 Sebastian A. Donations for 2013-04: * £25.00 Eduardo R. Donations for 2013-03: * £30.00 Haim G. * £0.01 Romain M. Donations for 2013-02: * £25.00 Christian B. * £20.00 Yeongjin C. Donations for 2013-01: * £30.00 Sebastian A. * £20.00 Graham D. * £20.00 Brian H. Donations for 2012-12: * £50.00 Bryan B. * £1.01 Romain M. Donations for 2012-11: * £20.00 Johannes E. Donations for 2012-09: * £400.00 Robert L. Donations for 2012-06: * £16.00 Talma J. Donations for 2012-05: * £100.00 Tomppa * £64.00 Baker J. Donations for 2012-04: * £10.00 Calogero L. * Server hosting from 6sync.com Donations for 2012-03: * £5.00 Łukasz P. Donations up to 2012-01-29: * £250.00 Robert L. * £50.00 Sebastiano P. * £10.00 Tim R. * £10.00 Patryk D. burp-2.4.0/LICENSE000066400000000000000000001212221404341324700134740ustar00rootroot00000000000000History: The original Bacula code was Copyright Kern Sibbald and John Walker. After November 2004, it became Copyright Kern Sibbald, and finally, the copyright was transferred to the Free Software Foundation Europe on 15 November 2006. The license was changed from GPLv2 to AGPLv3 on 24 July 2010. The first version of Burp was released on 22 January 2011, which was an extremely cut down and tweaked version of the Bacula code. The intention was to cut out everything except for the Windows VSS API parts and to combine that with librsync to make a new backup program. As well as the Windows VSS API parts these things remain: Some of src/attribs.c. Some of src/base64.c Some of src/client/find.c. A fragment of src/berrno.c. Some Makefiles and configure scripts. The contents of src/yajl is copyright (c) 2007-2014, Lloyd Hilaiel. The new Burp code is Copyright Graham Keeling. The license for the new Burp code continues to be AGPLv3. Trademark: The name Bacula is a registered trademark of Kern Sibbald. The name Burp is not a registered trademark of anybody. =================================== License: For the most part, Burp is licensed under the AGPL version 3. This code is listed under Copyright Free Software Foundation Europe e.V. What follows is the addition(s) to the AGPL version 3 license, that applies to code that is copyrighted by the Free Software Foundation Europe e.V. Linking: As a special exception to the AGPLv3, the Burp Project gives permission to link the code of its release of Burp with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU Affero General Public License in all respects for all of the code used other than "OpenSSL". As a special exception to the AGPLv3, the Burp Project gives permission to link the code of its release of the Burp Windows client with the Microsoft supplied Volume Shadow Copy (VSS) libraries and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than for the Microsoft VSS code, where you must obey their license terms. The Burp Project gives permission for plugins with AGPLv3 compatible licenses to be loaded and distributed with the Burp executables as long as the combined work is distributed under the terms listed in the Burp LICENSE file. A full list of AGPLv3 compatible licenses can be found at: http://www.fsf.org/licensing/licenses/. If you wish to load or distribute plugins with different licensing terms please contact the Burp Project via http://burp.grke.net/ =================================== What follows is information from the authors of the code: License: To the best of our knowledge, all code used in Burp, which is copyrighted by a third party, has licenses that are compatible with the OpenSSL license, and so given the exception that we have made to the AGPLv3 above, Burp can be freely linked and distributed with the OpenSSL libraries. Intellectual Property rights: Recipient understands that although each Contributor to Burp grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. Copyrights: Each Contributor to Burp represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. Windows: Certain source code used to build the Windows version of Burp is copyrighted and or trademarked by Microsoft and may contain Microsoft intellectual property (examples: Microsoft VC++, the source to the VSS libraries, the Microsoft C runtime libraries). As such we cannot and do not distribute that software. We are permitted however to distribute Burp with the necessary Microsoft libraries in binary form. You may obtain the parts that we cannot distribute as follows. The Microsoft compiler available for purchase, and Microsoft provides a free version of the compiler. The source code and libraries are available for download from Microsoft public Web servers. We have documented in the src/win32 directory the URLs from which we obtained the library source, and how we build the Windows File daemon and many users have succeeded in doing so themselves. Our intention is to respect as closely as possible Open Source practices while maintaining full respect for proprietary and copyrighted code. 3 clause BSD License notice for inclusion with the binary: src/win32/compat/getopt.c "... licensed under IBM copyrights to use the IBM-provided source code in any way he or she deems fit ..." src/win32/compat/sys/mtio.h (LGPL) Copyright (C) 1996, 1997 Free Software Foundation, Inc. Burp can be enabled with data encryption and/or communications encryption. If this is the case, you will be including OpenSSL code that that contains cryptographic software written by Eric Young (eay@cryptsoft.com) and also software written by Tim Hudson (tjh@cryptsoft.com). There are parts of Burp that are licensed under the LGPL so that those files may be used in proprietary code to interface with Burp. Finally there are parts of Burp that are in the public domain. 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 OWNER OR CONTRIBUTORS 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. ===================================== The entire AGPL is reproduced below, in the manuals distributed with the Burp documentation and can also be found online on the GNU web site as well as at www.bacula.org. You may also obtain a copy of the AGPL (or LGPL) by writing to: Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 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 Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are 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. 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. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. 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 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 work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. 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 AGPL, see . =========================================== burp-2.4.0/Makefile.am000077500000000000000000000407351404341324700145370ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -fno-strict-aliasing CLEANFILES = EXTRA_DIST = \ configs/client/burp.conf.in \ configs/server/burp.conf.in \ configs/server/summary_script.in \ configs/server/backup_tool_script.in \ configs/certs/CA/CA.cnf.in \ configs/certs/CA/burp_ca.in \ src/win32 \ utest/json_output \ $(TESTS) dist_doc_DATA = \ CHANGELOG \ CONTRIBUTORS \ DONATIONS \ LICENSE \ README \ UPGRADING \ docs/add-remove.txt \ docs/autoupgrade.txt \ docs/baremetal-windows2008.txt \ docs/baremetal-windows7and8.txt \ docs/baremetal-windows7-hirens.txt \ docs/baremetal-windows7.txt \ docs/burp_ca.txt \ docs/debug.txt \ docs/retention.txt \ docs/security-models.txt \ docs/server-basics.txt \ docs/shuffling.txt \ docs/status-monitor.txt \ docs/tests.txt \ docs/timer_script.txt \ docs/working_dir.txt \ docs/backup_tool_script.txt \ src/protocol1/backup_phases.txt \ src/protocol1/readwrite.txt dist_man8_MANS = \ manpages/bedup.8 \ manpages/bsigs.8 \ manpages/bsparse.8 \ manpages/$(PACKAGE_TARNAME).8 \ manpages/$(PACKAGE_TARNAME)_ca.8 \ manpages/vss_strip.8 configs = $(PACKAGE_TARNAME).conf $(PACKAGE_TARNAME)-server.conf CA.cnf CLEANFILES+= $(configs) install-all: install install-configs LN_S = ln -s -f install-exec-hook: $(AM_V_at)$(LN_S) $(PACKAGE_TARNAME) $(DESTDIR)$(sbindir)/bedup $(AM_V_at)$(LN_S) $(PACKAGE_TARNAME) $(DESTDIR)$(sbindir)/bsigs $(AM_V_at)$(LN_S) $(PACKAGE_TARNAME) $(DESTDIR)$(sbindir)/bsparse install-configs: $(configs) install-clientconfdir $(AM_V_at)$(MKDIR_P) $(DESTDIR)$(sysconfdir)/CA-client $(AM_V_at)$(MKDIR_P) $(DESTDIR)$(sysconfdir)/autoupgrade/server/win32 $(AM_V_at)$(MKDIR_P) $(DESTDIR)$(sysconfdir)/autoupgrade/server/win64/$(VERSION) echo $(configs) | while read files ; do \ $(INSTALL_DATA) $$files "$(DESTDIR)$(sysconfdir)" || exit $$?; \ done clientconfdir = $(sysconfdir)/clientconfdir incexcdir = $(clientconfdir)/incexc install-clientconfdir: $(AM_V_at)$(MKDIR_P) $(DESTDIR)$(clientconfdir) $(AM_V_at)$(MKDIR_P) $(DESTDIR)$(incexcdir) $(INSTALL_DATA) configs/server/clientconfdir/testclient "$(DESTDIR)$(clientconfdir)" $(INSTALL_DATA) configs/server/clientconfdir/incexc/example "$(DESTDIR)$(incexcdir)" EXTRA_PROGRAMS = main bin_PROGRAMS = vss_strip sbin_PROGRAMS = $(PACKAGE_TARNAME) sbin_SCRIPTS = $(PACKAGE_TARNAME)_ca dist_script_SCRIPTS = \ configs/server/timer_script \ configs/server/notify_script \ configs/server/ssl_extra_checks_script EXTRA_script_SCRIPTS = summary_script \ backup_tool_script EXTRA_scriptdir = $(scriptdir) CLEANFILES+= $(sbin_SCRIPTS) $(EXTRA_script_SCRIPTS) vss_strip_SOURCES = src/server/protocol1/vss_strip.c vss_strip_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(OPENSSL_INC) main_LDADD = \ $(ACL_LIBS) \ $(CRYPT_LIBS) \ $(NCURSES_LIBS) \ $(OPENSSL_LIBS) \ $(RSYNC_LIBS) \ $(SYSTEMD_LIBS) \ $(ZLIBS) \ $(CAP_LIBS) main_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DSYSCONFDIR=\"$(sysconfdir)\" \ $(OPENSSL_INC) main_LDFLAGS = \ $(AM_LDFLAGS) \ $(OPENSSL_LDFLAGS) main_SOURCES = \ src/action.h \ src/alloc.c src/alloc.h \ src/asfd.c src/asfd.h \ src/async.c src/async.h \ src/attribs.c src/attribs.h \ src/base64.c src/base64.h \ src/berrno.c src/berrno.h \ src/bfile.c src/bfile.h \ src/bu.c src/bu.h \ src/burp.h \ src/burpconfig.h \ src/cmd.c src/cmd.h \ src/cntr.c src/cntr.h \ src/conf.c src/conf.h \ src/conffile.c src/conffile.h \ src/cstat.c src/cstat.h \ src/forkchild.c src/forkchild.h \ src/fsops.c src/fsops.h \ src/fzp.c src/fzp.h \ src/handy.c src/handy.h \ src/hexmap.c src/hexmap.h \ src/incexc_recv.c \ src/incexc_send.c \ src/iobuf.c src/iobuf.h \ src/incexc_recv.h \ src/incexc_send.h \ src/ipacl.c src/ipacl.h \ src/linkhash.c src/linkhash.h \ src/lock.c src/lock.h \ src/log.c src/log.h \ src/msg.c src/msg.h \ src/pathcmp.c src/pathcmp.h \ src/prepend.c src/prepend.h \ src/prog.c \ src/regexp.c src/regexp.h \ src/run_script.c src/run_script.h \ src/sbuf.c src/sbuf.h \ src/slist.c src/slist.h \ src/ssl.c src/ssl.h \ src/strlist.c src/strlist.h \ src/times.c src/times.h \ src/yajl_gen_w.c src/yajl_gen_w.h \ src/client/acl.c src/client/acl.h \ src/client/auth.c src/client/auth.h \ src/client/autoupgrade.c src/client/autoupgrade.h \ src/client/backup.c src/client/backup.h \ src/client/backup_phase1.c src/client/backup_phase1.h \ src/client/ca.c src/client/ca.h \ src/client/cvss.c src/client/cvss.h \ src/client/delete.c src/client/delete.h \ src/client/extra_comms.c src/client/extra_comms.h \ src/client/extrameta.c src/client/extrameta.h \ src/client/find.c src/client/find.h \ src/client/find_logic.c src/client/find_logic.h \ src/client/glob_windows.c src/client/glob_windows.h \ src/client/list.c src/client/list.h \ src/client/main.c src/client/main.h \ src/client/monitor.c src/client/monitor.h \ src/client/restore.c src/client/restore.h \ src/client/xattr.c src/client/xattr.h \ src/client/monitor/json_input.c src/client/monitor/json_input.h \ src/client/monitor/lline.c src/client/monitor/lline.h \ src/client/monitor/sel.c src/client/monitor/sel.h \ src/client/monitor/status_client_ncurses.c src/client/monitor/status_client_ncurses.h \ src/client/protocol1/backup_phase2.c src/client/protocol1/backup_phase2.h \ src/client/protocol1/restore.c src/client/protocol1/restore.h \ src/client/protocol2/backup_phase2.c src/client/protocol2/backup_phase2.h \ src/client/protocol2/rabin_read.c src/client/protocol2/rabin_read.h \ src/client/protocol2/restore.c src/client/protocol2/restore.h \ src/protocol1/handy.c src/protocol1/handy.h \ src/protocol1/msg.c src/protocol1/msg.h \ src/protocol1/rs_buf.c src/protocol1/rs_buf.h \ src/protocol1/sbuf_protocol1.c src/protocol1/sbuf_protocol1.h \ src/protocol2/blist.c src/protocol2/blist.h \ src/protocol2/blk.c src/protocol2/blk.h \ src/protocol2/rabin/rabin.c src/protocol2/rabin/rabin.h \ src/protocol2/rabin/rconf.c src/protocol2/rabin/rconf.h \ src/protocol2/rabin/win.c src/protocol2/rabin/win.h \ src/protocol2/sbuf_protocol2.c src/protocol2/sbuf_protocol2.h \ src/server/auth.c src/server/auth.h \ src/server/autoupgrade.c src/server/autoupgrade.h \ src/server/backup.c src/server/backup.h \ src/server/backup_phase1.c src/server/backup_phase1.h \ src/server/backup_phase3.c src/server/backup_phase3.h \ src/server/bu_get.c src/server/bu_get.h \ src/server/ca.c src/server/ca.h \ src/server/child.c src/server/child.h \ src/server/compress.c src/server/compress.h \ src/server/delete.c src/server/delete.h \ src/server/diff.c src/server/diff.h \ src/server/dpth.c src/server/dpth.h \ src/server/extra_comms.c src/server/extra_comms.h \ src/server/list.c src/server/list.h \ src/server/main.c src/server/main.h \ src/server/manio.c src/server/manio.h \ src/server/manios.c src/server/manios.h \ src/server/quota.c src/server/quota.h \ src/server/restore.c src/server/restore.h \ src/server/resume.c src/server/resume.h \ src/server/rubble.c src/server/rubble.h \ src/server/run_action.c src/server/run_action.h \ src/server/sdirs.c src/server/sdirs.h \ src/server/timer.c src/server/timer.h \ src/server/timestamp.c src/server/timestamp.h \ src/server/monitor/browse.c src/server/monitor/browse.h \ src/server/monitor/cache.c src/server/monitor/cache.h \ src/server/monitor/cstat.c src/server/monitor/cstat.h \ src/server/monitor/json_output.c src/server/monitor/json_output.h \ src/server/monitor/status_server.c src/server/monitor/status_server.h \ src/server/protocol1/backup_phase2.c src/server/protocol1/backup_phase2.h \ src/server/protocol1/backup_phase4.c src/server/protocol1/backup_phase4.h \ src/server/protocol1/bedup.c src/server/protocol1/bedup.h \ src/server/protocol1/blocklen.c src/server/protocol1/blocklen.h \ src/server/protocol1/deleteme.c src/server/protocol1/deleteme.h \ src/server/protocol1/dpth.c src/server/protocol1/dpth.h \ src/server/protocol1/fdirs.c src/server/protocol1/fdirs.h \ src/server/protocol1/link.c src/server/protocol1/link.h \ src/server/protocol1/restore.c src/server/protocol1/restore.h \ src/server/protocol1/zlibio.c src/server/protocol1/zlibio.h \ src/server/protocol2/backup_phase2.c src/server/protocol2/backup_phase2.h \ src/server/protocol2/backup_phase4.c src/server/protocol2/backup_phase4.h \ src/server/protocol2/bsigs.c src/server/protocol2/bsigs.h \ src/server/protocol2/bsparse.c src/server/protocol2/bsparse.h \ src/server/protocol2/champ_chooser/candidate.c src/server/protocol2/champ_chooser/candidate.h \ src/server/protocol2/champ_chooser/champ_chooser.c src/server/protocol2/champ_chooser/champ_chooser.h \ src/server/protocol2/champ_chooser/champ_client.c src/server/protocol2/champ_chooser/champ_client.h \ src/server/protocol2/champ_chooser/champ_server.c src/server/protocol2/champ_chooser/champ_server.h \ src/server/protocol2/champ_chooser/dindex.c src/server/protocol2/champ_chooser/dindex.h \ src/server/protocol2/champ_chooser/hash.c src/server/protocol2/champ_chooser/hash.h \ src/server/protocol2/champ_chooser/incoming.c src/server/protocol2/champ_chooser/incoming.h \ src/server/protocol2/champ_chooser/scores.c src/server/protocol2/champ_chooser/scores.h \ src/server/protocol2/champ_chooser/sparse.c src/server/protocol2/champ_chooser/sparse.h \ src/server/protocol2/clist.c src/server/protocol2/clist.h \ src/server/protocol2/dpth.c src/server/protocol2/dpth.h \ src/server/protocol2/rblk.c src/server/protocol2/rblk.h \ src/server/protocol2/restore.c src/server/protocol2/restore.h \ src/server/protocol2/sparse_min.c src/server/protocol2/sparse_min.h \ src/yajl/yajl.c \ src/yajl/yajl_alloc.c src/yajl/yajl_alloc.h \ src/yajl/yajl_buf.c src/yajl/yajl_buf.h \ src/yajl/yajl_bytestack.h \ src/yajl/yajl_encode.c src/yajl/yajl_encode.h \ src/yajl/yajl_gen.c \ src/yajl/yajl_lex.c src/yajl/yajl_lex.h \ src/yajl/yajl_parser.c src/yajl/yajl_parser.h \ src/yajl/yajl_tree.c \ src/yajl/yajl_version.c \ src/yajl/api/yajl_common.h \ src/yajl/api/yajl_gen.h \ src/yajl/api/yajl_parse.h \ src/yajl/api/yajl_tree.h \ src/yajl/api/yajl_version.h TESTS = $(check_PROGRAMS) check_PROGRAMS = runner runner_SOURCES = \ utest/builders/build_attribs.c \ utest/builders/build_asfd_mock.c utest/builders/build_asfd_mock.h \ utest/builders/build_clientconfdir.c \ utest/builders/build_file.c utest/builders/build_file.h \ utest/builders/build_slist.c \ utest/builders/build_paths.c \ utest/builders/build.h \ utest/builders/protocol2/build_blist.c \ utest/builders/server/build_manifest.c \ utest/builders/server/build_storage_dirs.c utest/builders/server/build_storage_dirs.h \ utest/builders/server/protocol2/build_sparse_index.c \ utest/builders/server/protocol2/build_sparse_index.h \ utest/builders/server/protocol2/champ_chooser/build_dindex.c utest/builders/server/protocol2/champ_chooser/build_dindex.h \ utest/main.c \ utest/prng.c utest/prng.h \ utest/protocol1/test_handy.c \ utest/protocol1/test_rs_buf.c \ utest/protocol2/test_blist.c \ utest/protocol2/test_blk.c \ utest/protocol2/test_sbuf_protocol2.c \ utest/protocol2/rabin/test_rabin.c \ utest/protocol2/rabin/test_rconf.c \ utest/protocol2/rabin/test_win.c \ utest/client/monitor/test_json_input.c \ utest/client/monitor/test_lline.c \ utest/client/monitor/test_status_client_ncurses.c \ utest/client/protocol1/test_backup_phase2.c \ utest/client/protocol2/test_backup_phase2.c \ utest/client/protocol2/test_rabin_read.c \ utest/client/test_acl.c \ utest/client/test_auth.c \ utest/client/test_delete.c \ utest/client/test_extra_comms.c \ utest/client/test_extrameta.c \ utest/client/test_find.c \ utest/client/test_monitor.c \ utest/client/test_restore.c \ utest/client/test_xattr.c \ utest/server/monitor/test_browse.c \ utest/server/monitor/test_cache.c \ utest/server/monitor/test_cstat.c \ utest/server/monitor/test_json_output.c \ utest/server/monitor/test_status_server.c \ utest/server/protocol1/test_backup_phase2.c \ utest/server/protocol1/test_backup_phase4.c \ utest/server/protocol1/test_bedup.c \ utest/server/protocol1/test_blocklen.c \ utest/server/protocol1/test_dpth.c \ utest/server/protocol1/test_fdirs.c \ utest/server/protocol1/test_restore.c \ utest/server/protocol2/champ_chooser/test_champ_chooser.c \ utest/server/protocol2/champ_chooser/test_champ_server.c \ utest/server/protocol2/champ_chooser/test_dindex.c \ utest/server/protocol2/champ_chooser/test_hash.c \ utest/server/protocol2/champ_chooser/test_scores.c \ utest/server/protocol2/champ_chooser/test_sparse.c \ utest/server/protocol2/test_backup_phase2.c \ utest/server/protocol2/test_backup_phase4.c \ utest/server/protocol2/test_bsparse.c \ utest/server/protocol2/test_dpth.c \ utest/server/test_auth.c \ utest/server/test_autoupgrade.c \ utest/server/test_ca.c \ utest/server/test_backup_phase3.c \ utest/server/test_bu_get.c \ utest/server/test_delete.c \ utest/server/test_extra_comms.c \ utest/server/test_list.c \ utest/server/test_manio.c \ utest/server/test_resume.c \ utest/server/test_restore.c \ utest/server/test_run_action.c \ utest/server/test_sdirs.c \ utest/server/test_timer.c \ utest/test_alloc.c \ utest/test_asfd.c \ utest/test_attribs.c \ utest/test_base64.c \ utest/test_cmd.c \ utest/test_cntr.c \ utest/test_conf.c \ utest/test_conffile.c \ utest/test_fzp.c \ utest/test_hexmap.c \ utest/test_lock.c \ utest/test_pathcmp.c \ utest/test_slist.c \ utest/test_times.c \ utest/test.h runner_SOURCES+= $(main_SOURCES) runner_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(COVERAGE_CFLAGS) \ $(CHECK_CFLAGS) \ -DUTEST \ -DTOP_SRCDIR=\"$(top_srcdir)\" \ -DSYSCONFDIR=\"$(sysconfdir)\" \ $(OPENSSL_INC) runner_LDFLAGS = \ $(AM_LDFLAGS) \ $(COVERAGE_LDFLAGS) \ $(OPENSSL_LDFLAGS) runner_LDADD = \ $(ACL_LIBS) \ $(CHECK_LIBS) \ $(CRYPT_LIBS) \ $(NCURSES_LIBS) \ $(RSYNC_LIBS) \ $(SYSTEMD_LIBS) \ $(OPENSSL_LIBS) \ $(ZLIBS) \ $(CAP_LIBS) coverage: check if WITH_COVERAGE $(AM_V_GEN)$(LCOV) -q --capture --no-external -d . -b . --output-file burp-coverage.info $(AM_V_GEN)$(LCOV) -r burp-coverage.info \ '*/src/yajl/*' \ '*/utest/*' \ --output-file burp-coverage-clean.info $(AM_V_GEN)$(GENHTML) -q burp-coverage-clean.info --output-directory burp-coverage else @echo Coverage support is not enabled @echo run ./configure --with-coverage endif .PHONY: coverage install-data-local: $(AM_V_at)$(MKDIR_P) $(DESTDIR)$(localstatedir)/spool/$(PACKAGE_TARNAME) $(AM_V_at)$(MKDIR_P) $(DESTDIR)$(runstatedir) clean-local: clean-local-check mostlyclean-local: $(AM_V_at)-rm -f burp-coverage.info $(AM_V_at)-rm -f burp-coverage-clean.info $(AM_V_at)-rm -f $(runner_OBJECTS:.o=.gcno) $(AM_V_at)-rm -f $(runner_OBJECTS:.o=.gcda) $(AM_V_at)-rm -rf burp-coverage .PHONY: clean-local-check clean-local-check: $(AM_V_at)-rm -rf utest_acl $(AM_V_at)-rm -rf utest_find $(AM_V_at)-rm -f utest_lockfile $(AM_V_at)-rm -rf utest_restore $(AM_V_at)-rm -rf utest_xattr $(configs): Makefile $(PACKAGE_TARNAME): main $(AM_V_at)mv -fv main $(PACKAGE_TARNAME) $(PACKAGE_TARNAME).conf: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/configs/client/burp.conf.in >$@ $(PACKAGE_TARNAME)-server.conf: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/configs/server/burp.conf.in >$@ CA.cnf: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/configs/certs/CA/CA.cnf.in >$@ $(PACKAGE_TARNAME)_ca: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/configs/certs/CA/burp_ca.in >$@ summary_script: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/configs/server/summary_script.in >$@ backup_tool_script: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/configs/server/backup_tool_script.in >$@ manpages/bedup.8: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/manpages/bedup.8.in >$@ manpages/bsigs.8: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/manpages/bsigs.8.in >$@ manpages/bsparse.8: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/manpages/bsparse.8.in >$@ manpages/$(PACKAGE_TARNAME).8: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/manpages/burp.8.in >$@ manpages/$(PACKAGE_TARNAME)_ca.8: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/manpages/burp_ca.8.in >$@ manpages/vss_strip.8: $(AM_V_at)rm -f $@ $(AM_V_GEN)$(do_subst) <$(srcdir)/manpages/vss_strip.8.in >$@ do_subst = ( sed \ -e 's,[@]localstatedir[@],$(localstatedir),g' \ -e 's,[@]runstatedir[@],$(runstatedir),g' \ -e 's,[@]sbindir[@],$(sbindir),g' \ -e 's,[@]scriptdir[@],$(scriptdir),g' \ -e 's,[@]server_address[@],localhost,g' \ -e 's,[@]sysconfdir[@],$(sysconfdir),g' \ -e 's,[@]name[@],$(PACKAGE_TARNAME),g' \ -e 's,[@]package_url[@],$(PACKAGE_URL),g' \ -e 's,[@]human_name[@],$(PACKAGE_NAME),g' \ | $(SHELL) ./config.status --file=- \ ) burp-2.4.0/README000066400000000000000000000116711404341324700133550ustar00rootroot00000000000000Quick start ----------- The following assumes that you are installing the burp server from source. You need to compile burp on a unix-based operating system first. Make sure that you have openssl, zlib, librsync, and uthash libraries and development packages installed. - Debian/Ubuntu: apt-get install make pkg-config check g++ \ librsync-dev libz-dev libssl-dev uthash-dev - RHEL or CentOS 7: yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm yum install make gcc gcc-c++ bzip2 pkgconfig \ librsync-devel libzip-devel openssl-devel uthash-devel \ libacl-devel ncurses-devel openssl-perl libcap-devel - RHEL or Centos 8: dnf install epel-release dnf --enablerepo=PowerTools list uthash-devel dnf install make gcc librsync-devel openssl-devel libacl-devel \ ncurses-devel zlib-devel After this point, make sure you are standing in the burp source directory. If you are taking the source directly from git: Install these extra packages with your distribution's package manager: autoconf automake libtool autoreconf -vif Now run the configure script, noting that if you are using automake >= 1.16.1, you may need to add '--disable-dependency-tracking': ./configure --prefix=/usr --sysconfdir=/etc/burp --localstatedir=/var Then run: make All being well, burp will compile successfully. Install it as root: make install Now install configuration files. Note that this will overwrite existing configuration files in /etc/burp, so you will probably not want to run this if you have an existing burp installation: make install-configs Start the burp server: burp -c /etc/burp/burp-server.conf By default, the server will fork into the background and run as a daemon. To stay in the foreground, add the '-F' option. The first time that the server runs, its startup will be a slower than usual because it needs to generate its SSL certificates. However, this will probably only be noticeable on old machines. The server is now ready for clients to connect to it. Unix client ----------- If you are going to run the client on the same machine on which you installed the server, the client will already be installed because the client is built into the same binary (the behaviour of the binary depends upon the configuration file that is given to it). Otherwise, you can either go through the 'configure/make/make install' sequence on the client machine, or get burp via your distribution's package manager. The example client configuration file is /etc/burp/burp.conf. It will backup /home by default. You can either force a backup by hand: burp -a b Or add a regular cron job like the following to /etc/cron.d/burp (or equivalent). This makes the client ask the server every twenty minutes whether it is yet time for a new backup. It is probably a good idea to change the numbers for each client, in order to spread the load a bit: MAILTO= 7,27,47 * * * * root /usr/sbin/burp -a t Whether or not an instance of the client results in an actual backup depends upon the timer settings in the server config files. Windows client -------------- Pick the 32-bit or 64-bit Burp installer as appropriate - visit http://burp.grke.org/ to find one of them. If you need to compile one yourself, instructions can be found at src/win32/README in the source package. The Windows installer will ask you for the address of your server, the client name, and the client password. Burp will then install its files to: C:/Program Files/Burp (Note that the "C:/Program Files/" part may differ, depending on Windows' language settings). If you need to change client configuration settings, the file can be found at: C:/Program Files/Burp/burp.conf It will be configured to backup "C:/Users", if it existed at installation time, or otherwise "C:/Documents and Settings". A Windows Scheduler task will have been installed and will be configured to run every 20 minutes from the time of the installation. Whether or not this results in an actual backup depends upon the timer settings in the server config files. To force a backup by hand, start the burp client from within its directory: cd "C:\Program Files\Burp\bin" burp.exe -a b If you are running Windows 7, you might need to start the command prompt with "Run as Administrator". Mac client ---------- The installation on a Mac is performed via Homebrew (http://brew.sh). After installing Homebrew, with an admin account you just have to open a Terminal and type the command: brew install burp Burp will be installed in the subdirectories of /usr/local (see Homebrew website for details on the subdirectories). You'll then have to edit the client configuration file: /usr/local/etc/burp/burp.conf At the minimum, you'll have to change the line: include = /home to something like: include = /Users Automated deploy with ansible ----------------------------- For ansible roles to manage burp, please see: https://github.com/grke/burp/wiki/automated-deploy-and-maintenance For more help, see the man page - type 'man burp'. burp-2.4.0/TODO000066400000000000000000000016001404341324700131540ustar00rootroot00000000000000The list of things to do is at: https://github.com/grke/burp/issues However, that is mostly relating to protocol1 issues. So, this is a list of important stuff that needs to be done for protocol2, roughly in order of most important to least. * Need to improve restore speed - come up with a way to efficiently read the blocks into memory by looking ahead in the manifests. * Make the status monitor work. * Add data compression. * Add data encryption. * Make Windows EFS work. * Make Windows 'image' backups work. List of things that are not recorded as issues on GitHub: * Add diff with next backup, i.e. burp -a d Report the differences between the current backup and the backup that will be made next. burp -a d -b 2 -b n Report the differences between backup 1 and the backup that will be made next (use -a D for more verbosity). burp-2.4.0/UPGRADING000066400000000000000000000225071404341324700137400ustar00rootroot00000000000000Things to watch out for when upgrading. 2.3.12 ------ On Windows, the '-x' option has been split into two different options - '-x' and '-X'. '-x': Restore without using the Windows VSS api, and attempt to strip out any VSS data. This now works the same as the non-Windows client, and is a new option for Windows. '-X': Restore without using the Windows VSS api, and do not attempt to strip out any VSS data. This option only exists on Windows, and was the behaviour that you previously got with '-x'. Use this if your backup contains no VSS data. 2.3.4 ----- Clients will now error out if using protocol 2 and 'encryption_password' is set, because that option is not supported in protocol 2. 2.3.2 ----- The 'restore_client' option now takes into consideration the 'client_can_X' settings of the original client before considering the 'client_can_X' settings of the restore client. To get the previous behaviour, where only the restore client's settings were considered, use the new 'super_client' option instead of 'restore_client'. 2.2.14 ------ For security, the burp client will no longer let you restore over symlinks that point outside of your desired destination directory. Specifying a '-d ' with your restore parameters is now mandatory. If you wish to restore everything back to the original locations, you can give '-d /', which will also work on Windows clients. If you wish server-initiated restores to restore to original locations, you should add '-d /' to the timed job in the client side. 2.2.12 ------ The command line option to print the version number, is now '-V'. It has made way for the new '-v' option, which means 'log to stdout'. 2.2.10 ------ The server side options 'address', 'port', 'status_address', and 'status_port' have been removed. They have been replaced with 'listen' and 'listen_status'. 2.0.42 ------ A bug in generating protocol 2 rabin checksums was fixed. Any protocol 2 backups made up to this point should be discarded. 2.0.32 ------ 'make install' will now install everything except configuration files. 'make install-config' will install configuration files, and will overwrite without warning. 'make install-all' will install everything. 2.0.30 ------ The configure system has been rewritten and vastly improved. One difference to watch out for is that 'make install' will now overwrite any existing burp config files and scripts without any warning. 2.0.26 ------ The dhfile generation command in burp_ca has changed from 'openssl dhparam -out "$dhfile" 1024' to 'openssl dhparam -dsaparam -out "$dhfile" 2048' in order to make ssl key exchange more secure. Your existing /etc/burp/dhfile.pem on your burp server will not be regenerated unless you delete /etc/burp/dhfile.pem by hand (rm -f /etc/burp/dhfile.pem) and restart the burp server. 2.0.22 ------ The protocol2 manifest formats have changed. You should therefore not mix earlier 2.0.x servers and clients with this version, and protocol2 backups from earlier versions will no longer work. 2.0.20 ------ The option 'working_dir_recovery_method = use' has been removed. 2.0.12 ------ Executable scripts that used to be in /etc/burp are now installed in /usr/share/burp/scripts: notify_script, ssl_extra_checks_script, summary_script, and timer_script. On upgrading, you should probably fix the paths to these scripts in /etc/burp/burp-server.conf. Package building files have not yet been updated to reflect this change. Patches gratefully accepted. 2.0.10 ------ The status monitor system and ncurses client are now working to some degree. You now need to use a burp client to connect to the status port via SSL. This also means that you may use the ncurses client on a machine that is not the burp server. You can use 'burp -a m' to get a raw connection (useful for debugging or to see the JSON), or 'burp -a s' to use the ncurses client. You will not be able to see details of any clients unless you are connecting from the client in question, or you are a restore_client (see the man page for this option). On upgrading from previous versions of burp, you will not have some of the necessary config values for connecting to the status port. Both server and client will need 'status_port = 4972'. The client will need 'server = '. The server address may need to be '::1' if you are connecting locally. You may use 'netstat -plant | grep burp' to determine the address that burp is listening on. There are still some pieces in the ncurses client that are not fully implemented. More details about all this will be in documentation to be written soon. 2.0.9 ----- The configuration file parser now understands single quotes and double quotes properly. If you have single quotes or double quotes around your config value, the parser will now treat backslash as the escaping character. If you do not have quotes around your config value, the config value will be taken literally. 2.0.7 ----- The bedup program is now contained within the burp binary. 'make install' will create a 'bedup' symlink to the burp binary. Executing 'bedup' will then work as before. 2.0.6 ----- Warning about hardlinked_archive in protocol1 style backups: In this version, I have changed the way that hardlinked_archive works slightly, in order to fix https://github.com/grke/burp/issues/176 - unnecessary duplication and deletion. In the past, the previous current backup would have a file called 'hardlinked' created when the next backup was being finalised. So, the decision was being made during the next backup. Now, the decision is being made for each backup when it is being finalised itself (rather than waiting for the next one). This means that, when you upgrade, your most recent backup will look like a non-hardlinked_archive, and when you make you next protocol1 style backup with a 2.0.x server, it will treat the previous backup as a non-hardlinked_archive. If you need it to be a hardlinked_archive, make sure that you anticipate this before the next backup happens, by creating a file called something like: /var/spool/burp//current/hardlinked 2.0.4 ----- There has been a massive rewrite of most of burp. The intention is that burp1 clients will continue to work with burp2 servers. Burp2 clients can also be configured to work in burp1 mode. See the 'protocol' option in the man page and example config files. Burp2 code is capable of doing variable length chunking for inline deduplication, with sparse indexing on the server side. Use this version with extreme caution. Burp2 is currently missing some features, such as acl/xattr, Windows EFS, encryption and compression support. The burp1 protocol should still work with these features, but be careful. The status monitor is also not working. Due to the extent of the rewrite, pretty much anything may not working correctly. Do not expect burp2, and the backups that it makes, to be compatible with future releases of burp2. 1.3.24 ------ If installing with 'make install' and you want to use the server_script_pre/post_notify feature, then you will probably need to overwrite /etc/burp/notify_script by hand. The arguments to it have not changed though, so if you do not want to use that feature, you do not need to do this. 1.3.18 ------ Special care is advised when testing this release, as some of the main structure of the program has been tweaked in order to support the stripping out of Windows VSS information. 1.3.16 ------ If you have any clients on 1.3.14, you should upgrade them to 1.3.14 due to a bug in the exclude_comp feature. 1.3.14 ------ If installing with 'make install', you will probably need to overwrite /etc/burp/notify_script by hand. The arguments to it have changed, so you will also need to update the configuration in /etc/burp/burp-server.conf to be in the new style. The example config in configs/server/burp.conf will help. The '-l ' and 'logfile = ' options have been removed because they didn't work properly, and there was no sensible way to make the work properly. There are now three settings in the conf files: stdout=[0|1], syslog=[0|1], and progress_counters=[0|1]. If you want to see output, you may need to set these options. 1.3.3 ----- If installing with 'make install' and you want to use the 'initialise manual backup from server' feature, you will need to replace /etc/burp/timer_script with configs/server/timer_script from the source, because 'make install' will not overwrite the old version. 1.3.2 ----- The process of generating SSL certificates has been automated, so the old default keys and certificates have been removed from new installs. See docs/burp_ca.txt for more information. 1.3.1 ----- Due to Debian policy, burp and bedup will now be installed in /usr/sbin. Therefore, you may need to update any cron jobs that were previously using /sbin. 1.2.4 ----- There was a critical bug in 1.2.4. Please upgrade clients and servers to 1.2.5 as soon as you can. 1.1.x to 1.2.0 --------------- * Clients on 1.1.70 or older will not work with a 1.2.0 server. You will need to upgrade the clients at the same time. Your old backups will continue to work. * If you previously compiled with './configure --prefix=/usr', you will probably now want to compile with just './configure'. The binary will now go to /sbin/burp, with a symlink from /usr/sbin/burp. * If you are installing from source onto the server, you may want to 'rm /etc/burp/notify_script' before 'make installation', otherwise you won't get the updated version. burp-2.4.0/configs/000077500000000000000000000000001404341324700141175ustar00rootroot00000000000000burp-2.4.0/configs/certs/000077500000000000000000000000001404341324700152375ustar00rootroot00000000000000burp-2.4.0/configs/certs/CA/000077500000000000000000000000001404341324700155225ustar00rootroot00000000000000burp-2.4.0/configs/certs/CA/CA.cnf.in000066400000000000000000000013511404341324700171020ustar00rootroot00000000000000# simple config for burp_ca CA_DIR = @sysconfdir@/CA [ ca ] dir = $ENV::CA_DIR database = $dir/index.txt serial = $dir/serial.txt certs = $dir/certs new_certs_dir = $dir/newcerts crlnumber = $dir/crlnumber.txt unique_subject = no default_md = sha256 default_days = 7300 default_crl_days = 7300 #???? name_opt = ca_default cert_opt = ca_default x509_extensions = usr_cert copy_extensions = copy policy = policy_anything [ usr_cert ] basicConstraints = CA:FALSE [ policy_anything ] commonName = supplied burp-2.4.0/configs/certs/CA/README000066400000000000000000000044051404341324700164050ustar00rootroot00000000000000Graham Keeling says: > Below are Patrick Koppen's notes on his burp_ca script. > I haven't had time yet to integrate it into burp properly, but I think it > is probably nearly there as it stands. It just needs a little bit more time > and effort to finish it off. > We need to bear in mind that this kind of thing also needs to be easily done > for operating systems that don't have bash - like Windows. > How would you imagine that would work - generate the things on a Debian > machine and just copy them over? I wrote a 'burp_ca' for testing. It should be installed to /usr/sbin. CA.cnf goes to /etc/burp/. I think it would be better not to install any certificates. Maybe you can add a 'make install; make demo_certs'. The debian package could something similar during postinstall. burp_ca --init --key --request --sign --batch (add --config CA.cnf --dir CA if you don't use the defaults) This initializes a CA with `hostname -f` as the CN, generates the server key, requests the certificate and signs it. On a client you do something like: # generate key and csr burp_ca --key --request --dir . --name `hostname -f` # copy *.csr to the server to /etc/burp/CA (or whatever --dir ...) # on the server: burp_ca --ca `hostname -f` --sign --name # copy CA/.crt back to the client Problems: * I cannot add burp_ca and CA.cnf to your Makefile, because I don't understand your autoconf... > Graham Keeling says: > I have been adding scripts and conf files to the top level Makefile.in. > What I'm doing may not be quite right, but it has been working so far and > nobody has complained yet! > So these two new ones can probably just be added around line 113. * Default certs needs to be removed and replaced by symlinks to generated certificates (so you don't have to change the configs). Graham Keeling says: > If we switch to this new mechanism, we can just leave the old default bits > in their old places, which will keep working, and explain in the release notes > what you have to do to start using the new way. People starting fresh should > just start using the new way automatically without knowing any different. * add ssl_key option, so key and cert can be in different files Graham Keeling says: > This should be easy for me to do. burp-2.4.0/configs/certs/CA/burp_ca.bat.in000066400000000000000000000045721404341324700202420ustar00rootroot00000000000000echo off REM This script is based on @name@_ca, runs on Windows, and contains only the REM stuff that the client needs to run. REM REM It is going to be run with arguments that look like the following: REM @name@_ca.bat --key --keypath "C:/Program Files/@human_name@/ssl_cert-client.key" --request --requestpath "C:/Program Files/@human_name@/CA/win2008.csr" --name win2008 REM The tildas here stop Windows putting quotes around the variable. set "keypath=%~3" set "requestpath=%~6" set "name=%~8" REM Need to set OPENSSL_CONF otherwise openssl tries to find an conf file from REM within my mingw build environment. Set it to an empty file that the REM installer will create. REM @human_name@ used to always put things in C:\Program Files\@human_name@\, REM but as of 1.3.11, it changed to putting them in %PROGRAMFILES%, but still REM want to let the old way work. IF EXIST "C:\Program Files\@human_name@" ( set "OPENSSL_CONF=C:\Program Files\@human_name@\openssl.conf" set "openssl=C:\Program Files\@human_name@\bin\openssl.exe" set "ca_dir=C:\Program Files\@human_name@\CA" ) ELSE ( set "OPENSSL_CONF=%PROGRAMFILES%\@human_name@\openssl.conf" set "openssl=%PROGRAMFILES%\@human_name@\bin\openssl.exe" set "ca_dir=%PROGRAMFILES%\@human_name@\CA" ) set "tmpconf=%ca_dir%\tmp.conf" if %3.==. goto notenoughparams if %6.==. goto notenoughparams if %8.==. goto notenoughparams REM Need to change forward slashes to backslashes in the paths. set keypath=%keypath:/=\% set requestpath=%requestpath:/=\% IF NOT EXIST "%ca_dir%" mkdir "%ca_dir%" echo "generating key %name%: %keypath%" "%openssl%" genrsa -out "%keypath%" 2048 REM Need to create a config file for openssl in order to make a certicate REM signing request. There must be a neater way to do it than one line at a time REM and %tmpconf% at the end each time. echo [ req ] >> "%tmpconf%" echo distinguished_name = req_distinguished_name >> "%tmpconf%" echo prompt = no >> "%tmpconf%" echo [ v3_req ] >> "%tmpconf%" echo basicConstraints=CA:false >> "%tmpconf%" echo [ req_distinguished_name ] >> "%tmpconf%" echo commonName = %name% >> "%tmpconf%" echo "generating certificate signing request: %requestpath%" "%openssl%" req -config "%tmpconf%" -new -key "%keypath%" -out "%requestpath%" -extensions v3_req del "%tmpconf%" exit 0 :notenoughparams echo "%0 not given enough parameters" exit 1 burp-2.4.0/configs/certs/CA/burp_ca.in000077500000000000000000000161101404341324700174670ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright: Patrick Koppen # License: GPLv3 set -eu -o pipefail # Locking code to try to make sure that two instances do not run together and # corrupt the CA files. LOCKDIR="/tmp/@name@_ca.lockdir" PIDFILE="$LOCKDIR/pid" cleanup() { # Perform program exit housekeeping. # Optionally accepts an exit status. rm -rf "$LOCKDIR" trap - INT TERM EXIT } lock() { # Originally from http://wiki.bash-hackers.org/howto/mutex, and # adjusted somewhat. if mkdir "$LOCKDIR" 2>/dev/null ; then trap cleanup INT TERM EXIT echo $$ > "$PIDFILE" || return 1 return 0 else oldpid="$(cat "$PIDFILE")" || return 1 if ! ps -p "$oldpid" &>/dev/null; then echo "Removing stale lock with pid $oldpid" 1>&2 rm -rf "$LOCKDIR" || return 1 echo "Restarting myself" 1>&2 exec "$0" "$@" fi return 1 fi } maxwait=60 sleeptime=1 slept=0 while ! lock; do sleep "$sleeptime" let slept+="$sleeptime" let sleeptime*=2 if [ "$slept" -ge "$maxwait" ]; then echo "Failed to acquire lock $LOCKDIR in $slept seconds" 1>&2 exit 1 fi done # Solaris, AIX, NetBSD and OpenBSD don't support 'hostname -f' and, # in fact, set the hostname to '-f' with that call. name= case "$(uname -s)" in SunOS) name="$(check-hostname | cut -d' ' -f7)" ;; AIX) name="$(host "$(hostname)" | awk '{ print $1}')" ;; NetBSD) name="$(hostname || true)" ;; OpenBSD) name="$(uname -n || true)" ;; *) name="$(hostname -f || true)" ;; esac # Try NetBSD style. [ -n "$name" ] || name="$(hostname)" etc="@sysconfdir@" dir="$etc"/CA conf="$etc"/CA.cnf days= ca_days=7300 size=2048 init= key= keypath= request= requestpath= sign= batch= revoke= crl= ca= dhfile= altname= def_umask=022 sec_umask=077 function help() { cat < path to new key -r|--request generate certificate sign request -R|--requestpath path to certificate sign request -s|--sign sign csr (use --ca and --name ) --batch do not prompt for anything --revoke revoke certificate with serial number --crl generate certificate revoke list -d|--dir ca output dir (default: $dir) -c|--config config file (default: $conf) -n|--name name (default: $name) -D|--days valid days for certificate (default in config file) --ca_days valid days for CA certificate (default: $ca_days) -S|--size key size (default: $size) -a|--ca ca name if different from name -f|--dhfile generate Diffie-Hellman file -A|--altname subjectAltName EOF } check_second_arg() { if [ "$1" -eq 0 ] ; then help exit 1 fi } while [ $# -gt 0 ] do case "$1" in -h|--help) help; exit 0 ;; -i|--init) init=yes ;; -k|--key) key=yes ;; -K|--keypath) check_second_arg "$#"; keypath="$2"; shift ;; -r|--request) request=yes ;; -R|--requestpath) check_second_arg "$#"; requestpath="$2"; shift ;; -s|--sign) sign=yes ;; --batch) batch="-batch" ;; --revoke) check_second_arg "$#"; revoke="$2"; shift ;; --crl) crl=yes ;; -d|--dir) check_second_arg "$#"; dir="$2"; shift ;; -c|--config) check_second_arg "$#"; conf="$2"; shift ;; -n|--name) check_second_arg "$#"; name="$2"; shift ;; -D|--days) check_second_arg "$#"; days="-days $2"; shift ;; --ca_days) check_second_arg "$#"; ca_days="$2"; shift ;; -S|--size) check_second_arg "$#"; size="$2"; shift ;; -a|--ca) check_second_arg "$#"; ca="$2"; shift ;; -f|--dhfile) check_second_arg "$#"; dhfile="$2"; shift ;; -A|--altname) check_second_arg "$#"; altname="$2"; shift ;; --) shift; break ;; -*) echo "$0: error - unrecognized option $1" 1>&2; exit 1 ;; *) break ;; esac shift done if [ -n "$dhfile" ] ; then openssl dhparam \ -dsaparam \ -out "$dhfile" \ 2048 chmod 600 "$dhfile" fi if [ -z "$ca" ]; then ca="$name" fi if [ -n "$altname" ]; then altname="subjectAltName=$altname" fi export CA_DIR="$dir" # init CA if [ "$init" = "yes" ]; then echo "Init... $ca" if [ ! -f "$conf" ]; then echo "$0: error - config $conf missing" 1>&2 exit 1 fi if [ -d "$dir" ]; then echo "$0: error - $dir exists, ca initialized" 1>&2 exit 1 fi mkdir "$dir" mkdir "$dir"/certs mkdir "$dir"/newcerts umask "$sec_umask" openssl genrsa \ -out "$dir/CA_$ca.key" \ "$size" umask "$def_umask" TEMP="$(mktemp "/tmp/@name@_ca.tmp.XXXXXXXX" || echo "/tmp/@name@_ca.tmp.$$")" cat <<-EOF > "$TEMP" [ req ] distinguished_name = req_distinguished_name prompt = no [ v3_ca ] basicConstraints=CA:true subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer:always [ req_distinguished_name ] commonName = $ca EOF openssl req \ -config "$TEMP" \ -new -x509 \ -days "$ca_days" \ -key "$dir/CA_$ca.key" \ -out "$dir/CA_$ca.crt" \ -extensions v3_ca rm -f "$TEMP" : > "$dir"/index.txt echo "00" > "$dir"/serial.txt echo "00" > "$dir"/crlnumber.txt fi [ -z "$keypath" ] && keypath="$dir/$name.key" # generate key if [ "$key" = "yes" ]; then echo "generating key $name: $keypath" umask "$sec_umask" openssl genrsa \ -out "$keypath" \ "$size" umask "$def_umask" fi # generate signing request [ -z "$requestpath" ] && requestpath="$dir/$name.csr" if [ "$request" = "yes" ]; then echo "generating request $name" [ -d "$(dirname "$requestpath")" ] || mkdir "$(dirname "$requestpath")" TEMP="$(mktemp "/tmp/@name@_ca.tmp.XXXXXXXX" || echo "/tmp/@name@_ca.tmp.$$")" cat <<-EOF > "$TEMP" req_extensions = v3_req [ req ] distinguished_name = req_distinguished_name prompt = no [ v3_req ] basicConstraints=CA:false $altname [ req_distinguished_name ] commonName = $name EOF openssl req \ -config "$TEMP" \ -new \ -key "$keypath" \ -out "$requestpath" \ -extensions v3_req rm -f "$TEMP" fi # sign if [ "$sign" = "yes" ]; then serial="$(cat "$dir"/serial.txt)" if [ -n "$days" ] ; then openssl ca \ -config "$conf" \ -name ca \ -in "$dir/$name.csr" \ -out "$dir/$name.crt" \ -days "$days" \ -keyfile "$dir/CA_$ca.key" \ -cert "$dir/CA_$ca.crt" \ $batch else openssl ca \ -config "$conf" \ -name ca \ -in "$dir/$name.csr" \ -out "$dir/$name.crt" \ -keyfile "$dir/CA_$ca.key" \ -cert "$dir/CA_$ca.crt" \ $batch fi [ -f "$dir/newcerts/$serial.pem" ] || exit 0 mv "$dir/newcerts/$serial.pem" "$dir/certs/$serial.pem" # rehash the certificates for file in "$dir"/certs/*.pem; do hash=$(openssl x509 -hash -noout -in "$file") ln -s -f "$file" "$dir/certs/$hash.0" done fi [ -n "$revoke" ] && \ openssl ca \ -config "$conf" \ -name ca \ -revoke "$dir/certs/$revoke.pem" \ -keyfile "$dir/CA_$ca.key" \ -cert "$dir/CA_$ca.crt" \ $batch [ -n "$crl" ] && \ openssl ca \ -config "$conf" \ -name ca \ -gencrl \ -out "$dir/CA_$ca.crl" \ -keyfile "$dir/CA_$ca.key" \ -cert "$dir/CA_$ca.crt" exit 0 burp-2.4.0/configs/client/000077500000000000000000000000001404341324700153755ustar00rootroot00000000000000burp-2.4.0/configs/client/burp.conf-win.in000066400000000000000000000016621404341324700204210ustar00rootroot00000000000000# An example config file to use if you are not running the Windows installer. mode = client server = 10.0.0.1:4971 status_port = 4972 cname = clientname password = abcdefgh server_can_restore = 0 stdout = 1 progress_counter = 1 nobackup = .nobackup lockfile = C:/Program Files/@human_name@/lockfile include = C:/Users exclude_regex = ^[A-Z]:/recycler$ exclude_regex = ^[A-Z]:/\$recycle\.bin$ exclude_regex = ^[A-Z]:/pagefile\.sys$ exclude_regex = ^[A-Z]:/swapfile\.sys$ exclude_regex = ^[A-Z]:/hiberfil\.sys$ split_vss = 0 strip_vss = 0 encryption_password = password ca_@name@_ca = C:/Program Files/@human_name@/bin/@name@_ca.bat ca_csr_dir = C:/Program Files/@human_name@/CA ssl_cert_ca = C:/Program Files/@human_name@/ssl_cert_ca.pem ssl_cert = C:/Program Files/@human_name@/ssl_cert-client.pem ssl_key = C:/Program Files/@human_name@/ssl_cert-client.key ssl_key_password = password ssl_peer_cn = @name@server burp-2.4.0/configs/client/burp.conf.in000066400000000000000000000122131404341324700176200ustar00rootroot00000000000000# This is an example config file for the @name@ client. mode = client server = @server_address@:4971 status_port = 4972 password = abcdefgh cname = testclient # A different port to use for restores - see the man page for more options. #port_restore = 5971 # Choose the protocol to use. # 0 to decide automatically, 1 to force protocol1 mode (file level granularity # with a pseudo mirrored storage on the server and optional rsync). 2 forces # protocol2 mode (inline deduplication with variable length blocks). # protocol = 0 pidfile = @runstatedir@/@name@.client.pid syslog = 0 stdout = 1 progress_counter = 1 # Ratelimit throttles the send speed. Specified in Megabits per second (Mb/s). # ratelimit = 1.5 # Network timeout defaults to 7200 seconds (2 hours). # network_timeout = 7200 # The directory to which autoupgrade files will be downloaded. # To never autoupgrade, leave it commented out. # autoupgrade_dir=@sysconfdir@/autoupgrade/client # OS path component for the autoupgrade directory on the server. # autoupgrade_os=test_os # Wait a random number of seconds between 0 and the given number before # contacting the server on a timed backup. # randomise = 1200 # Set server_can_restore to 0 if you do not want the server to be able to # initiate a restore (setting it to 1 also requires 'restoreprefix'). server_can_restore = 0 # This option is prefixed to the path of all restores, and is overridden by # '-d' on the command line. # restoreprefix = /my/restore/directory # Set server_can_override_includes to 0 if you do not want the server to be # able to override the local include/exclude list. The default is 1. # server_can_override_includes = 1 # Set an encryption password if you do not trust the server with your data. # Note that this will mean that network deltas will not be possible. Each time # a file changes, the whole file will be transferred on the next backup. # On restoration, take care when copying and pasting special characters between # client conf files, as the encoding of the config file matters. # encryption_password = My^$pAsswIrD%@ # More configuration files can be read, using syntax like the following # (without the leading '# '). # . path/to/more/conf # Run as different user/group. # user=graham # group=nogroup # Set to 1 to keep 'readall' capability (linux only) when dropping privileges # (default is 0, do not keep capability). Readall capability allows process # to read all files (even not owned by user) without requiring root privileges. # When using readall=1 if user= is not set it's assumed nobody. # readall=1 cross_filesystem=/home cross_all_filesystems=0 # Uncomment the following lines to automatically generate a certificate signing # request and send it to the server. ca_@name@_ca = @sbindir@/@name@_ca ca_csr_dir = @sysconfdir@/CA-client # SSL certificate authority - same file on both server and client ssl_cert_ca = @sysconfdir@/ssl_cert_ca.pem # Client SSL certificate ssl_cert = @sysconfdir@/ssl_cert-client.pem # Client SSL key ssl_key = @sysconfdir@/ssl_cert-client.key # Client SSL ciphers #ssl_ciphers = # Client SSL compression. Default is zlib5. Set to zlib0 to turn it off. #ssl_compression = zlib5 # SSL key password, for loading a certificate with encryption. #ssl_key_password = password # Common name in the certificate that the server gives us ssl_peer_cn = @name@server # Example syntax for pre/post scripts #backup_script_pre=/path/to/a/script #backup_script_post=/path/to/a/script #restore_script_pre=/path/to/a/script #restore_script_post=/path/to/a/script # The following options specify exactly what to backup. # The server will override them if there is at least one 'include=' line on # the server side and 'server_can_override_includes=1'. include = /home #exclude = /home/graham/testdir/librsync-0.9.7/testsuite #include = /home/graham/testdir/librsync-0.9.7/testsuite/deep #include = /home/graham/xdir #exclude = /home/graham/testdir/libr # Exclude file names ending in '.vdi' or '.vmdk' (case insensitive) #exclude_ext = vdi #exclude_ext = vmd # Exlude file path matching a regular expression # (note that 'include_regex' is not yet implemented) #exclude_regex = \.cache # Exclude various temporary file systems. You may want to add devfs, devpts, # proc, ramfs, etc. exclude_fs = sysfs exclude_fs = tmpfs # Exclude files based on size. Defaults are 0, which means no limit. #min_file_size = 0 Mb #max_file_size = 0 Mb # The content of directories containing a filesystem entry named like this # will not be backed up. nobackup = .nobackup # By default, @name@ backups up the fifos themselves, rather than reading from # them. These two options let you choose a particular fifo to read, or read # from all fifos. #read_fifo=/path/to/a/fifo #read_all_fifos=0 # The same for block device nodes. #read_blockdev=/path/to/a/blockdev #read_all_blockdevs=0 # Exclude files from compression by extension. exclude_comp=bz2 exclude_comp=gz # When backing up, whether to enable O_NOATIME when opening files and # directories. The default is atime=0, which enables O_NOATIME. #atime=1 # When enabled, this causes problems in the phase1 scan (such as an 'include' # being missing) to be treated as fatal errors. The default is 0. #scan_problem_raises_error=1 burp-2.4.0/configs/client/cron-summary.example000066400000000000000000000006231404341324700214070ustar00rootroot00000000000000MAILTO="" # Put this file in /etc/cron.d and uncomment the following line to send a # summary at 6 every morning. It works via the client-side status monitor. # If your client is using a different config file to /etc/burp/burp.conf, # change the first argument to its path. #0 6 * * * root /usr/share/burp/scripts/summary_script /etc/burp/burp.conf youremail@example.com "Daily backup summary" burp-2.4.0/configs/client/cron.example000066400000000000000000000004701404341324700177140ustar00rootroot00000000000000# Run the burp client every 20 minutes with the 'timed' option. # Sleep a random number of seconds from 0 to 1200 (20 minutes) before # contacting the server. # The burp server will decide whether it is yet time to do a backup or not. 0,20,40 * * * * root /usr/sbin/burp -a t -q 1200 >>/var/log/burp-client 2>&1 burp-2.4.0/configs/client/openssl.conf-win.in000066400000000000000000000000001404341324700211150ustar00rootroot00000000000000burp-2.4.0/configs/client/zfs_script000077500000000000000000000100251404341324700175070ustar00rootroot00000000000000#!/usr/bin/env bash # # ZFS backup script. # # Creates temporary ZFS snapshots, and 'zfs sends' them (or differences # between them and previous snapshots) down fifo nodes in # /etc/burp/fifos. # # On success, the ZFS script renames the temporary snapshot # to a more permanent name so that the snapshots can be tracked consistently. # # To use, put something like this in your burp.conf. # You can add multiple pools. # # backup_script=/etc/burp/zfs_script # backup_script_arg=7 # backup_script_arg=rpool/export/home@burp # backup_script_arg=rpool/ROOT/s10x_u9wos_14a/var@burp # backup_script_post_run_on_fail=1 # # A snapshot numbered between 1 to $MAX_SNAPS is created on a backup. The # number is incremented for each backup. Number 1 is always a full ZFS snapshot. # The rest are incrementals based on the previous number. After $MAX_SNAPS, # the sequence starts from 1 again. # # You will want to configure the burp server to keep enough backups so that # a ZFS 'full' snapshot is always available. For example, if you set $MAX_SNAPS # to 7, you will want to keep at least 14 backups on the server to guarantee # that you can always restore at least 7 backups into the past. # # To do a restore, you will need to do this (for example), starting from number # 1: # mkfifo /etc/burp/fifos/rpool/export/home@burp # cat /etc/burp/fifos/rpool/export/home@burp | zfs recv # # Then repeat, each time incrementing the number until you reach the backup # that you want to end up on. if [ $# -lt 7 ] ; then echo "$0: not enough arguments" 1>&2 exit 1 fi MODE="$1" ; shift FAILED="$1" ; shift; shift; shift; shift MAX_SNAPS="$1" ; shift if [ "$MODE" = "pre" ] ; then declare -a arr=($@) # Set up the fifo directory PIPE_DIR=/etc/burp/fifos rm -rf "$PIPE_DIR" || exit 1 mkdir -p "$PIPE_DIR" || exit 1 # Create all the snapshots that we will need. a=0 while [ $a -lt $# ] ; do SNAPSHOT="${arr[$a]}" SNAPSHOT_TMP="$SNAPSHOT"tmp # Find out how many snapshots already exist. inc=1 while [ "$inc" -le $MAX_SNAPS ] ; do /usr/sbin/zfs list "$SNAPSHOT$inc" >/dev/null 2>&1 || break inc=$((inc+1)) done if [ "$inc" -gt $MAX_SNAPS ] ; then # Had enough snapshots now. Start from 1 again. inc=1 fi # Destroy all the unneeded snapshots i=$inc while [ "$i" -le $MAX_SNAPS ] ; do /usr/sbin/zfs destroy "$SNAPSHOT$i" >/dev/null 2>&1 /usr/sbin/zfs destroy "$SNAPSHOT_TMP$i" >/dev/null 2>&1 i=$((i+1)) done # Create the new snapshot. /usr/sbin/zfs snapshot -r "$SNAPSHOT_TMP$inc" || exit 1 # Create a pipe. ZFS_PIPE=$PIPE_DIR/$SNAPSHOT$inc mkdir -p "${ZFS_PIPE%/*}" mkfifo "$ZFS_PIPE" || exit 1 chmod 777 "$ZFS_PIPE" || exit 1 a=$((a+1)) done # Need to exec here, otherwise burp will wait forever for this script to # return. exec > /dev/null 2>&1 # Now, go through the list and do zfs sends for all the new snapshots. a=0 while [ $a -lt $# ] ; do SNAPSHOT="${arr[$a]}" SNAPSHOT_TMP="${arr[$a]}tmp" # Figure out the most recent snapshot again. inc=1 while [ "$inc" -le $MAX_SNAPS ] ; do /usr/sbin/zfs list "$SNAPSHOT$inc" >/dev/null 2>&1 || break inc=$((inc+1)) done ZFS_PIPE=$PIPE_DIR/$SNAPSHOT$inc if [ "$inc" = 1 ] ; then # First in the chain. Just do the full zfs send. /usr/sbin/zfs send "$SNAPSHOT_TMP$inc" > "$ZFS_PIPE" & else # Not the first in the chain. Do an incremental zfs send. /usr/sbin/zfs send -i "$SNAPSHOT$((inc-1))" "$SNAPSHOT_TMP$inc" > "$ZFS_PIPE" & fi a=$((a+1)) done exit 0 elif [ "$MODE" = "post" ] ; then while [ "$#" -gt 0 ] ; do SNAPSHOT=$1 ; shift SNAPSHOT_TMP="$SNAPSHOT"tmp i=1 if [ "$FAILED" = "1" ] ; then while [ "$i" -le $MAX_SNAPS ] ; do /usr/sbin/zfs destroy "$SNAPSHOT_TMP$i" >/dev/null 2>&1 i=$((i+1)) done else while [ "$i" -le $MAX_SNAPS ] ; do /usr/sbin/zfs list "$SNAPSHOT_TMP$i" >/dev/null 2>&1 && \ /usr/sbin/zfs rename "$SNAPSHOT_TMP$i" "$SNAPSHOT$i" i=$((i+1)) done fi done exit 0 else echo "$0: unknown mode: $MODE" 1>&2 exit 1 fi burp-2.4.0/configs/server/000077500000000000000000000000001404341324700154255ustar00rootroot00000000000000burp-2.4.0/configs/server/autoupgrade/000077500000000000000000000000001404341324700177455ustar00rootroot00000000000000burp-2.4.0/configs/server/autoupgrade/debian.script000066400000000000000000000001711404341324700224140ustar00rootroot00000000000000#!/usr/bin/env bash dpkg -i --force-overwrite --force-confdef /etc/burp/autoupgrade/client/package &> /dev/null exit 0 burp-2.4.0/configs/server/autoupgrade/windows.script000066400000000000000000000036131404341324700226700ustar00rootroot00000000000000@echo off :: Upgrade script 2019040301 set product=Burp set curdir=%~dp0 set curdir=%curdir:~0,-1% cd "%curdir%" echo Updating %product% IF EXIST "%curdir%\package.exe" start /wait "" "%curdir%\package.exe" /S && GOTO NEXT :: Fallback for elder than 1.3.11 burp packages where everything was in C:\Program Files\Burp IF EXIST "C:\Program Files\Burp\autoupgrade\package.exe" start /wait "" "C:\Program Files\Burp\autoupgrade\package.exe" /S && GOTO NEXT echo Update Failed GOTO END :NEXT :: The following optional part replaces server and port directives by server = fqdn:port and optional server_failover = fqdn:port :: Comment out the following line and set options below in order to enable config file update GOTO END set server=my.server.local:4971 set server_failover=my.other.server.local:443 set failover_on_backup_error=1 echo Updating configuration :: The following section does :: First let's make a config file backup :: Then lets remove server, port and failover_on_backcup_error statements :: Add new server_failover to config file if specified Add new :: server_failover to config file if specified Add failover_on_backup_error statement :: Put new config in place IF EXIST "%curdir%\..\%product%.conf" ( copy "%curdir%\..\%product%.conf" "%curdir%\..\%product%.conf.backup" > NUL 2>&1 type "%curdir%\..\%product%.conf" | FINDSTR /I /V /R /C:"^server =" | FINDSTR /I /V /R /C:"^port =" | FINDSTR /I /V /R /C:"^failover_on_backup_error =" > "%curdir%\..\%product%.conf.tmp" (echo server = %server%)> "%curdir%\..\%product%.conf" IF NOT "%server_failover%"=="" (echo server_failover = %server_failover%) >> "%curdir%\..\%product%.conf" (echo failover_on_backup_error = %failover_on_backup_error%) >> "%curdir%\..\%product%.conf" type "%curdir%\..\%product%.conf.tmp">> "%curdir%\..\%product%.conf" del "%curdir%\..\%product%.conf.tmp" /S /Q > NUL 2>&1 ) :END exit burp-2.4.0/configs/server/backup_tool_script.in000066400000000000000000002324561404341324700216570ustar00rootroot00000000000000#!/usr/bin/env bash PROGRAM="backup_tool_script" PROGRAM_VERSION=0.5.0 PROGRAM_BUILD=2019081901 AUTHOR="(C) 2017-2019 by Orsiris de Jong" CONTACT="http://www.netpower.fr - ozy@netpower.fr" IS_STABLE=true ## backup_tool_script - A script to check @name@ backup sanity ## backup_tool_script can verify a given number of backups for each client. It can run verifiy operations in parallel. ## Verify operations are timed in order to stop them after a given amount of time, leaving the system performance ready for backup operations. ## The script can also list clients that have outdated backups. It uses two different methods to list clients in order to detect rogue clients. ## It can also ensure that the @name@ server service is running properly, relaunch it if needed, on a scheduled basis. ## The script can send a warning / error when problems are found, even while operating. ## The script can send a warning / error when disk quotas exceed, even while operating. ## backup_tool_script can also launch vss_strip for each file found in a given directory. ## The script can also send mails directly to the client, if an email address is given in client config file as 'label = email_address : some@example.com' ## When exiting, backup_tool_script ensures that no forked @name@ processes remain, without touching other @name@ processes that didn't belong to backup_tool_script. ## Set an unique identifier for the script which will be used for logs and alert mails INSTANCE_ID="base" ## Backup verifications timers ## After how much time (in seconds) for a single verification a warning should be logged (defaults to 3 hours) SOFT_MAX_EXEC_TIME_PER_VERIFY=10800 ## After how much time (in seconds) for a single verification the process should be stopped (defaults to 5 hours) HARD_MAX_EXEC_TIME_PER_VERIFY=64800 ## After how much seconds of execution of all steps a warning should be logged (defaults to 10 hours) SOFT_MAX_EXEC_TIME=64800 ## After how much seconds of execution of all steps a verification process should be stopped (defaults to 12 hours) HARD_MAX_EXEC_TIME=82800 # Verify operations checks ## When a client isn't idle, we can postpone the it's backup verification process. How many times should we retry the verification command. Set this to 0 to disable operation postponing POSTPONE_RETRY=2 ## When postponed, how much time (in seconds) before next try (defauls to 1 hour) POSTPONE_TIME=3600 ## Backup executable (can be set to /usr/sbin/@name@, /usr/local/sbin/@name@, or autodetect via $(type -p @name@)) BACKUP_EXECUTABLE=@sbindir@/@name@ # PROD = @sbindir@/@name@ ## @name@ service type (can be "initv" or "systemd") SERVICE_TYPE=initv # PROD = initv ## How many simultaneous verify operations should be launched (please check I/O and CPU usage before increasing this) PARELLEL_VERIFY_CONCURRENCY=2 # ------------ Mail alert settings ------------- ## General alert mail subject MAIL_ALERT_MSG="Execution of $PROGRAM instance $INSTANCE_ID on $(date) has warnings/errors." ## Optional change of mail body encoding (using iconv) ## By default, all mails are sent in UTF-8 format without headers ## You may specify an optional encoding here (like "ISO-8859-1" or whatever iconv can handle) for maximal compatibility MAIL_BODY_CHARSET="ISO-8859-1" # ------------ Client specific alert email template -------------- ## Email subject CLIENT_ALERT_SUBJECT="@name@ backup - Warning about your backup" ## Valid message body placeholders are ## [NUMBERDAYS] is the number of days we check. ## [QUOTAEXCEED] is the size of the actual backup versus the quota. ## [CLIENT] is the client name ## [INSTANCE] is the current instance name ## Message sent directly to client email address when no recent backups are found. CLIENT_ALERT_BODY_OUTDATED="Hello, No valid backup sets found in the last [NUMBERDAYS] day(s) for client [CLIENT]. Please leave your computer online enough time for a backup to occur. Contact your system administrator for further information. @name@ backup server [INSTANCE]. " # Message sent directly to client email address when quota exceeded. CLIENT_ALERT_BODY_QUOTA="Hello, Your backup disk quota has been exceeded ([QUOTAEXCEED]) for client [CLIENT]. Contact your system administrator for further information. @name@ backup server [INSTANCE]. " # ------------ Do not modify under this line unless you have great cow powers -------------- if ! type "$BASH" > /dev/null; then echo "Please run this script only with bash shell. Tested on bash >= 3.2" exit 127 fi export LC_ALL=C _LOGGER_SILENT=false _LOGGER_VERBOSE=false _LOGGER_ERR_ONLY=false _LOGGER_PREFIX="date" if [ "$KEEP_LOGGING" == "" ]; then KEEP_LOGGING=1801 fi # Initial error status, logging 'WARN', 'ERROR' or 'CRITICAL' will enable alerts flags ERROR_ALERT=false WARN_ALERT=false LOCAL_USER=$(whoami) LOCAL_HOST=$(hostname) SCRIPT_PID=$$ # Get a random number on Windows BusyBox alike, also works on most Unixes that have dd, if dd is not found, then return $RANDOM # Get a random number of digits length on Windows BusyBox alike, also works on most Unixes that have dd function PoorMansRandomGenerator { local digits="${1}" # The number of digits to generate local number # Some read bytes can't be used, se we read twice the number of required bytes dd if=/dev/urandom bs=$digits count=2 2> /dev/null | while read -r -n1 char; do number=$number$(printf "%d" "'$char") if [ ${#number} -ge $digits ]; then echo ${number:0:$digits} break; fi done } # Initial TSTMAP value before function declaration TSTAMP=$(date '+%Y%m%dT%H%M%S').$(PoorMansRandomGenerator 5) ALERT_LOG_FILE="$RUN_DIR/$PROGRAM.$SCRIPT_PID.$TSTAMP.last.log" ## Default log file until config file is loaded if [ -w /var/log ]; then LOG_FILE="/var/log/$PROGRAM.log" elif ([ "$HOME" != "" ] && [ -w "$HOME" ]); then LOG_FILE="$HOME/$PROGRAM.log" elif [ -w . ]; then LOG_FILE="./$PROGRAM.log" else LOG_FILE="/tmp/$PROGRAM.log" fi ## Default directory where to store temporary run files if [ -w /tmp ]; then RUN_DIR=/tmp elif [ -w /var/tmp ]; then RUN_DIR=/var/tmp else RUN_DIR=. fi #### DEBUG SUBSET #### ## allow function call checks #__WITH_PARANOIA_DEBUG if [ "$_PARANOIA_DEBUG" == true ];then #__WITH_PARANOIA_DEBUG _DEBUG=false #__WITH_PARANOIA_DEBUG fi #__WITH_PARANOIA_DEBUG ## allow debugging from command line with _DEBUG=true if [ ! "$_DEBUG" == true ]; then _DEBUG=false _LOGGER_VERBOSE=false else trap 'TrapError ${LINENO} $?' ERR _LOGGER_VERBOSE=true fi if [ "$SLEEP_TIME" == "" ]; then # Leave the possibity to set SLEEP_TIME as environment variable when runinng with bash -x in order to avoid spamming console SLEEP_TIME=.1 fi #### DEBUG SUBSET END #### #### Logger SUBSET #### #### RemoteLogger SUBSET #### # Array to string converter, see http://stackoverflow.com/questions/1527049/bash-join-elements-of-an-array # usage: joinString separaratorChar Array function joinString { local IFS="$1"; shift; echo "$*"; } # Sub function of Logger function _Logger { local logValue="${1}" # Log to file local stdValue="${2}" # Log to screeen local toStdErr="${3:-false}" # Log to stderr instead of stdout if [ "$logValue" != "" ]; then echo -e "$logValue" >> "$LOG_FILE" # Build current log file for alerts if we have a sufficient environment if [ "$RUN_DIR/$PROGRAM" != "/" ]; then echo -e "$logValue" >> "$RUN_DIR/$PROGRAM._Logger.$SCRIPT_PID.$TSTAMP" fi fi if [ "$stdValue" != "" ] && [ "$_LOGGER_SILENT" != true ]; then if [ $toStdErr == true ]; then # Force stderr color in subshell (>&2 echo -e "$stdValue") else echo -e "$stdValue" fi fi } # General log function with log levels: # Environment variables # _LOGGER_SILENT: Disables any output to stdout & stderr # _LOGGER_ERR_ONLY: Disables any output to stdout except for ALWAYS loglevel # _LOGGER_VERBOSE: Allows VERBOSE loglevel messages to be sent to stdout # Loglevels # Except for VERBOSE, all loglevels are ALWAYS sent to log file # CRITICAL, ERROR, WARN sent to stderr, color depending on level, level also logged # NOTICE sent to stdout # VERBOSE sent to stdout if _LOGGER_VERBOSE=true # ALWAYS is sent to stdout unless _LOGGER_SILENT=true # DEBUG & PARANOIA_DEBUG are only sent to stdout if _DEBUG=true function Logger { local value="${1}" # Sentence to log (in double quotes) local level="${2}" # Log level local retval="${3:-undef}" # optional return value of command if [ "$_LOGGER_PREFIX" == "time" ]; then prefix="TIME: $SECONDS - " elif [ "$_LOGGER_PREFIX" == "date" ]; then prefix="$(date '+%Y-%m-%d %H:%M:%S') - " else prefix="" fi if [ "$level" == "CRITICAL" ]; then _Logger "$prefix($level):$value" "$prefix\e[1;33;41m$value\e[0m" true ERROR_ALERT=true # ERROR_ALERT / WARN_ALERT is not set in main when Logger is called from a subprocess. Need to keep this flag. echo -e "[$retval] in [$(joinString , ${FUNCNAME[@]})] SP=$SCRIPT_PID P=$$\n$prefix($level):$value" >> "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID.$TSTAMP" return elif [ "$level" == "ERROR" ]; then _Logger "$prefix($level):$value" "$prefix\e[91m$value\e[0m" true ERROR_ALERT=true echo -e "[$retval] in [$(joinString , ${FUNCNAME[@]})] SP=$SCRIPT_PID P=$$\n$prefix($level):$value" >> "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID.$TSTAMP" return elif [ "$level" == "WARN" ]; then _Logger "$prefix($level):$value" "$prefix\e[33m$value\e[0m" true WARN_ALERT=true echo -e "[$retval] in [$(joinString , ${FUNCNAME[@]})] SP=$SCRIPT_PID P=$$\n$prefix($level):$value" >> "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.warn.$SCRIPT_PID.$TSTAMP" return elif [ "$level" == "NOTICE" ]; then if [ "$_LOGGER_ERR_ONLY" != true ]; then _Logger "$prefix$value" "$prefix$value" fi return elif [ "$level" == "VERBOSE" ]; then if [ $_LOGGER_VERBOSE == true ]; then _Logger "$prefix($level):$value" "$prefix$value" fi return elif [ "$level" == "ALWAYS" ]; then _Logger "$prefix$value" "$prefix$value" return elif [ "$level" == "DEBUG" ]; then if [ "$_DEBUG" == true ]; then _Logger "$prefix$value" "$prefix$value" return fi elif [ "$level" == "PARANOIA_DEBUG" ]; then #__WITH_PARANOIA_DEBUG if [ "$_PARANOIA_DEBUG" == true ]; then #__WITH_PARANOIA_DEBUG _Logger "$prefix$value" "$prefix\e[35m$value\e[0m" #__WITH_PARANOIA_DEBUG return #__WITH_PARANOIA_DEBUG fi #__WITH_PARANOIA_DEBUG else _Logger "\e[41mLogger function called without proper loglevel [$level].\e[0m" "\e[41mLogger function called without proper loglevel [$level].\e[0m" true _Logger "Value was: $prefix$value" "Value was: $prefix$value" true fi } #### Logger SUBSET END #### # Portable child (and grandchild) kill function tester under Linux, BSD and MacOS X function KillChilds { local pid="${1}" # Parent pid to kill childs local self="${2:-false}" # Should parent be killed too ? # Paranoid checks, we can safely assume that $pid should not be 0 nor 1 if [ $(IsInteger "$pid") -eq 0 ] || [ "$pid" == "" ] || [ "$pid" == "0" ] || [ "$pid" == "1" ]; then Logger "Bogus pid given [$pid]." "CRITICAL" return 1 fi if kill -0 "$pid" > /dev/null 2>&1; then if children="$(pgrep -P "$pid")"; then if [[ "$pid" == *"$children"* ]]; then Logger "Bogus pgrep implementation." "CRITICAL" children="${children/$pid/}" fi for child in $children; do Logger "Launching KillChilds \"$child\" true" "DEBUG" #__WITH_PARANOIA_DEBUG KillChilds "$child" true done fi fi # Try to kill nicely, if not, wait 15 seconds to let Trap actions happen before killing if [ "$self" == true ]; then # We need to check for pid again because it may have disappeared after recursive function call if kill -0 "$pid" > /dev/null 2>&1; then kill -s TERM "$pid" Logger "Sent SIGTERM to process [$pid]." "DEBUG" if [ $? -ne 0 ]; then sleep 15 Logger "Sending SIGTERM to process [$pid] failed." "DEBUG" kill -9 "$pid" if [ $? -ne 0 ]; then Logger "Sending SIGKILL to process [$pid] failed." "DEBUG" return 1 fi # Simplify the return 0 logic here else return 0 fi else return 0 fi else return 0 fi } function KillAllChilds { local pids="${1}" # List of parent pids to kill separated by semi-colon local self="${2:-false}" # Should parent be killed too ? __CheckArguments 1 $# "$@" #__WITH_PARANOIA_DEBUG local errorcount=0 IFS=';' read -a pidsArray <<< "$pids" for pid in "${pidsArray[@]}"; do KillChilds $pid $self if [ $? -ne 0 ]; then errorcount=$((errorcount+1)) fi done return $errorcount } # osync/obackup/pmocr script specific mail alert function, use SendEmail function for generic mail sending function SendAlert { local runAlert="${1:-false}" # Specifies if current message is sent while running or at the end of a run local attachment="${2:-true}" # Should we send the log file as attachment __CheckArguments 0-2 $# "$@" #__WITH_PARANOIA_DEBUG local attachmentFile local subject local body if [ "$DESTINATION_MAILS" == "" ]; then return 0 fi if [ "$_DEBUG" == true ]; then Logger "Debug mode, no warning mail will be sent." "NOTICE" return 0 fi if [ $attachment == true ]; then attachmentFile="$LOG_FILE" if type "$COMPRESSION_PROGRAM" > /dev/null 2>&1; then eval "cat \"$LOG_FILE\" \"$COMPRESSION_PROGRAM\" > \"$ALERT_LOG_FILE\"" if [ $? -eq 0 ]; then attachmentFile="$ALERT_LOG_FILE" fi fi fi body="$MAIL_ALERT_MSG"$'\n\n'"Last 1000 lines of current log"$'\n\n'"$(tail -n 1000 "$RUN_DIR/$PROGRAM._Logger.$SCRIPT_PID.$TSTAMP")" if [ $ERROR_ALERT == true ]; then subject="Error alert for $INSTANCE_ID" elif [ $WARN_ALERT == true ]; then subject="Warning alert for $INSTANCE_ID" else subject="Alert for $INSTANCE_ID" fi if [ $runAlert == true ]; then subject="Currently runing - $subject" else subject="Finished run - $subject" fi SendEmail "$subject" "$body" "$DESTINATION_MAILS" "$attachmentFile" "$SENDER_MAIL" "$SMTP_SERVER" "$SMTP_PORT" "$SMTP_ENCRYPTION" "$SMTP_USER" "$SMTP_PASSWORD" # Delete tmp log file if [ "$attachment" == true ]; then if [ -f "$ALERT_LOG_FILE" ]; then rm -f "$ALERT_LOG_FILE" fi fi } # Generic email sending function. # Usage (linux / BSD), attachment is optional, can be "/path/to/my.file" or "" # SendEmail "subject" "Body text" "receiver@example.com receiver2@otherdomain.com" "/path/to/attachment.file" # Usage (Windows, make sure you have mailsend.exe in executable path, see http://github.com/muquit/mailsend) # attachment is optional but must be in windows format like "c:\\some\path\\my.file", or "" # smtp_server.domain.tld is mandatory, as is smtpPort (should be 25, 465 or 587) # encryption can be set to tls, ssl or none # smtpUser and smtpPassword are optional # SendEmail "subject" "Body text" "receiver@example.com receiver2@otherdomain.com" "/path/to/attachment.file" "senderMail@example.com" "smtpServer.domain.tld" "smtpPort" "encryption" "smtpUser" "smtpPassword" # If text is received as attachment ATT00001.bin or noname, consider adding the following to /etc/mail.rc #set ttycharset=iso-8859-1 #set sendcharsets=iso-8859-1 #set encoding=8bit function SendEmail { local subject="${1}" local message="${2}" local destinationMails="${3}" local attachment="${4}" local senderMail="${5}" local smtpServer="${6}" local smtpPort="${7}" local encryption="${8}" local smtpUser="${9}" local smtpPassword="${10}" __CheckArguments 3-10 $# "$@" #__WITH_PARANOIA_DEBUG local mail_no_attachment= local attachment_command= local encryption_string= local auth_string= local i if [ "${destinationMails}" != "" ]; then for i in "${destinationMails[@]}"; do if [ $(CheckRFC822 "$i") -ne 1 ]; then Logger "Given email [$i] does not seem to be valid." "WARN" fi done else Logger "No valid email addresses given." "WARN" return 1 fi # Prior to sending an email, convert its body if needed if [ "$MAIL_BODY_CHARSET" != "" ]; then if type iconv > /dev/null 2>&1; then echo "$message" | iconv -f UTF-8 -t $MAIL_BODY_CHARSET -o "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.iconv.$SCRIPT_PID.$TSTAMP" message="$(cat "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.iconv.$SCRIPT_PID.$TSTAMP")" else Logger "iconv utility not installed. Will not convert email charset." "NOTICE" fi fi if [ ! -f "$attachment" ]; then attachment_command="-a $attachment" mail_no_attachment=1 else mail_no_attachment=0 fi if [ "$LOCAL_OS" == "Busybox" ] || [ "$LOCAL_OS" == "Android" ]; then if [ "$smtpPort" == "" ]; then Logger "Missing smtp port, assuming 25." "WARN" smtpPort=25 fi if type sendmail > /dev/null 2>&1; then if [ "$encryption" == "tls" ]; then echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) -f "$senderMail" -H "exec openssl s_client -quiet -tls1_2 -starttls smtp -connect $smtpServer:$smtpPort" -au"$smtpUser" -ap"$smtpPassword" "$destinationMails" elif [ "$encryption" == "ssl" ]; then echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) -f "$senderMail" -H "exec openssl s_client -quiet -connect $smtpServer:$smtpPort" -au"$smtpUser" -ap"$smtpPassword" "$destinationMails" elif [ "$encryption" == "none" ]; then echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) -f "$senderMail" -S "$smtpServer:$smtpPort" -au"$smtpUser" -ap"$smtpPassword" "$destinationMails" else echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) -f "$senderMail" -S "$smtpServer:$smtpPort" -au"$smtpUser" -ap"$smtpPassword" "$destinationMails" Logger "Bogus email encryption used [$encryption]." "WARN" fi if [ $? -ne 0 ]; then Logger "Cannot send alert mail via $(type -p sendmail) !!!" "WARN" # Do not bother try other mail systems with busybox return 1 else return 0 fi else Logger "Sendmail not present. Will not send any mail" "WARN" return 1 fi fi if type mutt > /dev/null 2>&1 ; then # We need to replace spaces with comma in order for mutt to be able to process multiple destinations echo "$message" | $(type -p mutt) -x -s "$subject" "${destinationMails// /,}" $attachment_command if [ $? -ne 0 ]; then Logger "Cannot send mail via $(type -p mutt) !!!" "WARN" else Logger "Sent mail using mutt." "NOTICE" return 0 fi fi if type mail > /dev/null 2>&1 ; then # We need to detect which version of mail is installed if ! $(type -p mail) -V > /dev/null 2>&1; then # This may be MacOS mail program attachment_command="" elif [ "$mail_no_attachment" -eq 0 ] && $(type -p mail) -V | grep "GNU" > /dev/null; then attachment_command="-A $attachment" elif [ "$mail_no_attachment" -eq 0 ] && $(type -p mail) -V > /dev/null; then attachment_command="-a$attachment" else attachment_command="" fi echo "$message" | $(type -p mail) $attachment_command -s "$subject" "$destinationMails" if [ $? -ne 0 ]; then Logger "Cannot send mail via $(type -p mail) with attachments !!!" "WARN" echo "$message" | $(type -p mail) -s "$subject" "$destinationMails" if [ $? -ne 0 ]; then Logger "Cannot send mail via $(type -p mail) without attachments !!!" "WARN" else Logger "Sent mail using mail command without attachment." "NOTICE" return 0 fi else Logger "Sent mail using mail command." "NOTICE" return 0 fi fi if type sendmail > /dev/null 2>&1 ; then echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) "$destinationMails" if [ $? -ne 0 ]; then Logger "Cannot send mail via $(type -p sendmail) !!!" "WARN" else Logger "Sent mail using sendmail command without attachment." "NOTICE" return 0 fi fi # Windows specific if type "mailsend.exe" > /dev/null 2>&1 ; then if [ "$senderMail" == "" ]; then Logger "Missing sender email." "ERROR" return 1 fi if [ "$smtpServer" == "" ]; then Logger "Missing smtp port." "ERROR" return 1 fi if [ "$smtpPort" == "" ]; then Logger "Missing smtp port, assuming 25." "WARN" smtpPort=25 fi if [ "$encryption" != "tls" ] && [ "$encryption" != "ssl" ] && [ "$encryption" != "none" ]; then Logger "Bogus smtp encryption, assuming none." "WARN" encryption_string= elif [ "$encryption" == "tls" ]; then encryption_string=-starttls elif [ "$encryption" == "ssl" ]:; then encryption_string=-ssl fi if [ "$smtpUser" != "" ] && [ "$smtpPassword" != "" ]; then auth_string="-auth -user \"$smtpUser\" -pass \"$smtpPassword\"" fi $(type mailsend.exe) -f "$senderMail" -t "$destinationMails" -sub "$subject" -M "$message" -attach "$attachment" -smtp "$smtpServer" -port "$smtpPort" $encryption_string $auth_string if [ $? -ne 0 ]; then Logger "Cannot send mail via $(type mailsend.exe) !!!" "WARN" else Logger "Sent mail using mailsend.exe command with attachment." "NOTICE" return 0 fi fi # pfSense specific if [ -f /usr/local/bin/mail.php ]; then echo "$message" | /usr/local/bin/mail.php -s="$subject" if [ $? -ne 0 ]; then Logger "Cannot send mail via /usr/local/bin/mail.php (pfsense) !!!" "WARN" else Logger "Sent mail using pfSense mail.php." "NOTICE" return 0 fi fi # If function has not returned 0 yet, assume it is critical that no alert can be sent Logger "Cannot send mail (neither mutt, mail, sendmail, sendemail, mailsend (windows) or pfSense mail.php could be used)." "ERROR" # Is not marked critical because execution must continue } #### TrapError SUBSET #### function TrapError { local job="$0" local line="$1" local code="${2:-1}" if [ $_LOGGER_SILENT == false ]; then (>&2 echo -e "\e[45m/!\ ERROR in ${job}: Near line ${line}, exit code ${code}\e[0m") fi } #### TrapError SUBSET END #### # Quick and dirty performance logger only used for debugging function _PerfProfiler { #__WITH_PARANOIA_DEBUG local perfString #__WITH_PARANOIA_DEBUG local i #__WITH_PARANOIA_DEBUG #__WITH_PARANOIA_DEBUG perfString=$(ps -p $$ -o args,pid,ppid,%cpu,%mem,time,etime,state,wchan) #__WITH_PARANOIA_DEBUG #__WITH_PARANOIA_DEBUG for i in $(pgrep -P $$); do #__WITH_PARANOIA_DEBUG perfString="$perfString\n"$(ps -p $i -o args,pid,ppid,%cpu,%mem,time,etime,state,wchan | tail -1) #__WITH_PARANOIA_DEBUG done #__WITH_PARANOIA_DEBUG #__WITH_PARANOIA_DEBUG if type iostat > /dev/null 2>&1; then #__WITH_PARANOIA_DEBUG perfString="$perfString\n"$(iostat) #__WITH_PARANOIA_DEBUG fi #__WITH_PARANOIA_DEBUG #__WITH_PARANOIA_DEBUG Logger "PerfProfiler:\n$perfString" "PARANOIA_DEBUG" #__WITH_PARANOIA_DEBUG } # Checks email address validity function CheckRFC822 { local mail="${1}" local rfc822="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$" if [[ $mail =~ $rfc822 ]]; then echo 1 else echo 0 fi } # Function is busybox compatible since busybox ash does not understand direct regex, we use expr function IsInteger { local value="${1}" if type expr > /dev/null 2>&1; then expr "$value" : '^[0-9]\{1,\}$' > /dev/null 2>&1 if [ $? -eq 0 ]; then echo 1 else echo 0 fi else if [[ $value =~ ^[0-9]+$ ]]; then echo 1 else echo 0 fi fi } # Usage [ $(IsNumeric $var) -eq 1 ] function IsNumeric { local value="${1}" if type expr > /dev/null 2>&1; then expr "$value" : '^[-+]\{0,1\}[0-9]*\.\{0,1\}[0-9]\{1,\}$' > /dev/null 2>&1 if [ $? -eq 0 ]; then echo 1 else echo 0 fi else if [[ $value =~ ^[-+]?[0-9]+([.][0-9]+)?$ ]]; then echo 1 else echo 0 fi fi } function IsNumericExpand { eval "local value=\"${1}\"" # Needed eval so variable variables can be processed echo $(IsNumeric "$value") } ## Modified version of http://stackoverflow.com/a/8574392 ## Usage: [ $(ArrayContains "needle" "${haystack[@]}") -eq 1 ] function ArrayContains () { local needle="${1}" local haystack="${2}" local e if [ "$needle" != "" ] && [ "$haystack" != "" ]; then for e in "${@:2}"; do if [ "$e" == "$needle" ]; then echo 1 return fi done fi echo 0 return } _OFUNCTIONS_SPINNER="|/-\\" function Spinner { if [ $_LOGGER_SILENT == true ] || [ "$_LOGGER_ERR_ONLY" == true ]; then return 0 else printf " [%c] \b\b\b\b\b\b" "$_OFUNCTIONS_SPINNER" _OFUNCTIONS_SPINNER=${_OFUNCTIONS_SPINNER#?}${_OFUNCTIONS_SPINNER%%???} return 0 fi } ## Main asynchronous execution function ## Function can work in: ## WaitForTaskCompletion mode: monitors given pid in background, and stops them if max execution time is reached. Suitable for multiple synchronous pids to monitor and wait for ## ParallExec mode: takes list of commands to execute in parallel per batch, and stops them if max execution time is reahed. ## Example of improved wait $! ## ExecTasks $! "some_identifier" false 0 0 0 0 true 1 1800 false ## Example: monitor two sleep processes, warn if execution time is higher than 10 seconds, stop after 20 seconds ## sleep 15 & ## pid=$! ## sleep 20 & ## pid2=$! ## ExecTasks "some_identifier" 0 0 10 20 1 1800 true true false false 1 "$pid;$pid2" ## Example of parallel execution of four commands, only if directories exist. Warn if execution takes more than 300 seconds. Stop if takes longer than 900 seconds. Exeute max 3 commands in parallel. ## commands="du -csh /var;du -csh /etc;du -csh /home;du -csh /usr" ## conditions="[ -d /var ];[ -d /etc ];[ -d /home];[ -d /usr]" ## ExecTasks "$commands" "some_identifier" false 0 0 300 900 true 1 1800 true false false 3 "$conditions" ## Bear in mind that given commands and conditions need to be quoted ## ExecTasks has the following ofunctions subfunction requirements: ## Spinner ## Logger ## JoinString ## KillChilds ## Full call ##ExecTasks "$mainInput" "$id" $readFromFile $softPerProcessTime $hardPerProcessTime $softMaxTime $hardMaxTime $counting $sleepTime $keepLogging $spinner $noTimeErrorLog $noErrorLogsAtAll $numberOfProcesses $auxInput $maxPostponeRetries $minTimeBetweenRetries $validExitCodes function ExecTasks { # Mandatory arguments local mainInput="${1}" # Contains list of pids / commands separated by semicolons or filepath to list of pids / commands # Optional arguments local id="${2:-base}" # Optional ID in order to identify global variables from this run (only bash variable names, no '-'). Global variables are WAIT_FOR_TASK_COMPLETION_$id and HARD_MAX_EXEC_TIME_REACHED_$id local readFromFile="${3:-false}" # Is mainInput / auxInput a semicolon separated list (true) or a filepath (false) local softPerProcessTime="${4:-0}" # Max time (in seconds) a pid or command can run before a warning is logged, unless set to 0 local hardPerProcessTime="${5:-0}" # Max time (in seconds) a pid or command can run before the given command / pid is stopped, unless set to 0 local softMaxTime="${6:-0}" # Max time (in seconds) for the whole function to run before a warning is logged, unless set to 0 local hardMaxTime="${7:-0}" # Max time (in seconds) for the whole function to run before all pids / commands given are stopped, unless set to 0 local counting="${8:-true}" # Should softMaxTime and hardMaxTime be accounted since function begin (true) or since script begin (false) local sleepTime="${9:-.5}" # Seconds between each state check. The shorter the value, the snappier ExecTasks will be, but as a tradeoff, more cpu power will be used (good values are between .05 and 1) local keepLogging="${10:-1800}" # Every keepLogging seconds, an alive message is logged. Setting this value to zero disables any alive logging local spinner="${11:-true}" # Show spinner (true) or do not show anything (false) while running local noTimeErrorLog="${12:-false}" # Log errors when reaching soft / hard execution times (false) or do not log errors on those triggers (true) local noErrorLogsAtAll="${13:-false}" # Do not log any errros at all (useful for recursive ExecTasks checks) # Parallelism specific arguments local numberOfProcesses="${14:-0}" # Number of simulanteous commands to run, given as mainInput. Set to 0 by default (WaitForTaskCompletion mode). Setting this value enables ParallelExec mode. local auxInput="${15}" # Contains list of commands separated by semicolons or filepath fo list of commands. Exit code of those commands decide whether main commands will be executed or not local maxPostponeRetries="${16:-3}" # If a conditional command fails, how many times shall we try to postpone the associated main command. Set this to 0 to disable postponing local minTimeBetweenRetries="${17:-300}" # Time (in seconds) between postponed command retries local validExitCodes="${18:-0}" # Semi colon separated list of valid main command exit codes which will not trigger errors __CheckArguments 1-18 $# "$@" local i Logger "${FUNCNAME[0]} id [$id] called by [${FUNCNAME[1]} < ${FUNCNAME[2]} < ${FUNCNAME[3]} < ${FUNCNAME[4]} < ${FUNCNAME[5]} < ${FUNCNAME[6]} ...]." "PARANOIA_DEBUG" #__WITH_PARANOIA_DEBUG #__WITH_PARANOIA_DEBUG # Since ExecTasks takes up to 17 arguments, do a quick preflight check in DEBUG mode if [ "$_DEBUG" == true ]; then declare -a booleans=(readFromFile counting spinner noTimeErrorLog noErrorLogsAtAll) for i in "${booleans[@]}"; do test="if [ \$$i != false ] && [ \$$i != true ]; then Logger \"Bogus $i value [\$$i] given to ${FUNCNAME[0]}.\" \"CRITICAL\"; exit 1; fi" eval "$test" done declare -a integers=(softPerProcessTime hardPerProcessTime softMaxTime hardMaxTime keepLogging numberOfProcesses maxPostponeRetries minTimeBetweenRetries) for i in "${integers[@]}"; do test="if [ $(IsNumericExpand \"\$$i\") -eq 0 ]; then Logger \"Bogus $i value [\$$i] given to ${FUNCNAME[0]}.\" \"CRITICAL\"; exit 1; fi" eval "$test" done fi # Expand validExitCodes into array IFS=';' read -r -a validExitCodes <<< "$validExitCodes" # ParallelExec specific variables local auxItemCount=0 # Number of conditional commands local commandsArray=() # Array containing commands local commandsConditionArray=() # Array containing conditional commands local currentCommand # Variable containing currently processed command local currentCommandCondition # Variable containing currently processed conditional command local commandsArrayPid=() # Array containing commands indexed by pids local commandsArrayOutput=() # Array containing command results indexed by pids local postponedRetryCount=0 # Number of current postponed commands retries local postponedItemCount=0 # Number of commands that have been postponed (keep at least one in order to check once) local postponedCounter=0 local isPostponedCommand=false # Is the current command from a postponed file ? local postponedExecTime=0 # How much time has passed since last postponed condition was checked local needsPostponing # Does currentCommand need to be postponed local temp # Common variables local pid # Current pid working on local pidState # State of the process local mainItemCount=0 # number of given items (pids or commands) local readFromFile # Should we read pids / commands from a file (true) local counter=0 local log_ttime=0 # local time instance for comparaison local seconds_begin=$SECONDS # Seconds since the beginning of the script local exec_time=0 # Seconds since the beginning of this function local retval=0 # return value of monitored pid process local subRetval=0 # return value of condition commands local errorcount=0 # Number of pids that finished with errors local pidsArray # Array of currently running pids local newPidsArray # New array of currently running pids for next iteration local pidsTimeArray # Array containing execution begin time of pids local executeCommand # Boolean to check if currentCommand can be executed given a condition local hasPids=false # Are any valable pids given to function ? #__WITH_PARANOIA_DEBUG local functionMode local softAlert=false # Does a soft alert need to be triggered, if yes, send an alert once local failedPidsList # List containing failed pids with exit code separated by semicolons (eg : 2355:1;4534:2;2354:3) local randomOutputName # Random filename for command outputs local currentRunningPids # String of pids running, used for debugging purposes only # Initialise global variable eval "WAIT_FOR_TASK_COMPLETION_$id=\"\"" eval "HARD_MAX_EXEC_TIME_REACHED_$id=false" # Init function variables depending on mode if [ $numberOfProcesses -gt 0 ]; then functionMode=ParallelExec else functionMode=WaitForTaskCompletion fi if [ $readFromFile == false ]; then if [ $functionMode == "WaitForTaskCompletion" ]; then IFS=';' read -r -a pidsArray <<< "$mainInput" mainItemCount="${#pidsArray[@]}" else IFS=';' read -r -a commandsArray <<< "$mainInput" mainItemCount="${#commandsArray[@]}" IFS=';' read -r -a commandsConditionArray <<< "$auxInput" auxItemCount="${#commandsConditionArray[@]}" fi else if [ -f "$mainInput" ]; then mainItemCount=$(wc -l < "$mainInput") readFromFile=true else Logger "Cannot read main file [$mainInput]." "WARN" fi if [ "$auxInput" != "" ]; then if [ -f "$auxInput" ]; then auxItemCount=$(wc -l < "$auxInput") else Logger "Cannot read aux file [$auxInput]." "WARN" fi fi fi if [ $functionMode == "WaitForTaskCompletion" ]; then # Force first while loop condition to be true because we don't deal with counters but pids in WaitForTaskCompletion mode counter=$mainItemCount fi Logger "Running ${FUNCNAME[0]} as [$functionMode] for [$mainItemCount] mainItems and [$auxItemCount] auxItems." "PARANOIA_DEBUG" #__WITH_PARANOIA_DEBUG # soft / hard execution time checks that needs to be a subfunction since it is called both from main loop and from parallelExec sub loop function _ExecTasksTimeCheck { if [ $spinner == true ]; then Spinner fi if [ $counting == true ]; then exec_time=$((SECONDS - seconds_begin)) else exec_time=$SECONDS fi if [ $keepLogging -ne 0 ]; then # This log solely exists for readability purposes before having next set of logs if [ ${#pidsArray[@]} -eq $numberOfProcesses ] && [ $log_ttime -eq 0 ]; then log_ttime=$exec_time Logger "There are $((mainItemCount-counter+postponedItemCount)) / $mainItemCount tasks in the queue of which $postponedItemCount are postponed. Currently, ${#pidsArray[@]} tasks running with pids [$(joinString , ${pidsArray[@]})]." "NOTICE" fi if [ $(((exec_time + 1) % keepLogging)) -eq 0 ]; then if [ $log_ttime -ne $exec_time ]; then # Fix when sleep time lower than 1 second log_ttime=$exec_time if [ $functionMode == "WaitForTaskCompletion" ]; then Logger "Current tasks still running with pids [$(joinString , ${pidsArray[@]})]." "NOTICE" elif [ $functionMode == "ParallelExec" ]; then Logger "There are $((mainItemCount-counter+postponedItemCount)) / $mainItemCount tasks in the queue of which $postponedItemCount are postponed. Currently, ${#pidsArray[@]} tasks running with pids [$(joinString , ${pidsArray[@]})]." "NOTICE" fi fi fi fi if [ $exec_time -gt $softMaxTime ]; then if [ "$softAlert" != true ] && [ $softMaxTime -ne 0 ] && [ $noTimeErrorLog != true ]; then Logger "Max soft execution time [$softMaxTime] exceeded for task [$id] with pids [$(joinString , ${pidsArray[@]})]." "WARN" softAlert=true SendAlert true false fi fi if [ $exec_time -gt $hardMaxTime ] && [ $hardMaxTime -ne 0 ]; then if [ $noTimeErrorLog != true ]; then Logger "Max hard execution time [$hardMaxTime] exceeded for task [$id] with pids [$(joinString , ${pidsArray[@]})]. Stopping task execution." "ERROR" fi for pid in "${pidsArray[@]}"; do KillChilds $pid true if [ $? -eq 0 ]; then Logger "Task with pid [$pid] stopped successfully." "NOTICE" else if [ $noErrorLogsAtAll != true ]; then Logger "Could not stop task with pid [$pid]." "ERROR" fi fi errorcount=$((errorcount+1)) done if [ $noTimeErrorLog != true ]; then SendAlert true false fi eval "HARD_MAX_EXEC_TIME_REACHED_$id=true" if [ $functionMode == "WaitForTaskCompletion" ]; then return $errorcount else return 129 fi fi } function _ExecTasksPidsCheck { newPidsArray=() if [ "$currentRunningPids" != "$(joinString " " ${pidsArray[@]})" ]; then Logger "ExecTask running for pids [$(joinString " " ${pidsArray[@]})]." "DEBUG" currentRunningPids="$(joinString " " ${pidsArray[@]})" fi for pid in "${pidsArray[@]}"; do if [ $(IsInteger $pid) -eq 1 ]; then if kill -0 $pid > /dev/null 2>&1; then # Handle uninterruptible sleep state or zombies by ommiting them from running process array (How to kill that is already dead ? :) pidState="$(eval $PROCESS_STATE_CMD)" if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then # Check if pid hasn't run more than soft/hard perProcessTime pidsTimeArray[$pid]=$((SECONDS - seconds_begin)) if [ ${pidsTimeArray[$pid]} -gt $softPerProcessTime ]; then if [ "$softAlert" != true ] && [ $softPerProcessTime -ne 0 ] && [ $noTimeErrorLog != true ]; then Logger "Max soft execution time [$softPerProcessTime] exceeded for pid [$pid]." "WARN" if [ "${commandsArrayPid[$pid]}]" != "" ]; then Logger "Command was [${commandsArrayPid[$pid]}]]." "WARN" fi softAlert=true SendAlert true false fi fi if [ ${pidsTimeArray[$pid]} -gt $hardPerProcessTime ] && [ $hardPerProcessTime -ne 0 ]; then if [ $noTimeErrorLog != true ] && [ $noErrorLogsAtAll != true ]; then Logger "Max hard execution time [$hardPerProcessTime] exceeded for pid [$pid]. Stopping command execution." "ERROR" if [ "${commandsArrayPid[$pid]}]" != "" ]; then Logger "Command was [${commandsArrayPid[$pid]}]]." "WARN" fi fi KillChilds $pid true if [ $? -eq 0 ]; then Logger "Command with pid [$pid] stopped successfully." "NOTICE" else if [ $noErrorLogsAtAll != true ]; then Logger "Could not stop command with pid [$pid]." "ERROR" fi fi errorcount=$((errorcount+1)) if [ $noTimeErrorLog != true ]; then SendAlert true false fi fi newPidsArray+=($pid) fi else # pid is dead, get its exit code from wait command wait $pid retval=$? # Check for valid exit codes if [ $(ArrayContains $retval "${validExitCodes[@]}") -eq 0 ]; then if [ $noErrorLogsAtAll != true ]; then Logger "${FUNCNAME[0]} called by [$id] finished monitoring pid [$pid] with exitcode [$retval]." "ERROR" if [ "$functionMode" == "ParallelExec" ]; then Logger "Command was [${commandsArrayPid[$pid]}]." "ERROR" fi if [ -f "${commandsArrayOutput[$pid]}" ]; then Logger "Truncated output:\n$(head -c16384 "${commandsArrayOutput[$pid]}")" "ERROR" fi fi errorcount=$((errorcount+1)) # Welcome to variable variable bash hell if [ "$failedPidsList" == "" ]; then failedPidsList="$pid:$retval" else failedPidsList="$failedPidsList;$pid:$retval" fi else Logger "${FUNCNAME[0]} called by [$id] finished monitoring pid [$pid] with exitcode [$retval]." "DEBUG" fi fi hasPids=true ##__WITH_PARANOIA_DEBUG fi done # hasPids can be false on last iteration in ParallelExec mode if [ $hasPids == false ] && [ "$functionMode" = "WaitForTaskCompletion" ]; then ##__WITH_PARANOIA_DEBUG Logger "No valable pids given." "ERROR" ##__WITH_PARANOIA_DEBUG fi ##__WITH_PARANOIA_DEBUG pidsArray=("${newPidsArray[@]}") # Trivial wait time for bash to not eat up all CPU sleep $sleepTime if [ "$_PERF_PROFILER" == true ]; then ##__WITH_PARANOIA_DEBUG _PerfProfiler ##__WITH_PARANOIA_DEBUG fi ##__WITH_PARANOIA_DEBUG } while [ ${#pidsArray[@]} -gt 0 ] || [ $counter -lt $mainItemCount ] || [ $postponedItemCount -ne 0 ]; do _ExecTasksTimeCheck retval=$? if [ $retval -ne 0 ]; then return $retval; fi # The following execution bloc is only needed in ParallelExec mode since WaitForTaskCompletion does not execute commands, but only monitors them if [ $functionMode == "ParallelExec" ]; then while [ ${#pidsArray[@]} -lt $numberOfProcesses ] && ([ $counter -lt $mainItemCount ] || [ $postponedItemCount -ne 0 ]); do _ExecTasksTimeCheck retval=$? if [ $retval -ne 0 ]; then return $retval; fi executeCommand=false isPostponedCommand=false currentCommand="" currentCommandCondition="" needsPostponing=false if [ $readFromFile == true ]; then # awk identifies first line as 1 instead of 0 so we need to increase counter currentCommand=$(awk 'NR == num_line {print; exit}' num_line=$((counter+1)) "$mainInput") if [ $auxItemCount -ne 0 ]; then currentCommandCondition=$(awk 'NR == num_line {print; exit}' num_line=$((counter+1)) "$auxInput") fi # Check if we need to fetch postponed commands if [ "$currentCommand" == "" ]; then currentCommand=$(awk 'NR == num_line {print; exit}' num_line=$((postponedCounter+1)) "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-postponedMain.$id.$SCRIPT_PID.$TSTAMP") currentCommandCondition=$(awk 'NR == num_line {print; exit}' num_line=$((postponedCounter+1)) "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-postponedAux.$id.$SCRIPT_PID.$TSTAMP") isPostponedCommand=true fi else currentCommand="${commandsArray[$counter]}" if [ $auxItemCount -ne 0 ]; then currentCommandCondition="${commandsConditionArray[$counter]}" fi if [ "$currentCommand" == "" ]; then currentCommand="${postponedCommandsArray[$postponedCounter]}" currentCommandCondition="${postponedCommandsConditionArray[$postponedCounter]}" isPostponedCommand=true fi fi # Check if we execute postponed commands, or if we delay them if [ $isPostponedCommand == true ]; then # Get first value before '@' postponedExecTime="${currentCommand%%@*}" postponedExecTime=$((SECONDS-postponedExecTime)) # Get everything after first '@' temp="${currentCommand#*@}" # Get first value before '@' postponedRetryCount="${temp%%@*}" # Replace currentCommand with actual filtered currentCommand currentCommand="${temp#*@}" # Since we read a postponed command, we may decrase postponedItemCounter postponedItemCount=$((postponedItemCount-1)) #Since we read one line, we need to increase the counter postponedCounter=$((postponedCounter+1)) else postponedRetryCount=0 postponedExecTime=0 fi if ([ $postponedRetryCount -lt $maxPostponeRetries ] && [ $postponedExecTime -ge $minTimeBetweenRetries ]) || [ $isPostponedCommand == false ]; then if [ "$currentCommandCondition" != "" ]; then Logger "Checking condition [$currentCommandCondition] for command [$currentCommand]." "DEBUG" eval "$currentCommandCondition" & ExecTasks $! "subConditionCheck" false 0 0 1800 3600 true $SLEEP_TIME $KEEP_LOGGING true true true subRetval=$? if [ $subRetval -ne 0 ]; then # is postponing enabled ? if [ $maxPostponeRetries -gt 0 ]; then Logger "Condition [$currentCommandCondition] not met for command [$currentCommand]. Exit code [$subRetval]. Postponing command." "NOTICE" postponedRetryCount=$((postponedRetryCount+1)) if [ $postponedRetryCount -ge $maxPostponeRetries ]; then Logger "Max retries reached for postponed command [$currentCommand]. Skipping command." "NOTICE" else needsPostponing=true fi postponedExecTime=0 else Logger "Condition [$currentCommandCondition] not met for command [$currentCommand]. Exit code [$subRetval]. Ignoring command." "NOTICE" fi else executeCommand=true fi else executeCommand=true fi else needsPostponing=true fi if [ $needsPostponing == true ]; then postponedItemCount=$((postponedItemCount+1)) if [ $readFromFile == true ]; then echo "$((SECONDS-postponedExecTime))@$postponedRetryCount@$currentCommand" >> "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-postponedMain.$id.$SCRIPT_PID.$TSTAMP" echo "$currentCommandCondition" >> "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-postponedAux.$id.$SCRIPT_PID.$TSTAMP" else postponedCommandsArray+=("$((SECONDS-postponedExecTime))@$postponedRetryCount@$currentCommand") postponedCommandsConditionArray+=("$currentCommandCondition") fi fi if [ $executeCommand == true ]; then Logger "Running command [$currentCommand]." "DEBUG" randomOutputName=$(date '+%Y%m%dT%H%M%S').$(PoorMansRandomGenerator 5) eval "$currentCommand" >> "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$id.$pid.$randomOutputName.$SCRIPT_PID.$TSTAMP" 2>&1 & pid=$! pidsArray+=($pid) commandsArrayPid[$pid]="$currentCommand" commandsArrayOutput[$pid]="$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$id.$pid.$randomOutputName.$SCRIPT_PID.$TSTAMP" # Initialize pid execution time array pidsTimeArray[$pid]=0 else Logger "Skipping command [$currentCommand]." "DEBUG" fi if [ $isPostponedCommand == false ]; then counter=$((counter+1)) fi _ExecTasksPidsCheck done fi _ExecTasksPidsCheck done Logger "${FUNCNAME[0]} ended for [$id] using [$mainItemCount] subprocesses with [$errorcount] errors." "PARANOIA_DEBUG" #__WITH_PARANOIA_DEBUG # Return exit code if only one process was monitored, else return number of errors # As we cannot return multiple values, a global variable WAIT_FOR_TASK_COMPLETION contains all pids with their return value eval "WAIT_FOR_TASK_COMPLETION_$id=\"$failedPidsList\"" if [ $mainItemCount -eq 1 ]; then return $retval else return $errorcount fi } function CleanUp { __CheckArguments 0 $# "$@" #__WITH_PARANOIA_DEBUG if [ "$_DEBUG" != true ]; then rm -f "$RUN_DIR/$PROGRAM."*".$SCRIPT_PID.$TSTAMP" # Fix for sed -i requiring backup extension for BSD & Mac (see all sed -i statements) rm -f "$RUN_DIR/$PROGRAM."*".$SCRIPT_PID.$TSTAMP.tmp" fi } #__BEGIN_WITH_PARANOIA_DEBUG function __CheckArguments { # Checks the number of arguments of a function and raises an error if some are missing if [ "$_DEBUG" == true ]; then local numberOfArguments="${1}" # Number of arguments the tested function should have, can be a number of a range, eg 0-2 for zero to two arguments local numberOfGivenArguments="${2}" # Number of arguments that have been passed local minArgs local maxArgs # All arguments of the function to check are passed as array in ${3} (the function call waits for $@) # If any of the arguments contains spaces, bash things there are two aguments # In order to avoid this, we need to iterate over ${3} and count callerName="${FUNCNAME[1]}" local iterate=3 local fetchArguments=true local argList="" local countedArguments while [ $fetchArguments == true ]; do cmd='argument=${'$iterate'}' eval $cmd if [ "$argument" == "" ]; then fetchArguments=false else argList="$argList[Argument $((iterate-2)): $argument] " iterate=$((iterate+1)) fi done countedArguments=$((iterate-3)) if [ $(IsInteger "$numberOfArguments") -eq 1 ]; then minArgs=$numberOfArguments maxArgs=$numberOfArguments else IFS='-' read minArgs maxArgs <<< "$numberOfArguments" fi Logger "Entering function [$callerName]." "PARANOIA_DEBUG" if ! ([ $countedArguments -ge $minArgs ] && [ $countedArguments -le $maxArgs ]); then Logger "Function $callerName may have inconsistent number of arguments. Expected min: $minArgs, max: $maxArgs, count: $countedArguments, bash seen: $numberOfGivenArguments." "ERROR" Logger "$callerName arguments: $argList" "ERROR" else if [ ! -z "$argList" ]; then Logger "$callerName arguments: $argList" "PARANOIA_DEBUG" fi fi fi } #__END_WITH_PARANOIA_DEBUG ############################################ END OF OFUNCTIONS CODE function TrapQuit { local exitcode # Get ERROR / WARN alert flags from subprocesses that call Logger if [ -f "$RUN_DIR/$PROGRAM.Logger.warn.$SCRIPT_PID.$TSTAMP" ]; then WARN_ALERT=true fi if [ -f "$RUN_DIR/$PROGRAM.Logger.error.$SCRIPT_PID.$TSTAMP" ]; then ERROR_ALERT=true fi if [ $ERROR_ALERT == true ]; then Logger "$PROGRAM finished with errors." "ERROR" if [ "$_DEBUG" != true ] then SendAlert false false else Logger "Debug mode, no alert mail will be sent." "NOTICE" fi exitcode=1 elif [ $WARN_ALERT == true ]; then Logger "$PROGRAM finished with warnings." "WARN" if [ "$_DEBUG" != true ] then SendAlert false false else Logger "Debug mode, no alert mail will be sent." "NOTICE" fi exitcode=2 # Warning exit code must not force daemon mode to quit else Logger "$PROGRAM finished." "ALWAYS" exitcode=0 fi CleanUp KillChilds $SCRIPT_PID > /dev/null 2>&1 Logger "Elapsed [$SECONDS] seconds." "DEBUG" exit $exitcode } # Takes as many file arguments as needed function InterleaveFiles { local counter=0 local hasLine=true local i while [ $hasLine == true ]; do hasLine=false for i in "$@"; do line=$(awk 'NR == num_line {print; exit}' num_line=$((counter+1)) "$i") if [ -n "$line" ]; then echo "$line" hasLine=true fi done counter=$((counter+1)) done } function ListClients { local backupDir="${1}" local configFile="${2}" local clientConfDir="${3}" __CheckArguments 0-3 $# "$@" #__WITH_PARANOIA_DEBUG local clientIsIncluded local clientIsExcluded local excludeArray local client local clientEmail local configString local i local j if [ -f "$configFile" ]; then configString="-c \"$configFile\"" fi if [ "$clientConfDir" == "" ]; then clientConfDir="/etc/@name@/clientconfdir" fi if [ -d "$backupDir" ]; then # File 'backup_stats' is there only when a backup is finished find "$backupDir" -mindepth 3 -maxdepth 3 -type f -name "backup_stats" | grep -e '.*' > /dev/null if [ $? -ne 0 ]; then Logger "The directory [$backupDir] does not seem to be a @name@ folder. Please check the path. Additionnaly, protocol 2 directores need to specify the dedup group directory and the client subfolder." "ERROR" Logger "This message may also show if the current user running this script does not have sufficient privileges on the @name@ folder." "ERROR" fi fi # Autodetect clients or use provided client with --client or -C option if [ "$GIVEN_CLIENT" == "" ]; then # Using both @name@ -a S list and find method in order to find maximum backup clients cmd="$BACKUP_EXECUTABLE $configString -a S | grep \"last backup\" | awk '{print \$1}' > \"$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-1.$SCRIPT_PID.$TSTAMP\"" Logger "Running cmd [$cmd]." "DEBUG" eval "$cmd" & ExecTasks $! "${FUNCNAME[0]}_lastbackup" false 0 0 1800 3600 true $SLEEP_TIME $KEEP_LOGGING if [ $? -ne 0 ]; then Logger "Enumerating @name@ clients via [$BACKUP_EXECUTABLE $configString -a S] failed. Check provided @name@ client config file." "ERROR" else Logger "@name@ detection method found the following clients:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}-1.$SCRIPT_PID.$TSTAMP)" "DEBUG" fi # First exp removes everything before last '/' # sed tested on linux and BSD (should work on MacOS too) if [ -d "$backupDir" ]; then find "$backupDir" -mindepth 1 -maxdepth 1 -type d | sed -e "s/\(.*\)\/\(.*\)/\2/g" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-2.$SCRIPT_PID.$TSTAMP" while IFS=$'\n' read -r client; do find "$backupDir$client" -mindepth 2 -maxdepth 2 -type f -name "backup_stats" | grep -e '.*' > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "$client" >> "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-3.$SCRIPT_PID.$TSTAMP" fi done < "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-2.$SCRIPT_PID.$TSTAMP" fi if [ ! -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-3.$SCRIPT_PID.$TSTAMP" ]; then touch "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-3.$SCRIPT_PID.$TSTAMP" fi Logger "Backup file detection method found the following clients:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}-3.$SCRIPT_PID.$TSTAMP)" "DEBUG" # Merge all clients found by @name@ executable and manual check sort "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-1.$SCRIPT_PID.$TSTAMP" "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-3.$SCRIPT_PID.$TSTAMP" | uniq > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-4.$SCRIPT_PID.$TSTAMP" else echo "$GIVEN_CLIENT" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-4.$SCRIPT_PID.$TSTAMP" fi while IFS=$'\n' read -r client; do clientIsIncluded=false clientIsExcluded=false IFS=',' read -a includeArray <<< "$INCLUDE_CLIENTS" for i in "${includeArray[@]}"; do echo "$client" | grep -e "^"$i"$" > /dev/null 2>&1 if [ $? -eq 0 ]; then clientIsIncluded=true fi done IFS=',' read -a excludeArray <<< "$EXCLUDE_CLIENTS" for i in "${excludeArray[@]}"; do echo "$client" | grep -e "^"$i"$" > /dev/null 2>&1 if [ $? -eq 0 ]; then clientIsExcluded=true fi done if ([ $clientIsIncluded == false ] && [ $clientIsExcluded == true ]); then Logger "Ommiting client [$client]." "NOTICE" else if [ -f "$backupDir$client/current/timestamp" ]; then Logger "Found client [$client] backup directory." "NOTICE" elif [ -d "$backupDir" ]; then Logger "Client [$client] does not seem to have any backups (check provided backup directory)." "WARN" fi CLIENT_LIST+=("$client") # Client email is a label in client config file # Check whether we can fetch the default value if [ -f "$clientConfDir/$client" ]; then clientEmail=$(egrep "^label( )?=( )?email_address( )?:( )?" "$clientConfDir/$client") if [ "$clientEmail" != "" ]; then clientEmail="${clientEmail#*:}" # Remove eventual spaces clientEmail="${clientEmail/ /}" for j in $clientEmail; do if [ $(CheckRFC822 "$j") -eq 1 ]; then if [ "${CLIENT_EMAIL["$client"]}" == "" ]; then CLIENT_EMAIL["$client"]="$j" else CLIENT_EMAIL["$client"]=${CLIENT_EMAIL["$client"]}" $j" fi else Logger "Client [$client] has a bogus mail address [$j]." "WARN" fi done else Logger "Client [$client] has no mail address set." "NOTICE" fi elif ([ "$CLIENTS_ALERT_QUOTAS" == true ] || [ "$CLIENTS_ALERT_OUTDATED" == true ]); then Logger "Cannot find client config file [$clientConfDir/$client] to fetch email address." "ERROR" fi fi done < "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-4.$SCRIPT_PID.$TSTAMP" } function IsClientIdle { local client="${1}" local configFile="${2}" __CheckArguments 2 $# "$@" #__WITH_PARANOIA_DEBUG local exitCode local configString if [ -f "$configFile" ]; then configString="-c \"$configFile\"" fi Logger "Checking if client [$client] is currently idle." "DEBUG" cmd="$BACKUP_EXECUTABLE $configString -a S -C $client | grep \"Status: idle\" > /dev/null 2>&1" ExecTasks $! "${FUNCNAME[0]}_idle" false 0 0 120 300 true $SLEEP_TIME $KEEP_LOGGING exitCode=$? if [ $exitCode -ne 0 ]; then Logger "Client [$client] is currently backing up." "NOTICE" return $exitCode else return $exitCode fi } function VerifyBackups { local backupDir="${1}" local numberToVerify="${2}" local configFile="${3}" __CheckArguments 2-3 $# "$@" #__WITH_PARANOIA_DEBUG local backupNumber local exitCode local client local configString local interleaveFileArgs=() local interleaveConditionsFileArgs=() local safeExitCodes="0;2;3" # 0: success, 2: warnings, 3: timer conditions not met if [ -f "$configFile" ]; then configString="-c \"$configFile\"" fi for client in "${CLIENT_LIST[@]}"; do # Only backups containing file backup_stats are valid find "$backupDir$client" -mindepth 2 -maxdepth 2 -type f -name "backup_stats" | sort -nr | head -n $numberToVerify | sed -e 's/.*\([0-9]\{7\}\).*/\1/' > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-1.$SCRIPT_PID.$TSTAMP" Logger "Can check $(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}-1.$SCRIPT_PID.$TSTAMP | wc -l) backups for [$client]." "NOTICE" while IFS=$'\n' read -r backupNumber; do # sed here removes all lines containing only block logs (64 chars + number) # sed regex isn't complete (lacks \+$ because BSD / macOS sed does not like extended regex) Logger "Preparing verification of backup [$backupNumber] for client [$client]." "NOTICE" echo "$BACKUP_EXECUTABLE $configString -C $client -a v -b $backupNumber | sed '/^[rRSDBWfydlLsmnkceaipwxzbMFqYZGOPQEtvVuU]\{64\} [0-9]/d' >> \"$LOG_FILE\" 2>&1" >> "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-2.$client.$SCRIPT_PID.$TSTAMP" echo "$BACKUP_EXECUTABLE $configString -a S -C $client | grep \"Status: idle\" > /dev/null 2>&1" >> "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-2.1.$client.$SCRIPT_PID.$TSTAMP" done < "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-1.$SCRIPT_PID.$TSTAMP" if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-2.$client.$SCRIPT_PID.$TSTAMP" ]; then interleaveFileArgs+=("$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-2.$client.$SCRIPT_PID.$TSTAMP") fi if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-2.1.$client.$SCRIPT_PID.$TSTAMP" ]; then interleaveConditionsFileArgs+=("$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-2.1.$client.$SCRIPT_PID.$TSTAMP") fi done InterleaveFiles "${interleaveFileArgs[@]}" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-3.$SCRIPT_PID.$TSTAMP" InterleaveFiles "${interleaveConditionsFileArgs[@]}" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-3.1.$SCRIPT_PID.$TSTAMP" Logger "Now running backup verifications, concurrency set to $PARELLEL_VERIFY_CONCURRENCY." "NOTICE" Logger "Executing parallel commands\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}-3.$SCRIPT_PID.$TSTAMP)" "DEBUG" ExecTasks "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-3.$SCRIPT_PID.$TSTAMP" "${FUNCNAME[0]}" true $SOFT_MAX_EXEC_TIME_PER_VERIFY $HARD_MAX_EXEC_TIME_PER_VERIFY $SOFT_MAX_EXEC_TIME $HARD_MAX_EXEC_TIME true $SLEEP_TIME $KEEP_LOGGING true false false $PARELLEL_VERIFY_CONCURRENCY "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}-3.1.$SCRIPT_PID.$TSTAMP" $POSTPONE_RETRY $POSTPONE_TIME "$safeExitCodes" exitCode=$? if [ $exitCode -ne 0 ]; then Logger "Client backup verification produced errors [$exitCode]." "ERROR" else Logger "Client backup verification succeed." "NOTICE" fi Logger "Backup verification done." "NOTICE" } function ListOutdatedClients { local backupDir="${1}" local oldDays="${2}" __CheckArguments 2 $# "$@" #__WITH_PARANOIA_DEBUG local found=false local clientAlertBody local recentBackupTimestamp local CurrentTimestamp Logger "Checking for outdated clients." "NOTICE" for client in "${CLIENT_LIST[@]}"; do if [ -f "$backupDir$client/current/backup_stats" ]; then # This extracts time_end timestamp from last backup stats recentBackupTimestamp=$(awk '/"name": "time_end"/{p=1}{if(p>0){p=p+1}}{if(p==4){print $2}}' "$backupDir$client/current/backup_stats") else recentBackupTimestamp=0 fi # Current timestamp currentTimestamp="$(date +%s)" if [ $((currentTimestamp - (24*3600*oldDays))) -gt $recentBackupTimestamp ]; then Logger "Client [$client] has no backups newer than [$oldDays] days." "ERROR" if [ $CLIENTS_ALERT_OUTDATED == true ]; then clientAlertBody="${CLIENT_ALERT_BODY_OUTDATED/"[CLIENT]"/$client}" clientAlertBody="${clientAlertBody/"[NUMBERDAYS]"/$oldDays}" clientAlertBody="${clientAlertBody/"[INSTANCE]"/$INSTANCE_ID}" if [ "${CLIENT_EMAIL[$client]}" != "" ]; then SendEmail "$CLIENT_ALERT_SUBJECT" "$clientAlertBody" "${CLIENT_EMAIL[$client]}" Logger "Sent outdated client mail to [${CLIENT_EMAIL[$client]}]." "NOTICE" else Logger "Client [$client] does not have a mail address. Cannot send notification." "ERROR" fi fi found=true fi done if [ $found == false ]; then Logger "No outdated clients found." "NOTICE" else Logger "Outdated client checks done." "NOTICE" fi } function ListQuotaExceedClients { local backupDir="${1}" __CheckArguments 1 $# "$@" #__WITH_PARANOIA_DEBUG local found=false local lastBackupDir local clientAlertBody local bytesEstimated local quotaExceed local quotaDiff Logger "Checking for clients with exceeded quota." "NOTICE" for client in "${CLIENT_LIST[@]}"; do lastBackupDir=$(find "$backupDir$client" -maxdepth 2 -name "*current") if [ -f "$backupDir$client/current/log.gz" ]; then if zcat "$lastBackupDir/log.gz" | grep quota > /dev/null 2>&1; then bytesEstimated=$(zcat "$lastBackupDir/log.gz" | grep "Bytes estimated") bytesEstimated="${bytesEstimated##*:}" # Remove leading spaces bytesEstimated="${bytesEstimated# *}" quotaExceed=$(zcat "$lastBackupDir/log.gz" | grep "quota") quotaExceed="${quotaExceed##*:}" quotaDiff="$bytesEstimated /$quotaExceed)" Logger "Client [$client] quota exceed ($quotaDiff)." "WARN" if [ $CLIENTS_ALERT_QUOTAS == true ]; then clientAlertBody="${CLIENT_ALERT_BODY_QUOTA/"[CLIENT]"/$client}" clientAlertBody="${clientAlertBody/"[QUOTAEXCEED]"/$quotaDiff}" if [ "${CLIENT_EMAIL[$client]}" != "" ]; then SendEmail "$CLIENT_ALERT_SUBJECT" "$clientAlertBody" "${CLIENT_EMAIL[$client]}" Logger "Sent quota exceeded mail to [${CLIENT_EMAIL[$client]}]." "NOTICE" else Logger "Client [$client] does not have a mail address. Cannot send notification" "ERROR" fi fi found=true fi else Logger "No valid log file found for analysis of client [$client]." "WARN" fi done if [ $found == false ]; then Logger "No clients with exceeded quota found." "NOTICE" else Logger "Quota checks done." "NOTICE" fi } function VerifyLastWarnings { local backupDir="${1}" __CheckArguments 1 $# "$@" #__WITH_PARANOIA_DEBUG local found=false Logger "Checking for warnings in last backups." "NOTICE" for client in "${CLIENT_LIST[@]}"; do if [ -f "$backupDir$client/current/log.gz" ]; then if zcat "$backupDir$client/current/log.gz" | grep "WARNING" > /dev/null 2>&1; then Logger "Client [$client] has the following warnings:" "WARN" Logger "$(zcat $backupDir$client/current/log.gz | grep WARNING)" "WARN" found=true fi elif [ -f "$backupDir$client/current/log" ]; then if cat "$backupDir$client/current/log" | grep "WARNING" > /dev/null 2>&1; then Logger "Client [$client] has the following warnings:" "WARN" Logger "$(grep WARNING $backupDir$client/current/log)" "WARN" found=true fi else Logger "No log file found for warning analysis in [$backupDir$client/current]." "WARN" fi done if [ $found == false ]; then Logger "No warnings found in last backups." "NOTICE" fi } function UnstripVSS { local path="${1}" __CheckArguments 1 $# "$@" #__WITH_PARANOIA_DEBUG # We need to have a modular temp extension so we will not overwrite potential existing files local tempExtension="$SCRIPT_PID.$TSTAMP.old" if ! type vss_strip > /dev/null 2>&1; then Logger "Could not find vss_strip binary. Please check your path variable." "CRITICAL" exit 1 fi find "$path" -type f -print0 | while IFS= read -r -d $'\0' file; do Logger "Unstripping file [$file]." "NOTICE" mv -f "$file" "$file.$tempExtension" if [ $? -ne 0 ]; then Logger "Could not move [$file] to [$file.$tempExtension] for processing." "WARN" continue else vss_strip -i "$file.$tempExtension" -o "$file" if [ $? -ne 0 ]; then Logger "Could not vss_strip [$file.$tempExtension] to [$file]." "WARN" mv -f "$file.$tempExtension" "$file" if [ $? -ne 0 ]; then Logger "Coult not move back [$file.$tempExtension] to [$file]." "WARN" fi else rm -f "$file.$tempExtension" if [ $? -ne 0 ]; then Logger "Could not delete temporary file [$file.$tempExtension]." "WARN" continue fi fi fi done # Cannot get exitcode since find uses a subshell. Getting exit code from Logger if [ -f "$RUN_DIR/$PROGRAM.Logger.warn.$SCRIPT_PID.$TSTAMP" ]; then return 2 fi if [ -f "$RUN_DIR/$PROGRAM.Logger.error.$SCRIPT_PID.$TSTAMP" ]; then return 1 fi return 0 } function VerifyService { local serviceName="${1}" local serviceType="${2}" __CheckArguments 2 $# "$@" #__WITH_PARANOIA_DEBUG local serviceNameArray local serviceStatusCommand local serviceStartCommand local i if [ "$serviceName" == "" ]; then Logger "No service name(s) given." "WARN" return fi IFS=',' read -a serviceNameArray <<< "$serviceName" for i in "${serviceNameArray[@]}"; do if [ "$serviceType" == "initv" ]; then serviceStatusCommand="service $i status" serviceStartCommand="service $i start" elif [ "$serviceType" == "systemd" ]; then serviceStatusCommand="systemctl status $i" serviceStartCommand="systemctl start $i" else serviceStatusCommand="service $i status" serviceStartCommand="systemctl start $i" Logger "No valid service type given [$serviceType]. Trying default initV style." "ERROR" fi eval "$serviceStatusCommand" > /dev/null 2>&1 & ExecTasks $! "${FUNCNAME[0]}_statuscmd" false 0 0 120 300 true $SLEEP_TIME $KEEP_LOGGING if [ $? -ne 0 ]; then Logger "Service [$i] is not started. Trying to start it." "WARN" eval "$serviceStartCommand" > /dev/null 2>&1 & ExecTasks $! "${FUNCNAME[0]}_startcmd" false 0 0 120 300 true $SLEEP_TIME $KEEP_LOGGING if [ $? -ne 0 ]; then Logger "Cannot start service [$i]." "CRITICAL" SendAlert false false else Logger "Service [$i] was successfuly started." "WARN" SendAlert false false fi else Logger "Service [$i] is running." "NOTICE" fi done } function DisplayBackupCalendar { local configFile="${1}" __CheckArguments 1 $# "$@" #__WITH_PARANOIA_DEBUG local client local cmd local backups local months local days local days_expr local cdate local month_count=1 if [ -f "$configFile" ]; then configString="-c \"$configFile\"" fi for client in "${CLIENT_LIST[@]}"; do cmd="$BACKUP_EXECUTABLE $configString -a l -C \"$client\" | grep \"^Backup: \" |awk '{print \$3}' > \"$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID.$TSTAMP\"" Logger "Running cmd [$cmd]." "DEBUG" eval "$cmd" & ExecTasks $! "${FUNCNAME[0]}" false 0 0 1800 3600 true $SLEEP_TIME $KEEP_LOGGING if [ $? -ne 0 ]; then Logger "Failed to enumerate backups for client [$client]." "ERROR" elif [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID.$TSTAMP" ]; then backups="$(cat "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID.$TSTAMP")" months=$(echo "$backups" | cut -d'-' -f1-2 | sort -V | uniq | tail -n $month_count) Logger "Calendar for client [$client]:" "NOTICE" for month in $months; do days=$(echo "$backups" | grep "$month" | cut -d'-' -f3 | sort | uniq ) days_expr=$(echo "$days" | tr '\n' '|'|rev|cut -c2-|rev) cdate="$(date --date="$month-01" +"%m %Y")" cal $cdate | head -1 cal $cdate | tail -n +2 | sed -r 's/\x5f\x08//g;s/ ([0-9]( |$))/0\1/g' | grep --color -EC10 "((^| )$days_expr( |$))" done else Logger "No backups were found for client [$client]" "NOTICE" fi done } function Init { # Set error exit code if a piped command fails set -o pipefail set -o errtrace trap TrapQuit TERM EXIT HUP QUIT } function Usage { if [ "$IS_STABLE" != true ]; then echo -e "\e[93mThis is an unstable dev build. Please use with caution.\e[0m" fi echo "$PROGRAM $PROGRAM_VERSION $PROGRAM_BUILD" echo "$AUTHOR" echo "$CONTACT" echo "" echo "Usage:" echo "$0 [OPTIONS]" echo "" echo "[OPTIONS]" echo "-d, --backup-dir=\"\" The directory where the client backup directories are" echo "-o, --check-outdated-clients=n Check for clients that don't have backups newer than n days" echo "-v, --verify-last-backups=n Verify the last n backups of all clients" echo "-q, --verify-quotas Check for client quotas" echo "-i, --include-clients=\"\" Comma separated list of clients to include. This list takes grep -e compatible regular expressions, includes prevail excludes" echo "-e, --exclude-clients=\"\" Comma separated list of clients to exclude. This list takes grep -e compatible regular expressions" echo "-c, --config-file=\"\" Path to optional @name@ client configuration file (defaults to /etc/@name@/@name@.conf)" echo "-s, --vss-strip-path=\"\" Run vss_strip for all files in given path" echo "-j, --verify-service=\"\" Comma separated list of @name@ services to check and restart if they aren't running" echo "-w, --verify-warnings Check for warnings in last backup logs" echo "-A, --clients-alert-quotas Use email defined in client config to alert users about exceeded disk quotas (see email template in header)" echo "-a, --clients-alert-outdated Use email defined in client config to alert users about outdated backups (see email template in header)" echo "-z, --clientconfdir=\"\" Path of clientconfdir in order to fetch email addresses when client alerts are used (defaults to /etc/@name@/clientconfdir)" echo "-C, --client=\"\" Specify specific client instead of detecting clients." echo "--calendar Show backup calendar" echo "" echo "Examples:" echo "$0 -d /path/to/@name@/protocol1 -v 3 -c /etc/@name@/@name@.conf" echo "$0 -d /path/to/@name@/protocol2/global/clients --check-outdated-clients7 --exclude-clients=restoreclient,@name@-ui.local" echo "$0 --vss-strip-path=/path/to/restored/files" echo "$0 -j @name@.service" echo "Exclude via regex all clients beginning with 'cli' and otherclient1/2:" echo "$0 --backup-dir=/path/to/@name@/protocol1 --exclude-clients=cli.*,otherclient1,otherclient2" echo "" echo "Additionnal options" echo "--no-maxtime Don't stop checks after the configured maximal time in script" echo "-s, --silent Don't output to stdout, log file only" echo "--errors-only Don't output anything but errors." echo "" echo "--destination-mails=\"\" Space separated list of email adresses where to send warning and error mails" echo "--instance-id=\"\" Arbitrary identifier for log files and alert mails" exit 128 } #### SCRIPT ENTRY POINT DESTINATION_MAILS="" no_maxtime=false ERROR_ALERT=false WARN_ALERT=false CONFIG_FILE="" BACKUP_DIR="" VERIFY_BACKUPS="" INCLUDE_CLIENTS="" EXCLUDE_CLIENTS="" OUTDATED_DAYS="" CLIENT_LIST=() GIVEN_CLIENT="" declare -A CLIENT_EMAIL VSS_STRIP_DIR="" VERIFY_SERVICE=false VERIFY_WARNINGS=false VERIFY_QUOTAS=false CLIENTS_ALERT_QUOTAS=false CLIENTS_ALERT_OUTDATED=false CLIENT_CONF_DIR="" SHOW_BACKUP_CALENDAR=false function GetCommandlineArguments { local isFirstArgument=true if [ $# -eq 0 ] then Usage fi while [ $# -gt 0 ]; do ## Options name is $1, argument is $2 unless there is a separator other than space case $1 in --instance-id=*) INSTANCE_ID="${1##*=}" ;; --silent) _LOGGER_SILENT=true ;; --verbose) _LOGGER_VERBOSE=true ;; --no-maxtime) no_maxtime=true ;; --help|-h|--version) Usage ;; --backup-dir=*) BACKUP_DIR="${1##*=}" ;; -d) BACKUP_DIR="${2}" shift ;; --check-outdated-clients=*) OUTDATED_DAYS="${1##*=}" ;; -o) OUTDATED_DAYS="${2}" shift ;; --verify-last-backups=*) VERIFY_BACKUPS="${1##*=}" ;; -v) VERIFY_BACKUPS="${2}" shift ;; -q|--verify-quotas) VERIFY_QUOTAS=true ;; --include-clients=*) INCLUDE_CLIENTS="${1##*=}" ;; -i) INCLUDE_CLIENTS="${2}" shift ;; --exclude-clients=*) EXCLUDE_CLIENTS="${1##*=}" ;; -e) EXCLUDE_CLIENTS="${2}" shift ;; --config-file=*) CONFIG_FILE="${1##*=}" ;; -c) CONFIG_FILE="${2}" shift ;; -C) GIVEN_CLIENT="${2}" shift ;; --client=*) GIVEN_CLIENT="${1##*=}" ;; --calendar) SHOW_BACKUP_CALENDAR=true ;; --vss-strip-path=*) VSS_STRIP_DIR="${1##*=}" ;; -s) VSS_STRIP_DIR="${2}" shift ;; -j) VERIFY_SERVICE=true VERIFY_SERVICES_NAMES="${2}" shift ;; --verify-service=*) VERIFY_SERVICE=true VERIFY_SERVICE_NAMES="${1##*=}" ;; -A|--client-alert-quotas) CLIENTS_ALERT_QUOTAS=true ;; -a|--client-alert-outdated) CLIENTS_ALERT_OUTDATED=true ;; -z) CLIENT_CONF_DIR="${2}" shift ;; --clientconfdir=*) CLIENT_CONF_DIR="${1##*=}" ;; -w|--verify-warnings) VERIFY_WARNINGS=true ;; --errors-only) _LOGGER_ERR_ONLY=true ;; --destination-mails=*) DESTINATION_MAILS="${1##*=}" ;; --no-maxtime) SOFT_MAX_EXEC_TIME=0 HARD_MAX_EXEC_TIME=0 ;; *) if [ $isFirstArgument == false ]; then Logger "Unknown option '${1}'" "CRITICAL" Usage fi ;; esac shift isFirstArgument=false done } GetCommandlineArguments "$@" Init if [ "$LOGFILE" == "" ]; then if [ -w /var/log ]; then LOG_FILE="/var/log/$PROGRAM.$INSTANCE_ID.log" elif ([ "$HOME" != "" ] && [ -w "$HOME" ]); then LOG_FILE="$HOME/$PROGRAM.$INSTANCE_ID.log" else LOG_FILE="./$PROGRAM.$INSTANCE_ID.log" fi else LOG_FILE="$LOGFILE" fi if [ ! -w "$(dirname $LOG_FILE)" ]; then echo "Cannot write to log [$(dirname $LOG_FILE)]." else Logger "Script begin, logging to [$LOG_FILE]." "DEBUG" fi DATE=$(date) Logger "---------------------------------------------------------------------" "NOTICE" Logger "$DATE - $PROGRAM $PROGRAM_VERSION script begin." "ALWAYS" Logger "---------------------------------------------------------------------" "NOTICE" Logger "Instance [$INSTANCE_ID] launched as $LOCAL_USER@$LOCAL_HOST (PID $SCRIPT_PID)" "NOTICE" if [ $no_maxtime == true ]; then SOFT_MAX_EXEC_TIME_PER_VERIFY=0 HARD_MAX_EXEC_TIME_PER_VERIFY=0 SOFT_MAX_EXEC_TIME=0 HARD_MAX_EXEC_TIME=0 fi if ! type -p "$BACKUP_EXECUTABLE" > /dev/null 2>&1; then Logger "Cannot find [$BACKUP_EXECUTABLE]. Please modify binary path in $0 script header." "CRITICAL" exit 126 fi if [ "$VSS_STRIP_DIR" != "" ]; then if [ -d "$VSS_STRIP_DIR" ]; then UnstripVSS "$VSS_STRIP_DIR" exit $? else Logger "Bogus path given to unstrip [$VSS_STRIP_DIR]." "CRITICAL" exit 1 fi fi if [ "$VERIFY_SERVICE" == true ]; then VerifyService "$VERIFY_SERVICES_NAMES" "$SERVICE_TYPE" fi if [ "$BACKUP_DIR" != "" ]; then if [ ! -d "$BACKUP_DIR" ]; then Logger "Backup dir [$BACKUP_DIR] doesn't exist." "CRITICAL" exit 1 else # Make sure there is only one trailing slash on path BACKUP_DIR="${BACKUP_DIR%/}/" fi fi if [ "$CONFIG_FILE" != "" ]; then if [ ! -f "$CONFIG_FILE" ]; then Logger "Bogus configuration file [$CONFIG_FILE] given." "CRITICAL" exit 1 fi fi if [ "$CLIENT_CONF_DIR" != "" ]; then if [ ! -d "$CLIENT_CONF_DIR" ]; then Logger "Bogus clientconfdir [$CLIENT_CONF_DIR] given." "CRITICAL" exit 1 fi fi ListClients "$BACKUP_DIR" "$CONFIG_FILE" "$CLIENT_CONF_DIR" if [ $SHOW_BACKUP_CALENDAR == true ]; then DisplayBackupCalendar "$CONFIG_FILE" fi if [ $VERIFY_WARNINGS == true ]; then VerifyLastWarnings "$BACKUP_DIR" fi if [ $VERIFY_QUOTAS == true ]; then ListQuotaExceedClients "$BACKUP_DIR" fi if [ "$OUTDATED_DAYS" != "" ]; then if [ $(IsInteger "$OUTDATED_DAYS") -ne 0 ]; then ListOutdatedClients "$BACKUP_DIR" $OUTDATED_DAYS else Logger "Bogus --check-outdated-clients value [$OUTDATED_DAYS]." "CRITICAL" exit 1 fi fi if [ "$VERIFY_BACKUPS" != "" ]; then if [ $(IsInteger "$VERIFY_BACKUPS") -ne 0 ]; then VerifyBackups "$BACKUP_DIR" $VERIFY_BACKUPS "$CONFIG_FILE" else Logger "Bogus --verify-last-backups value [$VERIFY_BACKUPS]." "CRITICAL" exit 1 fi fi # v0.5.0 # - Outdated backups are now chekced against backup_stats file instead backup dir ctime (resolves no outdated backup after copy issue) # - Check against missing service names for service verification # - Ported minor fixes from osync project # - Prettier logs # - Fixed run files cleanup # - Fixed RFC822 mail checks # - Start speedup (changed PoorMansRandomGenerator) # - Fixed potential bash buffer overflow when logging very large file outputs # v0.4.8 # - Removed attachment sending to prevent mailbox clogging # - Fixed possible missing characters from log results (see https://github.com/grke/burp/issues/801 and burp/src/cmd.h) # - Fixed quota warning when no client log file exists # - Smaller fixes ported from ofunctions project # v0.4.6 # - Added command output to logs # - Fixed not using [INSTANCE] placeholder in example text # - Raised default HARD_MAX_EXEC_TIME_PER_VERIFY to 64800 seconds # - Replaced yes/no with true/false booleans # v0.4.4 # - More explicit log messages # - Fix for missing date %N in BSD / MacOS # - Ported some minor fixes from osync project # v0.4.2 # - Added Hakong's backup calendar display (using --calendar) # - Added [INSTANCE] placeholder for email sending # - Client detection now also happens when no data directory is set # - Fixed warning not shown when email address cannot be fetched and client alerts are enabled # - Fixed multiple email adresses not being checked against RFC822 # v0.4.0 first public release, merged into @name@ codebase burp-2.4.0/configs/server/burp.conf.in000066400000000000000000000151231404341324700176530ustar00rootroot00000000000000# This is an example config file for the @name@ server. mode = server listen = 0.0.0.0:4971 max_children = 5 # Optionally configure additional addresses and ports. # listen = :::5971 # max_children = 6 # Think carefully before changing the status port address, as it can be used # to view the contents of backups. # If you do not wish to run a status server at all, leave it commented out. #listen_status = 127.0.0.1:4972 #max_status_children = 5 # Optionally configure additional addresses and ports. # listen_status = ::1:5972 # max_status_children = 6 directory = @localstatedir@/spool/@name@ dedup_group = global clientconfdir = @sysconfdir@/clientconfdir # Choose the protocol to use. # 0 to decide automatically, 1 to force protocol1 mode (file level granularity # with a pseudo mirrored storage on the server and optional rsync). 2 forces # protocol2 mode (inline deduplication with variable length blocks). # Like many other settings, this can be set per client in the clientconfdir # files. # protocol = 0 pidfile = @runstatedir@/@name@.server.pid hardlinked_archive = 0 working_dir_recovery_method = delete umask = 0022 syslog = 1 stdout = 0 # The following options can restrict what the client can do. # Restore clients can override all of these except for force_backup. client_can_delete = 1 client_can_diff = 1 # Set client_can_force_backup to 0 to only allow timed backups. client_can_force_backup = 1 client_can_list = 1 client_can_monitor = 1 # Set client_can_restore to 0 if you want restores to only be initialised by # the server. client_can_restore = 1 client_can_verify = 1 # Ratelimit throttles the send speed. Specified in Megabits per second (Mb/s). # ratelimit = 1.5 # Network timeout defaults to 7200 seconds (2 hours). # network_timeout = 7200 # Server storage compression. Default is zlib9. Set to zlib0 to turn it off. #compression = zlib9 # When the client version does not match the server version, log a warning. # Set to 0 to turn it off. version_warn = 1 # More configuration files can be read, using syntax like the following # (without the leading '# '). # . path/to/more/conf # Location of autoupgrade files to serve to clients. Leave it commented out # to not autoupgrade clients. # autoupgrade_dir = @sysconfdir@/autoupgrade/server # You can have as many 'keep' lines as you like. # For example, if running backups daily, setting keep 7, keep 4, keep 6 will keep # 7 daily backups, 4 weekly, and 6 four-weekly backups. keep = 7 # keep = 4 # keep = 6 # Run as different user/group. # user=graham # group=nogroup # CA options. # If you want your server to be a certificate authority and generate its own # certificates, uncomment the following lines. If the directory specified in # ca_conf does not exist, the server will create, populate it, and the paths # indicated by ssl_cert_ca, ssl_cert, ssl_key and ssl_dhfile below will be # overwritten. See docs/@name@_ca.txt for more information. ca_conf = @sysconfdir@/CA.cnf ca_name = @name@CA ca_server_name = @name@server ca_@name@_ca = @sbindir@/@name@_ca # Check for revoked certificates in the certificate revocation list. # Turn this off if you use the old ssl_extra_checks_script server script. ca_crl_check = 1 # SSL certificate authority - same file on both server and client ssl_cert_ca = @sysconfdir@/ssl_cert_ca.pem # Server SSL certificate ssl_cert = @sysconfdir@/ssl_cert-server.pem # Server SSL key ssl_key = @sysconfdir@/ssl_cert-server.key # Server SSL ciphers #ssl_ciphers = # Server SSL compression. Default is zlib5. Set to zlib0 to turn it off. #ssl_compression = zlib5 # SSL key password, for loading a certificate with encryption. #ssl_key_password = password # Server DH file. ssl_dhfile = @sysconfdir@/dhfile.pem # The default timer_script treats the first timer_arg as the minimum interval # Ensure that 20 hours elapse between backups # Available units: # s (seconds), m (minutes), h (hours), d (days), w (weeks), n (months) timer_arg = 20h # Allow backups to start in the evenings and nights during weekdays timer_arg = Mon,Tue,Wed,Thu,Fri,00,01,02,03,04,05,19,20,21,22,23 # Allow more hours at the weekend. timer_arg = Sat,Sun,00,01,02,03,04,05,06,07,08,17,18,19,20,21,22,23 # Allow backups to start any time. #timer_arg = always # Note that, if you specify no timebands, the timer will never allow backups. # Uncomment the notify_success_* lines for email notifications of backups that # succeeded. # In the subject line, the following are substituted: # %b - "backup"/"restore"/"verify" # %c - client name # %w - number of warnings, if any #notify_success_script = @scriptdir@/notify_script #notify_success_arg = sendmail -t #notify_success_arg = To: youremail@example.com #notify_success_arg = From: @name@ #notify_success_arg = Subject: %b succeeded: %c %w #notify_success_arg = Content-Type: text/plain; charset=utf-8 # Uncomment the following to have success notifications only if there were # warnings. #notify_success_warnings_only = 1 # Uncomment the following to have success notifications only if there were # new or changed files. #notify_success_changes_only = 1 # Uncomment the following for email notifications of backups that failed. #notify_failure_script = @scriptdir@/notify_script #notify_failure_arg = sendmail -t #notify_failure_arg = To: youremail@example.com #notify_failure_arg = From: @name@ #notify_failure_arg = Subject: %b failed: %c %w #notify_failure_arg = Content-Type: text/plain; charset=utf-8 # The server can run scripts on each connection after authentication and before # disconnecting. #server_script_pre = @scriptdir@/ssl_extra_checks_script #server_script_pre_arg = @sysconfdir@/crl #server_script_pre_arg = @sysconfdir@/@name@-server.conf #server_script_pre_arg = @scriptdir@/server-pre-script.local # Set server_script_pre_notify to 1 to have notifications on server_script_pre # returning non-zero. Most people will want to leave this off - it could # result in a lot of emails because clients normally connect once every 20 # minutes. Requires notify_failure_script to be set above. #server_script_pre_notify = 0 #server_script_post = #server_script_post_arg = #server_script_post_arg = #server_script_post_run_on_fail=0 # As for server_script_pre_notify, but for post. #server_script_post_notify = 0 # Clients that are able to list and restore files belonging to any other # client. If this is too permissive, you may set a super_client for # individual original clients in the individual clientconfdir files. # super_client = someclient # super_client = someotherclient # Whether or not the server process should cache the tree when a monitor client # is browsing a backup. Advantage: speed. Disadvantage: more memory is used. #monitor_browse_cache = 1 burp-2.4.0/configs/server/clientconfdir/000077500000000000000000000000001404341324700202505ustar00rootroot00000000000000burp-2.4.0/configs/server/clientconfdir/incexc/000077500000000000000000000000001404341324700215215ustar00rootroot00000000000000burp-2.4.0/configs/server/clientconfdir/incexc/example000066400000000000000000000014201404341324700230740ustar00rootroot00000000000000# If you add at least one 'include=' line, the server will override the # rest of the client options below, which define exactly what to backup. # Setting any of the other options here will then also take effect on the # client. # (This file needs to be included in the clientconfdir file for the client, # using the '. path/to/this/file' syntax. Alternatively, these options can # be added to the clientconfdir file directly). # include=/home # exclude=/home/dontwant # exclude_ext=vdi # exclude_regex=/\.cache/ # exclude_fs=tmpfs # exclude_comp=gz # min_file_size=0 # max_file_size=0 # cross_filesystem=/some/path # cross_all_filesystems=0 # nobackup=.nobackup # read_fifo=/some/path/to/a/fifo # read_all_fifos=0 # split_vss=1 # strip_vss=0 # atime=0 # scan_problem_raises_error=0 burp-2.4.0/configs/server/clientconfdir/testclient000066400000000000000000000001511404341324700223460ustar00rootroot00000000000000password = abcdefgh # More configuration files can be read, using syntax like the following. . incexc/* burp-2.4.0/configs/server/cron.example000066400000000000000000000005011404341324700177370ustar00rootroot00000000000000MAILTO="" # The following will run file deduplication over all client storages every # Saturday at 8 in the morning. Again, if your server is using a different # config file to /etc/burp/burp-server.conf, change that argument. 0 8 * * 6 root /usr/sbin/bedup -l -c /etc/burp/burp-server.conf >>/var/log/burp-bedup 2>&1 burp-2.4.0/configs/server/notify_script000066400000000000000000000046371404341324700202560ustar00rootroot00000000000000#!/usr/bin/env bash echo "Running notify script: $@" # Arguments specified by the burp binary come first. # Normally, most of the following get set as you would expect. # In the server_script_pre/post case, burp does not have a directory from # which to read a log. In that case, it will set 'client' to the clientname, # 'basedir' blank, and 'storagedir' to the log buffer. client="$1" ; shift basedir="$1" ; shift storagedir="$1" ; shift file="$1" ; shift brv="$1" ; shift # one of backup/restore/verify/delete/list/unknown warning_count="$1" ; shift # Arguments given by the user in the conf files come next. sendmail="$1" ; shift working="$basedir/working" finishing="$basedir/finishing" while [ "$#" -gt 0 ] ; do case "$1" in Subject:*) w="" [ -n "$warning_count" -a "$warning_count" != "0" ] \ && w="($warning_count warnings)" h="$1" h="${h//%c/$client}" h="${h//%w/$w}" h="${h//%b/$brv}" ;; *) h="$1" ;; esac if [ -z "$headers" ] ; then headers="$h" else headers=$(printf "%s\n%s\n" "$headers" "$h") fi shift done catcmd="gunzip -c" # Look for a log to attach if [ "$brv" = "backup" ] ; then [ -z "$log" -a -f "$working/$file" ] && \ log="$working/$file" && id=$(cat "$working"/timestamp) && catcmd="cat" [ -z "$log" -a -f "$working/$file.gz" ] && \ log="$working/$file.gz" && id=$(cat "$working"/timestamp) [ -z "$log" -a -f "$finishing/$file" ] && \ log="$finishing/$file" && id=$(cat "$finishing"/timestamp) && catcmd="cat" [ -z "$log" -a -f "$finishing/$file.gz" ] && \ log="$finishing/$file.gz" && id=$(cat "$finishing"/timestamp) fi [ -z "$log" -a -f "$storagedir/$file" ] && \ log="$storagedir/$file" && id=$(cat "$storagedir"/timestamp) && catcmd="cat" [ -z "$log" -a -f "$storagedir/$file.gz" ] && \ log="$storagedir/$file.gz" && id=$(cat "$storagedir"/timestamp) if [ -z "$log" -a -z "$basedir" -a -n "$storagedir" ] ; then # This is the case where burp has no log directory and has given a buffer to # log via the storagedir argument. (echo "$headers" && echo && echo "$storagedir") | $sendmail elif [ -z "$log" ] ; then echo "$headers" && echo && echo "No log found to send in email" | $sendmail else # The normal case. maximum_line_count=1000 (echo "$headers" && echo && echo "$id" && echo && echo "Last $maximum_line_count lines of $log:" && echo && ($catcmd "$log" 2>/dev/null || cat "$log") | tail -n "$maximum_line_count") | $sendmail fi exit 0 burp-2.4.0/configs/server/offsite-backup000066400000000000000000000066551404341324700202660ustar00rootroot00000000000000#!/usr/bin/env bash # # -------------------------------------------------------------- # WARNING: This script is not quite working properly for me. # The rsync '--compare-dest' option appears not to work as # I expect. Files with a small change in them appear to be fully # transferred instead of getting rsync-d with the previous copy # in the old current directory as a basis. # # This script is included so that it can be fixed at a later point. # On the positive side, if you use hardlinked_archive=1 in your # server config, the hardlinks are transferred efficiently, and # only changed files are transferred whole. # -------------------------------------------------------------- # # # This script demonstrates how to run efficient offsite rsyncs using the burp # server side post script mechanism. # You need to place something like this in the server config file: # server_script_post = /etc/burp/offsite-backup # server_script_post_run_on_fail = 0 # This can be given globally, or differently for individual clients. prog=$(basename $0) preorpost="$1" ; shift action="$1" ; shift # either "backup" or "backup_timed" client="$1" ; shift success="$1" ; shift timer="$1" ; shift # You will neeed to adjust the following variables as appropriate (or turn # them into parameters passed in via 'server_script_post_arg = ' options in # the config files). # The storage directory for the client on the burp server side. src="/var/spool/burp/$client/" # The user/address of the remote site. dst="graham@10.253.0.13:" # The directory to update at the remote site. rdir="~/remote-backup/$client" # The private ssh key on the local machine that has access to the remote site. private_key="/home/graham/.ssh/id_rsa" usage() { echo "usage: $prog post [backup|backup_timed] [0|1] [0|1]" 1>&2 echo "The third argument is whether the backup succeeded (0 for ok)" 1>&2 echo "The fourth argument is whether the timer script allowed a backup (0 for ok)" 1>&2 exit 1 } if [ -z "$preorpost" -o -z "$action" -o -z "$client" -o -z "$success" -o -z "timer" ] || [ "$preorpost" != "post" ] then usage fi if [ "$action" != "backup" -a "$action" != "backup_timed" ] ; then # It was not a backup that ran. exit 0 fi if [ "$action" = "backup_timed" ] && [ "$timer" != "0" ] ; then # Server did not allow timed backup to be attempted. exit 0 fi if [ "$success" != "0" ] ; then # Backup failed - do not run the offsite backup. exit 0 fi # Needs two passes. # The previous 'curent' directory on the server side can be utilised to save # time. 'current' is a symlink pointing to the latest backup. # The first pass is to upload all the data, using the remote 'current' # directory as a reference. Do not include symlinks in this pass, so that # 'current' is not updated. if ! /usr/bin/rsync \ --verbose \ --recursive \ --perms \ --times \ --compare-dest="$rdir/current/" \ --hard-links \ --delay-updates \ --delete-after \ -e "ssh -i $private_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" \ "$src" \ "$dst$rdir" then echo "Phase 1 of remote sync failed for $client." exit 1 fi # Phase two is to only include the 'current' symlink. It gets moved at the # end when everything else is in place. if ! /usr/bin/rsync \ --verbose \ --links \ -e "/usr/bin/ssh -i $private_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" \ "$src/current" \ "$dst$rdir" then echo "Phase 2 of remote sync failed for $client." exit 1 fi exit 0 burp-2.4.0/configs/server/out_of_date_report_script000066400000000000000000000033741404341324700226260ustar00rootroot00000000000000#!/usr/bin/env bash # # This script will generate list of outdated clients # Create folders /usr/local/share/burp-custom and /var/log/burp-custom # chmod +x the sh file # Locate this file in /usr/local/share/burp-custom/burp-custom-reports.sh # link to if desired ln -s /usr/local/share/burp-custom/burp-custom-reports.sh /usr/local/bin/bur-custom-reports.sh outdated=`date -d "1 days ago" +%F` sendto=emailto@server.com sendfrom=emailsender@from.com timestamp=`date +%F-%H-%M` logfolder=/var/log/burp-custom/ #Variable to use on outdated hosts function repoutdfile=$logfolder/burp-outdated.$outdated.$timestamp.log #Variable to use for not outdated hosts function repokfile=$logfolder/burp-ok.$outdated.$timestamp.log logsclean(){ find $logfolder -name "*.log" -type f -mtime +30 -delete } make_dirs() { if [[ ! -d $1 ]] ; then mkdir -p $1 ; fi } make_dirs "$logfolder" report_outdated(){ date_backup=`echo $1 | sed -e 's/\s\+/ /g' | cut -d' ' -f6` echo "line is $1" echo "date_backup is $date_backup" if [[ "$date_backup" < "$outdated" ]] ; then echo "$1" >> $repoutdfile else echo "$1" >> $repokfile fi } reports(){ report_outdated "$1" } send_email_report(){ cat $1 | mail -s "$2" $sendto -aFrom:$sendfrom } # example output line: # hosname idle last backup: 0000023 2015-04-10 16:04:01 # read by line report from burp burp -a S | grep -i "last backup" | while read -r line; do reports "$line" ; done echo "finishing and sending email" #Send email using function, parse file and then subject inside "subject line" send_email_report $repoutdfile "Outdated hosts older than $outdated on $HOSTNAME" send_email_report $repokfile "Burp Hosts ok newer than $outdated on $HOSTNAME" #Clean old logs logsclean burp-2.4.0/configs/server/ssl_extra_checks_script000066400000000000000000000117741404341324700222720ustar00rootroot00000000000000#!/bin/sh # # This server pre script was contributed by Jason Haar. ( cat</dev/null export prog="$(basename $0)" logEvent() { logger -t "$prog[$X509_PEER_SERIALNUMBER]" "$*" echo "$*" } export error_peerDate=101 export error_noCRLs=102 export error_revoked=103 export error_crlExpired=104 export error_crlError=105 export error_crlNoArgs=106 export error_crlNoDir=107 usage() { logEvent "usage: $prog " exit $error_crlNoArgs } # Whether it is a pre or post script. export preorpost="$1" ; shift # What the client requested. Ignored. export action="$1" ; shift # The burp client name. Ignored. export client="$1" ; shift export reserved4="$1" ; shift export reserved5="$1" ; shift export BURP_CRLDIR="$1" export BURP_SERVER_CONFIG="$2" export BURP_EXTRA_SCRIPT="$3" [ -z "$BURP_CRLDIR" ] && usage [ -z "$BURP_SERVER_CONFIG" ] && usage export SERVER_CERT="`egrep '^ssl_cert[^_]' $BURP_SERVER_CONFIG 2>/dev/null|awk '{print $NF}'`" #this should never not work if [ "$X509_PEER_NOT_BEFORE" = "" ] ; then logEvent "cannot detect peer cert notBefore date field" exit $error_peerDate fi #this should never not work if [ "$X509_PEER_NOT_AFTER" = "" ] ; then logEvent "cannot detect peer cert notAfter date field" exit $error_peerDate fi PEER_DATE_BEGINS=`date --date "$X509_PEER_NOT_BEFORE" +%s` PEER_DATE_ENDS=`date --date "$X509_PEER_NOT_AFTER" +%s` NOW=`date +%s` #if this surprises you and the time checks fail - look at the clock! if [ $NOW -lt $PEER_DATE_BEGINS ] ; then logEvent "EXPIRATION: peer cert not usable until $X509_PEER_NOT_BEFORE" exit $error_peerDate fi if [ $NOW -gt $PEER_DATE_ENDS ] ; then logEvent "EXPIRATION: peer cert expired on $X509_PEER_NOT_AFTER" exit $error_peerDate fi #crls are defined within the signed certs themselves, if there isn't one, then #no need for CRL checks export CRL_POINTS=`(openssl x509 -noout -inform PEM -text -in $SERVER_CERT 2>/dev/null|| openssl x509 -inform DER -noout -text -in $SERVER_CERT 2>/dev/null) |grep URI:http|sed -e 's/^.*URI:http/http/g'` updateCRL() { downloadedCRL=0 mkdir -p $BURP_CRLDIR cd $BURP_CRLDIR CRL_FILE="`echo $X509_ISSUER_CN|sed 's/[^0-9a-zA-Z]/_/g'`.crl" CRL_AGE=`stat -c %Y "$CRL_FILE" 2>/dev/null` CRL_AGE=${CRL_AGE:-0} if [ ! -f "$CRL_FILE" -o `expr $NOW - $CRL_AGE` -gt 3600 ] ; then for crl in $CRL_POINTS do if [ $downloadedCRL -eq 0 ] ; then wget -O - $crl > tmpfile 2>/dev/null if [ -s tmpfile ] ; then mv -f tmpfile "$CRL_FILE" downloadedCRL=1 fi fi done if [ $downloadedCRL -eq 0 ] ; then logEvent "CRL: failed to update CRL cache" #don't error on this fi fi } if [ "$CRL_POINTS" = "" ] ; then logEvent "CRL: no http CRL points - skipping CRL checks" else updateCRL > /dev/null 2>&1& if [ "`/bin/ls $BURP_CRLDIR/*.crl 2>/dev/null`" = "" ] ; then #wait 10sec in case this is the first time updateCRL has run sleep 10 if [ "`/bin/ls $BURP_CRLDIR/*.crl 2>/dev/null`" = "" ] ; then logEvent "CRL: failed crl checks - no CRL files in $BURP_CRLDIR" exit $error_noCRLs fi fi FOUND_CRL=0 for crl in $BURP_CRLDIR/*.crl do if [ "`(openssl crl -in $crl -inform DER -text -noout 2>/dev/null || openssl crl -in $crl -inform PEM -text -noout 2>/dev/null)|grep Issuer:|egrep \"$X509_ISSUER_CN\"|egrep \"$X509_ISSUER_emailAddress\"`" != "" ] ; then FOUND_CRL=1 if [ "`(openssl crl -in $crl -inform DER -text -noout 2>/dev/null || openssl crl -in $crl -inform PEM -text -noout 2>/dev/null)|egrep \"Serial Number: $X509_PEER_SERIALNUMBER\$\"`" != "" ] ; then logEvent "CRL: failed crl checks - serial $X509_PEER_SERIALNUMBER for $X509_PEER_CN revoked" exit $error_revoked fi fi CRL_EXPIRES=`(openssl crl -in $crl -inform DER -noout -nextupdate || openssl crl -in $crl -inform PEM -noout -nextupdate )|sed 's/nextUpdate=//gi'` CRL_DATE=`date --date "$CRL_EXPIRES" +%s` if [ $NOW -gt $CRL_DATE ] ; then logEvent "CRL: failed crl checks - CRL for \"$X509_ISSUER_CN\" out-of-date and no update available (crl file expired $CRL_EXPIRES)" exit $error_crlExpired fi done if [ "$FOUND_CRL" = "0" ] ; then logEvent "CRL: failed crl checks - no CRL file matching ISSUER \"$X509_ISSUER_CN\"" exit $error_crlError fi fi ## ## All CRL and cert checks completed, carry on ## logEvent "INFO: connect from $X509_PEER_CN" if [ -n "$BURP_EXTRA_SCRIPT" -a -x "$BURP_EXTRA_SCRIPT" ] ; then exec "$BURP_EXTRA_SCRIPT" else exit 0 fi burp-2.4.0/configs/server/summary_script.in000066400000000000000000000004131404341324700210340ustar00rootroot00000000000000#!/bin/sh if [ -z "$1" -o -z "$2" -o -z "$3" ] ; then echo "Not enough arguments" 1>&2 exit 1 fi (echo "To: $2" echo "From: @name@" echo "Subject: $3" echo "Content-Type: text/plain; charset=utf-8" @sbindir@/@name@ -c "$1" -a S echo) | /usr/sbin/sendmail -t burp-2.4.0/configs/server/timer_script000066400000000000000000000061021404341324700200530ustar00rootroot00000000000000#!/usr/bin/env bash # # Script that determines whether it is time to run a backup. echo "Running timer script: $@" client="$1" ; shift current="$1" ; shift storage_dir="$1" ; shift reserved1="$1" ; shift reserved2="$1" ; shift interval="$1" ; shift timestamp="$current/timestamp" # A 'backup' file placed in the storage directory tells this script that # a backup needs to be done right now. # This gives the 'server initiates a manual backup' feature. manual_file="$storage_dir/$client/backup" if [ -f "$manual_file" ] ; then echo "Found $manual_file" echo "Do a backup of $client now" rm -f "$manual_file" exit 0 fi # The rest of the arguments, if any, should be timebands. # Set LANG=C and LC_TIME=C so that 'date' returns English day names. curdayhour=$(LANG=C LC_TIME=C date +"*%a*%H*") intimeband=0 # If no timebands given, default to not OK. while [ "$#" -gt 0 ] ; do intimeband=0 timeband="$1" case "$timeband" in $curdayhour|always) echo "In timeband: $timeband" intimeband=1 break ;; *) echo "Out of timeband: $timeband" ;; esac shift done get_intervals() { if [ ! -e "$current" ] ; then echo "No prior backup of $client" return 0 fi if [ ! -f "$timestamp" ] ; then echo "$0: Timestamp file missing for $client." return 0 fi if [ -z "$interval" ] ; then echo "$0: No time interval given for $client." return 0 fi case "$interval" in [0-9]*s) i=${interval%s*} ; intervalsecs=$i ;; [0-9]*m) i=${interval%m*} ; intervalsecs=$((i*60)) ;; [0-9]*h) i=${interval%h*} ; intervalsecs=$((i*60*60)) ;; [0-9]*d) i=${interval%d*} ; intervalsecs=$((i*60*60*24)) ;; [0-9]*w) i=${interval%w*} ; intervalsecs=$((i*60*60*24*7)) ;; [0-9]*n) i=${interval%n*} ; intervalsecs=$((i*60*60*24*30)) ;; *) echo "$0: interval $interval not understood for $client." return 0 ;; esac if [ -z "$intervalsecs" ] ; then echo "$0: interval $interval not understood for $client." return 0 fi read junk ts < "$timestamp" if ! secs=$(LANG=C LC_TIME=C date +%s -d "$ts") \ || ! now=$(LANG=C LC_TIME=C date +"%Y-%m-%d %H:%M:%S") \ || ! nowsecs=$(LANG=C LC_TIME=C date +%s -d "$now") then echo "$0: Date command returned error for $client." return 0 fi min_timesecs=$((secs+intervalsecs)) # GNU coreutils 'date' command should accept the following (even # slightly old versions). if ! min_time=$(LANG=C LC_TIME=C date -d "Jan 1, 1970 00:00:00 +0000 + $min_timesecs seconds" +"%Y-%m-%d %H:%M:%S") then # FreeBSD 'date' will return an error with the above, so try # a version that FreeBSD 'date' should be happy with. if ! min_time=$(LANG=C LC_TIME=C date -r $min_timesecs +"%Y-%m-%d %H:%M:%S") then echo "$0: Date command returned error for $client." return 0 fi fi echo "Last backup: $ts" echo "Next after : $min_time (interval $interval)" return 1 } if [ "$intimeband" = "0" ] ; then get_intervals exit 1 fi if get_intervals ; then echo "Currently in timeband" exit 0 fi if [ "$min_timesecs" -lt "$nowsecs" ] ; then echo "$min_time < $now." echo "Currently in timeband" exit 0 fi echo "Currently out of timeband" exit 1 burp-2.4.0/configure.ac000066400000000000000000000402141404341324700147560ustar00rootroot00000000000000dnl Process this file with autoconf to produce a configure script. dnl require a recent autoconf AC_PREREQ([2.61]) AC_INIT([Burp],[2.4.0],[https://github.com/grke/burp/issues],[burp],[http://burp.grke.net/]) AC_CONFIG_AUX_DIR([autoconf]) AC_CONFIG_HEADERS([src/config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([src/prog.c]) AM_INIT_AUTOMAKE([foreign -Wall dist-bzip2 no-dist-gzip tar-ustar subdir-objects 1.11]) AM_SILENT_RULES([yes]) dnl Check for pkg-config PKG_PROG_PKG_CONFIG if test x"${PKG_CONFIG}" = "x" ; then echo "" echo " Error! You must install pkg-config to continue compiling." exit 1 fi AC_LANG([C]) : ${CFLAGS="-Wall -g -O2"} AC_CANONICAL_HOST AC_USE_SYSTEM_EXTENSIONS dnl ------------------------------------------------------- dnl Check for compiler. dnl ------------------------------------------------------ AC_PROG_CC_C99 AM_PROG_CC_C_O dnl ------------------------------------------------------- dnl Check for programs. dnl ------------------------------------------------------ AC_PROG_INSTALL AC_PROG_MKDIR_P dnl -------------------------------------------------- dnl Libtool config dnl -------------------------------------------------- #LT_INIT([disable-static]) dnl -------------------------------------------------- dnl OS determination dnl -------------------------------------------------- case $host in *-*-linux*) AC_DEFINE([HAVE_LINUX_OS], [1], [Set to 1 if the OS is Linux]) ;; *-*-darwin*) AC_DEFINE([HAVE_DARWIN_OS], [1], [Set to 1 if the OS is Darwin]) ;; *-*-dragonfly*) AC_DEFINE([HAVE_DRAGONFLY_OS], [1], [Set to 1 if the OS is DragonFlyBSD]) CPPFLAGS="-I/usr/local/include $CPPFLAGS" LDFLAGS="-L/usr/local/lib $LDFLAGS" ;; *-*-freebsd*) AC_DEFINE([HAVE_FREEBSD_OS], [1], [Set to 1 if the OS is FreeBSD]) CPPFLAGS="-I/usr/local/include $CPPFLAGS" LDFLAGS="-L/usr/local/lib $LDFLAGS" ;; *-*-netbsd*) AC_DEFINE([HAVE_NETBSD_OS], [1], [Set to 1 if the OS is NetBSD]) CPPFLAGS="-I/usr/pkg/include" LDFLAGS="-L/usr/pkg/lib" ;; *-*-openbsd*) AC_DEFINE([HAVE_OPENBSD_OS], [1], [Set to 1 if the OS is OpenBSD]) CPPFLAGS="-I/usr/local/include $CPPFLAGS" LDFLAGS="-L/usr/local/lib $LDFLAGS" ;; *-*-solaris*) AC_DEFINE([HAVE_SUN_OS], [1], [Set to 1 if the OS is Solaris]) ;; esac dnl See if we can use 64 bit file addresses AC_SYS_LARGEFILE dnl -------------------------------------------------------------------------- dnl Check for utimebuf structure dnl -------------------------------------------------------------------------- AC_CHECK_TYPES([struct utimbuf], [], [], [[ #include #include ]] ) dnl -------------------------------------------------------------------------- dnl Check for endian headers dnl -------------------------------------------------------------------------- AC_CHECK_HEADERS([endian.h sys/endian.h sys/byteorder.h libkern/OSByteOrder.h], [break] ) dnl -------------------------------------------------------------------------- dnl Check for required functions dnl -------------------------------------------------------------------------- AC_CHECK_FUNCS_ONCE([lockf lutimes chflags]) AC_FUNC_ALLOCA AC_SEARCH_LIBS([sqrt], [m]) AC_SEARCH_LIBS([inet_ntop], [nsl]) AC_SEARCH_LIBS([socket], [socket]) dnl -------------------------------------------------------------------------- dnl Check for IPv6 dnl -------------------------------------------------------------------------- AC_MSG_CHECKING([whether to enable IPv6 support]) AC_ARG_ENABLE([ipv6], [AS_HELP_STRING([--enable-ipv6], [enable IPv6 support @<:@default=auto@:>@])], [], [enable_ipv6=auto] ) AC_MSG_RESULT([$enable_ipv6]) if test "$enable_ipv6" != "no"; then AC_MSG_CHECKING([for IPv6 support]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[ #include #include #include ]], [[ int fd; struct sockaddr_in6 foo; fd = socket(AF_INET6, SOCK_STREAM, 0); return(fd >= 0 ? 0 : 1); ]] )], [ enable_ipv6=yes AC_DEFINE([HAVE_IPV6], [1], [Define to 1 if system supports IPV6]) ], [ if test "$enable_ipv6" = "yes"; then AC_MSG_RESULT([no]) AC_MSG_ERROR([Unable to detect IPv6 support]) fi ] ) AC_MSG_RESULT([yes]) fi dnl ----------------------------------------------------------- dnl Check whether OpenSSL is available dnl ----------------------------------------------------------- AC_MSG_CHECKING([For OpenSSL]) AC_ARG_WITH([openssl], [AS_HELP_STRING([--with-openssl@<:@=DIR@:>@], [root of the OpenSSL directory])], [ case "$withval" in "" | yes | no) AC_MSG_ERROR([Invalid --with-openssl value]) ;; *) openssl_dir=$withval ;; esac ] ) AC_SUBST([OPENSSL_LIBS], ["-lssl -lcrypto"]) AC_SUBST([OPENSSL_LDFLAGS]) AC_SUBST([OPENSSL_INC]) if test "$openssl_dir" != ""; then OPENSSL_LDFLAGS="-L${openssl_dir}/lib" OPENSSL_INC="-I${openssl_dir}/include" fi save_LIBS="$LIBS" save_LDFLAGS="$LDFLAGS" save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" LIBS="$LIBS $OPENSSL_LIBS" CPPFLAGS="$OPENSSL_INC $CPPFLAGS" AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[#include ]], [[SSL_new(NULL)]] )], [ have_ssl=yes AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_OPENSSL], [1], [Define to 1 if OpenSSL library is available]) ], [ have_ssl=no AC_MSG_RESULT([no]) AC_MSG_ERROR([Unable to find OpenSSL library]) ] ) CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" dnl ----------------------------------------------------------- dnl Check whether zlib is available dnl ----------------------------------------------------------- AC_CHECK_HEADERS([zlib.h], [ AC_CHECK_LIB([z], [deflate], [ ZLIBS="-lz" ], [AC_MSG_ERROR([Unable to find libz])] ) ], [AC_MSG_ERROR([Unable to find zlib.h])] ) AC_SUBST([ZLIBS]) dnl ----------------------------------------------------------- dnl Check whether libcrypt is available dnl ----------------------------------------------------------- save_LIBS="$LIBS" AC_SEARCH_LIBS([crypt], [crypt], [ have_crypt=yes CRYPT_LIBS="$LIBS" AC_DEFINE([HAVE_CRYPT], [1], [Define to 1 if we have libcrypt]) ], [have_crypt=no] ) LIBS="$save_LIBS" AC_SUBST([CRYPT_LIBS]) dnl ----------------------------------------------------------- dnl Check whether uthash.h is available dnl ----------------------------------------------------------- AC_CHECK_HEADERS([uthash.h], [], [AC_MSG_ERROR([Cannot find uthash.h, please install uthash])] ) dnl ----------------------------------------------------------- dnl Check whether librsync is available dnl ----------------------------------------------------------- AC_CHECK_HEADERS([librsync.h], [ AC_CHECK_LIB([rsync], [rs_delta_begin], [RSYNC_LIBS="-lrsync"], [AC_MSG_ERROR([Unable to find librsync])] ) ], [AC_MSG_ERROR([Unable to find librsync.h, please install librsync])] ) AC_SUBST([RSYNC_LIBS]) dnl ----------------------------------------------------------- dnl Check for headers, functions and libraries required to dnl support keeping readall capabilities dnl ----------------------------------------------------------- have_readall=no AC_CHECK_HEADERS(sys/prctl.h sys/capability.h) AC_CHECK_FUNCS(prctl setreuid) AC_CHECK_LIB([cap], [cap_set_proc], [CAP_LIBS="-lcap"], [CAP_LIBS=]) if test x$CAP_LIBS = x-lcap; then have_readall=yes AC_DEFINE(HAVE_LIBCAP, 1, [Define if you have libcap]) fi AC_SUBST([CAP_LIBS]) dnl -------------------------------------------------------------------------- dnl Check whether librsync has RS_BLAKE2_SIG_MAGIC dnl -------------------------------------------------------------------------- AC_MSG_CHECKING([whether librsync has RS_BLAKE2_SIG_MAGIC]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[ #include ]], [[ int x=RS_BLAKE2_SIG_MAGIC; return x; ]] )], [ enable_blake2=yes AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_BLAKE2], [1], [Define to 1 if librsync supports RS_BLAKE2_SIG_MAGIC]) ], [ AC_MSG_RESULT([no]) ] ) dnl ----------------------------------------------------------- dnl Check whether libncurses is available dnl ----------------------------------------------------------- have_ncurses=no m4_ifdef([PKG_CHECK_MODULES], [ PKG_CHECK_MODULES([ncurses], [ncurses], [ have_ncurses=yes NCURSES_LIBS="$ncurses_LIBS" CFLAGS="$CFLAGS $ncurses_CFLAGS -DHAVE_NCURSES_H=1" AC_DEFINE([HAVE_NCURSES], [1], [Set to 1 if we have ncurses]) ], [ AC_CHECK_HEADERS([ncurses.h ncurses/ncurses.h], [ AC_CHECK_LIB([ncurses], [curs_set], [ NCURSES_LIBS="-lncurses" have_ncurses=yes AC_DEFINE([HAVE_NCURSES], [1], [Set to 1 if we have ncurses]) ] ) ] ) ] ) ] ) AC_SUBST([NCURSES_LIBS]) dnl ----------------------------------------------------------- dnl Check whether systemd integration is allowed and libsystemd is available dnl ----------------------------------------------------------- AC_MSG_CHECKING([whether to enable systemd integration]) AC_ARG_ENABLE([systemd], [AS_HELP_STRING([--enable-systemd], [enable systemd integration @<:@default=auto@:>@])], [], [enable_systemd=auto] ) AC_MSG_RESULT([$enable_systemd]) have_systemd=no if test "$enable_systemd" != "no"; then m4_ifdef([PKG_CHECK_MODULES], [ PKG_CHECK_MODULES([systemd], [libsystemd], [ have_systemd=yes SYSTEMD_LIBS="$systemd_LIBS" CFLAGS="$CFLAGS $systemd_CFLAGS -DHAVE_SYSTEMD=1" AC_DEFINE([HAVE_SYSTEMD], [1], [Set to 1 if we have systemd]) ], [ AC_CHECK_HEADERS([systemd/sd-daemon.h], [ AC_CHECK_LIB([systemd], [sd_listen_fds], [ SYSTEMD_LIBS="-lsystemd" have_systemd=yes AC_DEFINE([HAVE_SYSTEMD], [1], [Set to 1 if we have systemd]) ] ) ] [ if test "$enable_systemd" = "yes"; then AC_MSG_ERROR([systemd integration cannot be enabled; perhaps systemd is not installed?]) fi ] ) ] ) ] ) AC_SUBST([SYSTEMD_LIBS]) fi dnl ----------------------------------------------------------- dnl Check whether libcheck ('Check') is available dnl ----------------------------------------------------------- m4_ifdef([PKG_CHECK_MODULES], [ PKG_CHECK_MODULES([CHECK], [check], [], [ AC_MSG_NOTICE([You will need to install 'Check' to run the test suite]) ] ) ] ) dnl ----------------------------------------------------------- dnl Check for ACL support and libraries dnl ----------------------------------------------------------- AC_MSG_CHECKING([whether to enable ACL support]) AC_ARG_ENABLE([acl], [AS_HELP_STRING([--enable-acl], [enable acl support @<:@default=auto@:>@])], [], [enable_acl=auto] ) AC_MSG_RESULT([$enable_acl]) have_acl=no if test "$enable_acl" != "no"; then AC_CHECK_HEADERS([sys/acl.h], [ save_LIBS="$LIBS" AC_SEARCH_LIBS([acl_get_file], [acl posix1e], [ have_acl=yes ACL_LIBS="$LIBS" AC_DEFINE([HAVE_ACL], [1],[Define to 1 if we have ACL support]) ], [ if test "$enable_acl" = "yes"; then AC_MSG_ERROR([function 'acl_get_file not found'. Perhaps you need to install libacl?]) fi ] ) ACL_LIBS="$LIBS" LIBS="$save_LIBS" ], [ if test "$enable_acl" = "yes"; then AC_MSG_ERROR([sys/acl.h not found]) fi ] ) fi AC_SUBST([ACL_LIBS]) dnl ----------------------------------------------------------- dnl Check for extended attribute support dnl ----------------------------------------------------------- AC_MSG_CHECKING([whether to enable XATTR support]) AC_ARG_ENABLE([xattr], [AS_HELP_STRING([--enable-xattr], [enable xattr support @<:@default=auto@:>@])], [], [enable_xattr=auto] ) AC_MSG_RESULT([$enable_xattr]) have_xattr=no dnl First try the BSD implementation if test "$enable_xattr" != "no"; then AC_CHECK_HEADERS([sys/extattr.h], [ AC_CHECK_HEADERS([libutil.h]) AC_CHECK_DECLS([extattr_get_link, extattr_set_link, extattr_list_link, extattr_namespace_to_string, extattr_string_to_namespace], [have_xattr=yes], [have_xattr=no], [AC_INCLUDES_DEFAULT [ #ifdef HAVE_SYS_EXTATTR_H #include #endif #ifdef HAVE_LIBUTIL_H #include #endif ] ] ) ], [have_xattr=no] ) fi dnl Then check for the Linux or OSX implementation if test "$enable_xattr" != "no" && test "$have_xattr" = "no" ; then AC_CHECK_HEADERS([sys/xattr.h], [ AC_CHECK_FUNCS([llistxattr lgetxattr lsetxattr listxattr getxattr setxattr], [have_xattr=yes], [have_xattr=no] ) ], [have_xattr=no] ) fi if test "$enable_xattr" = "yes" && test "$have_xattr" = "no"; then AC_MSG_ERROR([xattr support explicitly enabled but no supported xattr implementation found]) fi if test "$have_xattr" = "yes"; then AC_DEFINE([HAVE_XATTR], [1], [Extended Attributes support]) fi AM_CONDITIONAL([WITH_XATTR], [test "$have_xattr" = "yes"]) dnl ----------------------------------------------------------- dnl Check if we want the tests to have code coverage support dnl ----------------------------------------------------------- AC_SUBST([COVERAGE_CFLAGS]) AC_SUBST([COVERAGE_LDFLAGS]) AC_MSG_CHECKING([whether to build the test suite with code coverage support]) AC_ARG_WITH([coverage], [AS_HELP_STRING([--with-coverage], [build test suite with code coverage support])], [ AC_SUBST([COVERAGE_CFLAGS], [--coverage]) AC_SUBST([COVERAGE_LDFLAGS], [--coverage]) ], [with_coverage=no] ) AC_MSG_RESULT([$with_coverage]) AM_CONDITIONAL([WITH_COVERAGE], [test "$with_coverage" = "yes"]) if test "$with_coverage" = "yes"; then AC_PATH_PROG([LCOV], [lcov], [no]) fi if test "$with_coverage" = "yes" && test "$LCOV" = "no"; then AC_MSG_ERROR([Unable to find lcov]) fi if test "$with_coverage" = "yes"; then AC_PATH_PROG([GENHTML], [genhtml], [no]) fi if test "$with_coverage" = "yes" && test "$GENHTML" = "no"; then AC_MSG_ERROR([Unable to find genhtml]) fi dnl ----------------------------------------------------------- dnl Set some defaults dnl ----------------------------------------------------------- dnl set sysconfdir to /etc/${PACKAGE_TARNAME} unless specified otherwise if test "$prefix" = "NONE" && test "$sysconfdir" = '${prefix}/etc'; then sysconfdir="/etc/${PACKAGE_TARNAME}" fi if test "x$runstatedir" = x; then AC_SUBST([runstatedir], ['${localstatedir}/run']) fi if test "x$scriptdir" = x; then AC_SUBST([scriptdir], ['${pkgdatadir}/scripts']) fi # we only build with a c++ compiler at the moment AC_CONFIG_FILES([Makefile]) AC_OUTPUT AC_MSG_NOTICE([]) AC_MSG_NOTICE([Configuration summary]) AC_MSG_NOTICE([=====================]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ Package: ${PACKAGE_NAME}]) AC_MSG_NOTICE([ Tar name: ${PACKAGE_TARNAME}]) AC_MSG_NOTICE([ Version: ${PACKAGE_VERSION}]) AC_MSG_NOTICE([ Host: ${host}]) AC_MSG_NOTICE([ Install binaries: ${sbindir}]) AC_MSG_NOTICE([ Install config files: ${sysconfdir}]) AC_MSG_NOTICE([ C Compiler: ${CC}]) AC_MSG_NOTICE([ Preprocessor flags: ${CPPFLAGS}]) AC_MSG_NOTICE([ Compiler flags: ${CFLAGS}]) AC_MSG_NOTICE([ Linker flags: ${LDFLAGS}]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ acl: ${have_acl}]) AC_MSG_NOTICE([ crypt: ${have_crypt}]) AC_MSG_NOTICE([ ipv6: ${enable_ipv6}]) AC_MSG_NOTICE([ ncurses: ${have_ncurses}]) AC_MSG_NOTICE([ systemd: ${have_systemd}]) AC_MSG_NOTICE([ readall: ${have_readall}]) AC_MSG_NOTICE([ openssl: ${have_ssl}]) AC_MSG_NOTICE([ xattr: ${have_xattr}]) AC_MSG_NOTICE([ zlib: ${ac_cv_header_zlib_h}]) AC_MSG_NOTICE([]) burp-2.4.0/docs/000077500000000000000000000000001404341324700134175ustar00rootroot00000000000000burp-2.4.0/docs/add-remove.txt000066400000000000000000000157021404341324700162100ustar00rootroot00000000000000Adding a new client ------------------- First, you will need to tell the server about the new client. Each client is represented by a file in the /etc/burp/clientconfdir directory on the server. So, to add a client called 'data_store', you would create a file on the server called /etc/burp/clientconfdir/data_store. As a minimum, the file needs to contain a line like this (unless you are using the 'password_check = 0' option): password = abcedfgh Now, you can install the burp client. If you are using Windows, the installer will walk you through the steps (see the separate documentation on this). On other operating systems, you will need to edit the client burp.conf so that the 'cname' line matches the clientconfdir file name on the server, the 'password' line matches the its the clientconfdir file's contents, and the 'server' line contains the address of the server. In our example's case, those three lines will look like this: cname = data_store password = abcdefgh server= 1.2.3.4 (Note that you don't necessarily have to specify cname - see the 'automatic client names' section below) You will also need to set up a timed job on the client, to run 'burp -a t'. This is done automatically on Windows. On other operating systems, you will need to add a cron job. The first time that the new client connects to the server, it will perform the SSL certificate exchange (see the separate documentation on this). I find it useful to run 'burp -a l' by hand initially, to see the exchange happen and be sure that the connection is working. 'burp -a l' is a request for a list of backups. With your new client, there will of course be no backups, but you will see successful communication between client and server. The Windows equivalent of this command is: C:\Program Files\Burp\bin\burp.exe -a l Disabling a client ------------------ To stop a client from successfully interacting with the server, you should move the /etc/burp/clientconfdir/ file aside. The client, if it still exists, will keep trying to connect. For example: mv /etc/burp/clientconfdir/ /etc/burp/clientconfdir/.disable However, this will not stop the client trying to connect to the server based on its timed job. If you can still access the client, I would recommend turning off the timed job. Revoking a client ----------------- The fact that the client uses a certificate, with the correct CN, signed by the server is what allows it to complete the SSL part of the connection. To make a particular certificate stop working, you need to revoke it. There is currently no automatic mechanism in burp that will let you do this (see https://github.com/grke/burp/issues/89). The following procedure is for burp-2 only. Revoking a client certificate will cause the server to reject a client connection, from the moment the 'ca_crl_check' option has been enabled in the burp-server.conf configuration file. Before you can revoke certificates with the following method, you need to enable the Certificate Revocation List (CRL). See the documentation page on 'certificate authority and certificates' for details on this. The procedure below will revoke a client certificate which has been signed by the server certificate authority. These commands are run on the server. 1. Get the certificate serial of the client: openssl x509 -serial -noout -in /etc/burp/CA/.crt 2. Revoke the certificate ('01' is the serial number output in step 2): burp_ca --name burpCA --revoke 01 3. Regenerate the crl: burp_ca --name burpCA --crl 4. Check the certificate has been revoked: openssl crl -in /etc/burp/CA/CA_burpCA.crl -text The client will not be able to connect any more. 5. Left over files for the client can now be deleted on the server: rm /etc/burp/CA/.* rm /etc/burp/clientconfdir/ 6. You may, or may not, also want to delete any backups left on the server: rm -r /var/spool/burp/ 7. To see a list of revoked certificates: openssl crl -in /etc/burp/CA/CA_burpCA.crl -text -noout Notes: 1. The presence of the '/etc/burp/CA/.*' files will prevent a client with the same CN doing a certificate signing process. 2. If not deleted, the presence of '/etc/burp/clientconfdir/' could present a security risk as somebody knowing the client name and password could start a certificate signing process and then access any remaining backup files. 3. The procedure above checks the certificate against the burp_ca CRL. If the certificate authority is different, it is recommended to use the provided 'ssl_extra_checks_script' which will download the certificate authority CRL for validation before allowing the client to proceed further with the backup/list/restore operations. 4. .csr is the client's initial signing request and .crt is the signed certificate that the server gave back to the client. Automatic client names ---------------------- The following procedure is for burp-2 only. If you do not specify a cname in the client burp.conf, and it hasn't yet generated its SSL certificates, it will use its own fully qualified domain name as the common name in its new SSL certificate. If you do not specify a cname in the client burp.conf, and you have generated its SSL certificate, it will use the common name in the certificate as the cname. There are some further options related to the automatic cname on the client side: *) cname_fqdn=[0|1] - If you want to use the hostname instead of the whole fqdn, set this to 0. The default is 1. *) cname_lowecase=[0|1] - If you want to force it to lowercase, use 1. The default is 0. If you have already generated the SSL certificate before using these options, you will need adjust a few things. Assume the cname was originally testclient.f.q.d.n and now you want to set cname_fqdn=0 on the client side. Now, on the server: 1) Make sure that no backup of the client is already running. 2) You have to move your storage directory if you already have backups: cd /var/spool/burp && mv testclient.f.q.d.n testclient 3) You have to setup clientconfdir accordingly: cd /etc/burp/clientconfdir && mv testclient.f.q.d.n testclient 4) The certificate won't match on the server side because it has the common name testclient.f.q.d.n and we are now looking for testclient. You can add 'ssl_peer_cn=testclient.f.q.d.n' in /etc/burp/clientconfdir/testclient and it will work again. 5) On the client, run 'burp -a l' to check that things work correctly. The cname_fqdn and cname_lowercase options are also available server-side, in burp-server.conf. When set here, they adjust the cname server side. So, following the example above, the client would still be sending 'testclient.f.q.d.n', but the server will treat it according to its own options. Again, if you set these options once a client has already introduced itself, you will have to follow the same steps above. burp-2.4.0/docs/aix-7.1-gcc.txt000066400000000000000000000111161404341324700157760ustar00rootroot00000000000000BURP on IBM AIX 7.1 using GCC ============================= This document assumes AIX 7.1 and GCC from Perzl. For more information, visit http://perzl.org/aix/index.php?n=Main.Instructions Packages -------- This section lists the various packages that need to be installed in order to build or run burp on an AIX machine. You need to have the following filesets from IBM installed before continuing: * bos.adt.base * bos.adt.include * bos.mp64 * bos.adt.libm Additionally, assuming that you are using Perzls RPMs, you need to install the IBM Linux toolchain (for rpm support). If you intend to compile uthash into an RPM package, you need version 4 of the rpm fileset. The following are required to build burp, which are the latest available at the time of writing. Older or newer versions may work as well. * autoconf-2.69-2 * automake-1.15-2 * bash-4.3-18 * bzip2-1.0.6-1 * coreutils-64bit-8.25-2 * gcc-4.9.4-1 * gcc-cpp-4.9.4-1 * gdbm-1.9.1-1 * gmp-6.1.2-1 * grep-3.1-1 * info-5.2-2 * libgcc-4.9.3-1 * libiconv-1.15-1 * libmpc-1.0.3-1 * librsync-0.9.7-1 * librsync-devel-0.9.7-1 * libsigsegv-2.10-1 * libstdc++-4.9.3-1 * libtool-2.4.6-1 * m4-1.4.18-1 * make-4.2.1-1 * mpfr-3.1.5-1 * ncurses-5.9-1 * ncurses-devel-5.9-1 * openssl-1.0.1t-1 * pcre-8.41-1 * perl-5.8.8-2 * popt-1.16-2 (needed by librsync) * readline-7.0-3 * rsync-3.1.2-1 * sed-4.4-1 * tar-1.28-1 * zlib-1.2.8-1 * zlib-devel-1.2.8-1 After that, the following are required to run burp: * bash * libgcc * ncurses * mktemp (for burp_ca) * libiconv * librsync * openssl * pcre * zlib Build Environment ----------------- Setup your build environment like Perzl does for GCC, for ease of use just put this into your ~/.bashrc: ```bash # Create 64bit objects export OBJECT_MODE=64 # Force bash over system default KSH export CONFIG_SHELL=/opt/freeware/bin/bash export CONFIG_ENV_ARGS=/opt/freeware/bin/bash export CC="gcc -maix64 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES" export CFLAGS="-DSYSV -D_AIX -D_AIX32 -D_AIX41 -D_AIX43 -D_AIX51 -D_AIX52 -D_AIX53 -D_AIX61 -D_AIX71 -D_ALL_SOURCE -DFUNCPROTO=15 -O -I/opt/freeware/include" export CXX="gcc -maix64 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES" export CXXFLAGS=$CFLAGS export LD=ld export LDFLAGS="-L/opt/freeware/lib64 -L/opt/freeware/lib -Wl,-blibpath:/opt/freeware/lib64:/opt/freeware/lib:/usr/lib:/lib -Wl,-bmaxdata:0x80000000 -Wl,-brtl" export PATH=/opt/freeware/bin:/opt/freeware/sbin:/usr/bin:/bin:/etc:/usr/sbin:/usr/ucb:/usr/bin/X11:/sbin:/usr/vac/bin:/usr/vacpp/bin:/usr/ccs/bin:/usr/dt/bin:/usr/opt/perl5/bin::/usr/local/bin:/usr/lib/instl ``` uthash ------ uthash has to be installed manually as there are no RPMs for it for AIX yet. uthash-1.9.9.1 works fine. popt ---- Unfortunately, there's no 64bit popt library available for AIX, so linking fails with the following error: ``` ld: 0711-738 ERROR: Input file /opt/freeware/lib/libpopt.so: XCOFF32 object files are not allowed in 64-bit mode. collect2: error: ld returned 8 exit status make: 1254-004 The error code from the last command is 1. ``` The reason is that because librsync defines popt as a dependency, the autotools try to link burp against popt. But there is no suitable version. The easiest way to fix this is to edit the librsync dependency file "/usr/lib/librsync.la". Change the line "dependency_libs" to not include "/opt/freeware/lib/libpopt.la". Beware, this is a system-wide change so unless you're using your LPAR to build burp only, you should revert the file to its original state afterwards. Building BURP ------------- If you're using a release tarball, you may skip this step and fast-forward to the configure call. ```bash libtoolize --install --copy --force --automake aclocal -I m4 autoconf --force autoheader automake --add-missing --copy --foreign --force-missing ``` While a simple "./configure" works, adapting the paths to match those of IBMs aix toolbox and the other 3rd party RPM packages requires a few options: ```bash ./configure --program-prefix= --prefix=/opt/freeware --exec-prefix=/opt/freeware --bindir=/opt/freeware/bin --sbindir=/opt/freeware/sbin --sysconfdir=/etc --datadir=/opt/freeware/share --includedir=/opt/freeware/include --libdir=/opt/freeware/lib --libexecdir=/opt/freeware/libexec --localstatedir=/opt/freeware/var --sharedstatedir=/opt/freeware/com --mandir=/opt/freeware/share/man --infodir=/opt/freeware/share/info --sysconfdir=/etc/burp ``` Then "make" and "make install". The burp binary will end up in /opt/freeware/sbin, so make sure to add that folder to your PATH or specify a different sbindir. burp-2.4.0/docs/autoupgrade.txt000066400000000000000000000051421404341324700165020ustar00rootroot00000000000000Autoupgrade ----------- Clients need to be on version 1.3.0 or newer to have the ability to autoupgrade. Also, the server needs to be newer than 1.3.0. On the client machines, you need to have fields in burp.conf that look like this: autoupgrade_os = win32 autoupgrade_dir = C:/Program Files/Burp/autoupgrade or autoupgrade_os = some_os autoupgrade_dir = /etc/burp/autoupgrade/client The 'os' values match a directory component on the server. The directories given need to exist. On the server, you need to have a field in the burp-server.conf that looks like this: autoupgrade_dir = /etc/burp/autoupgrade/server The directory needs to exist on the server. Subdirectories need to exist for the 'autoupgrade_os' values on the client, with further subdirectories named after the current server version. For example: /etc/burp/autoupgrade/server/win32/1.3.1 /etc/burp/autoupgrade/server/some_os/1.3.1 The version directory needs to contain two files called "script" and "package". If all your packages are going to be installed the same way for a particular OS, you may also place "script" at the top level for that OS. Like this, for example: /etc/burp/autoupgrade/server/win32/script Given all the above, then when the client version is 1.3.0 or newer, and the server version is greater than the client version, the next time the client connects, it will download the two files, execute the "script" file, and then exit. Examples of "script" files are given in the source package, in configs/server/autoupgrade. The "package" file is the new package to be installed. I shall explain once more, by example. Scenario: You have a 32 bit Windows client called 'win2k8' that is currently on 1.3.0, installed in C:/Program Files/Burp/. You have a Debian server that is currently on 1.3.1. You want to automatically upgrade the Windows client to 1.3.1. You have a Windows burp installer called burp-win32-installer-1.3.1.exe. Make sure this is in the client burp.conf file: autoupgrade_os = win32 autoupgrade_dir = C:/Program Files/Burp/autoupgrade Make sure this is in the server burp-server.conf file: autoupgrade_dir = /etc/burp/autoupgrade/server Make sure this directory exists on the server: /etc/burp/autoupgrade/server/win32/1.3.1 Copy an appropriate script file to the OS directory (and rename it "script"): cp burp/configs/server/autoupgrade/windows.script \ /etc/burp/autoupgrade/server/win32/script Copy the installer to the version directory (and rename it "package"): cp burp-win32-installer-1.3.1.exe \ /etc/burp/autoupgrade/server/win32/1.3.1/package The client should now automatically upgrade to 1.3.1 when it next connects. burp-2.4.0/docs/backup_tool_script.txt000066400000000000000000000167071404341324700200610ustar00rootroot00000000000000Backup verification script -------------------------- backup_tool_script is a script to check backup sanity on the server. backup_tool_script can verify a given number of backups for each client. It can run verify operations in parallel, interleaving different client backups in order to prevent performance bottlenecks on backend storage. Some features: * Run verify operations, but stop after a given amount of time, leaving the system ready for backup operations. * List clients that have outdated backups. It uses two different methods to list clients in order to detect rogues. * Ensure that the server service is running properly, relaunch it if needed, on a scheduled basis. * Summarise all most recent backups containing warning messages. * Send a warning / error when problems are found, even while operating. * Check disk quotas and send a warning when quotas are exceeded. * Launch vss_strip for each file found in a given directory. * Show a calendar of successful backups (Thanks to https://github.com/hakong) Usage ----- You will find the script here: /usr/local/share/burp/scripts/backup_tool_script Even though it is installed on both client(s) and server, it is intended for use by a system administrator on the host running the burp server, as it needs access to the backup storage backend, the server process and its configuration files. In a standard installation, it needs elevated (root) privileges (e.g. sudo backup_tool_script ...). To begin with, modify the script and configure the following variables: INSTANCE_ID: Some unique string to identify the instance of the script, given that you may run multiple versions with different settings. MAIL_ALERT_MSG: The warning / error email subject. SOFT_MAX_EXEC_TIME_PER_VERIFY: Amount of time for each verification process (in seconds), after which a warning message is sent. HARD_MAX_EXEC_TIME_PER_VERIFY: Amount of time for each verification process (in seconds), after which the process is forcefully stopped. SOFT_MAX_EXEC_TIME: Amount of global time (in seconds), after which a warning message is sent. HARD_MAX_EXEC_TIME: Amount of global time (in seconds), after which all verification operations are forcefully stopped. POSTPONE: If client isn't idle, should we postpone it's verification (YES/NO). POSTPONE_TIME: Amount of time (in seconds) after which a postponed verification is tried again. POSTPONE_RETRY: Number of times a postponed verification is retried. BACKUP_EXECUTABLE: Path to the burp executable. SERVICE_TYPE: Which service system your are using. Can be set to "initv" or "systemd". PARALLEL_VERIFY_CONCURRENCY: How many simultaneous verify operations will be launched. Syntax examples --------------- The path given to backup_tool_script must be the path that leads to every client directory, regardless of protocol 1 or 2 usage (for protocol 2, you'll have to dive into dedup group directory). Check for clients that don't have backups newer than 3 days: backup_tool_script -d /path/to/clients/backups/dir -o 3 Launch backup verification for last 7 backups for each client: backup_tool_script -d /path/to/clients/backups/dir -v 7 Run with a different client configuration file (default is /etc/burp/burp.conf) and check for clients that don't have backups newer than 5 days: backup_tool_script -c /etc/burp/burp-client.conf \ -d /path/to/clients/backup/dir -o 5 Run vss_strip for all files in /tmp/burp_restore: backup_tool_script -s /tmp/burp_restore Check that the burp service is running: backup_tool_script -j burp Check for warnings in most recent backups: backup_tool_script -d /path/to/clients/backups/dir -w Check for exceeded quotas in most recent backups: backup_tool_script -d /path/to/clients/backups/dir -q Example of full configuration, which checks the service health, checks for outdated clients, verifies the most recent 5 backups, checks for warning in the most recent backups, and sends a report to an email address specifying a trivial instance name: backup_tool_script -d /path/to/clients/backup/dir -o 3 -v 5 -j burp \ -c /etc/burp/burp.conf -w --destination-mails=backupadmin@example.com \ --instance-id=burp_checks Show calendar of successful backups: backup_tool_script --calendar You can check the full syntax simply by running the script without any arguments. Client inclusions and exclusions -------------------------------- You may want to include / exclude some clients on the checks. backup_tool_script uses grep compatible regular expressions to do so. Inclusions are evaluated before exclusions. Example - Include all clients which names begin with "servers", and exclude everything else: backup_tool_script -i servers.* -e ".*" -d /path/to/clients/backup/dir Example - Exclude all clients which names begin with customerX: /usr/local/share/burp/scripts/backup_tool_script -e customerX.* \ -d /path/to/clients/backup/dir Scheduling ---------- Once you have found the right settings for your usage, you may schedule the script by adding a cron entry. Example - On RHEL / CentOS, add the following line to /etc/crontab in order to run the script every day at 10am: 00 10 * * * root /usr/local/share/burp/scripts/backup_tool_script On Ubuntu, you'll need to remove the "root" username on the crontab line. Scheduling considerations ------------------------- The tool can detect that a backup is currently running and will postpone verifications, but considering that backups may occur nightly depending on your configuration, it would make more sense to run verifications out of the backup timeframe. Be aware that if you run the verifications while performing backups, backup numbers may change, so verifications may fail. This is especially true when trying to check more backups than the actual retention policy. Example - keeping 7 daily backups with 'keep 7': If you launch backup_tool_script with -v 7, if the verification happens while backing up, the 7th backup may be deleted after successful creation of the new up to date backup, and backup_tool_script will fail verifying the 7th backup it listed on script start. In order to avoid this, verify the last 6 backups when having 7 rotating copies, or simply let backup_tool_script run only when no backups are currently running. Client emails ------------- You may want to send an email directly whenever no recent backups are found or disk quotas exceed. In this case, you have to add an email address to the client configuration file. Exmaple in /etc/burp/clientconfdir/test.local: include /some/path label = email_address : some@example.com With this additional information, the script can send an email directly to the client address. In order to send an alert on exceeded quota, add the '-A' parameter or '--client-alert-quotas'. In order to send an alert on outdated backups, add the '-a' parameter or '--client-alert-outdated'. Additionally, you may want to customize the messages sent. Please see the configuration header in the file in order to customize the emails. You may use placeholders for clientname, disk quotas statistics and outdated clients. Logs ---- By default, backup_tool_script logs to: /var/log/backup_tool_script.[instance-id].log If /var/log is not writable, it will log to: ${HOME} Customisation and development ----------------------------- As of today, the script should work on all linux platforms. Some work is needed to make some regular expressions work on *BSD. Feel free to fork and help. :) Orsiris de Jong burp-2.4.0/docs/baremetal-ubuntu.txt000066400000000000000000000132231404341324700174350ustar00rootroot00000000000000Ubuntu bare metal restore steps ------------------------------- Author ------ Wybren Buijs To do a bare metal restore, boot some sort of live environment that is able to run burp. Also before you try a bare-metal restore be sure the backups are backups of the complete system and not just a part like /home. In our case we use a 15.10 ubuntu live image booted from the network or a usb drive. On the backup server You need to rename the keys because the live cd does not have these burp keys. On the server go to /etc/burp/CA/ and rename the keys of the host you are going to restore. Get the password the client used from /etc/burp/clientconfdir/clientname On the client. Boot your live environment. If burp is not installed install it. Edit /etc/burp/burp.conf Change the following items in the config: server = xx.xx.xx.xx (use the ip address to avoid resolving problems) password = get it from the backup server cname = get it from the backup server If it is a new disk/system. You first need to partition it in the right way. But before doing that you need to know a few things. What was the partition scheme? Was it gpt or mbr Was it a md raid system Do I want the same scheme as before? You could have a peek in /etc/fstab of your backup to find out if there was a md raid installed. You could use the size of your backup to determine the minimum partition space needed for the restore. You can set your swap with chroot. But I think it might be of good practice if you want to be able to do smooth bare-metal restores to make note how your systems should be partitioned. So let's start creating a system with all disk space available - swap. Start parted on the disk of choice and create 10GB primary partition and 2GB swap. Some parted info http://www.thegeekstuff.com/2011/09/parted-command-examples/ For gpt parted /dev/sda mklabel gpt mkpart boot 1049KB 10.5MB <- you need this for non uefi systems on uefi not sure if you have to. mkpart root 12MB 10GB mkpart swap 10GB 12GB set 1 bios_grub on print quit For mbr parted mkpart primary 1049KB 10GB mkpart extended 10GB 12GB mkpart logical 10GB 12GB set 1 boot on set 2 lba off print quit If your boot disk is a raid than you will need mdadm to create a new raid system. Mdadm is probably not installed so install it. Start parted on sda parted /dev/sda mklabel gpt mkpart boot 1049KB 2097KB mkpart root 2098KB 12GB mkpart swap 12GB 14GB set 1 bios_grub on set 2 raid on set 3 raid on Select sdb select /dev/sdb mklabel gpt mkpart boot 1049KB 2097KB mkpart root 2098KB 12GB mkpart swap 12GB 14GB set 1 bios_grub on set 2 raid on set 3 raid on quit Create your raids. mdadm --create /dev/md0 --bitmap=internal --level=1 -n 2 /dev/sd[ab]2 mdadm --create /dev/md1 --bitmap=internal --level=1 -n 2 /dev/sd[ab]3 create partitions on your raids. parted /dev/md0 mklabel loop select /dev/md1 mklabel loop quit For all the following commands if you use a raid use md0 and md1 instead of sda and sdb accept for installing grub. Create an ext4 file system mkfs.ext4 /dev/sda1 If you want you can already create your swap it can also be done when your system is restored and running. gpt-> mkswap /dev/sda3 mbr-> mkswap /dev/sda5 When done with the partitioning or your using a disk that was already partitioned, mount the partition of the os. In this case we mounted /dev/sda1 to /home/ubuntu/restore mkdir /home/ubuntu/restore sudo mount /dev/sda1 /home/ubuntu/restore Start the restore procedure add -f if you need to overwrite files sudo burp -a r -d /home/ubuntu/restore/ Once the restore is done you need to install grub to the boot drive to do this we chroot into the restore directory. If it is a new disk create the excluded proc dir. mkdir restore/proc Mount the critical virtual filesystems. Run the following as a single command: for i in /dev /dev/pts /proc /sys /run; do sudo mount -B $i /home/ubuntu/restore$i; done If it complains about a missing folder in your restore folder like proc create it. Chroot into your restore system device: sudo chroot /home/ubuntu/restore If its a md raid first install mdadm apt-get install mdadm Install GRUB 2 (substitute the correct device with the device you used before): grub-install /dev/sda If the system partitions are on a software RAID install GRUB 2 on all disks in the RAID. Example (software RAID using /dev/sda and /dev/sdb): grub-install /dev/sda grub-install /dev/sdb Recreate the GRUB 2 menu file (grub.cfg) update-grub Exit chroot: CTRL-D on keyboard Reboot. sudo reboot If you did all of this on a new disk or system some things will be messed up. Your fstab will have wrong uuid's in it. You should match them with your current uuid's You might need to create a swap space. To check if you have a working swap free If all the numbers next to swap are 0 you have no swap When it was a raid system check what the current name of your raid is it can be quite different. cat /proc/mdstat Create and enable a swap mkswap /dev/sda5 swapon /dev/sda5 Correct the uuids in your fstab. Fist list your uuids. ls -la /dev/disk/by-uuid lrwxrwxrwx 1 root root 10 Apr 22 17:08 e4e0f3f6-f774-46a0-a476-ea56a00dd4cf -> ../../sda5 lrwxrwxrwx 1 root root 10 Apr 22 17:08 f757c477-2c7f-4073-a6ae-6d53625c3573 -> ../../sda1 If you do not have 2 uuid's you probably did not do the mkswap step. Edit your fstab and change the uuid's for the correct ones vi /etc/fstab Info on installing grub https://help.ubuntu.com/community/Grub2/Installing Info on creating a swap. https://www.centos.org/docs/rhel-sag-en-3/s1-swap-adding.html Info on creating mdraid. http://askubuntu.com/questions/505446/how-to-install-ubuntu-14-04-with-raid-1-using-desktop-installer burp-2.4.0/docs/baremetal-windows2008.txt000066400000000000000000000014771404341324700201270ustar00rootroot00000000000000Windows 2008 bare metal restore steps ------------------------------------- Unfortunately, I have not yet completed a Windows 2008 bare metal restore. The process should be nearly identical to the Windows 7 steps, but I get stuck at the point of making the new disk bootable. TODO: Have another go at this. Perhaps I need to create a different version of the WinPE disk. However, burp user Dave Ludlow reports success with burp 2.0.54 and Server 2008 R2 using the Windows 7 and 8 instructions: "The only catch is that I restored to a Virtual Machine and had to handle the change of hardware causing 0x7B BSODs on boot. I used a registry patch from http://www.andysblog.de/windows-mergeide-v3-0 to do it. It adds support for the basic IDE/SCSI drivers in just a couple of keystrokes during the WinPE portion of the restore." burp-2.4.0/docs/baremetal-windows7-hirens.txt000066400000000000000000000230251404341324700211630ustar00rootroot00000000000000Bare metal restore for Windows 7, using Hiren's Boot CD ------------------------------------------------------- Authors ------- Scott Brown: Original author. Graham Keeling: Edits. Herve Moser: Extra bootrec/bootsect boot repair steps. Herve reports that this procedure successfully restored Win7en, Win7fr, Win8.1, and Win Server 2012. The Windows 7 install CD was used to fix the booting for 8.1 and 2012 as both their respective medias weren't able to do the job. Prerequisites ------------- You will need a Windows 7 installation disk, and Hiren's Boot CD. Hiren's Boot CD is available here: http://www.hirensbootcd.org/download/ At the time of writing, version 15.2 was used. You could use any type of Windows PE envronment though. Prepare hard drive ------------------ Insert the Hiren's Boot CD into the client machine to be restored and boot from it. When the menu appears select: "Mini Windows Xp" Wait for it to boot. Assuming your are replacing your hard drive you need to prepare it first. Right click on the "My Computer" icon on the desktop and select "Disk Management". Your new drive should be listed in the window. Format and partition it as you want it to be set up. You may need to reboot to fully initialise the disk - it will tell you if you need to do so. Once it is initialised, go back into "Disk Management", right click the partition and mark it as 'active'. DO NOT FORGET TO MARK YOUR FIRST PARTITION AS ACTIVE - YOUR OS WILL NOT BE RECOGNISED LATER OTHERWISE... UNLESS YOU HAVE A SYSTEM RESERVED PARTITION: System Reserved Partition ------------------------- If you also backed up your System Reserved Partition you will want to restore this too. Create a 400MB Partition at the start of the disk (I've not seen one larger than ~250MB but if you don’t know what size your System Partition was, 400MB should safely cover it) then mark it as ACTIVE. IF YOU HAVE A SYSTEM RESERVED PARTITION THIS WILL BE THE PARTITION THAT NEEDS TO BE SET AS ACTIVE - NOT YOUR C DRIVE!!! Prepare Burp ------------ With the new drive prepared download and install Burp using the "restore" account you should have created on the server, if you dont have one, connect to your server and edit the /etc/burp/burp-server.conf file. (TIP - you can use putty to connect to your server - it is available inside "HBCD Menu", under "Programs" -> "Network". To get on the network in the first place, you may need to run the "Network Setup" icon on the desktop.) Look for the part in burp-server.conf that says: super_client = Change it to this and save it: super_client = restore "restore" is now the name of your restore client. Now we want to add the restore user to the clients list (this is also how you add a new client), again on the server: cd /etc/burp/clientconfdir Copy 'testclient' to make a new 'restore' client: cp testclient restore Edit 'restore' using your favourite text editor (vi or nano, etc). Change the password to what you want it to be for the restore account and save the file. Back on the "Mini Windows Xp" machine, download a burp Windows installer (for example, by starting a web browser and downloading the installer from the internet), and run through the screens. Enter the server address, 'restore' for the client name, and the password that you chose. You may need to enter your encryption password if you use one for your backups. Note - it seems that "Mini Windows Xp" requires the 32 bit version of burp. Restoring --------- Open a command prompt and type the following B: Note - Not C: - B: is the temporary drive used by "Mini Windows Xp") cd Program Files\burp\bin NOTE - Going forward - IF and only if you execute burp and it complains about certificates, you can go on the server in the /etc/burp/CA directory and run the following command (with root privilidges): rm restore.* Then edit index.txt using a text editor to remove the whole "restore" line. For example, delete this line, then save and retry: V XXXXXXXXXXXXXXX 04 unknown /CN=restore Optional step - you may view all available backups for the client you want to restore by typing: burp -a l -C Optional step - you may verify your backup to make sure its checksums are correct: burp -a v -C NOTE - you may specify a specific backup to restore or verify using the -b option. For example, this will verify the third backup: burp -a v -C -b 3 To restore the *LATEST* backup TO THE SAME DRIVE LETTERS YOU BACKED UP FROM, once you are happy you have the correct and a valid backup; run: burp -a r -C -d / (Use the optional '-b ' parameter to specify a specific backup) Restoring to a different drive letter ------------------------------------- To restore the latest backup to a different drive letter from which you backed up from, run: burp -a r -C -r ^: -d : -s 1 For example: burp -a r -C -r ^A: -d F: -s 1 (Use the optional '-b ' parameter to specify a specific backup) In this example, A: drive is the original drive letter in the backup and F: is the drive you wish to restore these files too. This is useful when using Hiren’s as it wouldn’t let me assign a newly created System Reserved Partition for restore a drive letter of A: which was the original location of the files. You can also do the following:- burp -a r -C -r ^C: -d C: -s 1 To restore only the contents from the latest backup of C: back to C: as an example. The ^ is required. Be patient ---------- Should you get a message about an unexpected command from the server and the backup fails pretty much immediately, make sure your hard drive is visible in windows explorer and you can browse to it. You did partition and format it didn't you...? The restore will take some time, usually hours, to complete. Depending on your network connection to the server and the size of the restore, it's best to leave this running overnight. There are a few ways to monitor the progress of the restore: *) On the server, you can watch in burp's live status monitor: burp -c /etc/burp/burp-server.conf -a s *) If you open up Windows Explorer from the "Mini Windows Xp" desktop you can watch as directories and files are created. Note from Graham - When restoring using burp-1.4.18, there were warnings about setting file times on junction points like 'C:/Documents and Settings'. The restore ended with an error on 'C:/' and timed out - but since this was the last entry to be attempted anyway, it turned out OK. I will try to investigate these bugs for future releases. Making the system bootable -------------------------- Now that you have restored all your files we need to repair the hard disk boot sector to get windows to load. If you're still in "Mini Windows Xp" shut it down. Find a copy of the Windows 7 install CD and put that in your drive and boot into that, being careful not to miss the press any key to boot from CD or DVD prompt. NOTE - The process for Windows 8 may be similar but i do not have the ability to test this. When it boots to the install windows screen click next. On the next screen near the bottom there will be an option to "Repair your computer" - click that. Windows will then scan for windows installations and a popup will appear saying it found problems and give you the option to "Repair and Restart" - click that and when it restarts boot back into the recovery disk once more. Again being careful not to miss the press any key to boot from CD or DVD prompt. NOTE - If the popup does not appear to say it has found problems and there is no OS listed - double check you marked the disk as ACTIVE in the first step above by booting back into "Mini Windows Xp". Once again when it boots to the install windows screen, click next. On the next screen near the bottom once again select the option to "Repair your computer". This time it should show your OS in the window and no popup will appear, make sure it is highlighted and "Use recovery tools that can help fix problems...." radio button is selected. Click "Next". When the recovery options window appears select "Startup Repair" and let it do it's thing. Click "Finish". The computer will reboot. NOTE: YOU MAY HAVE TO REPEAT THIS LAST STEP WHERE YOU USE STARTUP REPAIR SEVERAL TIMES - ~2 to 3 times on average - do not worry if it does not boot after the first or even second repair, just load the repair disk and do exactly the same thing again. I believe it only repairs the first issue it finds each time rather than every issue. You should now be able to boot into your restored OS. Note from Graham - I tested using VirtualBox, and it worked fine for me. The Hiren's boot CD is 32 bit and I was trying to restore a 64 bit machine. So, in "Mini Windows Xp", I downloaded the 32 bit burp client, and when I needed to make the system bootable, I used a 64 bit Windows 7 install disk. It would not boot the install disk until I turned 'Enable I/O APIC' on in the VirtualBox settings. Note from Herve - he reports that, when installing to a blank physical hard drive, or a new VMWare vmdk, the following two commands are necessary after three times through the Windows 7 install disk repair and reboot procedure: bootrec /fixmbr bootsect /nt60 all Then exit, reboot normally and the OS boots like a charm. Scott confirmed that this also helped him to boot on VMWare, but then he got a blue screen of death later in the boot sequence - suspect that this is probably due to massive hardware differences on the VM when the backup was made on a real hardware. The restore worked fine on the original hardware. burp-2.4.0/docs/baremetal-windows7.txt000066400000000000000000000063471404341324700177050ustar00rootroot00000000000000Windows 7 bare metal restore steps ---------------------------------- Thanks to: Wai Keong Phan, who first completed a bare metal restore, wrote up the first draft and provided help when I was stuck. Michael Da Cova, who also provided help when I was stuck. a) Install the burp client on Windows 7. b) Make a burp backup of your C: drive (plus any others you want to keep). That means that 'include = C:/' should be in the client burp.conf. You may also 'exclude = C:/pagefile.sys', because it is known to be pointless to backup that file. c) Download MS Win AIK via this link (at the time of writing): http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=5753 d) Install MS Win AIK e) Run a command prompt with administrator privileges, and execute these commands: > copype.cmd amd64 c:\winpe_amd64 > cd c:\winpe_amd64 > copy winpe.wim iso\sources\boot.wim > dism /mount-wim /wimfile:iso\sources\boot.wim /index:1 /mountdir:mount (at this point, I found it easiest to copy the whole 'C:\Program Files\Burp' directory to mount\windows, although you may just install it on the target machine via a file server later) > dism /unmount-wim /mountdir:mount /commit > oscdimg -n -betfsboot.com iso winpe_amd64.iso f) Make a bootable CD with the iso image. g) With a new or reprovisioned machine, boot the machine up with WinPE. It will go to a command prompt that looks like 'X:\windows\system32>' h) Create a new 'C:' partition by executing these commands: > select disk 0 > create partition primary > active > assign letter=C You can change 'ntfs' to something else in the following command: > format fs=ntfs quick To verify disk status: > detail disk i) At this point, you can grab the burp client installer from your file server, if you did not but it on the WinPE disk in step (e). Make sure the appropriate burp server name, burp client name and password are entered during the installer. j) Start the restore by executing the following command: > "cd C:\Program Files\Burp\bin" > burp.exe -a r -f -d / k) Shutdown the machine. l) Reboot from a Windows 7 installation disk. m) Select a language, a time and currency, and a keyboard or input method, and click next. It doesn't matter what you choose here, as long as you can continue to provide input. n) Select 'Repair your computer'. o) Follow the menus to a command prompt. You need to run diskpart again to make the 'C:' partition active, and then rebuild the boot sector. > select disk 0 > select partition primary > active > bootrec /rebuildbcd p) Reboot into the Windows 7 installation disk twice more, selecting 'Repair your computer' both times. q) Windows 7 should now boot from the disk by itself. Unresolved issues: There is something a bit weird about the entries in the top level of the file system, though. I have these odd directories: C:\$Recycle.Bin C:\Documents and Settings C:\System Volume Information C:\Temp C:\C: <- Very odd because ':' is not a valid character in Windows names. I think most of these exist on the original system, but are somehow hidden. It has also given me a new small E:\ partition. Perhaps that was something to do with the recovery via the installation disk. burp-2.4.0/docs/baremetal-windows7and8.txt000066400000000000000000000144051404341324700204520ustar00rootroot00000000000000Bare metal restore for Windows 7 and 8 -------------------------------------- Authors ------- Wai Keong Phan: First completed a bare metal restore on Windows 7 and wrote the first draft. Graham Keeling: Edits to the original draft, with help from Wai Keong Phan and Michael Da Cova. Peter Maloney: First completed a bare metal restore on Windows 8, updated the instructions and converted them to the wiki. Prepare ------- Install burp on the admin box Install one of these on an admin box ADK (Windows 8) http://www.microsoft.com/en-us/download/details.aspx?id=30652 AIK (Windows 7) http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=5753 You should use the latest one. The Windows 8 version works for win8 or older (win7, xp, etc.). Maybe another boot CD will do also. Basically all that is requires is that it has: diskpart burp (It has been suggested that a Linux CD would work. It will not, because Linux does not know how to restore Windows VSS files) The other things needed (eg. "bootrec /rebuildbcd") can be done from the Windows install CD. Create and mount the winpe boot CD on the admin box, run "Deployment and Imaging Tools Environment" with administrator rights. > copype.cmd amd64 c:\winpe_amd64 > cd c:\winpe_amd64 > copy winpe.wim media\sources\boot.wim > dism /mount-wim /wimfile:media\sources\boot.wim /index:1 /mountdir:mount Put burp on the CD (this assumes you have the burp client installed on the admin box) > cd \Program Files Back up the old burp client, so a burp restore can be built in the default location (workaround for hardcoded paths) > xcopy /E Burp BurpClient Does BurpClient specify a file name or directory name on the target (F = file, D = directory)? D Prepare the BurpRestore directory (currently named Burp) > cd Burp > del ssl_cert* > del CA\* > notepad burp.conf Make sure the burp.conf has the right user name and password that the server will accept for a restore. (burp-server.conf has a line like "super_client = restore"). Set cname/username to "restore" > bin\burp -a l This will generate the SSL keys. If there are problems here, you can go on the server in the /etc/burp/CA directory rm restore.* Edit index.txt to remove the "restore*" lines. Retry. > cd .. > move Burp "C:\winpe_amd64\mount\BurpRestore" Put the old burp client back where it goes. > move BurpClient Burp Finish the BurpRestore setup by setting the correct paths as they are when you are booted on the CD. > cd "C:\winpe_amd64\mount\BurpRestore" > notepad burp.conf s|C:/Program Files/Burp|X:/BurpRestore| > cd ..\.. Unmount and create iso. > dism /unmount-wim /mountdir:mount /commit > oscdimg -n -betfsboot.com media burprestore2_amd64.iso Burn the iso to a CD to use it in physical machines, or test it in a virtual machine. Remember this CD has the restore user password and private keys on it, so keep it secure. Backup ------ Obviously for a bare metal restore to work, you should include the whole operating system volume. Here is an example client file. include = C:/ # temp stuff exclude_regex = "[A-Z]:/Users/[^/]+/AppData/Local/Temp" exclude_regex = "[A-Z]:/Documents and Settings/[^/]+/Cookies" exclude_regex = "[A-Z]:/Documents and Settings/[^/]+/Recent" exclude_regex = "[A-Z]:/Documents and Settings/[^/]+/Local Settings/Temp" # iometer test file exclude_regex = "[A-Z]:/iobw.tst" # system stuff that is not important in a restored system exclude_regex = "[A-Z]:/RECYCLER" # swap file (Windows XP, 7, 8) exclude_regex = "[A-Z]:/pagefile.sys" # swap file?? (Windows 8) exclude_regex = "[A-Z]:/swapfile.sys" # note that we are backing up C:/System Volume Information" Restore ------- Boot the CD. Create file system. > diskpart DISKPART> select disk 0 DISKPART> create partition primary TESING: select partition 1 DISKPART> active DISKPART> assign letter=C DISKPART> format fs=ntfs quick DISKPART> detail disk DISKPART> exit Run restore. > x: > cd \BurpRestore Just a test. > bin\burp -a l -c burp.conf -C $name Run restore (the -f is optional; if it fails, try without). > bin\burp -a r -f -c burp.conf -C $name -d / Be patient... this takes a long time. > c: > cd \windows\system32 > shutdown /r Takes very long to actually shut down... put in the regular Windows CD while you wait. Run from regular Windows install CD in repair mode "command prompt". > diskpart DISKPART> select disk 0 DISKPART> select partition 1 DISKPART> active DISKPART> exit > bootrec /rebuildbcd (In Windows 8) close the command prompt by clicking X, then click "continue to your OS" or something like that, and it will reboot. When testing win8 final 64 bit, I forgot to do "bootrec /rebuildbcd" and ran repair instead, which seemed to work. For Windows 7 and 8: Run the install CD's automatic repair 2 times, then reboot to your OS. Don't run it from the same boot as the "bootrec" stuff above or it won't properly detect Windows to repair it; it will fail saying it can't fix the problem "Missing BootMGR". Test install: firefox (probably only file config, plus default browser set) burp (includes scheduled task) .... (something that relies on the registry...) I found this works well. It seemed like a perfect restore on Windows 7 and 8. Only Windows 8 preview had issues. If something goes wrong: (for when the OS does not work properly, but it boots). Try this backup plan which creates an incomplete but pretty good system (OS works, but some apps relying on the registry need reinstall). In my testing with Windows 8 release preview, the system would crash and reboot every few minutes, but worked fine with Windows 8 final. So for win8 preview, I needed to fix with these steps. The fix: Boot the installer disk. Run repair mode "refresh" (consider this the 'rebuild the registry and delete all your installed stuff' mode). Boot into OS to finish the "refresh", boot again until you have the login screen. Log in, and wait for the process to complete. Boot the BurpRestore disk again. Repeat the restore without using the "-f" option. This might be quicker than a full restore. Ideally it would only restore OS stuff (Windows, Program Files) without overwriting other large files on your disk. Reboot into OS. burp-2.4.0/docs/burp_ca.txt000066400000000000000000000154761404341324700156100ustar00rootroot00000000000000SSL - Certificate Authority and certificates -------------------------------------------- As of burp-1.3.2, the process of generating SSL certificates has been automated with the use of burp_ca. Effectively, the automation means that you can set up a new client on the server (in /etc/burp/clientconfdir/), and the first client to connect that matches that client name and password will get its certificate signed and given back. If a different, second client, happens to connect with the same client name and password, it will be rejected because it doesn't have a matching certificate. The clientconfdir passwords can now be thought of as being 'first contact' passwords, as after that, SSL will provide assurance that the peers are who they claim to be (although the server will still check the passwords too). There is the option of setting 'password_check = 0' on the server, which will disable the checking of passwords completely (but won't disable the checking of SSL certificates). Of course, you should take into account the environment that your server and clients are running in before making the choice to either disable the passwords, leave them all the same, or make them all different. Server ------ Server CA options: ca_conf = /etc/burp/CA.cnf ca_name = burpCA ca_server_name = burpserver ca_burp_ca = /usr/sbin/burp_ca Server SSL options: ssl_dhfile = /etc/burp/dhfile.pem ssl_cert_ca = /etc/burp/ssl_cert_ca.pem ssl_cert = /etc/burp/ssl_cert-server.pem ssl_key = /etc/burp/ssl_cert-server.key ssl_key_password = password (only needed if you have an encrypted certificate) If you do not want the server to automate the CA stuff, do not set ca_conf. When the server starts, the ca_conf file will be read, and the CA_DIR value from that file will be read. If the CA_DIR directory already exists, the automatic CA setup finishes here. Next, if ssl_dhfile does not exist, an appropriate dhfile will be generated. The dhfile is used in encypting the SSL connection with the Diffie-Hellman protocol. Next, the CA directory is initialised using ca_burp_ca, which should point to the burp_ca script. The name of the CA will be the value of ca_name. A key for the burp server is then generated, along with a certificate signing request to give to the CA. The CA then signs the request. The ssl_cert_ca, ssl_cert and ssl_key paths are then overwritten with symlinks to the new keys and certificates. This concludes the CA setup on the server. Client ------ Client CA options: ca_burp_ca=/usr/sbin/burp_ca ca_csr_dir=/etc/burp/CA Client SSL options: ssl_cert_ca = /etc/burp/ssl_cert_ca.pem ssl_cert = /etc/burp/ssl_cert-client.pem ssl_key = /etc/burp/ssl_cert-client.key ssl_key_password = password (only needed if you have an encrypted certificate) ssl_peer_cn = burpserver If you do not want the client to automate the CA stuff, do not set ca_burp_ca. Clients as of 1.3.2 will not include the old default certificates, so if you want to install new clients connecting to a server that is using the old certificates, you will also have to copy the old certificates onto the client. When the client runs (for example, with 'burp -a b'), it will try to connect to the server using the burp client name (cname) and password. If you haven't got the client name and password right, everything stops here. If the path to ssl_key exists, the client will not try to generate a key and certificate signing request - it assumes that this has already been done. Otherwise, a client key is generated at the path of ssl_key, and a certificate request is generated in the ca_csr_dir directory, named after the client name. This is copied to CA_DIR on the server, unless the server already has a file named the same, or a signed certificate with the same name. If either already exists, the server will reject the request. If the server accepts the request, it will generate a signed certificate and send it back to the client, along with the CA certificate. The ssl_cert_ca and ssl_cert paths will be overwritten on the client, and burp.conf will be rewritten with ssl_peer_cn set correctly. The connection between server and client is dropped and the client will reconnect using the new certificates to start the real work (for example, a backup). In previous versions of burp, you needed to set ssl_peer_cn in the client's clientconfdir file on the server. You don't need to set this any more, because the server will default to using the client name instead. This concludes the certificate setup on the client. ----- The following is the (slightly tweaked) original documentation for burp_ca. It might still be of use in order to help understand the process, or if it is necessary to do it by hand. Note that there is a burp_ca.bat script if you are generating certificate signing requests on Windows. Setup the server: 1) generate Diffie-Hellman Parameters burp_ca --dhfile /etc/burp/dhfile.pem 2) Initialise CA burp_ca --init --ca myCA This creates /etc/burp/CA, generate private key for CA and self signed certificates. 3) Generate server key and cert signing request burp_ca --key --request --name myServer 4) Sign request burp_ca --sign --ca myCA --name myServer --batch 5) Link or copy cert and key to /etc/burp ln -s CA/CA_myCA.crt /etc/burp/ssl_cert_ca.pem ln -s CA/myServer.crt /etc/burp/ssl_cert-server.pem ln -s CA/myServer.key /etc/burp/ssl_cert-server.key (use the ssl_cert and ssl_key options in burp-server.conf !) Setup the client (the easy way - insecure) 1) Generate client key and cert __on the server__ burp_ca --name myClient --ca myCA --key --request --sign --batch 2) copy key and certs from server to client server:/etc/burp/ssl_cert_ca.pem -> client:/etc/burp/ssl_cert_ca.pem server:/etc/burp/CA/myClient.crt -> client:/etc/burp/ssl_cert-client.pem server:/etc/burp/CA/myClient.key -> client:/etc/burp/ssl_cert-client.key chmod 600 /etc/burp/ssl_cert-client.key (use the ssl_cert and ssl_key options in burp.conf !) Setup the client (the usual way) 1) on the client you dont need a CA, so just mkdir /etc/burp/CA chmod 700 /etc/burp/CA 2) Generate client key and cert signing request burp_ca --key --request --name myClient (on Windows: burp_ca.bat --key --keypath \ --request --requestpath --name myClient) 3) copy the request from client to the server client:/etc/burp/CA/myClient.csr -> server:/etc/burp/CA/myClient.csr 4) sign the request __on the server__ burp_ca --name myClient --ca myCA --sign --batch 5) copy the certs back to the client server:/etc/burp/CA/CA_myCA.crt -> client:/etc/burp/CA/CA_myCA.crt server:/etc/burp/CA/myClient.crt -> client:/etc/burp/CA/myClient.crt 6) link or copy the files ln -s CA/CA_myCA.crt /etc/burp/ssl_cert_ca.pem ln -s CA/myClient.crt /etc/burp/ssl_cert-client.pem ln -s CA/myClient.key /etc/burp/ssl_cert-client.key (use the ssl_cert and ssl_key options in burp.conf !) burp-2.4.0/docs/debug.txt000066400000000000000000000027121404341324700152500ustar00rootroot00000000000000Debugging a crash ----------------- In the unlikely event that you have a problem with burp crashing, I would like to be able to fix it. This is easier if the crash is reproducible. That is, if you can run the client on the command line and watch it crash on demand. You then need to run it in a debugger. I use gdb. There are Windows versions of gdb too. How to get gdb for Windows: Go to the following URL. http://sourceforge.net/projects/mingw-w64/files/External%20binary%20packages%20%28Win64%20hosted%29/gdb/ If you are using 32 bit Windows, download one of the i686- variants. If you are using 64 bit Windows, download one of the x86_64- variants. Unpack the download to C:\ To run gdb on Windows: cd "C:\Program Files\Burp\bin" Change mingw32 to mingw64 in the following if you are using the 64 bit version. C:\mingw32\bin\gdb.exe --args burp.exe (append the arguments that make it crash) Then enter "run" and the program will start running. When it crashes, enter "bt", and it will hopefully give you useful information for tracking down the code that it ran before it crashed. Send the output to the mailing list. If you are not running Windows, running gdb is exactly the same except that the paths to gdb and burp are simpler. If you have a problem with a burp server crashing, then the procedure is the same, except that I always find it easier to run the server in 'no-fork' and 'foreground' mode. You can do this with '-F -n' appended to the server arguments. burp-2.4.0/docs/restoring.txt000066400000000000000000000072361404341324700162040ustar00rootroot00000000000000Restoring --------- Here are some examples of how you might like to think about restoring. Some extraneous output has been snipped, for clarity. On the client side, list your available backups: $ burp -a l Backup: 0000001 2018-01-26 10:47:26 +0000 (deletable) Backup: 0000002 2018-01-26 10:48:13 +0000 Backup: 0000003 2018-01-27 01:30:42 +0000 Backup: 0000004 2018-01-27 01:30:55 +0000 There are four backups available. Let's look at the contents of backup 3: $ burp -a l -b 3 Backup: 0000003 2018-01-27 01:30:42 +0000 /home/graham/testdir/mydir1 /home/graham/testdir/mydir1/B17689730947800001.pdf /home/graham/testdir/mydir1/Invoice INV-8453.pdf /home/graham/testdir/mydir1/On The Run 2017-05-13.mp3 /home/graham/testdir/mydir1/On the run 2016-11-26.mp3 /home/graham/testdir/mydir1/Red Pill 2016-11-26.mp3 /home/graham/testdir/mydir1/cswdcd07.mp3 /home/graham/testdir/mydir1/cswdcd12.mp3 /home/graham/testdir/mydir2 /home/graham/testdir/mydir2/1195.pdf /home/graham/testdir/mydir2/1199.txt /home/graham/testdir/mydir2/1200.x To restore everything to their original location, simply change the 'l' in the previous command to an 'r'. For me, this produces a lot of warnings because the original files still exist in their original locations. '-d' is also required, because you need to tell burp the target directory for the restore: $ burp -a r -b 3 -d / 2018-01-27 01:33:25 +0000: burp[5917] WARNING: Path exists: /home/graham/testdir/mydir1/B17689730947800001.pdf 2018-01-27 01:33:25 +0000: burp[5917] WARNING: Path exists: /home/graham/testdir/mydir1/Invoice INV-8453.pdf ...etc... Instead, I can add '-f' to force overwriting any existing files, and I no longer get any warnings: $ burp -a r -b 3 -d / -f ... OK, so maybe you want to restore the files to a different location. Just add your path to the '-d' option. Burp will create the directory if it doesn't exist: $ burp -a r -b 3 -d /tmp/restore ... After running the above, and looking in /tmp/restore, you will find that the whole original directory structure is preserved. For example: $ ls -l /tmp/restore/home/graham/testdir/mydir1/cswdcd07.mp3 -rw-r--r-- 1 graham graham 36402567 Jan 18 11:05 /tmp/restore/home/graham/testdir/mydir1/cswdcd07.mp3 So, maybe you want to strip off some of the original leading directories with the '-s' option. We will strip off '/home/graham/': $ burp -a r -b 3 -d /tmp/restore -s 3 ... $ ls -l /tmp/restore/testdir/mydir1/cswdcd07.mp3 -rw-r--r-- 1 graham graham 36402567 Jan 18 11:05 /tmp/restore/testdir/mydir1/cswdcd07.mp3 Now, perhaps you want to restore only the things in 'mydir2'. You can use a regular expression to match the paths that you want. I recommend doing a list first, to see that your expression works as expected: $ burp -a l -b 3 -r mydir2 Backup: 0000003 2018-01-27 01:30:42 +0000 With regex: mydir2 /home/graham/testdir/mydir2 /home/graham/testdir/mydir2/1195.pdf /home/graham/testdir/mydir2/1199.txt /home/graham/testdir/mydir2/1200.x That looks fine to me, so just change the 'l' to an 'r' and add the target directory. $ burp -a r -b 3 -r mydir2 -d / ... Of course, that will restore to the original location again, so you can adjust the '-d' option and add the '-s' option again if you like: $ burp -a r -b 3 -r mydir2 -d /tmp/restore -s 3 ... What if you just wanted to restore a single file? Well, just use a regular expression that matches your single file. $ burp -a l -b 3 -r /home/graham/testdir/mydir2/1195.pdf -d / ... $ burp -a r -b 3 -r /home/graham/testdir/mydir2/1195.pdf -d / ... Since I only have one path that contains 1195.pdf, I could have used a much shorter regular expression. For example: $ burp -a l -b 3 -r 1195.pdf -d / ... $ burp -a r -b 3 -r 1195.pdf -d / ... burp-2.4.0/docs/retention.txt000066400000000000000000000122721404341324700161730ustar00rootroot00000000000000Retention --------- Each time burp creates a backup, it will be given a number that is one greater than the previous successful backup. If there is no previous backup, it will start with the number 1. It will also be given a timestamp. So, after five days, your backups of a client in /var/spool/burp/testclient/ might look like this: 0000001 2014-05-19 19:00:04 0000002 2014-05-20 19:00:02 0000003 2014-05-21 19:00:05 0000004 2014-05-22 19:00:05 0000005 2014-05-23 19:00:03 It is important to note that burp's retention works on the backup numbers, not the timestamp. Single 'keep' setting --------------------- The retention is configured via the 'keep' server side configuration option, which can be overridden per-client in the clientconfdir files. At the end of each successful backup, the server will run its retention code and delete old backups. It will delete any backup that the 'keep' settings and the 'deletable' criteria allow it to. A backup is 'deletable' if it is the oldest backup, or comes immediately after a 'hardlinked' backup. This is explained further in the section about multiple 'keep' values, below. For example, the burp default is 'keep = 7'. Intuitively, this means 'keep seven backups'. After nine backups, your backup list will look like this: 0000003 2014-05-21 19:00:04 0000004 2014-05-22 19:00:02 0000005 2014-05-23 19:00:05 0000006 2014-05-24 19:00:05 0000007 2014-05-25 19:00:03 0000008 2014-05-26 19:00:01 0000009 2014-05-27 19:00:04 At the end of backup 8, backup 1 was deleted. At the end of backup 9, backup 2 was deleted. A single 'keep' setting is just a special case of the algorithm that burp uses when you have multiple 'keep' settings. Multiple 'keep' settings ------------------------ You may want configure multiple 'keep' values. This will make burp prune the the backups so that as you go further back in time, there are fewer of them. In effect, an exponential decay. For example, you may set: keep = 7 keep = 4 keep = 12 This guarantees to keep 7 backups in a row, plus 4 on multiples of 7, plus 12 on multiples of 4*7. At the end of a successful backup, burp takes the 'keep' settings and the list of numbered backups. It re-indexes the list so that the most recent backup is at postition 1. It splits the re-indexed list into regions based on the 'keep' settings (eg, boundaries at 1..7, 7*1, 7*2, 7*3, 7*4, 7*4*1, 7*4*2, ..., 7*4*12. It then attempts to reduce the number of backups in each region to 1 (or 0 if the region is beyond the end of the 'keep' settings). It can only delete backups that are 'deletable'. The very oldest backup is deletable. A backup that comes immediately after a 'hardlinked' backup is deletable. Burp makes all backups 'hardlinked' if they are made while 'hardlinked_archive=1' is set. Otherwise, backups that are created on the boundaries defined by the first 'keep' setting are made 'hardlinked' (ie, 1, 7, 14, etc). This is an example set of backups on a server on which I set 'keep' values of 7 and 4: 0000260 2012-04-03 20:07:02 0000267 2012-04-22 09:14:44 0000274 2012-05-06 19:06:43 0000281 2012-05-13 19:27:03 0000283 2012-05-15 19:47:03 0000284 2012-05-16 19:27:03 0000285 2012-05-17 19:27:03 0000286 2012-05-18 19:47:04 0000287 2012-05-19 19:27:04 0000288 2012-05-20 19:27:03 0000289 2012-05-21 19:27:03 You can see the gaps of 7 between backups 260,267, 274 and 281. Backups 283 to 289 are in the range of 1 to 7. Changing 'keep' settings ------------------------ A question that sometimes comes up is: What happens if I change my 'keep' settings? On the next backup of a client, burp will do it's reduction algorithm based on the new settings for that client. If it has more than 1 backup in its new regions, it will try to reduce them to 1 if it is able to. In the following examples, somebody initially set... keep = 14 keep = 4 keep = 2 ...and they then change them to: keep = 14 keep = 7 Example A: In this case, the 'range' of backups has been reduced. 14*4*2 = 112 7*4*2 = 56 Therefore, if there were 72 backups, the new settings will result in any left in the range 57-112 (the oldest ones) being deleted. Since the regions are now smaller, some of the regions will now have 0 backups. Obviously nothing will happen to those regions. Other regions will still have 1. Nothing will happen to those. If a multiple that expanded the regions were chosen, there may be a region with more than 1 backup, which then might be reduced to 1 (depending on 'deletable' criteria). Example B: If there were backups 10 9 8 7 6 5 4 3 2 1, and hardlinked_archive=1 were set the whole time, after next backup, there would be: 11 10 9 8 7 6 5 1 If hardlinked_archive=0 were set the whole time with 14*4*2, the result would be the same: 11 10 9 8 7 6 5 1 But when carrying on, there will be a small difference. The hardlinked_archive=1 example would become: 21 20 19 18 17 16 15 14 7 1 The other would become: 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 1 Burp would like to delete 8 to 13 based on the current 'keep' settings, but it can't because 7 to 13 were not hardlinked. However, you can still delete 7, then 8, 9, 10, 11, 12, 13 in that order by hand if you like (with the client 'delete' option), because each one successively does not have any backups that depend upon it. burp-2.4.0/docs/security-models.txt000066400000000000000000000032551404341324700173150ustar00rootroot00000000000000Security models --------------- (Note that communications between burp clients and servers always use SSL, so that the network connections are encrypted. The SSL certificates give you some assurance that you are speaking to the correct peer. The following is mostly talking about the data that ends up written on disks.) Burp is designed with two different security models in mind. Untrustworthy server -------------------- You are a client, and you don't trust the server. * You must encrypt your data, using the client-side data encryption option, which is 'encryption_password=[password]'. If you lose the password, you will not be able to get your data back. Note that path names are not encrypted, and turning on client encryption means that you cannot do network librsync deltas. (If you do not encrypt your data, be aware that it can be read and copied by anybody that can read the server filesystem). * You must not set the autoupgrade options in the client burp.conf. * You must set 'server_can_restore=0' in the client burp.conf to prevent server initiated restores. * You must set 'server_can_override_includes=0' in the client burp.conf to prevent the server from being able to backup files you do not want it to access. Untrustworthy users ------------------- You are a site administrator with a burp server and several clients, you don't trust your users. You have the ability to turn off various client abilities by editing the server configuration: * client_can_delete=0 * client_can_diff=0 * client_can_force_backup=0 * client_can_list=0 * client_can_monitor=0 * client_can_restore=0 * client_can_verify=0 burp-2.4.0/docs/server-basics.txt000066400000000000000000000011411404341324700167250ustar00rootroot00000000000000Server basics ------------- The burp server is designed so that nearly all options can be changed without requiring a reload or a restart. It manages this by forking a child for each client that connects, and then having the child process re-read the main server config file, before reading the client-specific config file. In a normal setup, the server config file will be /etc/burp/burp-server.conf, and the client-specific config files will be in the /etc/burp/clientconfdir directory. Many global options set in the burp-server.conf can be overridden per-client via the /etc/burp/clientconfdir files. burp-2.4.0/docs/shuffling.txt000066400000000000000000000124541404341324700161530ustar00rootroot00000000000000Shuffling, hardlinked_archive, librsync --------------------------------------- The following was originally posted on the mailing list, but possibly helpful to people with more questions about the shuffling pase, librsync=[0|1], and hardlinked_archive=[0|1]. You can set both those options globally in the server burp.conf, or for individual clients in the server clientconfdir files. On Thu, May 10, 2012 at 07:29:09PM +0200, Davide Bozzelli wrote: > Just a simple question: what is the purpose of the shuffle operation at the > end of backup ? On Thu, May 10, 2012 at 9:54 PM, Graham Keeling wrote: > Once all the files and bits of files have been transferred to the server, > it needs to assemble them into the final backup. > By default, this includes hardlinking files that haven't changed since the > immediately previous backup into place, patching the ones that have changed > with the new deltas, generating reverse deltas to be able to recreate the > previous files that change, and deleting the files that changed from the > previous backup. On Thu, May 10, 2012 at 07:29:09PM +0200, Davide Bozzelli wrote: > I see that a lot of time is taken by it (even much more that the time taken > for transfer file). > > Is there a way to speedup this process ? On Thu, May 10, 2012 at 9:54 PM, Graham Keeling wrote: > Yes, you can stop it from generating the reverse deltas and deleting the > original files by setting hardlinked_archive=1, which means that a complete > copy of every version of each file will be kept. This also has the benefit > of speeding up restores for older backups, because no reverse deltas need > to be applied, but it has the disadvantage of taking up more disk space. On Tue, May 15, 2012 at 09:20:50PM +0200, Davide Bozzelli wrote: > Does it means that on every backup run ALL the files need to be trasferred > ? or onlu the changed ones ? On Tue, May 15, 2012 at 10:35:10PM +0100, Graham Keeling wrote: > Only the changed ones. On Wed, May 16, 2012 at 11:12:50AM +1200, Jason Haar wrote: > Just to clarify this for myself and others, can you confirm burp works > like this > > client -> I have one file that is different -> server > server -> server copies old version (from last backup) into temp dir > client -> sends whole file using rsync tricks [compression, deltas] -> > server pointing at temp dir > > server then compares this temp file against last backup version, creates > delta, deletes temp file, creates hardlink to old version and documents > that file == last backup + delta. This is the "shuffling" phase. Does > that sum it up? No. In the source, there is a file called 'backup_phases.txt' that explains it: ------ backup_phase1_client: Scan the client filesystem and send stats to the server. backup_phase1_server: Receive the stats from the client. backup_phase2_server: Request and receive changes from the client and create an unchanged list and a changed list. backup_phase2_client: Send the changes that the server requests. The work of the client is now finished. backup_phase3_server: Generate the new manifest list for the backup out of the unchanged list and the changed list. backup_phase4_server: Finish off the backup by jiggling the received data around and putting everything in the correct place. Need to generate reverse deltas in order to save space for the previous backup (unless hardlinked_archive is turned on). ------ (phase4 is the shuffling bit) > One last question about "hardlinked_archive=1". This means for each > backup, if the file hasn't changed since the last backup, burp hardlinks > the old file to the new? Yes, and it doesn't generate reverse deltas for the files that changed and it doesn't delete the original files. > If "hardlinked_archive=0", then burp... does what? It generates reverse deltas for the files that changed and it deletes the original files. > The documentation says that "hardlinked_archive=1" uses more > diskspace - but I can't understand how. Surely "hardlinked_archive=0" > would mean burp is keeping a zero-sized delta plus metadata referring to > the original backup - that must take up about as much diskspace as a > hardlink? No, for at least three reasons: a) If a file didn't change and 'hardlinked_archive = 0', burp will just delete the original and keep the new version, and not keep a delta at all (zero-sized deltas actually have a few bytes in them). b) A complete file plus a reverse delta takes up less space than two complete files. Imagine you have a 1MB file, back it up, then add a byte to it, then back it up again. With 'hardlinked_archive = 1', the server stores both complete versions of the files. With 'hardlinked_archive = 0', the server stores the most recent version, plus a reverse delta (which contains a small block that has the effect of removing a byte from the recent version) so that it can re-generate the older version if it has to. c) Your filesystem doesn't have unlimited inodes. A hardlink uses an inode. You can also set librsync=0, which turns off librsync so that, if a file has changed, the whole file is transferred from the client. This means that deltas don't have to be applied to the previously backed up files. So, if the speed of your network beats the speed of being able to apply deltas, you might want to set librsync=0. See the man page for more information on these options. burp-2.4.0/docs/status-monitor.txt000066400000000000000000000471051404341324700171770ustar00rootroot00000000000000WORK IN PROGRESS NOTE: While status monitor functionality also exists in burp-1, this documentation describes the functionality as it exists in burp-2, unless otherwise stated. The burp-1 feature has significantly less functionality than the burp-2. Status port ----------- On the server side, there is the ability to run a status monitor. In the most basic sense, it is a port on which the burp server listens to requests, and to which it outputs information. Burp-2: ----------------------------------------------- | server (burp -c /etc/burp/burp-server.conf) | ----------------------------------------------- | SSL/JSON port 4972 | ---------------------- | client (burp -a m) | ---------------------- | stdin/stdout | ------------------------------------- | status monitor client (burp -a s) | ------------------------------------- Burp-1: ----------------------------------------------- | server (burp -c /etc/burp/burp-server.conf) | ----------------------------------------------- | Plain text port 4972 | ------------------------------------------------------------------- | status monitor client (burp -c /etc/burp/burp-server.conf -a s) | ------------------------------------------------------------------- Security -------- CAUTION: The burp-1 status port operates in plain text only and allows you to see information on any client. It uses a server configuration file rather than a client configuration file, and only allows connections locally on the server. In burp-2, the status port connection is secured via SSL. The client that you are connecting from will be able to see its own details, and the details of any other client that you are a 'super_client' or 'restore_client' of. Since it uses a client configuration file, you can run status monitor clients remotely. Address and port number ----------------------- The port that the server runs on is determined by the 'status_port' option in /etc/burp/burp-server.conf. By default, this is 4972. No 'status_port' option means that the burp server will not listen at all. Burp-1 will only listen for local connections. That is, 127.0.0.1 for IPv4, or ::1 for IPv6, depending on what was chosen/available at build time (to force IPv4, use the --disable-ipv6 configure option). Burp-2 will listen for local connections by default. You can set the 'status_address' option in /etc/burp/burp-server.conf to explicitly choose the listen address. Once you start the burp server, you may check the status address and port by running a command like this: netstat -plant | grep 4972 Raw connection -------------- You may connect to the status port using a burp client with the syntax 'burp -a m'. This will let you see the raw JSON output, and let you input commands via standard input. If you have not connected from this client before, burp will attempt to set up SSL certificates in the usual way first. You may need to configure the 'server' and 'status_port' options in your client burp.conf before it will connect. Built-in status monitor clients ------------------------------- These fork a 'burp -a m' child process and then communicate with it using stdin/stdout. If you have connection problems, it can be useful to try running 'burp -a m' manually. Ncurses: This requires that burp be compiled with ncurses enabled. Once the burp server is running, the ncurses status monitor client can be run with a command like this: burp -a s You should see a list of clients along with their current status and the time of their most recent backup. The cursor can be moved with the arrow keys. Hitting enter or the right arrow key will give you a list of backups for the selected client. Choosing a backup will give you the options to browse the backup, view logs, or look at live statistics. Snapshot: Running the following command will print a snapshot of the status monitor status screen to stdout: burp -a S One application is that a script can run this and email an administrator the output on a cron job. This doesn't require ncurses support. There are additional options that can be given, listed below. -C [client] Limit the output to a single client. #WORK IN PROGRESS - These options are not yet working in burp-2. # -b [number] # Show listable files in a particular backup (requires -C). # # -z [file] # Dump a particular log file in a backup (requires -C and -b). # # -d [path] # Show a particular path in a backup (requires -C and -b). Other status monitor clients ---------------------------- An example of another status monitor client is burp-ui, which presents a nice web interface. Request format -------------- c: - Request a summary of all clients. c: - Request the backup list of a particular client. c::b: - Request details of a particular backup. c::b::p: - Request the contents of a directory from a backup. c::b::l: - Request the contents of a log file from a backup. The available log types for each backup are listed with the backups themselves. j:pretty-print-on - Turn JSON response pretty printing on (the default). j:pretty-print-off - Turn JSON response pretty printing off. j:response-markers-on - Turn JSON response start and end markers on. j:response-markers-off - Turn JSON response start and end markers off (the default). j:peer_version= - Override the peer version to get alternative JSON formats. Response format --------------- The response is JSON formatted. Example 1. There are two idle clients. 'testclient' has had three backups and 'laptop' has had none. The output is indicating the most recent backup of each client. Request: "c:" Response: { "clients": [ { "name": "testclient", "run_status": "idle", "protocol": 1, "backups": [ { "number": 3, "timestamp": 1421271819, "flags": [ "current", "manifest" ], "logs": { "list": [ "backup", "backup_stats" ] } } ] }, { "name": "laptop", "run_status": "idle", "backups": [ ] } ] } Example 2. Requesting the backup list of 'testclient', which is idle. The 'flags' section indicates properties of the particular backup that it appears in - whether it is deletable, the current backup, and so on. The 'logs' section indicates the log files that are able to be returned by the status monitor. Request: "c:testclient" Response: { "clients": [ { "name": "testclient", "run_status": "idle", "protocol": 1, "backups": [ { "number": 3, "timestamp": 1421271819, "flags": [ "current", "manifest" ], "logs": { "list": [ "backup", "backup_stats" ] } }, { "number": 2, "timestamp": 1421271581, "flags": [ "manifest" ], "logs": { "list": [ "backup", "backup_stats" ] } }, { "number": 1, "timestamp": 1421199720, "flags": [ "deletable", "manifest" ], "logs": { "list": [ "backup", "backup_stats" ] } } ] } ] } Example 3. Requesting an individual backup. Request: "c:testclient:b:2" Response: { "clients": [ { "name": "testclient", "run_status": "idle", "protocol": 1, "backups": [ { "number": 2, "timestamp": 1421271581, "flags": [ "manifest" ], "logs": { "list": [ "backup", "backup_stats" ] } } ] } ] } Example 4. Requesting the contents of a directory in a backup. With large backups, each request might take some time because the manifest file needs to parsed on the server. In order to speed this up, there is an option called 'monitor_browse_cache' on the server side. Turning it on makes the server cache the results of the parsing in memory, thereby allowing it to respond faster to subsequent queries about the same backup on the same connection. The memory is freed on querying the contents of a different backup or closing the connection. Request: "c:testclient:b:2:p:/usr/lib/xul-ext/webaccounts/content" Response: { "clients": [ { "name": "testclient", "run_status": "idle", "protocol": 1, "backups": [ { "number": 2, "timestamp": 1421271581, "flags": [ "manifest" ], "logs": { "list": [ "backup", "backup_stats" ] }, "browse": { "directory": "/usr/lib/xul-ext/webaccounts/content", "entries": [ { "name": "browser.js", "dev": 2050, "ino": 1837644, "mode": 33188, "nlink": 1, "uid": 0, "gid": 0, "rdev": 0, "size": 2166, "blksize": 4096, "blocks": 8, "atime": 1420519821, "ctime": 1407972494, "mtime": 1390969550 }, { "name": "browser.xul", "dev": 2050, "ino": 1837645, "mode": 33188, "nlink": 1, "uid": 0, "gid": 0, "rdev": 0, "size": 261, "blksize": 4096, "blocks": 8, "atime": 1420519821, "ctime": 1407972494, "mtime": 1373030720 } ] } } ] } ] } Example 5. Requesting the log of a particular backup. Request: "c:testclient:b:2:l:backup" Response: { "clients": [ { "name": "testclient", "run_status": "idle", "protocol": 1, "backups": [ { "number": 2, "timestamp": 1421271581, "flags": [ "manifest" ], "logs": { "list": [ "backup", "backup_stats" ], "backup": [ "2015-01-15 07:39:41: burp[7398] Client version: 2.0.11", "2015-01-15 07:39:41: burp[7398] Protocol: 1", "2015-01-15 07:39:41: burp[7398] Begin phase1 (file system scan)", "2015-01-15 07:39:42: burp[7398] End phase1 (file system scan)", "2015-01-15 07:39:42: burp[7398] Begin phase2 (receive file data)", "2015-01-15 07:39:47: burp[7398] End phase2 (receive file data)", "2015-01-15 07:39:47: burp[7398] Backup ending - disconnect from client.", "2015-01-15 07:39:47: burp[7398] Begin phase3 (merge manifests)", "2015-01-15 07:39:47: burp[7398] End phase3 (merge manifests)", "2015-01-15 07:39:47: burp[7398] Begin phase4 (shuffle files)", "2015-01-15 07:39:47: burp[7398] Previous backup is not a hardlinked_archive", "2015-01-15 07:39:47: burp[7398] will generate reverse deltas", "2015-01-15 07:39:47: burp[7398] Duplicating current backup.", "2015-01-15 07:39:48: burp[7398] New backup is not a hardlinked_archive", "2015-01-15 07:39:48: burp[7398] Doing the atomic data jiggle...", "2015-01-15 07:39:59: burp[7398] End phase4 (shuffle files)", "--------------------------------------------------------------------------------", "Start time: 2015-01-15 07:39:41", " End time: 2015-01-15 07:39:59", "Time taken: 00:18", " New Changed Unchanged Deleted Total | Scanned", " ------------------------------------------------------------", " Files: 1554 0 19153 0 20707 | 20707", " Meta data: 2 0 0 0 2 | 2", " Directories: 1 0 1976 0 1977 | 1977", " Hard links: 0 0 14 0 14 | 14", " Soft links: 380 0 1579 0 1959 | 1959", " Grand total: 1937 0 22722 0 24659 | 24659", " ------------------------------------------------------------", "", " Warnings: 0", "", " Bytes estimated: 2385159143 (2.22 GB)", " Bytes in backup: 2384195671 (2.22 GB)", " Bytes received: 227824877 (217.27 MB)", " Bytes sent: 0", "--------------------------------------------------------------------------------", "2015-01-15 07:39:59: burp[7398] Backup completed." ] } } ] } ] } Example 6. Listing all clients whilst one of them is running. Request: "c:" Response: { "clients": [ { "name": "testclient", "run_status": "running", "protocol": 1, "children": [ { "pid": 3943, "backup": 4, "action": "backup" "phase": "backup", "counters": [ { "name": "files", "type": "f", "count": 0, "changed": 0, "same": 0, "deleted": 0, "scanned": 16 }, { "name": "directories", "type": "d", "count": 0, "changed": 0, "same": 0, "deleted": 0, "scanned": 1 }, { "name": "grand_total", "type": "Z", "count": 0, "changed": 0, "same": 0, "deleted": 0, "scanned": 17 }, { "name": "bytes_estimated", "type": "G", "count": 420151988 }, { "name": "time_start", "type": "b", "count": 1495345509 }, { "name": "time_end", "type": "E", "count": 1495345510 } ] } ], "backups": [ { "number": 3, "timestamp": 1421271819, "flags": [ "current", "manifest" ], "logs": { "list": [ "backup", "backup_stats" ] } }, { "number": 4, "timestamp": 1421272639, "flags": [ "working" ], "logs": { "list": [ "backup" ] } } ] }, { "name": "laptop", "run_status": "idle", "backups": [ ] } ] } The "children" section lists each of the child processes that the server is running for a client. The "action" section may be one of: "backup", "list", "restore", "verify", "delete", or "diff". The "phase" section breaks that down slightly further to one of: "scanning", "backup", "merging", "shuffling", "listing", "restoring", "verifying", "deleting", or "diffing" Example 7. Problem with request. Request: "c:blah" Response: { "warning": "Could not find client" } burp-2.4.0/docs/tests.txt000066400000000000000000000026111404341324700153220ustar00rootroot00000000000000This section will provide code coverage reports from the burp unit tests, starting from version 2.0.26. There are two sets of tests in burp now: 1) An 'end-to-end' system that basically runs a server, and does backups/verifies/restores, and changes config settings between each one. These are time consuming tests, and they are run from scripts in the 'test' directory of the burp source. These tests do not show up in the code coverage report. 2) Unit tests that exercise small parts of the code hard. If you wish to run them yourself, change to the top level of the burp source directory and do this: make clean ./configure make check If you have lcov installed and you wish to generate a code coverage report when the tests pass, you can do so like this: make clean ./configure --with-coverage make coverage At first glance, the code coverage report will look like not much is getting tested, but that is not true, because you will not see (1) showing up in it. The intention is that more unit tests will be added as time goes on, so the coverage should climb. The code coverage reports can be found here. Although both sets of tests are run automatically when I commit code to burp, they absolutely do not cover all conditions and all environments. Please remember that burp comes with absolutely no warranty. You are responsible for testing it and ensuring that it works for you. burp-2.4.0/docs/timer_script.txt000066400000000000000000000070321404341324700166660ustar00rootroot00000000000000Timed backups ------------- The usual case is that the client is running the 'burp -a t' command regularly (via cron or the Windows scheduler) at a rate of three times an hour. The server will then decide whether or not it needs a backup of the client at that moment, and will accept or reject the request as appropriate. This means that an interrupted backup will be reattempted promptly. The decision as to whether it is time to backup is made by the 'timer script', as described below. Timer script ------------ The location of the timer script is defined in /etc/burp/burp-server.conf: timer_script = /etc/burp/timer_script It may be overridden per-client in individual /etc/burp/clientconfdir files. The server will treat a return code of 0 as meaning that it is time to backup. Any other return code means that it is not time to backup. Note: As of 2.1.22, the timer script has been internalised as part of the main C code. You may still use an external timer script though. To use the internal version, don't define a timer_script option. Both versions use the same timer_arg parameters described below. Timer script arguments ---------------------- The server reserves the first five arguments for itself. These are: 1) The name of the client. 2) Path to the latest backup. 3) Path to the backup storage directory for the client. 4) Reserved for future use. 5) Reserved for future use. Arguments after these five can be controlled by the user via the server configuration files. The defaults are found in /etc/burp/burp-server.conf and can be overridden per-client in individual /etc/burp/clientconfdir files. These are the defaults from /etc/burp/burp-server.conf: timer_arg = 20h timer_arg = Mon,Tue,Wed,Thu,Fri,00,01,02,03,04,05,19,20,21,22,23 timer_arg = Sat,Sun,00,01,02,03,04,05,06,07,08,17,18,19,20,21,22,23 They are passed to the timer script in the order in which they are found in the configuration file. Any number of timer_arg settings in a clientconfdir file will overrides all of the timer_arg settings in burp-server.conf. Interval -------- The default timer_script treats the first timer_arg as the minimum interval since the last successful backup. The example above is setting 20 hours. If there was no previous successful backup, it is time to backup now (dependent upon the time band arguments below). The following are valid time unit specifiers for the interval: s (seconds) m (minutes) h (hours) d (days) w (weeks) n (months) Time bands ---------- The default timer script treats every argument after the interval as a time band. When it runs, it will create a glob expression for the current day and hour, using the command: 'LANG=C LC_TIME=C date +"*%a*%H*"' For example, this may return '*Mon*10*'. If this glob expression matches one of the time band arguments, the timer script decides that it is time to backup. Customisation ------------- You may customise your timer script so that it makes the decision to back up based on whatever you like. An example is in the default timer script, where it will decide that it is time to back up based on the presence of a file called 'backup' within the client's storage directory. Forcing backups --------------- A client may attempt to force a backup at any time with 'burp -a b'. Some cases where attempting to force a backup will not work: *) The server is configured with 'client_can_force_backup=0'. *) The server is already dealing with a connection from the same client. *) The server is already dealing with a connection from a super_client or restore_client that is using the same client. burp-2.4.0/docs/working_dir.txt000066400000000000000000000062011404341324700164750ustar00rootroot00000000000000Working directory and recovery methods -------------------------------------- Whilst a backup is taking place, the server will create a directory in the storage area for the client with a number and a timestamp. For example: /var/spool/burp//0000027 2015-04-12 01:24:29 During backup phases 1 (file system scan) and 2 (send actual data), there will be a symlink pointing to the directory called 'working': /var/spool/burp//working -> 0000027 2015-04-12 01:24:29 During backup phases 3 (manifest generation) and 4 (shuffling), the symlink will be renamed to 'finishing': /var/spool/burp//finishing -> 0000027 2015-04-12 01:24:29 When the backup is complete, the symlink will be renamed to 'current': /var/spool/burp//current -> 0000027 2015-04-12 01:24:29 If there is some error that happens during the backup and the backup is interrupted, the burp server needs to decide what happens next. The behaviour of what happens next is triggered the next time that the client contacts the server. If the interruption left a 'finishing' symlink behind, the server will attempt to carry on and complete the backup. Part of phase 4 may involve operations that alter the immediately previous backup (it may need to convert its files into reverse deltas), so once it is 'finishing', burp can only try to move forwards. Note that when the backup is 'finishing', no more data is required from the client. If the interruption left a 'working' symlink behind, the server will check the 'working_dir_recovery_method' server-side option to decide what to do next. There are three choices: delete: Just delete the old working directory. resume: Simply continue the previous backup from the point at which it left off. NOTE: If the client has changed its include/exclude configuration since the backup was interrupted, the recovery method will automatically switch to 'delete'. Note also that if the backup was interrupted in phase 1 (file system scan), the recovery method will automatically switch to 'delete'. Setting 'delete' is the safest option (and also the default), because it means that an entire backup run has to finish uninterrupted for a backup to be considered complete. If you want to do Windows bare metal restores, you should choose this option so that you don't end up with a mixture of files from different VSS snapshots. For 'resume', burp will use the working directory's original client file system scan in order to request the remaining files that it needs to finish the backup. If you are using Windows (or some other OS that similarly generates browsable file system snapshots for backing up), it will mean that files have been copied from more than one snapshot, so the restore will be inconsistent if you are considering a bare metal restore. Additionally, there are occasionally bugs reported in the resume code, which can be avoided if you use 'delete'. Personally, I use 'resume' for my backups and have had no problems. Some people have a lot of data, and need to give an initial backup extra help to complete. They might set 'resume' until they have that initial backup, and switch to 'delete' after that. burp-2.4.0/m4/000077500000000000000000000000001404341324700130075ustar00rootroot00000000000000burp-2.4.0/m4/.gitignore000066400000000000000000000000061404341324700147730ustar00rootroot00000000000000/*.m4 burp-2.4.0/manpages/000077500000000000000000000000001404341324700142625ustar00rootroot00000000000000burp-2.4.0/manpages/bedup.8.in000066400000000000000000000041321404341324700160570ustar00rootroot00000000000000.TH bedup 8 "February 10, 2012" "" "bedup" .SH NAME bedup \- deduplication program with additional knowledge of @name@ .SH SYNOPSIS .B bedup .RI [ options ] .br .LP A file deduplication program with additional knowledge of @name@ protocol 1 storage directories. This program comes with the @name@ backup and restore package. .SH OPTIONS .TP \fB\-c\fR \fBpath\fR Path to config file (default: /etc/@name@/@name@.conf). .TP \fB\-g\fR \fB\fR Only run on the directories of clients that are in one of the groups specified. The list is comma-separated. To put a client in a group, use the 'dedup_group' option in the client configuration file on the server. .TP \fB\-h|-?\fR \fB\fR Print help text and exit. .TP \fB\-d \fR \fB\fR Delete any duplicate files found. (non-@name@ mode only, use with caution!) .TP \fB\-l \fR \fB\fR Hard link any duplicate files found. .TP \fB\-m \fR \fB\fR Maximum number of hard links to a single file. (non-@name@ mode only - in @name@ mode, use the max_hardlinks option in the configuration file) The default is 10000. On ext3, the maximum number of links possible is 32000, but space is needed for the normal operation of @name@. .TP \fB\-n\fR \fB\fR Non-@name@ mode. Deduplicate any (set of) directories. .TP \fB\-v\fR \fB\fR Print duplicate paths. Useful if you want to double check the files that would be hard linked or deleted before running with one of those options turned on.\fR .TP \fB\-V\fR \fB\fR Print version and exit.\fR .TP By default, bedup will read /etc/@name@/@name@.conf and deduplicate protocol 1 client storage directories using special knowledge of the structure.\fR .TP With '\-n', this knowledge is turned off and you have to specify the directories to deduplicate on the command line. Running with '\-n' is therefore dangerous if you are deduplicating @name@ storage directories. .SH BUGS If you find bugs, please report them to the email list. See the website <@package_url@> for details. .SH AUTHOR The main author of @human_name@ is Graham Keeling. .SH COPYRIGHT See the LICENCE file included with the source distribution. burp-2.4.0/manpages/bsigs.8.in000066400000000000000000000021051404341324700160650ustar00rootroot00000000000000.TH bsigs 8 "March 18, 2016" "" "bsigs" .SH NAME bsigs \- program for debugging @name@ protocol2 manifest files and indexes .SH SYNOPSIS .B bsigs .RI [ filename ] .br .LP A program for debugging @name@ protocol2 manifest files and indexes. It reads a file containing binary signatures and checksums, converts them to hex, and writes them to stdout. This program comes with the @name@ backup and restore package. .SH EXAMPLES .TP \fBcd /var/spool/@name@\fR .TP \fBbsigs global/clients/testclient/current/manifest/00000022\fR .TP \fBbsigs global/clients/testclient/current/manifest/dfiles\fR .TP \fBbsigs global/clients/testclient/current/manifest/dindex/00000002\fR .TP \fBbsigs global/clients/testclient/current/manifest/hooks/00000002\fR .TP \fBbsigs global/clients/testclient/current/manifest/sparse\fR .TP \fBbsigs global/data/sparse\fR .SH BUGS If you find bugs, please report them to the email list. See the website <@package_url@> for details. .SH AUTHOR The main author of @human_name@ is Graham Keeling. .SH COPYRIGHT See the LICENCE file included with the source distribution. burp-2.4.0/manpages/bsparse.8.in000066400000000000000000000014661404341324700164260ustar00rootroot00000000000000.TH bsparse 8 "September 05, 2016" "" "bsparse" .SH NAME bsparse \- program for regenerating @name@ protocol2 sparse files .SH SYNOPSIS .B bsparse [OPTIONS] [PATH_TO_DEDUP_GROUP] .br .LP A program for regenerating @name@ protocol2 sparse files. .SH OPTIONS .TP \fB\-c\fR \fBpath\fR Path to config file (default: /etc/@name@/@name@.conf). .SH EXAMPLES .TP \fBbsparse -c /etc/@name@/@name@-server.conf /var/spool/@name@/global\fR .TP Regenerates the sparse index for the dedup_group 'global', using the client storage directories under /var/spool/@name@/global/clients. .SH BUGS If you find bugs, please report them to the email list. See the website <@package_url@> for details. .SH AUTHOR The main author of @human_name@ is Graham Keeling. .SH COPYRIGHT See the LICENCE file included with the source distribution. burp-2.4.0/manpages/burp.8.in000066400000000000000000001662241404341324700157430ustar00rootroot00000000000000.\" manual page [] for @human_name@ .\" SH section heading .\" SS subsection heading .\" LP paragraph .\" IP indented paragraph .\" TP hanging label .TH @human_name@ 8 "@human_name@" .SH NAME @human_name@ \- BackUp and Restore Program .SH SYNOPSIS .B @name@ [OPTIONS] .SH DESCRIPTION .LP BackUp and Restore Program. .SH SERVER OPTIONS .TP \fB\-a c\fR \fB\fR Run as a stand-alone champion chooser process (useful for debugging protocol2 style backups). .TP \fB\-c\fR \fB[path]\fR Short for 'config file'. The argument is a path to the config file. The default is /etc/@name@/@name@.conf. .TP \fB\-F\fR \fB\fR Foreground mode. The server will fork into the background and run as a daemon if you do not give this option. .TP \fB\-g\fR \fB\fR Generate initial CA keys and certificates, and then exit. .TP \fB\-h\fR \fB\fR Print help and then exit. .TP \fB\-?\fR \fB\fR Print help and then exit. .TP \fB\-i\fR \fB\fR Print an index table of symbols that humans may see @name@ produce, and exit. .TP \fB\-n\fR \fB\fR No forking mode. The program will accept a single query, deal with it, and then exit. This is useful for debugging. Implies '\-F'. If you intend to debug a protocol2 session, you will also want to run a separate champion chooser process ('\-a c' below). .TP \fB\-o\fR \fB[option=value]\fR Override a given option. You can use this flag several times to override multiple options. You can even reset list options with the special syntax ':=' (example: '-o "include:=/tmp"'). .TP \fB\-Q\fR \fB\fR Do not log to stdout (overrides config file 'stdout' setting). .TP \fB\-t\fR \fB\fR Dry-run mode to test config file syntax. .TP \fB\-v\fR \fB\fR Log to stdout (overrides config file 'stdout' setting). .TP \fB\-V\fR \fB\fR Print version and exit. .TP ADDITIONAL SERVER OPTIONS TO USE WITH '\-a c' .TP \fB\-C\fR \fB[client]\fR Run as if forked via a connection from this client. .SH CLIENT OPTIONS .TP \fB\-a\fR \fB[b|t|r|R|l|L|p|v|V|delete|e|T|d|D]\fR Short for 'action'. The arguments mean backup, timed backup, restore, Restore, list, long list, parseable list, verify, Verify, delete, estimate, timer check, diff, or long diff, respectively. .TP \fB\-b\fR \fB[number|a]\fR Short for 'backup number'. The argument is a number, or 'a' to select all backups. .TP \fB\-c\fR \fB[path]\fR Short for 'config file'. The argument is a path to the config file. The default is /etc/@name@/@name@.conf, or %PROGRAMFILES%\\@human_name@\\@name@.conf on Windows. .TP \fB\-C\fR \fB[client]\fR Allows you to specify an alternative client to list or restore from. Requires that the server configuration of the alternative client permits your client to do this. See the 'restore_client' and 'super_client' options. .TP \fB\-d\fR \fB[path]\fR Short for 'directory'. When restoring, the argument is a path to an alternative directory to restore to. When listing, the argument is the directory to list. .TP \fB\-f\fR Short for 'force overwrite'. Without this option set, a restore will not overwrite existing files. .TP \fB\-h\fR \fB\fR Print help and then exit. .TP \fB\-?\fR \fB\fR Print help and then exit. .TP \fB\-o\fR \fB[option=value]\fR Override a given option. You can use this flag several times to override multiple options. You can even reset list options with the special syntax ':=' (example: '-o "include:=/tmp"'). .TP \fB\-i\fR \fB\fR Print an index table of symbols that humans may see @name@ produce, and exit. .TP \fB\-q\fR \fB[max secs]\fR When running a timed backup, sleep for a random number of seconds (between 0 and the number given) before contacting the server. Alternatively, this can be specified by the 'randomise' configuration file option. .TP \fB\-Q\fR \fB\fR Do not log to stdout (overrides config file 'stdout' setting). .TP \fB\-r\fR \fB[regex]\fR Short for 'regular expression'. The argument is a case sensitive regular expression with which to match backup files. Use it for lists, restores and verifies. .TP \fB\-R\fR \fB[regex]\fR Case insensitive alternative to '-r'. .TP \fB\-s\fR \fB[number]\fR For use with restores \- strip a number of leading path components. .TP \fB\-t\fR \fB\fR Dry-run mode to test config file syntax. .TP \fB\-v\fR \fB\fR Log to stdout (overrides config file 'stdout' setting). .TP \fB\-x\fR On restore, do not use the Windows VSS API, and strip out the VSS data. Works both on Windows and non-Windows. .TP \fB\-X\fR (Windows only) On restore, do not use the Windows VSS API and do not attempt to strip out the VSS data. Give this option when you are restoring a backup that contains no VSS data. .TP \fB\-a s\fR \fB\fR Run this to connect to a running server to get a live monitor of the status of all your backup clients. The live monitor requires ncurses support at compile time. .TP \fB\-a S\fR \fB\fR Similar to '\-a s', but it prints the main status monitor summary screen to stdout. One application is that a script can run this and email an administrator the output on a cron job. This doesn't require ncurses support. There are additional options that can be given with both these options, listed below. .TP ADDITIONAL CLIENT OPTIONS TO USE WITH '\-a s' and '\-a S' .TP \fB\-C\fR \fB[client]\fR Limit the output to a single client. .TP \fB\-b\fR \fB[number]\fR Show listable files in a particular backup (requires \-C). .TP \fB\-z\fR \fB[file]\fR Dump a particular log file in a backup (requires \-C and \-b). .TP \fB\-d\fR \fB[path]\fR Show a particular path in a backup (requires \-C and \-b). .TP \fB\-l\fR \fB[path]\fR Log file for status monitor - useful for debugging. .TP ADDITIONAL CLIENT OPTIONS TO USE WITH '\-a b' .TP \fB\-o seed_src= -o seed_dst=\fR Make a seeding backup. The source directory is replaced with the destination directory on completion. Both paths need to be absolute. Your configured include paths need to be the same as the source directory, or within the source directory. There must be no other existing backups of the client running the seed job. A use case might be to make an initial backup of a local hard drive, and then to ship the hard drive to a remote location for subsequent backups. .SH EXAMPLES .TP \fB@name@ \-a b\fR Runs a backup. .TP \fB@name@ \-a t\fR Timed backup. The same as '@name@ \-a b', except that a script is run on the server before deciding to go ahead. The intention is that this command will be run on a repeating cron job with a short interval, and that the server will decide when it is time for a new backup. .TP \fB@name@ \-a l\fR Lists the available backups and dates. .TP \fB@name@ \-a l \-b 1\fR Lists all the files in backup number 1. .TP \fB@name@ \-a l \-b a\fR Lists all the files in all the backups. .TP \fB@name@ \-a l \-b c\fR Lists all the files in the current backup. .TP \fB@name@ \-a l \-b 1 \-r myregex\fR Lists all the files in backup number 1 that match the regular expression 'myregex'. .TP \fB@name@ \-a L \-b 1 \-r myregex\fR Long lists all the files in backup number 1 that match the regular expression 'myregex'. This is like doing an 'ls \-l'. .TP \fB@name@ \-a L \-b 1 \-d ''\fR Long list the top level directory of backup 1. .TP \fB@name@ \-a L \-b 1 \-d '/home/graham'\fR Long list the /home/graham directory of backup 1. These '\-d' versions of the list function provide the ability to 'browse' backups. .TP \fB@name@ \-C altclient \-a L\fR Long list the top level directory of backup 1 of client 'altclient'. .TP \fB@name@ \-a r \-d /tmp/restoredir\fR Restores all the files in the most recent backup into the directory /tmp/restoredir. .TP \fB@name@ \-a r \-b 1 \-d /tmp/restoredir \-r myregex\fR Restores all the files in backup number 1 into the directory /tmp/restoredir, if they match the regular expression 'myregex'. .TP \fB@name@ \-a r \-b 1 \-d /tmp/restoredir \-r myregex \-s 2\fR Restores all the files in backup number 1 into the directory /tmp/restore, if they match the regular expression 'myregex', and strip 2 leading path components. .TP \fB@name@ \-C altclient \-a r \-b 1 \-d /tmp/restoredir \-r myregex\fR Restores all the files in backup number 1 of client 'altclient' into the directory /tmp/restoredir, if that match the regular expression 'myregex'. .TP \fB@name@ \-a v\fR Verifies the most recent backup. .TP \fB@name@ \-a v \-b 1 \-r myregex\fR Verifies everything in backup number 1 that matches the regular expression 'myregex'. .TP \fB@name@ \-a delete \-b 1\fR Deletes backup number 1. Note that @name@ will not delete backup directories that other backup directories depend upon. .TP \fB@name@ \-a d \-b 1 \-b 2\fR Report the differences between backups 1 and 2 (use \-a D for more verbosity). .TP \fB@name@ \-a p \-b 1\fR Lists all the files in backup number 1, in @name@-parsable list format. You may append something like '>/tmp/restore_list' to send to a file, edit, then use as an input for restoring. As in the following command: .TP \fB@name@ \-a r \-b 1 \-o restore_list=/tmp/restore_list\fR Restores from backup 7, matching the list of files given in /tmp/restore_list. The files given must be ordered as they would be in the output of a burp list command. .TP \fB@name@ \-a p -b 6 | @name@ \-a R \-b 7\fR Restores from backup 7, matching the list of files on standard input, which is provided by @name@'s list of backup 6 ('\-a R' is shorthand for '\-a r \-o restore_list=\-'). .TP \fB@name@ \-a p -b 6 | @name@ \-a V \-b 7\fR Verifies from backup 7, matching the list of files on standard input, which is provided by @name@'s list of backup 6 ('\-a V' is shorthand for '\-a v \-o restore_list=\-'). .TP \fB@name@ \-a s\fR Run the ncurses status monitor. .TP \fB@name@ \-a S\fR Print a status monitor snapshot, summarising all clients. .TP \fB@name@ \-C testclient \-a S\fR Print a status monitor snapshot, showing client 'testclient' only. .SH SERVER CONFIGURATION FILE OPTIONS .TP \fB. [glob]\fR Read additional configuration files. .TP \fBmode=server\fR Required to run in server mode. .TP \fBlisten=[address]:[port]\fR Defines the main TCP address and port that the server listens on. Specify multiple 'listen' entries on separate lines in order to listen on multiple addresses and ports. Each pair can be configured with its own 'max_children' value. .TP \fBlisten_status=[address]:[port]\fR Defines the main TCP address and port that the server listens on for status requests. Specify multiple 'listen_status' entries on separate lines in order to listen on multiple addresses and ports. Each pair can be configured with its own 'max_status_children' value. Comment out to have no status server. .TP \fBnetwork_allow=[string]\fR Allows access for the specified network or address. For example: '::1' or '127.0.0.1/8'. Specify multiple 'network_allow' entries on separate lines to allow multiple networks or addresses. If this option is not given, all networks are allowed. This option can be overridden by the client configuration files in clientconfdir on the server. .TP \fBnetwork_allow_status=[string]\fR Allows access for the specified network or address to the status server. For example: '::1' or '127.0.0.1/8'. Specify multiple 'network_allow' entries on separate lines to allow multiple networks or addresses. If this option is not given, all networks are allowed. This option can be overridden by the client configuration files in clientconfdir on the server. .TP \fBcname_lowercase=[0|1]\fR Whether to force lowercase cname when looking-up in clientconfdir. This also affects the fqdn lookup on the client (see client configuration options for details). The default is 0. When set to 1 the name provided by the client while authenticating will be lowercased. .TP \fBcname_fqdn=[0|1]\fR Whether to keep fqdn cname (like 'testclient.example.com') when looking-up in clientconfdir. This also affects the fqdn lookup on the client (see client configuration options for details). The default is 1. When set to 0, the fqdn provided by the client while authenticating will be stripped ('testclient.example.com' becomes 'testclient'). .TP \fBdaemon=[0|1]\fR Whether to daemonise. The default is 1. .TP \fBfork=[0|1]\fR Whether to fork children. The default is 1. .TP \fBdirectory=[path]\fR Path to the directory in which to store backups. .TP \fBdirectory_tree=[0|1]\fR When turned on (which is the default) and the client is on version 1.3.6 or greater, the structure of the storage directory will mimic that of the original filesystem on the client. .TP \fBtimestamp_format=[strftime format]\fR This allows you to tweak the format of the timestamps of individual backups. See 'man strftime' to see available substitutions. If this option is unset, @name@ uses "%Y-%m-%d %H:%M:%S %z". .TP \fBpassword_check=[0|1]\fR Allows you to turn client password checking on or off. The default is on. SSL certificates will still be checked if you turn passwords off. This option can be overridden by the client configuration files in clientconfdir on the server. .TP \fBclientconfdir=[path]\fR Path to the directory that contains client configuration files. .TP \fBprotocol=[0|1|2]\fR Choose which style of backups and restores to use. 0 (the default) automatically decides based on the client version and which protocol is set on the client side. 1 forces protocol1 style (file level granularity with a pseudo mirrored storage on the server and optional rsync). 2 forces protocol2 style (inline deduplication with variable length blocks). If you choose a forced setting, it will be an error if the client also chooses a forced setting. This option can be overridden by the client configuration files in clientconfdir on the server. .TP \fBlockfile=[path]\fR Path to the lockfile that ensures that two server processes cannot run simultaneously. .TP \fBpidfile=[path]\fR Synonym for lockfile. .TP \fBsyslog=[0|1]\fR Log to syslog. Defaults to off. .TP \fBstdout=[0|1]\fR Log to stdout. Defaults to on. .TP \fBkeep=[number]\fR Number of backups to keep. This can be overridden by the clientconfdir configuration files in clientconfdir on the server. Specify multiple 'keep' entries on separate lines in order to keep multiple periods of backups. For example, assuming that you are doing a backup a day, keep=7 keep=4 keep=6 (on separate lines) will keep 7 daily backups, 4 weekly backups (7x4=28), and 6 multiples of 4 weeks (7x4x6=168) \- roughly 6 monthly backups. Effectively, you will be guaranteed to be able to restore up to 168 days ago, with the number of available backups exponentially decreasing as you go back in time to that point. In this example, every 7th backup will be hardlinked to allow @name@ to safely delete intermediate backups when necessary. You can have as many 'keep' lines as you like, as long as they don't exceed 52560000 when multiplied together. That is, a backup every minute for 100 years. .TP \fBmanual_delete=[path]\fR This can be overridden by the clientconfdir configuration files in clientconfdir on the server. When the server needs to delete old backups, or rubble left over from generating reverse patches with librsync=1, it will normally delete them in place. If you use the 'manual_delete' option, the files will be moved to the path specified for deletion at a later point. You will then need to configure a cron job, or similar, to delete the files yourself. Do not specify a path that is not on the same filesystem as the client storage directory. .TP \fBhardlinked_archive=[0|1]\fR On the server, defines whether to keep hardlinked files in the backups, or whether to generate reverse deltas and delete the original files. Can be set to either 0 (off) or 1 (on). Disadvantage: More disk space will be used Advantage: Restores will be faster, and since no reverse deltas need to be generated, the time and effort the server needs at the end of a backup is reduced. .TP \fBmax_hardlinks=[number]\fR On the server, the number of times that a single file can be hardlinked. The bedup program also obeys this setting. The default is 10000. .TP \fBlibrsync=[0|1]\fR When set to 0, delta differencing will not take place. That is, when a file changes, the server will request the whole new file. The default is 1. This option can be overridden by the client configuration files in clientconfdir on the server. .TP \fBlibrsync_max_size=[B/KB/MB/GB]\fR Only use librsync when a file is less than the given size. Both the most recently backed up version of a file and the version to be backed up are checked. The default is 0, which means the option is off. This option can be overridden by the client configuration files in clientconfdir on the server. .TP \fBcompression=zlib[0-9] (or gzip[0-9])\fR Choose the level of zlib compression for files stored in backups. Setting 0 or zlib0 turns compression off. The default is zlib9. This option can be overridden by the client configuration files in clientconfdir on the server. 'gzip' is a synonym of 'zlib'. \fBThis option is applicable to protocol version 1 only.\fR .TP \fBhard_quota=[B/KB/MB/GB]\fR Do not back up the client if the estimated size of all files is greater than the specified size. Example: 'hard_quota = 100GB'. Set to 0 (the default) to have no limit. .TP \fBsoft_quota=[B/KB/MB/GB]\fR A warning will be issued when the estimated size of all files is greater than the specified size and smaller than hard_quota. Example: 'soft_quota = 95GB'. Set to 0 (the default) to have no warning. .TP \fBversion_warn=[0|1]\fR When this is on, which is the default, a warning will be issued when the client version does not match the server version. This option can be overridden by the client configuration files in clientconfdir on the server. .TP \fBpath_length_warn=[0|1]\fR When this is on, which is the default, a warning will be issued when the client sends a path that is too long to replicate in the storage area tree structure. The file will still be saved in a numbered file outside of the tree structure, regardless of the setting of this option. This option can be overridden by the client configuration files in clientconfdir on the server. .TP \fBclient_lockdir=[path]\fR Path to the directory in which to keep per-client lock files. By default, this is set to the path given by the 'directory' option. .TP \fBuser=[username]\fR Run as a particular user. This can be overridden by the client configuration files in clientconfdir on the server. .TP \fBgroup=[groupname]\fR Run as a particular group. This can be overridden by the client configuration files in clientconfdir on the server. .TP \fBreadall=[0|1]\fR Keep readall capability when dropping root privileges (default 0). When enabled changes default atime to 1. .TP \fBumask=[umask]\fR Set the file creation umask. Default is 0022. .TP \fBratelimit=[Mb/s]\fR Set the network send rate limit, in Mb/s. If this option is not given, @name@ will send data as fast as it can. If you want the server's sending speed to be limited, you will also need to set this option on the server side. .TP \fBnetwork_timeout=[s]\fR Set the network timeout in seconds. If no data is sent or received over a period of this length, @name@ will give up. The default is 7200 seconds (2 hours). .TP \fBworking_dir_recovery_method=[resume|delete]\fR This option tells the server what to do when it finds the working directory of an interrupted backup (perhaps somebody pulled the plug on the server, or something). This can be overridden by the client configurations files in clientconfdir on the server. Options are... .TP \fBdelete:\fR Just delete the old working directory. .TP \fBresume:\fR Continue the previous backup from the point at which it left off. NOTE: If the client has changed its include/exclude configuration since the backup was interrupted, the recovery method will automatically switch to 'delete'. See also the 'resume attempts' option. .TP \fBmax_resume_attempts=[number]\fR If working_dir_recovery_method is 'resume', this option tells the server how many times to attempt to resume before giving up and deleting the working directory. The default is 0, which means to never give up. .TP \fBclient_can_delete=[0|1]\fR Turn this off to prevent clients from deleting backups with the '\-a delete' option. The default is that clients can delete backups. Restore clients can override this setting. .TP \fBclient_can_diff=[0|1]\fR Turn this off to prevent clients from diffing backups with the '\-a d' option. The default is that clients can diff backups. Restore clients can override this setting. .TP .TP \fBclient_can_force_backup=[0|1]\fR Turn this off to prevent clients from forcing backups with the '\-a b' option. Timed backups will still work. The default is that clients can force backups. .TP \fBclient_can_list=[0|1]\fR Turn this off to prevent clients from listing backups with the '\-a l' option. The default is that clients can list backups. Restore clients can override this setting. .TP \fBclient_can_monitor=[0|1]\fR Turn this off to prevent clients from being able to run the status monitor. The default is that clients can run the status monitor. Restore clients can override this setting. .TP \fBclient_can_restore=[0|1]\fR Turn this off to prevent clients from initiating restores with the '\-a r' option. The default is that clients can initiate restores. Restore clients can override this setting. .TP \fBclient_can_verify=[0|1]\fR Turn this off to prevent clients from initiating a verify job with the '\-a v' option. The default is that clients can initiate a verify job. Restore clients can override this setting. .TP \fBsuper_client=[client]\fR A client that is permitted to list, verify, restore, delete, and diff files belonging to any other client, according to the super_client's client_can permissions (eg, 'client_can_list'). You may specify multiple super_clients. If this is too permissive, you may set a super_client for individual original clients in the individual clientconfdir files, or look at the 'restore_client' option. .TP \fBrestore_client=[client]\fR A client that is permitted to list, verify, restore, delete, and diff files belonging to any other client, according to the client_can permissions on both the restore_client and the original_client (eg, 'client_can_list'). You may specify multiple restore_clients. If this is too permissive, you may set a restore_client for individual original clients in the individual clientconfdir files. .TP \fBssl_cert_ca=[path]\fR The path to the SSL CA certificate. This file will probably be the same on both the server and the client. The file should contain just the certificate in PEM format. For more information on this, and the other ssl_* options, please see docs/@name@_ca.txt. .TP \fBssl_cert=[path]\fR The path to the server SSL certificate. It works for me when the file contains the concatenation of the certificate and private key in PEM format. .TP \fBssl_key=[path]\fR The path to the server SSL private key in PEM format. .TP \fBssl_key_password=[password]\fR Only needed for loading an encrypted certificate. .TP \fBssl_cert_password=[password]\fR Synonym for ssl_key_password. .TP \fBssl_ciphers=[cipher list]\fR Allowed SSL ciphers. See openssl ciphers for details. .TP \fBssl_compression=zlib[0|5] (or gzip[0|5])\fR Choose the level of zlib compression over SSL. Setting 0 or zlib0 turns SSL compression off. Setting non-zero gives zlib5 compression (it is not currently possible for openssl to set any other level). The default is 5. 'gzip' is a synonym of 'zlib'. .TP .TP \fBssl_verify_peer_early=[0|1]\fR Verify and authenticate client certificates at SSL layer before receiving or sending @human_name@ traffic. The default is to verify client certificates only after password authentication. .TP .TP \fBssl_dhfile=[path]\fR Path to Diffie-Hellman parameter file. To generate one with openssl, use a command like this: openssl dhparam \-dsaparam \-out dhfile.pem 2048 .TP \fBmax_parallel_backups=[number]\fR Defines the number of max parallel backups (the number of clients that are in a "working" state). The default is 0 - unlimited. .TP \fBmax_children=[number]\fR Defines the number of child processes to fork (the number of clients that can simultaneously connect. The default is 5. Specify multiple 'max_children' entries on separate lines if you have configured multiple 'listen' entries. .TP \fBmax_status_children=[number]\fR Defines the number of status child processes to fork (the number of status clients that can simultaneously connect. The default is 5. Specify multiple 'max_status_children' entries on separate lines if you have configured multiple 'listen_status' entries. .TP \fBmax_storage_subdirs=[number]\fR Defines the number of subdirectories in the data storage areas. The maximum number of subdirectories that ext3 allows is 32000. If you do not set this option, it defaults to 30000. .TP \fBtimer_script=[path]\fR Path to the script to run when a client connects with the timed backup option. If the script exits with code 0, a backup will run. The first three arguments are the client name, the path to the 'current' storage directory, and the path to the top level storage directories. The next two arguments are reserved, and user arguments (see timer_arg) are appended after that. An example timer script is provided. The timer_script option can be overridden by the client configuration files in clientconfdir on the server. If this option is not set, equivalent code internal to @human_name@ will be run instead. The internal code also uses the timer_arg parameters. .TP \fBtimer_arg=[string]\fR A user-definable argument to the timer script. You can have many of these. The timer_arg options can be overridden by the client configuration files in clientconfdir on the server. .TP \fBtimer_repeat_interval=[m]\fR During a timed backup, the timer script can be run at regular intervals. If the client is out of timeband, the backup will be interrupted. For example, setting 5 will cause burp to check every five minutes. The default is 0, which means that the script will only run once at the start of the backup. .TP \fBnotify_success_script=[path]\fR Path to the script to run when a backup succeeds. User arguments are appended after the first six reserved arguments. An example notify script is provided. The notify_success_script option can be overriddden by the client configuration files in clientconfdir on the server. .TP \fBnotify_success_arg=[string]\fR A user-definable argument to the notify success script. You can have many of these. The notify_success_arg options can be overriddden by the client configuration files in clientconfdir on the server. .TP \fBnotify_success_warnings_only=[0|1]\fR Set to 1 to send success notifications when there were warnings. If this and notify_success_changes_only are not turned on, success notifications are always sent. .TP \fBnotify_success_changes_only=[0|1]\fR Set to 1 to send success notifications when there were new or changed files. If this and notify_success_warnings_only are not turned on, success notifications are always sent. .TP \fBnotify_failure_script=[path]\fR The same as notify_success_script, but for backups that failed. .TP \fBnotify_failure_arg=[string]\fR The same as notify_success_arg, but for backups that failed. .TP \fBnotify_failure_on_backup_with_failovers_left=[0|1]\fR In the case where there are server_failover entries left to try, you may not wish to notify on backup failure. The default is on. .TP \fBnotify_failure_on_backup_working_dir_deletion=[0|1]\fR On the next backup attempt after a backup was interrupted, it may not be possible to resume the previous backup. In this case (or if working_dir_recovery_method is 'delete'), the previous backup will be deleted. You may wish to be notified about this. The default is off. .TP \fBdedup_group=[string]\fR Enables you to group clients together for file deduplication purposes. For example, you might want to set 'dedup_group=xp' for each Windows XP client, and then run the bedup program on a cron job every other day with the option '\-g xp'. .TP \fBserver_script_pre=[path]\fR Path to a script to run on the server after each successfully authenticated connection but before any work is carried out. The arguments to it are 'pre', '(client command)', '(client name)', '(0 or 1 for success or failure)', '(timer script exit code)', and then arguments defined by server_script_pre_arg. If the script returns non-zero, the task asked for by the client will not be run. This command and related options can be overriddden by the client configuration files in clientconfdir on the server. .TP \fBserver_script_pre_arg=[string]\fR A user-definable argument to the server pre script. You can have many of these. .TP \fBserver_script_pre_notify=[0|1]\fR Turn on to send a notification email when the server pre script returns non-zero. The output of the script will be included in the email. The default is off. Most people will not want this turned on because clients usually contact the server at 20 minute intervals and this could cause a lot of emails to be generated. Requires the notify_failure options to be set. .TP \fBserver_script_post=[path]\fR Path to a script to run on the server before the client disconnects. The arguments to it are 'post', '(client command)', '(client name), '(0 or 1 for success or failure)', '(timer script exit code)', and then arguments defined by server_script_post_arg. This command and related options can be overriddden by the client configuration files in clientconfdir on the server. .TP \fBserver_script_post_arg=[string]\fR A user-definable argument to the server post script. You can have many of these. .TP \fBserver_script_post_notify=[0|1]\fR Turn on to send a notification email when the server post script returns non-zero. The output of the script will be included in the email. The default is off. Requires the notify_failure options to be set. .TP \fBserver_script=[path]\fR You can use this to save space in your config file when you want to run the same server script twice. It overrides server_script_pre and server_script_post. This command and related options can be overriddden by the client configuration files in clientconfdir on the server. .TP \fBserver_script_arg=[path]\fR Goes with server_script and overrides server_script_pre_arg and server_script_post_arg. .TP \fBserver_script_notify=[0|1]\fR Turn on to send notifications email when the server pre and post scripts return non-zero. The output of the script will be included in the email. The default is off. Requires the notify_failure options to be set. .TP \fBserver_script_post_run_on_fail=[0|1]\fR If this is set to 1, server_script_post will always be run. The default is 0, which means that if the task asked for by the client fails, server_script_post will not be run. .TP \fBautoupgrade_dir=[path]\fR Path to autoupgrade directory from which upgrades are downloaded. The option can be left unset in order not to autoupgrade clients. Please see docs/autoupgrade.txt in the source package for more help with this option. .TP \fBca_conf=[path]\fR Path to certificate authority configuration file. The CA configuration file will usually be /etc/@name@/CA.cnf. The CA directory indicated by CA.cnf will usually be /etc/@name@/CA. If ca_conf is set and the CA directory does not exist, the server will create, populate it, and the paths indicated by ssl_cert_ca, ssl_cert, ssl_key and ssl_dhfile will be overwritten. For more detailed information on this and the other ca_* options, please see docs/@name@_ca.txt. .TP \fBca_name=[name]\fR Name of the CA that the server will generate when using the ca_conf option. .TP \fBca_server_name=[name]\fR The name that the server will put into its own SSL certificates when using the ca_conf option. .TP \fBca_@name@_ca=[path]\fR Path to the @name@_ca script when using the ca_conf option. .TP \fBca_crl=[path]\fR Override the default path to the certificate revocation list. .TP \fBca_crl_check=[0|1]\fR Whether to check for revoked certificates in the certificate revocation list. .TP \fBmonitor_browse_cache=[0|1]\fR Whether or not the server should cache the directory tree when a monitor client is browsing. Advantage: browsing is faster. Disadvantage: more memory is used. .TP \fBlabel=[string]\fR You can have multiple labels, and they can be overridden in the client configuration files in clientconfdir on the server. They will appear as an array of strings in the server status monitor JSON output. The idea is to provide a mechanism for arbirtrary values to be passed to clients of the server status monitor. .TP \fBenabled=[0|1]\fR Set this to 0 if you want to disable all clients. The default is 1. This option can be overridden per-client in the client configuration files in clientconfdir on the server. .TP \fBrblk_memory_max=[B/KB/MB/GB]\fR The maximum amount of data from the disk cached in server memory during a protocol2 restore/verify. The default is 256MB. This option can be overridden per-client in the client configuration files in clientconfdir on the server. .TP \fBsparse_size_max=[B/KB/MB/GB]\fR The maximum (uncompressed) size of the sparse file of each protocol 2 dedup_group. The default is 256MB. If the sparse file grows beyond this size, entries will be removed starting with the oldest, unless it is the only one left for a client. .TP \fBfail_on_warning=[0|1]\fR If a warning is generated during a backup, fail the backup. The default is 0. This option can be overridden per-client in the client configuration files in clientconfdir on the server. .SH CLIENT CONFIGURATION FILE OPTIONS .TP \fB. [glob]\fR Read additional configuration files. On Windows, the glob is unimplemented - you will need to specify an actual file. .TP \fBmode=client\fR Required to run in client mode. .TP \fBserver=[host:port]\fR Defines the server to connect to. If you don't specify a port here, you will need to specify it separately. .TP \fBserver_failover=[host:port]\fR Defines a failover server to connect to. You can provide more than one 'server_failover' entry. The client will try the next failover server if it fails to connect. IMPORTANT: The burp client currently does not have the capability to have a different SSL profile for each server, so for this to work for you, you will need to have the same CA and certificates on each server. .TP \fBfailover_on_backup_error=[0|1]\fR Failover to failover servers on backup errors as well as failures to connect. The default is off. .TP \fBport=[port]\fR Defines the TCP port on the server that we will send requests to. If this option is set, it is the default for these options, which can be overridden individually: port_backup, port_restore, port_verify, port_list, port_delete. If this option is not set, you will need to set all of the port options separately. .TP \fBport_backup=[port]\fR Defines the TCP port on the server that we will send backup requests to. If not set, it defaults to the port option. .TP \fBport_restore=[port]\fR Defines the TCP port on the server that we will send restore requests to. If not set, it defaults to the port option. .TP \fBport_verify=[port]\fR Defines the TCP port on the server that we will send verify requests to. If not set, it defaults to the port_restore option. .TP \fBport_list=[port]\fR Defines the TCP port on the server that we will send list requests to. If not set, it defaults to the port option. .TP \fBport_delete=[port]\fR Defines the TCP port on the server that we will send delete requests to. If not set, it defaults to the port option. .TP \fBstatus_port=[port]\fR Defines the TCP port that the server is listening on for status requests. .TP \fBcname=[client name]\fR Defines the client name to identify as to the server. .TP \fBcname_lowercase=[0|1]\fR Whether to force lowercase cname when detecting cname automatically (ie. no cname provided above). The default is 0. When set to 1 the name returned by the get_fqdn function will be lowercased. .TP \fBcname_fqdn=[0|1]\fR Whether to keep fqdn cname (like 'testclient.example.com') when detecting cname automatically (ie. no cname provided above). The default is 1. When set to 0, the fqdn returned by the get_fqdn function will be stripped ('testclient.example.com' becomes 'testclient'). .TP \fBprotocol=[0|1|2]\fR Choose which style of backups and restores to use. 0 (the default) automatically decides based on the server version and which protocol is set on the server side. 1 forces protocol1 style (file level granularity with a pseudo mirrored storage on the server and optional rsync). 2 forces protocol2 style (inline deduplication with variable length blocks). If you choose a forced setting, it will be an error if the server also chooses a forced setting. .TP \fBpassword=[password]\fR Defines the password to send to the server. .TP \fBenabled=[0|1]\fR Set this to 0 if you want to disable a client. The default is 1. This option can also be set in the client configuration files in clientconfdir on the server. .TP \fBlockfile=[path]\fR Path to the lockfile that ensures that two client processes cannot run simultaneously (this currently doesn't work on Windows). .TP \fBpidfile=[path]\fR Synonym for lockfile. .TP \fBsyslog=[0|1]\fR Log to syslog. Defaults to off. .TP \fBstdout=[0|1]\fR Log to stdout. Defaults to on. .TP \fBprogress_counter=[0|1]\fR Print progress counters on stdout. Defaults to on. .TP \fBrandomise=[max secs]\fR When running a timed backup, sleep for a random number of seconds (between 0 and the number given) before contacting the server. Alternatively, this can be specified by the '-q' command line option. .TP \fBuser=[username]\fR Run as a particular user (not supported on Windows). .TP \fBgroup=[groupname]\fR Run as a particular group (not supported on Windows). .TP \fBratelimit=[Mb/s]\fR Set the network send rate limit, in Mb/s. If this option is not given, @name@ will send data as fast as it can. If you want the client's sending speed to be limited, you will also need to set this option on the client side. .TP \fBnetwork_timeout=[s]\fR Set the network timeout in seconds. If no data is sent or received over a period of this length, @name@ will give up. The default is 7200 seconds (2 hours). .TP \fBca_@name@_ca=[path]\fR Path to the @name@_ca script (@name@_ca.bat on Windows). For more information on this, please see docs/@name@_ca.txt. .TP \fBca_csr_dir=[path]\fR Directory where certificate signing requests are generated. For more information on this, please see docs/@name@_ca.txt. .TP \fBssl_cert_ca=[path]\fR The path to the SSL CA certificate. This file will probably be the same on both the server and the client. The file should contain just the certificate in PEM format. For more information on this and the other ssl_* options, please see docs/@name@_ca.txt. .TP \fBssl_cert=[path]\fR The path to the client SSL certificate. It works for me when the file contains the concatenation of the certificate and private key in PEM format. .TP \fBssl_key=[path]\fR The path to the client SSL private key in PEM format. .TP \fBssl_key_password=[password]\fR Only needed for loading an encrypted certificate. .TP \fBssl_cert_password=[password]\fR Synonym for ssl_key_password. .TP \fBssl_peer_cn=[string]\fR Must match the common name in the SSL certificate that the server gives when it connects. If ssl_peer_cn is not set, the server name will be used instead. .TP \fBssl_ciphers=[cipher list]\fR Allowed SSL ciphers. See openssl ciphers for details. .TP \fBserver_can_override_includes=[0|1]\fR To prevent the server from being able to override your local include/exclude list, set this to 0. The default is 1. .TP \fBserver_can_restore=[0|1]\fR To prevent the server from initiating restores, set this to 0. The default is 1. You will also need to give a location for the files to be restored to with the 'restoreprefix' option. .TP \fBrestoreprefix=[path]\fR When restoring, this path will be prefixed to the restore path. The '-d' command line option overrides this setting. This setting is required if you are using server initiated restores. .TP \fBencryption_password=[password]\fR Set this to enable client side file Blowfish encryption. If you do not want encryption, leave this field out of your config file. \fBIMPORTANT:\fR Configuring an encryption_password renders delta differencing pointless, since the smallest real change to a file will make the whole file look different. Therefore, activating this option turns off delta differencing so that whenever a client file changes, the whole new file will be uploaded on the next backup. \fBALSO IMPORTANT:\fR If you manage to lose your encryption password, you will not be able to unencrypt your files. You should therefore think about having a copy of the encryption password somewhere off-box, in case of your client hard disk failing. Take care when copying and pasting special characters between client conf files, as the encoding of the config file matters. \fBFINALLY:\fR If you change your encryption password, you will end up with a mixture of files on the server with different encryption and it may become tricky to restore more than one file at a time. For this reason, if you change your encryption password, you may want to start a fresh chain of backups (by moving the original set aside, for example). @human_name@ will cope fine with turning the same encryption password on and off between backups, and will restore a backup of mixed encrypted and unencrypted files without a problem. \fBThis option is applicable to protocol version 1 only.\fR .TP \fBglob_after_script_pre=[0|1]\fR Set this to 0 if you do not want include_glob settings to be evaluated after the pre script is run. The default is 1. .TP \fBbackup_script_pre=[path]\fR Path to a script to run before a backup. It is not run if the server decides it is not yet time for a backup. The arguments to it are 'pre', 'reserved2' to 'reserved5', and then arguments defined by backup_script_pre_arg - unless the option 'backup_script_reserved_args' is off, then only arguments defined by backup_script_pre_arg are passed to it. If the script fails (ie. when the return code is not 0), then the backup is aborted. .TP \fBbackup_script_pre_arg=[string]\fR A user-definable argument to the backup pre script. You can have many of these. .TP \fBbackup_script_post=[path]\fR Path to a script to run after a backup. The arguments to it are 'post', [0|1] if the backup failed or succeeded, 'reserved3' to 'reserved5', and then arguments defined by backup_script_post_arg - unless the option 'backup_script_reserved_args' is off, then only arguments defined by backup_script_post_arg are passed to it. .TP \fBbackup_script_post_arg=[string]\fR A user-definable argument to the backup post script. You can have many of these. .TP \fBbackup_script_post_run_on_fail=[0|1]\fR If this is set to 1, backup_script_post will be run whether the backup succeeds or not. The default is 0, which means that backup_script_post will only be run if the backup succeeds. .TP \fBrestore_script_pre=[path]\fR Path to a script to run before a restore. The arguments to it are 'pre', 'reserved2' to 'reserved5', and then arguments defined by restore_script_pre_arg - unless the option 'restore_script_reserved_args' is off, then only arguments defined by restore_script_pre_arg are passed to it. .TP \fBrestore_script_pre_arg=[string]\fR A user-definable argument to the restore pre script. You can have many of these. .TP \fBrestore_script_post=[path]\fR Path to a script to run after a restore. The arguments to it are 'post', [0|1] if the restore failed or succeeded, 'reserved3' to 'reserved5', and then arguments defined by restore_script_post_arg - unless the option 'restore_script_reserved_args' is off, then only arguments defined by restore_script_post_arg are passed to it. .TP \fBrestore_script_post_arg=[string]\fR A user-definable argument to the restore post script. You can have many of these. .TP \fBrestore_script_post_run_on_fail=[0|1]\fR If this is set to 1, restore_script_post will be run whether the restore succeeds or not. The default is 0, which means that restore_script_post will only be run if the restore succeeds. .TP \fBbackup_script=[path]\fR You can use this to save space in your config file when you want to run the same script before and after a backup. It overrides backup_script_pre and backup_script_post. .TP \fBbackup_script_arg=[path]\fR Goes with backup_script and overrides backup_script_pre_arg and backup_script_post_arg. .TP \fBbackup_script_reserved_args=[0|1]\fR Whether to pass reserved arguments to backup scripts. The default is on. .TP \fBrestore_script=[path]\fR You can use this to save space in your config file when you want to run the same script before and after a restore. It overrides restore_script_pre and restore_script_post. .TP \fBrestore_script_arg=[path]\fR Goes with restore_script and overrides restore_script_pre_arg and restore_script_post_arg. .TP \fBrestore_script_reserved_args=[0|1]\fR Whether to pass reserved arguments to restore scripts. The default is on. .TP \fBautoupgrade_dir=[path]\fR Path to autoupgrade directory into which upgrades are downloaded. Please see docs/autoupgrade.txt in the source package for more help with this option. If you do not want your client to autoupgrade, do not set this option. .TP \fBautoupgrade_os=[string]\fR Name of the client operating system. Should match a directory name in the server's autoupgrade_dir. If you do not want your client to autoupgrade, do not set this option. .TP \fBmonitor_exe=[path]\fR Where to look to find the burp binary to use when forking a monitor client. This might be needed on systems that don't have any sensible way to self-determine a process' own path, such as openbsd. .SH INCLUDES / EXCLUDES .TP The following options specify exactly what is backed up. The client can specify these options, or if you include at least one 'include=' or 'include_glob=' in the client configuration files on the server, the server will override them all. .TP \fBinclude=[path]\fR Path to include in the backup. You can have multiple include lines. Use forward slashes '/', not backslashes '\\' as path delimiters. .TP \fBexclude=[path]\fR Path to exclude from the backup. You can have multiple exclude lines. Use forward slashes '/', not backslashes '\\' as path delimiters. .TP \fBinclude_glob=[glob expression]\fR Include paths that match the glob expression. For example, '/home/*/Documents' will include '/home/user1/Documents' and '/home/user2/Documents' if directories 'user1' and 'user2' exist in '/home'. The Windows implementation currently limits the expression to contain only one '*', with one exception - you may also specify '*:' to expand to detected fixed drives. .TP \fBinclude_regex=[regular expression]\fR Include paths that match the regular expression. You need at least one 'include' or 'include_glob' option to have files to be matched against the regex. If you have at least one 'include_regex' line, then any path NOT matching the regex will be excluded from your backup. .TP \fBexclude_regex=[regular expression]\fR Exclude paths that match the regular expression. .TP \fBinclude_logic=[logic expression]\fR Not yet implemented. See 'exclude_logic' for details on the 'logic expression' syntax. .TP \fBexclude_logic=[logic expression]\fR Exclude paths that match the 'logic expression'. A 'logic expression' may contain several tests chained with boolean operators. The supported operators are: 'and', 'or', 'not' as well as groups of expressions surrounded by parentheses. The supported tests are: 'file_size', 'file_match', 'path_match' and 'file_ext'. The 'file_size' test supports comparisons with '>', '>=', '<', '<=', '=' and take a size as parameter, example: 'file_size<=5Mb'. The 'file_ext' test takes an extension as parameter, example: 'file_ext=pst'. Finally, the 'file_match' and 'path_match' tests take a regular expression as parameter, example: file_match=b$. 'file_match' is ran against the filename (example 'file1') while 'path_match' is ran against the full path of the file (example: '/home/test/file1'). A complete expression may look like '(file_size>=5Mb and file_size<=10Mb) and (file_ext=pst or file_match=movies) and not file_ext=mp3'. There are some limitations though, white-spaces and parentheses must be escaped inside a test either by quoting the expression or escaping a given character: 'file_match="perso(nnal)?"', 'file_match=a\ filename'. .TP \fBinclude_ext=[extension]\fR Extensions to include in the backup. Case insensitive. Nothing else will be included in the backup. You can have multiple include extension lines. For example, set 'txt' to include files that end in '.txt'. You need to specify an 'include' line so that @name@ knows where to start looking. .TP \fBexclude_ext=[extension]\fR Extensions to exclude from the backup. Case insensitive. You can have multiple exclude extension lines. For example, set 'vdi' to exclude VirtualBox disk images. .TP \fBexclude_comp=[extension]\fR Extensions to exclude from compression. Case insensitive. You can have multiple exclude compression lines. For example, set 'gz' to exclude gzipped files from compression. .TP \fBexclude_fs=[fstype]\fR File systems to exclude from the backup. Case insensitive. You can have multiple exclude file system lines. For example, set 'tmpfs' to exclude tmpfs. @human_name@ has an internal mapping of file system names to file system IDs. If you know the file system ID, you can use that instead. For example, 'exclude_fs = 0x01021994' will also exclude tmpfs. .TP \fBinclude_fs=[fstype]\fR File systems to include into the backup. Case insensitive. You can have multiple include file system lines. For example, set 'ext4' to include ext4. @human_name@ has an internal mapping of file system names to file system IDs. If you know the file system ID, you can use that instead. For example, 'include_fs = 0x01021994' will also include tmpfs. If at least one file system is included, all other filesystems will be excluded per default. Included directories that do not live on an included file system will be skipped, even if \fBcross_all_filesystems\fR is enabled and they contain subdirectories with included file systems. Note that on SunOS systems \fBinclude_fs\fR and \fBexclude_fs\fR will do a case sensitive compare of the string descriptors of the file systems instead of the numeric IDs (see \fBf_basetype\fR member is \fBstruct statvfs\fR). .TP \fBmin_file_size=[B/KB/MB/GB]\fR Do not back up files that are less than the specified size. Example: 'min_file_size = 10MB'. Set to 0 (the default) to have no limit. .TP \fBmax_file_size=[B/KB/MB/GB]\fR Do not back up files that are greater than the specified size. Example: 'max_file_size = 10MB'. Set to 0 (the default) to have no limit. .TP \fBcross_filesystem=[path]\fR Allow backups to cross a particular filesystem mountpoint. .TP \fBcross_all_filesystems=[0|1]\fR Allow backups to cross all filesystem mountpoints. .TP \fBnobackup=[file name]\fR If this file system entry exists, the content of the directory containing it will not be backed up. .TP \fBread_fifo=[path]\fR Do not back up the given fifo itself, but open it for reading and back up the contents as if it were a regular file. .TP \fBread_all_fifos=[0|1]\fR Open all fifos for reading and back up the contents as if they were regular files. .TP \fBread_blockdev=[path]\fR Do not back up the given block device itself, but open it for reading and back up the contents as if it were a regular file. .TP \fBread_all_blockdevs=[0|1]\fR Open all block devices for reading and back up the contents as if they were regular files. .TP \fBsplit_vss=[0|1]\fR When backing up Windows computers with @name@ protocol 1, this option allows you to save the VSS header data separate from the file data. The default is off, which means that the VSS header data is saved prepended to the file data. This option has no effect in protocol 2. .TP \fBstrip_vss=[0|1]\fR When backing up Windows computers with @name@ protocol 1, this option allows you to prevent the VSS header data being backed up. The default is off. To restore a backup that has no VSS information on Windows, you need to give the client the '\-x' command line option. This option has no effect in protocol 2. .TP \fBvss_drives=[list of drive letters]\fR When backing up Windows computers, this option allows you to specify which drives have VSS snapshots taken of them. If you omit this option, @name@ will automatically decide based on the 'include' options. If you want no drives to have snapshots taken of them, you can specify '0'. .TP \fBacl=[0|1]\fR If acl support is compiled into @name@, this allows you to decide whether or not to backup acls at runtime. The default is '1'. .TP \fBxattr=[0|1]\fR If xattr support is compiled into @name@, this allows you to decide whether or not to backup xattrs at runtime. The default is '1'. .TP \fBatime=[0|1]\fR This allows you to control whether the client uses O_NOATIME when opening files and directories. The default is 0, which enables O_NOATIME. This means that the client can read files and directories without updating the access times. However, this is only possible if you are running as root, or are the owner of the file or directory. If this is not the case (perhaps you only have group or world access to the files), you will get errors until you set atime=1. With atime=1, the access times will be updated on the files and directories that get backed up. .TP \fBscan_problem_raises_error=[0|1]\fR When enabled, this causes problems in the phase1 scan (such as an 'include' being missing) to be treated as fatal errors. The default is off. .SH SERVER CLIENTCONFDIR FILE .TP For the server to know about clients that can contact it, you need to place a file named after the client in clientconfdir. Files beginning with '.' or ending with '~' are ignored. Directories are also ignored. .TP The file name must match the name in the 'cname' field on the client. .TP \fBssl_peer_cn=[string]\fR must match the common name in the SSL certificate that the client gives when it connects. If ssl_peer_cn is not set, the client name will be used instead (the clientconfdir file name). .TP The file needs to contain a line like \fBpassword=[password]\fR that matches the same field on the client, or \fBpasswd=[hash]\fR \- where the plain text password on the client will be tested against a hash of the kind you might find in /etc/passwd. .TP Additionally, the following options can be overridden here for each client: \fBclient_can_delete\fR \fBclient_can_force_backup\fR \fBclient_can_list\fR \fBclient_can_monitor\fR \fBclient_can_restore\fR \fBclient_can_verify\fR \fBclient_lockdir\fR \fBcompression\fR \fBdedup_group\fR \fBdirectory\fR \fBdirectory_tree\fR \fBenabled\fR \fBfail_on_warning\fR \fBhard_quota\fR \fBkeep\fR \fBlabel\fR \fBlibrsync\fR \fBlibrsync_max_size\fR \fBmanual_delete\fR \fBnetwork_allow\fR \fBnetwork_allow_status\fR \fBnotify_failure_arg\fR \fBnotify_failure_on_backup_with_failovers_left\fR \fBnotify_failure_on_backup_working_dir_deletion\fR \fBnotify_failure_script\fR \fBnotify_success_arg\fR \fBnotify_success_script\fR \fBnotify_success_warnings_only\fR \fBpassword_check\fR \fBpath_length_warn\fR \fBprotocol\fR \fBrblk_memory_max\fR \fBrestore_client\fR \fBserver_script_arg\fR \fBserver_script\fR \fBserver_script_notify\fR \fBserver_script_post_arg\fR \fBserver_script_post\fR \fBserver_script_post_notify\fR \fBserver_script_post_run_on_fail\fR \fBserver_script_pre_arg\fR \fBserver_script_pre\fR \fBserver_script_pre_notify\fR \fBsoft_quota\fR \fBsuper_client\fR \fBsyslog\fR \fBtimer_arg\fR \fBtimer_repeat_interval\fR \fBtimer_script\fR \fBtimestamp_format\fR \fBversion_warn\fR \fBworking_dir_recovery_method\fR .TP Additionally, the includes and excludes can be overridden here, as described in the section above. .TP As with the other configuration files, extra configuration can be included with the '. path/to/config/file' syntax. .SH Some notes on SSL certificates .TP The @name@ example configs come with example SSL certificates and keys. You can use these and @name@ will work. But if you are worried about network security, you should generate your own certificates and keys and point your config files to them. To create the example files, I used a handy interface to openssl, called 'tinyca' (http://tinyca.sm-zone.net/). If you are using Debian, you can run 'apt-get install tinyca' to get it. There is also the option of using @name@_ca, which you can find in the source distribution, courtesy of Patrick Koppen. .SH Examining backups .TP As well as using the client list options described above, you can go directly to the storage directory on the server. The backups for a client are in the directory named after the client. Inside each backup directory is a file called manifest.gz. .TP This contains a list of all the files in the backup, and where they originally came from on the client. .TP There is also a 'log.gz' file in the backup directory, which contains the output generated by the server during the backup. .TP The 'data' directory contains complete backup files. .TP The 'deltas.reverse' directory contains reverse deltas that can be applied to the data from the next backup in the sequence (indicated by the contents of the 'forward' file). .TP Anything with a .gz suffix is compressed in zlib (gzip) format. You can use standard tools, such as zcat, zless or cp, to view them or copy them elsewhere. Files from Windows backups will probably contain VSS headers and/or footers. For help stripping these, see the vss_strip man page. .SH Server initiated backups .TP You can queue a backup on the server, to be performed when the client next makes contact. To do this, you put a file called 'backup' into the top level of the client storage directory. The contents of the file are ignored. .SH Server initiated restores .TP You can queue a restore on the server, to be performed when the client next makes contact. To do this, you put a file called 'restore' into the top level of the client storage directory. The client can deny server initiated restores by setting "server_can_restore=0" in its @name@.conf. The client also needs to specify 'restoreprefix' in its configuration as a destination for the restored files. Valid fields to include in the restore file are: .TP \fBorig_client=[client]\fR The original client to restore from. Equivalent to '\-C' when initiating a restore from a client. Do not include this line when restoring to the original client. See also the 'restore_client' and 'super_client' server options. .TP \fBbackup=[number|a]\fR The number of the backup to restore from. Equivalent to '\-b' when initiating a restore from the client. .TP \fBoverwrite=[0|1]\fR Whether to overwrite existing files. Equivalent to '\-f' when initiating a restore from the client. .TP \fBstrip=[number]\fR Number of leading path components to strip. Equivalent to '\-s' when initiating a restore from the client. .TP \fBrestoreprefix=[path]\fR Appended to the client-side 'restoreprefix' setting, and then prefixed to the restore path. .TP \fBstripfrompath=[string]\fR Strip matching string from restore paths (before prefix is prepended). .TP \fBregex=[regular expression]\fR Only restore files matching the regular expression. Equivalent to '\-r' when initiating a restore from the client. .TP \fBinclude=[path]\fR Restore directories and files that match the path. If it is a directory, the contents of the directory will be restored. You can have multiple 'include' lines. There is no equivalent when initiating a restore from the client. .SH SIGNALS Sending signal 1 (HUP) to the main server process will cause it to reload. For the vast majority of configuration changes, a reload is unnecessary as the server will pick up changes "on-the-fly". Sending signal 12 (USR2) to the main server process will cause it to wait until there are no longer any child processes, and then exit. The intention is to help with upgrades without interrupting current backups. if you are running upstart, a new @name@ server process will start up when the old one exits. .SH RETURN CODES (SERVER) 0: success .br 1: error .SH RETURN CODES (CLIENT) 0: success .br 1: error .br 2: restore gave warnings .br 3: timer conditions on the server were not met .br 4: could not connect to server .br 5: max parallel backups reached .SH BUGS If you find bugs, please report them to the email list. See the website <@package_url@> for details. .SH AUTHOR The main author of @human_name@ is Graham Keeling. .SH COPYRIGHT See the LICENCE file included with the source distribution. burp-2.4.0/manpages/burp_ca.8.in000066400000000000000000000031411404341324700163720ustar00rootroot00000000000000.TH @name@_ca 8 "February 10, 2012" "" "@name@_ca" .SH NAME @name@_ca \- program for generating certificates for use with @name@ .SH SYNOPSIS .B @name@_ca .RI [ options ] .br .LP A script for generating certificates for use with @name@. This script comes with the @name@ backup and restore package, and was contributed by Patrick Koppen. .SH OPTIONS .TP \fB\-h|\-\-help\fR \fB\fR show help .TP \fB\-i|\-\-init\fR \fB\fR inititalize CA .TP \fB\-k|\-\-key\fR \fB\fR generate new key .TP \fB\-r|-\-request \fR \fB\fR generate certificate sign request .TP \fB\-s|\-\-sign\fR \fB\fR sign csr (use \-\-ca and \-\-name ) .IP \fB\-\-batch\fR \fB\fR do not prompt for anything .IP \fB\-\-revoke\fR \fB\fR revoke certificate with serial number .IP \fB\-\-crl\fR \fB\fR generate certificate revoke list .TP \fB\-d|\-\-dir\fR \fB\fR ca output dir (default: /etc/@name@/CA) .TP \fB\-c|\-\-config\fR \fB\fR config file (default: /etc/@name@/CA.cnf) .TP \fB\-n|\-\-name\fR \fB\fR name (default: builder) .TP \fB\-D|\-\-days\fR \fB\fR valid days for certificate (default in config file) .IP \fB\-\-ca_days\fR \fB\fR valid days for CA certificate (default: 3650) .TP \fB\-S|\-\-size\fR \fB\fR key size (default: 2048) .TP \fB\-a|\-\-ca\fR \fB\fR ca name if different from name .TP \fB\-f|\-\-dhfile\fR \fB\fR generate a new dhfile .SH .SH BUGS If you find bugs, please report them to the email list. See the website <@package_url@> for details. .SH AUTHOR Patrick Koppen wrote @name@_ca. The main author of @human_name@ is Graham Keeling. .SH COPYRIGHT See the LICENCE file included with the source distribution. burp-2.4.0/manpages/vss_strip.8.in000066400000000000000000000022051404341324700170130ustar00rootroot00000000000000.\"Created with GNOME Manpages Editor Wizard .\"http://sourceforge.net/projects/gmanedit2 .TH vss_strip 8 "November 2, 2012" "" "vss_strip" .SH NAME vss_strip \- program for extracting file data from a file containing VSS headers .SH SYNOPSIS .B vss_strip .RI [ options ] .br .LP A program for extracting file data from a file containing VSS headers. This program comes with the @name@ backup and restore package. .SH OPTIONS .TP \fB\-i\fR \fBpath\fR Input file. If -i is not given, input will be read on stdin. .TP \fB\-o\fR \fBpath\fR Output file. If -o is not given, output will be read on stdout. .TP \fB\-p\fR Print VSS header info. .TP \fB\-h|-?\fR \fB\fR Print help text and exit. .SH EXAMPLES .TP \fBvss_strip -i infile -o outfile\fR Takes uncompressed infile, and extracts the file data to outfile. .TP \fBzcat infile.gz | vss_strip\fR Takes the output of zcat as input, and outputs to stdout. .SH BUGS If you find bugs, please report them to the email list. See the website <@package_url@> for details. .SH AUTHOR The main author of @human_name@ is Graham Keeling. .SH COPYRIGHT See the LICENCE file included with the source distribution. burp-2.4.0/src/000077500000000000000000000000001404341324700132565ustar00rootroot00000000000000burp-2.4.0/src/action.h000066400000000000000000000006551404341324700147120ustar00rootroot00000000000000#ifndef _ACTION_H #define _ACTION_H // There is probably somewhere better to put these. enum action { ACTION_UNSET=-1, ACTION_BACKUP, ACTION_RESTORE, ACTION_VERIFY, ACTION_LIST, ACTION_LIST_LONG, ACTION_LIST_PARSEABLE, ACTION_BACKUP_TIMED, ACTION_STATUS, ACTION_STATUS_SNAPSHOT, ACTION_ESTIMATE, ACTION_DELETE, ACTION_TIMER_CHECK, ACTION_CHAMP_CHOOSER, ACTION_DIFF, ACTION_DIFF_LONG, ACTION_MONITOR, }; #endif burp-2.4.0/src/alloc.c000066400000000000000000000040301404341324700145110ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "log.h" #ifdef UTEST int alloc_debug=0; /* To use alloc_debug: CK_FORK=no ./runner > /tmp/out grep freed /tmp/out | cut -f 1 -d ' ' | sort > /tmp/freed && grep alloced /tmp/out | cut -f 1 -d ' ' | sort > /tmp/alloced && diff -u /tmp/alloced /tmp/freed */ int alloc_errors=0; uint64_t alloc_count=0; uint64_t free_count=0; void alloc_counters_reset(void) { alloc_count=0; free_count=0; } static char *errored(const char *func) { log_oom_w(__func__, func); return NULL; } #endif char *strdup_w(const char *s, const char *func) { char *ret; #ifdef UTEST if(alloc_errors) return errored(func); #endif if(!(ret=strdup(s))) log_oom_w(__func__, func); #ifdef UTEST else { alloc_count++; if(alloc_debug) printf("%p alloced s\n", ret); } #endif return ret; } void *realloc_w(void *ptr, size_t size, const char *func) { void *ret; #ifdef UTEST int already_alloced=0; if(alloc_errors) return errored(func); if(ptr) { already_alloced=1; if(alloc_debug) printf("%p freed r\n", ptr); } #endif if(!(ret=realloc(ptr, size))) log_oom_w(__func__, func); #ifdef UTEST else if(!already_alloced) alloc_count++; if(alloc_debug) printf("%p alloced r\n", ret); #endif return ret; } void *malloc_w(size_t size, const char *func) { void *ret; #ifdef UTEST if(alloc_errors) return errored(func); #endif if(!(ret=malloc(size))) log_oom_w(__func__, func); #ifdef UTEST else { alloc_count++; if(alloc_debug) printf("%p alloced m\n", ret); } #endif return ret; } void *calloc_w(size_t nmem, size_t size, const char *func) { void *ret; #ifdef UTEST if(alloc_errors) return errored(func); #endif if(!(ret=calloc(nmem, size))) log_oom_w(__func__, func); #ifdef UTEST else { alloc_count++; if(alloc_debug) printf("%p alloced c\n", ret); } #endif return ret; } void free_v(void **ptr) { if(!ptr || !*ptr) return; #ifdef UTEST if(alloc_debug) printf("%p freed\n", *ptr); #endif free(*ptr); *ptr=NULL; #ifdef UTEST free_count++; #endif } void free_w(char **str) { free_v((void **)str); } burp-2.4.0/src/alloc.h000066400000000000000000000010001404341324700145100ustar00rootroot00000000000000#ifndef _ALLOC_H #define _ALLOC_H #include "burp.h" #ifdef UTEST extern int alloc_errors; extern uint64_t alloc_count; extern uint64_t free_count; extern void alloc_counters_reset(void); #endif extern char *strdup_w(const char *s, const char *func); extern void *realloc_w(void *ptr, size_t size, const char *func); extern void *malloc_w(size_t size, const char *func); extern void *calloc_w(size_t nmem, size_t size, const char *func); extern void free_v(void **ptr); extern void free_w(char **str); #endif burp-2.4.0/src/asfd.c000066400000000000000000000436741404341324700143550ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "asfd.h" #include "async.h" #include "cmd.h" #include "fsops.h" #include "handy.h" #include "iobuf.h" #include "log.h" #include "server/protocol2/champ_chooser/incoming.h" // For IPTOS / IPTOS_THROUGHPUT. #ifdef HAVE_WIN32 #include #else #include #endif #ifdef HAVE_NCURSES_H #include #elif HAVE_NCURSES_NCURSES_H #include #endif #include "protocol2/blist.h" static void truncate_readbuf(struct asfd *asfd) { asfd->readbuf[0]='\0'; asfd->readbuflen=0; } static int asfd_alloc_buf(struct asfd *asfd, char **buf) { if(!*buf && !(*buf=(char *)calloc_w(1, asfd->bufmaxsize, __func__))) return -1; return 0; } static int extract_buf(struct asfd *asfd, size_t len, size_t offset) { if(offset+len>=asfd->bufmaxsize) { logp("%s: offset(%lu)+len(%lu)>=asfd->bufmaxsize(%lu) in %s!", asfd->desc, (unsigned long)offset, (unsigned long)len, (unsigned long)asfd->bufmaxsize, __func__); return -1; } if(!(asfd->rbuf->buf=(char *)malloc_w(len+1, __func__))) return -1; if(!(memcpy(asfd->rbuf->buf, asfd->readbuf+offset, len))) { logp("%s: memcpy failed in %s\n", asfd->desc, __func__); return -1; } asfd->rbuf->buf[len]='\0'; if(!(memmove(asfd->readbuf, asfd->readbuf+len+offset, asfd->readbuflen-len-offset))) { logp("%s: memmove failed in %s\n", asfd->desc, __func__); return -1; } asfd->readbuflen-=len+offset; asfd->rbuf->len=len; return 0; } #ifdef HAVE_NCURSES static int parse_readbuf_ncurses(struct asfd *asfd) { if(!asfd->readbuflen) return 0; // This is reading ints, and will be cast back to an int when it comes // to be processed later. if(extract_buf(asfd, asfd->readbuflen, 0)) return -1; return 0; } #endif static int parse_readbuf_line_buf(struct asfd *asfd) { static char *cp=NULL; static char *dp=NULL; static size_t len=0; if(!cp) { // Only start from the beginning if we previously got something // to extract. cp=asfd->readbuf; len=0; } for(; lenreadbuflen; cp++, len++) { if(*cp!='\n') continue; len++; if(extract_buf(asfd, len, 0)) return -1; // Strip trailing white space, like '\r\n'. dp=asfd->rbuf->buf; for(cp=&(dp[len-1]); cp>=dp && isspace(*cp); cp--, len--) *cp='\0'; asfd->rbuf->len=len; break; } cp=NULL; return 0; } static int parse_readbuf_standard(struct asfd *asfd) { unsigned int s=0; char command; if(asfd->readbuflen<5) return 0; if((sscanf(asfd->readbuf, "%c%04X", &command, &s))!=2) { logp("%s: sscanf of '%s' failed in %s\n", asfd->desc, asfd->readbuf, __func__); return -1; } if(s>=asfd->bufmaxsize) { logp("%s: given buffer length '%d', which is too big!\n", asfd->desc, s); return -1; } if(asfd->readbuflen>=s+5) { asfd->rbuf->cmd=(enum cmd)command; if(extract_buf(asfd, (size_t)s, 5)) return -1; } return 0; } static int asfd_parse_readbuf(struct asfd *asfd) { if(asfd->rbuf->buf) return 0; if(asfd->parse_readbuf_specific(asfd)) { truncate_readbuf(asfd); return -1; } return 0; } #ifdef HAVE_NCURSES static int asfd_do_read_ncurses(struct asfd *asfd) { static int i; i=getch(); asfd->readbuflen=sizeof(int); memcpy(asfd->readbuf, &i, asfd->readbuflen); return 0; } static int asfd_do_write_ncurses(__attribute__ ((unused)) struct asfd *asfd) { logp("This function should not have been called: %s\n", __func__); return -1; } #endif static int asfd_do_read(struct asfd *asfd) { ssize_t r; r=read(asfd->fd, asfd->readbuf+asfd->readbuflen, asfd->bufmaxsize-asfd->readbuflen); if(r<0) { if(errno==EAGAIN || errno==EINTR) return 0; logp("%s: read problem on fd %d: %s\n", asfd->desc, asfd->fd, strerror(errno)); goto error; } else if(!r) { // End of data. logp("%s: end of data\n", asfd->desc); goto error; } asfd->readbuflen+=r; asfd->rcvd+=r; return 0; error: truncate_readbuf(asfd); return -1; } static void peer_msg(void) { logp("This is probably caused by the peer exiting.\n"); logp("Please check the peer's logs.\n"); } static int asfd_do_read_ssl(struct asfd *asfd) { int e; ssize_t r; asfd->read_blocked_on_write=0; ERR_clear_error(); r=SSL_read( asfd->ssl, asfd->readbuf+asfd->readbuflen, asfd->bufmaxsize-asfd->readbuflen ); switch((e=SSL_get_error(asfd->ssl, r))) { case SSL_ERROR_NONE: asfd->readbuflen+=r; asfd->rcvd+=r; break; case SSL_ERROR_ZERO_RETURN: // End of data. logp("%s: Peer closed SSL session\n", asfd->desc); SSL_shutdown(asfd->ssl); goto error; case SSL_ERROR_WANT_READ: break; case SSL_ERROR_WANT_WRITE: asfd->read_blocked_on_write=1; break; case SSL_ERROR_SYSCALL: if(errno==EAGAIN || errno==EINTR) break; logp("%s: Got network read error\n", asfd->desc); // Fall through to read problem default: asfd->errors++; logp_ssl_err( "%s: network read problem in %s: %d - %d=%s\n", asfd->desc, __func__, e, errno, strerror(errno)); peer_msg(); goto error; } return 0; error: truncate_readbuf(asfd); return -1; } // Return 0 for OK to write, non-zero for not OK to write. static int check_ratelimit(struct asfd *asfd) { float f; time_t diff; if(!asfd->rlstart) asfd->rlstart=time(NULL); if((diff=asfd->as->now-asfd->rlstart)<0) { // It is possible that the clock changed. Reset ourselves. asfd->as->now=asfd->rlstart; asfd->rlbytes=0; logp("Looks like the clock went back in time since starting. " "Resetting ratelimit\n"); return 0; } if(!diff) return 0; // Need to get started somehow. f=(asfd->rlbytes)/diff; // Bytes per second. if(f>=asfd->ratelimit) { #ifdef HAVE_WIN32 // Windows Sleep is milliseconds, usleep is microseconds. // Do some conversion. Sleep(asfd->rlsleeptime/1000); #else usleep(asfd->rlsleeptime); #endif // If sleeping, increase the sleep time. if((asfd->rlsleeptime*=2)>=500000) asfd->rlsleeptime=500000; return 1; } // If not sleeping, decrease the sleep time. if((asfd->rlsleeptime/=2)<=9999) asfd->rlsleeptime=10000; return 0; } static int asfd_do_write(struct asfd *asfd) { ssize_t w; if(asfd->ratelimit && check_ratelimit(asfd)) return 0; w=write(asfd->fd, asfd->writebuf, asfd->writebuflen); if(w<0) { if(errno==EAGAIN || errno==EINTR) return 0; logp("%s: Got error in %s, (%d=%s)\n", __func__, asfd->desc, errno, strerror(errno)); asfd->errors++; return -1; } else if(!w) { logp("%s: Wrote nothing in %s\n", asfd->desc, __func__); asfd->errors++; return -1; } if(asfd->ratelimit) asfd->rlbytes+=w; asfd->sent+=w; /* { char buf[100000]=""; snprintf(buf, w+1, "%s", asfd->writebuf); printf("wrote %d: %s\n", w, buf); } */ memmove(asfd->writebuf, asfd->writebuf+w, asfd->writebuflen-w); asfd->writebuflen-=w; return 0; } static int asfd_do_write_ssl(struct asfd *asfd) { int e; ssize_t w; asfd->write_blocked_on_read=0; if(asfd->ratelimit && check_ratelimit(asfd)) return 0; ERR_clear_error(); w=SSL_write(asfd->ssl, asfd->writebuf, asfd->writebuflen); switch((e=SSL_get_error(asfd->ssl, w))) { case SSL_ERROR_NONE: /* { char buf[100000]=""; snprintf(buf, w+1, "%s", asfd->writebuf); printf("wrote %d: %s\n", w, buf); } */ if(asfd->ratelimit) asfd->rlbytes+=w; memmove(asfd->writebuf, asfd->writebuf+w, asfd->writebuflen-w); asfd->writebuflen-=w; asfd->sent+=w; break; case SSL_ERROR_WANT_WRITE: break; case SSL_ERROR_WANT_READ: asfd->write_blocked_on_read=1; break; case SSL_ERROR_SYSCALL: if(errno==EAGAIN || errno==EINTR) break; logp("%s: Got network write error\n", asfd->desc); // Fall through to write problem default: asfd->errors++; logp_ssl_err( "%s: network write problem in %s: %d - %d=%s\n", asfd->desc, __func__, e, errno, strerror(errno)); peer_msg(); return -1; } return 0; } static int append_to_write_buffer(struct asfd *asfd, const char *buf, size_t len) { memcpy(asfd->writebuf+asfd->writebuflen, buf, len); asfd->writebuflen+=len; asfd->writebuf[asfd->writebuflen]='\0'; return 0; } static enum append_ret asfd_append_all_to_write_buffer(struct asfd *asfd, struct iobuf *wbuf) { switch(asfd->streamtype) { case ASFD_STREAM_STANDARD: { size_t sblen=0; char sbuf[10]=""; if(asfd->writebuflen+6+(wbuf->len)>=asfd->bufmaxsize-1) return APPEND_BLOCKED; snprintf(sbuf, sizeof(sbuf), "%c%04X", wbuf->cmd, (unsigned int)wbuf->len); sblen=strlen(sbuf); append_to_write_buffer(asfd, sbuf, sblen); break; } case ASFD_STREAM_LINEBUF: if(asfd->writebuflen+wbuf->len>=asfd->bufmaxsize-1) return APPEND_BLOCKED; break; case ASFD_STREAM_NCURSES_STDIN: default: logp("%s: unknown asfd stream type in %s: %d\n", asfd->desc, __func__, asfd->streamtype); return APPEND_ERROR; } append_to_write_buffer(asfd, wbuf->buf, wbuf->len); //printf("append %s\n", iobuf_to_printable(wbuf)); wbuf->len=0; return APPEND_OK; } #ifdef IPTOS_THROUGHPUT static int asfd_connection_af(struct asfd *asfd) { struct sockaddr_storage s; socklen_t slen = sizeof(s); memset(&s, 0, sizeof(s)); if(getsockname(asfd->fd, (struct sockaddr *)&s, &slen)<0) return 0; return s.ss_family; } #endif static int asfd_set_bulk_packets(struct asfd *asfd) { #ifdef IPTOS_THROUGHPUT int opt=IPTOS_THROUGHPUT; if(asfd->fd<0) return -1; switch(asfd_connection_af(asfd)) { case AF_INET: if(setsockopt(asfd->fd, IPPROTO_IP, IP_TOS, (char *)&opt, sizeof(opt))>=0) break; logp("%s: error: set IPTOS throughput: %s\n", asfd->desc, strerror(errno)); return -1; case AF_INET6: if(setsockopt(asfd->fd, IPPROTO_IPV6, IPV6_TCLASS, (char *)&opt, sizeof(opt))>=0) break; logp("%s: error: set IPV6_TCLASS throughput: %s\n", asfd->desc, strerror(errno)); return -1; } #endif return 0; } static int asfd_read(struct asfd *asfd) { if(asfd->as->doing_estimate) return 0; while(!asfd->rbuf->buf) { if(asfd->errors) return -1; if(asfd->as->read_write(asfd->as)) return -1; } return 0; } int asfd_read_expect(struct asfd *asfd, enum cmd cmd, const char *expect) { int ret=0; if(asfd->read(asfd)) return -1; if(asfd->rbuf->cmd!=cmd || strcmp(asfd->rbuf->buf, expect)) { logp("%s: expected '%c:%s', got '%s'\n", asfd->desc, cmd, expect, iobuf_to_printable(asfd->rbuf)); ret=-1; } iobuf_free_content(asfd->rbuf); return ret; } static int asfd_write(struct asfd *asfd, struct iobuf *wbuf) { if(asfd->as->doing_estimate) return 0; while(wbuf->len) { if(asfd->errors) return -1; if(asfd->append_all_to_write_buffer(asfd, wbuf)==APPEND_ERROR) return -1; if(asfd->as->write(asfd->as)) return -1; } return 0; } static int asfd_write_str(struct asfd *asfd, enum cmd wcmd, const char *wsrc) { struct iobuf wbuf; wbuf.cmd=wcmd; wbuf.buf=(char *)wsrc; wbuf.len=strlen(wsrc); return asfd->write(asfd, &wbuf); } #ifndef UTEST static #endif int asfd_simple_loop(struct asfd *asfd, struct conf **confs, void *param, const char *caller, enum asl_ret callback(struct asfd *asfd, struct conf **confs, void *param)) { struct iobuf *rbuf=asfd->rbuf; while(1) { iobuf_free_content(rbuf); if(asfd->read(asfd)) goto error; if(!rbuf->buf) continue; if(rbuf->cmd!=CMD_GEN) { if(rbuf->cmd==CMD_WARNING || rbuf->cmd==CMD_MESSAGE) { struct cntr *cntr=NULL; if(confs) cntr=get_cntr(confs); log_recvd(rbuf, cntr, 0); } else if(rbuf->cmd==CMD_INTERRUPT) { // Ignore - client wanted to interrupt a file. } else { logp("%s: unexpected command in %s(), called from %s(): %s\n", asfd->desc, __func__, caller, iobuf_to_printable(rbuf)); goto error; } continue; } switch(callback(asfd, confs, param)) { case ASL_CONTINUE: break; case ASL_END_OK: iobuf_free_content(rbuf); return 0; case ASL_END_OK_RETURN_1: iobuf_free_content(rbuf); return 1; case ASL_END_ERROR: default: goto error; } } error: iobuf_free_content(rbuf); return -1; } static void asfd_set_timeout(struct asfd *asfd, int max_network_timeout) { asfd->max_network_timeout=max_network_timeout; asfd->network_timeout=asfd->max_network_timeout; } static char *get_asfd_desc(const char *desc, int fd) { char r[256]=""; snprintf(r, sizeof(r), "%s %d", desc, fd); return strdup_w(r, __func__); } static int asfd_init(struct asfd *asfd, const char *desc, struct async *as, int afd, const char *listen, SSL *assl, enum asfd_streamtype streamtype) { asfd->as=as; asfd->fd=afd; asfd->ssl=assl; asfd->streamtype=streamtype; asfd->rlsleeptime=10000; asfd->pid=-1; asfd->attempt_reads=1; asfd->bufmaxsize=(ASYNC_BUF_LEN*2)+32; #ifdef HAVE_WIN32 // Windows craps out if you try to read stdin into a buffer that is // too big! if(asfd->fd==fileno(stdin)) asfd->bufmaxsize=4096; #endif asfd->parse_readbuf=asfd_parse_readbuf; asfd->append_all_to_write_buffer=asfd_append_all_to_write_buffer; asfd->set_bulk_packets=asfd_set_bulk_packets; asfd->set_timeout=asfd_set_timeout; if(asfd->ssl) { asfd->do_read=asfd_do_read_ssl; asfd->do_write=asfd_do_write_ssl; } else { asfd->do_read=asfd_do_read; asfd->do_write=asfd_do_write; #ifdef HAVE_NCURSES if(asfd->streamtype==ASFD_STREAM_NCURSES_STDIN) { asfd->do_read=asfd_do_read_ncurses; asfd->do_write=asfd_do_write_ncurses; } #endif } asfd->read=asfd_read; asfd->simple_loop=asfd_simple_loop; asfd->write=asfd_write; asfd->write_str=asfd_write_str; switch(asfd->streamtype) { case ASFD_STREAM_STANDARD: asfd->parse_readbuf_specific=parse_readbuf_standard; break; case ASFD_STREAM_LINEBUF: asfd->parse_readbuf_specific=parse_readbuf_line_buf; break; #ifdef HAVE_NCURSES case ASFD_STREAM_NCURSES_STDIN: asfd->parse_readbuf_specific=parse_readbuf_ncurses; break; #endif default: logp("%s: unknown asfd stream type in %s: %d\n", desc, __func__, asfd->streamtype); return -1; } if(!(asfd->rbuf=iobuf_alloc()) || asfd_alloc_buf(asfd, &asfd->readbuf) || asfd_alloc_buf(asfd, &asfd->writebuf) || !(asfd->desc=get_asfd_desc(desc, asfd->fd)) || !(asfd->listen=strdup_w(listen, __func__))) return -1; return 0; } struct asfd *asfd_alloc(void) { struct asfd *asfd; asfd=(struct asfd *)calloc_w(1, sizeof(struct asfd), __func__); if(asfd) asfd->fd=-1; return asfd; } void asfd_close(struct asfd *asfd) { if(!asfd) return; if(asfd->ssl && asfd->fd>=0) { set_blocking(asfd->fd); // I do not think this SSL_shutdown stuff works right. // Ignore it for now. #ifndef HAVE_WIN32 signal(SIGPIPE, SIG_IGN); #endif if(!SSL_shutdown(asfd->ssl)) { shutdown(asfd->fd, 1); SSL_shutdown(asfd->ssl); } } if(asfd->ssl) { SSL_free(asfd->ssl); asfd->ssl=NULL; } close_fd(&asfd->fd); } static void asfd_free_content(struct asfd *asfd) { asfd_close(asfd); iobuf_free(&asfd->rbuf); free_w(&asfd->readbuf); free_w(&asfd->writebuf); free_w(&asfd->desc); free_w(&asfd->client); free_w(&asfd->listen); incoming_free(&asfd->in); blist_free(&asfd->blist); #ifdef USE_IPACL ipacl_free(&asfd->ipacl); #endif } void asfd_free(struct asfd **asfd) { if(!asfd || !*asfd) return; asfd_free_content(*asfd); free_v((void **)asfd); } static struct asfd *do_setup_asfd(struct async *as, const char *desc, int *fd, const char *listen, SSL *ssl, enum asfd_streamtype streamtype) { struct asfd *asfd=NULL; if(!fd || *fd<0) { logp("Given invalid descriptor in %s\n", __func__); goto error; } set_non_blocking(*fd); if(!(asfd=asfd_alloc()) || asfd_init(asfd, desc, as, *fd, listen, ssl, streamtype)) goto error; *fd=-1; as->asfd_add(as, asfd); return asfd; error: asfd_free(&asfd); return NULL; } struct asfd *setup_asfd_ssl(struct async *as, const char *desc, int *fd, SSL *ssl) { return do_setup_asfd(as, desc, fd, /*listen*/"", ssl, ASFD_STREAM_STANDARD); } struct asfd *setup_asfd(struct async *as, const char *desc, int *fd, const char *listen) { return do_setup_asfd(as, desc, fd, listen, /*ssl*/NULL, ASFD_STREAM_STANDARD); } static struct asfd *setup_asfd_linebuf(struct async *as, const char *desc, int *fd) { return do_setup_asfd(as, desc, fd, /*listen*/"", /*ssl*/NULL, ASFD_STREAM_LINEBUF); } struct asfd *setup_asfd_linebuf_read(struct async *as, const char *desc, int *fd) { return setup_asfd_linebuf(as, desc, fd); } struct asfd *setup_asfd_linebuf_write(struct async *as, const char *desc, int *fd) { struct asfd *asfd; if((asfd=setup_asfd_linebuf(as, desc, fd))) asfd->attempt_reads=0; return asfd; } static struct asfd *fileno_error(const char *func) { logp("fileno error in %s: %s\n", func, strerror(errno)); return NULL; } struct asfd *setup_asfd_stdin(struct async *as) { int fd=fileno(stdin); if(fd<0) return fileno_error(__func__); return setup_asfd_linebuf_read(as, "stdin", &fd); } struct asfd *setup_asfd_stdout(struct async *as) { int fd=fileno(stdout); if(fd<0) return fileno_error(__func__); return setup_asfd_linebuf_write(as, "stdout", &fd); } struct asfd *setup_asfd_ncurses_stdin(struct async *as) { int fd=fileno(stdin); if(fd<0) return fileno_error(__func__); return do_setup_asfd(as, "stdin", &fd, /*listen*/"", /*ssl=*/NULL, ASFD_STREAM_NCURSES_STDIN); } // Want to make sure that we are listening for reads too - this will let us // exit promptly if the client was killed. static int read_and_write(struct asfd *asfd) { // Protect against getting stuck in loops where we are trying to // flush buffers, but keep getting the same error. if(asfd->as->read_write(asfd->as)) return -1; if(!asfd->rbuf->buf) return 0; iobuf_log_unexpected(asfd->rbuf, __func__); return -1; } int asfd_flush_asio(struct asfd *asfd) { while(asfd && asfd->writebuflen>0) { if(asfd->errors) return -1; if(read_and_write(asfd)) return -1; } return 0; } int asfd_write_wrapper(struct asfd *asfd, struct iobuf *wbuf) { while(1) { if(asfd->errors) return -1; switch(asfd->append_all_to_write_buffer(asfd, wbuf)) { case APPEND_OK: return 0; case APPEND_BLOCKED: break; default: return -1; } if(read_and_write(asfd)) return -1; } return 0; } int asfd_write_wrapper_str(struct asfd *asfd, enum cmd wcmd, const char *wsrc) { static struct iobuf wbuf; iobuf_from_str(&wbuf, wcmd, (char *)wsrc); return asfd_write_wrapper(asfd, &wbuf); } burp-2.4.0/src/asfd.h000066400000000000000000000067441404341324700143570ustar00rootroot00000000000000#ifndef _ASFD_H #define _ASFD_H #include "burp.h" #include "cmd.h" #include "ssl.h" #include "cntr.h" #include "ipacl.h" // Return values for simple_loop(). enum asl_ret { ASL_END_ERROR=-1, ASL_CONTINUE=0, ASL_END_OK=1, ASL_END_OK_RETURN_1=2 }; enum asfd_streamtype { ASFD_STREAM_STANDARD=0, ASFD_STREAM_LINEBUF, ASFD_STREAM_NCURSES_STDIN }; enum asfd_fdtype { ASFD_FD_UNSET=0, ASFD_FD_SERVER_LISTEN_MAIN, ASFD_FD_SERVER_LISTEN_STATUS, ASFD_FD_SERVER_PIPE_READ, ASFD_FD_SERVER_PIPE_WRITE, }; enum append_ret { APPEND_ERROR=-1, APPEND_OK=0, APPEND_BLOCKED=1 }; // Async file descriptor. Can add these to a struct async. struct asfd { int fd; SSL *ssl; struct async *as; char *desc; enum asfd_streamtype streamtype; char *listen; const char *peer_addr; #ifdef USE_IPACL struct hipacl ipacl; #endif int network_timeout; int max_network_timeout; float ratelimit; time_t rlstart; int rlsleeptime; uint64_t rlbytes; struct iobuf *rbuf; int attempt_reads; int doread; char *readbuf; size_t readbuflen; int read_blocked_on_write; size_t bufmaxsize; int dowrite; char *writebuf; size_t writebuflen; int write_blocked_on_read; int errors; struct asfd *next; // Stuff for the champ chooser server. struct incoming *in; struct blist *blist; int blkcnt; uint64_t wrap_up; uint8_t want_to_remove; // For the champ chooser server main socket. uint8_t listening_for_new_clients; uint8_t new_client; // For the main server process. pid_t pid; enum asfd_fdtype fdtype; enum cntr_status cntr_status; char *client; // Counters uint64_t sent; uint64_t rcvd; // Function pointers. int (*parse_readbuf)(struct asfd *); int (*parse_readbuf_specific)(struct asfd *); enum append_ret (*append_all_to_write_buffer)(struct asfd *, struct iobuf *); int (*set_bulk_packets)(struct asfd *); void (*set_timeout)(struct asfd *, int max_network_timeout); int (*do_read)(struct asfd *); int (*do_write)(struct asfd *); int (*read)(struct asfd *); int (*simple_loop)(struct asfd *, struct conf **, void *, const char *, enum asl_ret callback(struct asfd *, struct conf **, void *)); int (*write)(struct asfd *, struct iobuf *); int (*write_str)(struct asfd *, enum cmd, const char *); #ifdef UTEST // To assist mocking functions in unit tests. void *data1; void *data2; #endif }; extern struct asfd *asfd_alloc(void); extern void asfd_close(struct asfd *asfd); // Maybe should be in the struct. extern void asfd_free(struct asfd **asfd); extern struct asfd *setup_asfd(struct async *as, const char *desc, int *fd, const char *listen); extern struct asfd *setup_asfd_ssl(struct async *as, const char *desc, int *fd, SSL *ssl); extern struct asfd *setup_asfd_linebuf_read(struct async *as, const char *desc, int *fd); extern struct asfd *setup_asfd_linebuf_write(struct async *as, const char *desc, int *fd); extern struct asfd *setup_asfd_stdin(struct async *as); extern struct asfd *setup_asfd_stdout(struct async *as); extern struct asfd *setup_asfd_ncurses_stdin(struct async *as); extern int asfd_flush_asio(struct asfd *asfd); extern int asfd_write_wrapper(struct asfd *asfd, struct iobuf *wbuf); extern int asfd_write_wrapper_str(struct asfd *asfd, enum cmd wcmd, const char *wsrc); extern int asfd_read_expect(struct asfd *asfd, enum cmd cmd, const char *expect); #ifdef UTEST extern int asfd_simple_loop(struct asfd *asfd, struct conf **confs, void *param, const char *caller, enum asl_ret callback(struct asfd *asfd, struct conf **confs, void *param)); #endif #endif burp-2.4.0/src/async.c000066400000000000000000000153631404341324700145470ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "asfd.h" #include "async.h" #include "handy.h" #include "iobuf.h" #include "log.h" void async_free(struct async **as) { if(!as || !*as) return; free_v((void **)as); } static void async_settimers(struct async *as, int sec, int usec) { as->setsec=sec; as->setusec=usec; } // The normal server and client processes will just exit on error within // async_io, but the champ chooser server needs to manage client fds and // remove them from its list if one of them had a problem. static int asfd_problem(struct asfd *asfd) { asfd->want_to_remove++; asfd->as->last_time=asfd->as->now; return -1; } #ifdef HAVE_WIN32 // Jesus H. Christ. How does anybody ever get anything done with windows? // The normal async stuff does not work for the client monitor, because the // windows select() only supports sockets, and windows stdin/stdout are not // sockets. // It seems to be impossible to do non-blocking i/o on windows stdin. // If you have a windows console, you can use PeekConsoleInput to look at // events in order to look ahead. This is not looking at stdin, which means // that this does not work for ssh via cygwin. static int windows_stupidity_hacks(struct asfd *asfd, fd_set *fsr, fd_set *fsw) { if(asfd->do_read && asfd->fd==fileno(stdin)) { DWORD len=0; INPUT_RECORD irec; HANDLE han=GetStdHandle(STD_INPUT_HANDLE); switch(WaitForSingleObject(han, 0)) { case WAIT_OBJECT_0: if(!PeekConsoleInput(han, &irec, 1, &len)) break; if(irec.EventType==KEY_EVENT && irec.Event.KeyEvent.bKeyDown) { // This will block until the user hits // the return key. if(asfd->do_read(asfd) || asfd->parse_readbuf(asfd)) return asfd_problem(asfd); } else { // Purge event we are not interested in. ReadConsoleInput(han, &irec, 1, &len); } break; default: break; } } if(asfd->do_write && asfd->fd==fileno(stdout)) { // This is saying that we think that stdout is always OK to // write to. Maybe this will not work all the time. FD_SET((unsigned int)asfd->fd, fsw); } return 0; } #endif static int async_io(struct async *as, int doread) { int mfd=-1; fd_set fsr; fd_set fsw; fd_set fse; int dosomething=0; struct timeval tval; struct asfd *asfd; static int s=0; as->now=time(NULL); if(!as->last_time) as->last_time=as->now; if(as->doing_estimate) goto end; FD_ZERO(&fsr); FD_ZERO(&fsw); FD_ZERO(&fse); tval.tv_sec=as->setsec; tval.tv_usec=as->setusec; for(asfd=as->asfd; asfd; asfd=asfd->next) { if(asfd->attempt_reads) asfd->doread=doread; else asfd->doread=0; asfd->dowrite=0; if(doread) { if(asfd->parse_readbuf(asfd)) return asfd_problem(asfd); if(asfd->rbuf->buf || asfd->read_blocked_on_write) asfd->doread=0; } if(asfd->writebuflen && !asfd->write_blocked_on_read) asfd->dowrite++; // The write buffer is not yet empty. if(!asfd->doread && !asfd->dowrite) continue; #ifdef HAVE_WIN32 if(asfd->fd==fileno(stdin) || asfd->fd==fileno(stdout)) { dosomething++; continue; } #endif add_fd_to_sets(asfd->fd, asfd->doread?&fsr:NULL, asfd->dowrite?&fsw:NULL, &fse, &mfd); dosomething++; } if(!dosomething) goto end; /* for(asfd=as->asfd; asfd; asfd=asfd->next) { printf("%s: %d %d %d %d\n", asfd->desc, asfd->doread, asfd->dowrite, asfd->readbuflen, asfd->writebuflen); } */ if(mfd>0) { errno=0; s=select(mfd+1, &fsr, &fsw, &fse, &tval); if(errno==EAGAIN || errno==EINTR) goto end; if(s<0) { logp("select error in %s: %s\n", __func__, strerror(errno)); as->last_time=as->now; return -1; } } for(asfd=as->asfd; asfd; asfd=asfd->next) { #ifdef HAVE_WIN32 if(windows_stupidity_hacks(asfd, &fsr, &fsw)) return -1; #endif if(FD_ISSET(asfd->fd, &fse)) { switch(asfd->fdtype) { case ASFD_FD_SERVER_LISTEN_MAIN: case ASFD_FD_SERVER_LISTEN_STATUS: as->last_time=as->now; return -1; default: logp("%s: had an exception\n", asfd->desc); return asfd_problem(asfd); } } if(asfd->doread && FD_ISSET(asfd->fd, &fsr)) // Able to read. { asfd->network_timeout=asfd->max_network_timeout; switch(asfd->fdtype) { case ASFD_FD_SERVER_LISTEN_MAIN: case ASFD_FD_SERVER_LISTEN_STATUS: // Indicate to the caller that we have // a new incoming client. asfd->new_client++; break; default: if(asfd->do_read(asfd) || asfd->parse_readbuf(asfd)) return asfd_problem(asfd); break; } } if(asfd->dowrite && FD_ISSET(asfd->fd, &fsw)) // Able to write. { asfd->network_timeout=asfd->max_network_timeout; if(asfd->do_write(asfd)) return asfd_problem(asfd); } if((!asfd->doread || !FD_ISSET(asfd->fd, &fsr)) && (!asfd->dowrite || !FD_ISSET(asfd->fd, &fsw))) { // Be careful to avoid 'read quick' mode. if((as->setsec || as->setusec) && asfd->max_network_timeout>0 && as->now-as->last_time>0 && asfd->network_timeout--<=0) { logp("%s: no activity for %d seconds.\n", asfd->desc, asfd->max_network_timeout); return asfd_problem(asfd); } } } end: as->last_time=as->now; return 0; } static int async_read_write(struct async *as) { return async_io(as, 1 /* Read too. */); } static int async_write(struct async *as) { return async_io(as, 0 /* No read. */); } static int async_read_quick(struct async *as) { int r; int savesec=as->setsec; int saveusec=as->setusec; as->setsec=0; as->setusec=0; r=as->read_write(as); // Maybe make an as->read(as) function some time. as->setsec=savesec; as->setusec=saveusec; return r; } static void async_asfd_add(struct async *as, struct asfd *asfd) { struct asfd *x; if(!as->asfd) { as->asfd=asfd; return; } // Add to the end; for(x=as->asfd; x->next; x=x->next) { } x->next=asfd; } static void async_asfd_remove(struct async *as, struct asfd *asfd) { struct asfd *l; if(!asfd) return; if(as->asfd==asfd) { as->asfd=as->asfd->next; return; } for(l=as->asfd; l; l=l->next) { if(l->next!=asfd) continue; l->next=asfd->next; return; } } void async_asfd_free_all(struct async **as) { struct asfd *a=NULL; struct asfd *asfd=NULL; if(!as || !*as) return; for(asfd=(*as)->asfd; asfd; asfd=a) { a=asfd->next; asfd_free(&asfd); } async_free(as); } static int async_init(struct async *as, int estimate) { as->setsec=1; as->setusec=0; as->last_time=0; as->doing_estimate=estimate; as->read_write=async_read_write; as->write=async_write; as->read_quick=async_read_quick; as->settimers=async_settimers; as->asfd_add=async_asfd_add; as->asfd_remove=async_asfd_remove; return 0; } struct async *async_alloc(void) { struct async *as; if(!(as=(struct async *)calloc_w(1, sizeof(struct async), __func__))) return NULL; as->init=async_init; return as; } burp-2.4.0/src/async.h000066400000000000000000000014601404341324700145450ustar00rootroot00000000000000#ifndef _ASYNC_H #define _ASYNC_H #define ASYNC_BUF_LEN 16000 #define ZCHUNK ASYNC_BUF_LEN struct async { struct asfd *asfd; int doing_estimate; int setsec; int setusec; time_t now; time_t last_time; // Let us try using function pointers. int (*init)(struct async *, int); // These two can return without completing the read or write, so check // rbuf->buf and/or wbuf->len. int (*read_write)(struct async *); int (*write)(struct async *); int (*read_quick)(struct async *); void (*asfd_add)(struct async *, struct asfd *); void (*asfd_remove)(struct async *, struct asfd *); void (*settimers)(struct async *, int, int); // For debug purposes. }; extern struct async *async_alloc(void); extern void async_free(struct async **as); extern void async_asfd_free_all(struct async **as); #endif burp-2.4.0/src/attribs.c000066400000000000000000000224041404341324700150740ustar00rootroot00000000000000/* Bacula® - The Network Backup Solution Copyright (C) 2002-2009 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. 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 Affero 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. Bacula® is a registered trademark of Kern Sibbald. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ /* * Encode and decode standard Unix attributes and * Extended attributes for Win32 and * other non-Unix systems, ... */ /* * Some of these functions come from src/findlib/attribs.c in bacula-5.0.3. * Hence, the copyright notice above is retained. * Graham Keeling, 2014 */ #include "burp.h" #include "attribs.h" #include "alloc.h" #include "base64.h" #include "berrno.h" #include "cmd.h" #include "cntr.h" #include "log.h" #include "sbuf.h" // Encode a stat structure into a base64 character string. int attribs_encode(struct sbuf *sb) { static char *p; static struct stat *statp; if(!sb->attr.buf) { sb->attr.cmd=CMD_ATTRIBS; // should not be needed if(!(sb->attr.buf=(char *)malloc_w(256, __func__))) return -1; } p=sb->attr.buf; statp=&sb->statp; if(sb->protocol2) { // Protocol1 does not have this field. p += to_base64(sb->protocol2->index, p); *p++ = ' '; // Protocol2 puts compression/encryption near the beginning. p += to_base64(sb->compression, p); *p++ = ' '; p += to_base64(sb->encryption, p); *p++ = ' '; } p += to_base64(statp->st_dev, p); *p++ = ' '; p += to_base64(statp->st_ino, p); *p++ = ' '; p += to_base64(statp->st_mode, p); *p++ = ' '; p += to_base64(statp->st_nlink, p); *p++ = ' '; p += to_base64(statp->st_uid, p); *p++ = ' '; p += to_base64(statp->st_gid, p); *p++ = ' '; p += to_base64(statp->st_rdev, p); *p++ = ' '; p += to_base64(statp->st_size, p); *p++ = ' '; #ifdef HAVE_WIN32 p += to_base64(0, p); // place holder *p++ = ' '; p += to_base64(0, p); // place holder #else p += to_base64(statp->st_blksize, p); *p++ = ' '; p += to_base64(statp->st_blocks, p); #endif *p++ = ' '; p += to_base64(statp->st_atime, p); *p++ = ' '; p += to_base64(statp->st_mtime, p); *p++ = ' '; p += to_base64(statp->st_ctime, p); *p++ = ' '; #ifdef HAVE_CHFLAGS // chflags is a FreeBSD function. p += to_base64(statp->st_flags, p); #else p += to_base64(0, p); // place holder #endif *p++ = ' '; p += to_base64(sb->winattr, p); if(sb->protocol1) { // Protocol1 puts compression/encryption at the end. *p++ = ' '; p += to_base64(sb->compression, p); *p++ = ' '; p += to_base64(sb->encryption, p); *p++ = ' '; p += to_base64(sb->protocol1->salt, p); } *p = 0; sb->attr.len=p-sb->attr.buf; return 0; } // Do casting according to unknown type to keep compiler happy. #define plug(st, val) st = (__typeof__(st))(val) // Decode a stat packet from base64 characters. void attribs_decode(struct sbuf *sb) { static const char *p; static int64_t val; static struct stat *statp; static int eaten; if(!(p=sb->attr.buf)) return; statp=&sb->statp; if(sb->protocol2) { // In protocol2, the first component (index) sometimes gets // stripped off of the attributes, so look out for that. if(*p!=' ') { // Protocol1 does not have this field. if(!(eaten=from_base64(&val, p))) return; p+=eaten; sb->protocol2->index=val; } // Compression for protocol2. if(!(eaten=from_base64(&val, p))) return; p+=eaten; sb->compression=val; // Encryption for protocol2. if(!(eaten=from_base64(&val, p))) return; p+=eaten; sb->encryption=val; } if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_dev, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_ino, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_mode, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_nlink, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_uid, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_gid, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_rdev, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_size, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; #ifdef HAVE_WIN32 // plug(statp->st_blksize, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; // plug(statp->st_blocks, val); #else plug(statp->st_blksize, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_blocks, val); #endif if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_atime, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_mtime, val); if(!(eaten=from_base64(&val, p))) return; p+=eaten; plug(statp->st_ctime, val); // FreeBSD user flags. if(!(eaten=from_base64(&val, p))) return; p+=eaten; #ifdef HAVE_CHFLAGS statp->st_flags=0; plug(statp->st_flags, val); #endif // Look for winattr. sb->winattr=0; if(!(eaten=from_base64(&val, p))) return; p+=eaten; sb->winattr=val; if(sb->protocol1) { sb->compression=-1; sb->encryption=ENCRYPTION_UNSET; // Compression for protocol1. if(!(eaten=from_base64(&val, p))) return; p+=eaten; sb->compression=val; // Encryption for protocol1. if(!(eaten=from_base64(&val, p))) return; p+=eaten; sb->encryption=val; // Salt for protocol1. if(!(eaten=from_base64(&val, p))) return; p+=eaten; sb->protocol1->salt=val; } } int attribs_set_file_times(struct asfd *asfd, const char *path, struct stat *statp, struct cntr *cntr) { int e; #ifdef HAVE_WIN32 // You (probably) cannot set times on Windows junction points. if(statp->st_rdev==WIN32_JUNCTION_POINT) return 0; // The mingw64 utime() appears not to work on read-only files. // Use the utime() from bacula instead. e=win32_utime(path, statp); #elif HAVE_LUTIMES struct timeval t[2]; t[0].tv_sec = statp->st_atime; t[0].tv_usec = 0; t[1].tv_sec = statp->st_mtime; t[1].tv_usec = 0; e=lutimes(path, t); #else struct timespec ts[2]; ts[0].tv_sec=statp->st_atime; ts[0].tv_nsec=0; ts[1].tv_sec=statp->st_mtime; ts[1].tv_nsec=0; e=utimensat(AT_FDCWD, path, ts, AT_SYMLINK_NOFOLLOW); #endif if(e<0) { struct berrno be; berrno_init(&be); logw(asfd, cntr, "Unable to set file times %s: ERR=%s\n", path, berrno_bstrerror(&be, errno)); return -1; } return 0; } uint64_t decode_file_no(struct iobuf *iobuf) { int64_t val=0; from_base64(&val, iobuf->buf); return (uint64_t)val; } uint64_t decode_file_no_and_save_path(struct iobuf *iobuf, char **save_path) { int64_t val; int eaten; char *p=iobuf->buf; if(!(eaten=from_base64(&val, iobuf->buf))) return 0; *save_path=p+eaten+1; return (uint64_t)val; } int attribs_set(struct asfd *asfd, const char *path, struct stat *statp, uint64_t winattr, struct cntr *cntr) { #ifdef HAVE_WIN32 win32_chmod(path, statp->st_mode, winattr); attribs_set_file_times(asfd, path, statp, cntr); return 0; #endif if(lchown(path, statp->st_uid, statp->st_gid)<0) { struct berrno be; berrno_init(&be); char msg[256]=""; snprintf(msg, sizeof(msg), "Unable to set file owner of %s to %d:%d: ERR=%s", path, statp->st_uid, statp->st_gid, berrno_bstrerror(&be, errno)); if(errno==EPERM) { static int do_owner_warning=1; if(getuid()!=0) { if(!do_owner_warning) return -1; logw(asfd, cntr, "%s - possibly because you are not root. Will suppress subsequent messages of this type.\n", msg); do_owner_warning=0; return -1; } } logw(asfd, cntr, "%s\n", msg); return -1; } /* Watch out, a metadata restore will have cmd set to CMD_METADATA or CMD_ENC_META, but that is OK at the moment because we are not doing meta stuff on links. */ if(S_ISLNK(statp->st_mode)) { if(attribs_set_file_times(asfd, path, statp, cntr)) return -1; } else { if(chmod(path, statp->st_mode) < 0) { struct berrno be; berrno_init(&be); logw(asfd, cntr, "Unable to set file modes %s: ERR=%s\n", path, berrno_bstrerror(&be, errno)); return -1; } if(attribs_set_file_times(asfd, path, statp, cntr)) return -1; #ifdef HAVE_CHFLAGS /* * FreeBSD user flags * * Note, this should really be done before the utime() above, * but if the immutable bit is set, it will make the utimes() * fail. */ if(chflags(path, statp->st_flags)<0) { struct berrno be; berrno_init(&be); logw(asfd, cntr, "Unable to set file flags %s: ERR=%s\n", path, berrno_bstrerror(&be, errno)); return -1; } #endif } return 0; } burp-2.4.0/src/attribs.h000066400000000000000000000010241404341324700150740ustar00rootroot00000000000000#ifndef __ATTRIBS_H #define __ATTRIBS_H #include "sbuf.h" extern int attribs_encode(struct sbuf *sb); extern void attribs_decode(struct sbuf *sb); extern int attribs_set(struct asfd *asfd, const char *path, struct stat *statp, uint64_t winattr, struct cntr *cntr); extern int attribs_set_file_times(struct asfd *asfd, const char *path, struct stat *statp, struct cntr *cntr); extern uint64_t decode_file_no(struct iobuf *iobuf); extern uint64_t decode_file_no_and_save_path(struct iobuf *iobuf, char **save_path); #endif burp-2.4.0/src/base64.c000066400000000000000000000064651404341324700145210ustar00rootroot00000000000000/* Bacula® - The Network Backup Solution Copyright (C) 2000-2007 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. 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 Affero 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. Bacula® is a registered trademark of Kern Sibbald. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ /* * Generic base 64 input and output routines * * Written by Kern E. Sibbald, March MM. */ /* * Originally from bacula-5.0.3:src/lib/base64.c, with minor formatting * changes. * Graham Keeling, 2014. */ #include "burp.h" #include "base64.h" static uint8_t const base64_digits[64]= { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; static uint8_t base64_map[128]; /* Initialize the Base 64 conversion routines */ void base64_init(void) { int i; memset(base64_map, 0, sizeof(base64_map)); for(i=0; i<64; i++) base64_map[(uint8_t)base64_digits[i]]=i; } /* * Convert a value to base64 characters. The result is stored in where, which * must be at least 13 bytes long. * * Returns the number of characters stored (not including the EOS). */ int to_base64(int64_t value, char *where) { uint64_t val; int i=0; int n; /* Handle negative values */ if(value<0) { where[i++]='-'; value=-value; } /* Determine output size */ val=value; do { val>>=6; i++; } while(val); n=i; /* Output characters */ val=value; where[i]=0; do { where[--i]=base64_digits[val & (uint64_t)0x3F]; val>>=6; } while(val); return n; } /* * Convert the Base 64 characters in where to a value. * * Returns the number of characters converted. */ int from_base64(int64_t *value, const char *where) { uint64_t val=0; int i=0; int neg=0; if(where[i]==' ') i++; /* Check if it is negative */ if(where[i]=='-') { i++; neg=1; } /* Construct value */ for(char c=where[i]; c && c!=' '; c=where[++i]) { if(!isalnum((unsigned char)c) && c!='+' && c!='/') continue; val<<=6; val+=base64_map[(uint8_t)c]; } *value=neg?-(int64_t)val:(int64_t)val; return i; } uint64_t base64_to_uint64(const char *buf) { int64_t val=0; from_base64(&val, buf); return (uint64_t)val; } void base64_from_uint64(uint64_t src, char *buf) { char *p=buf; p+=to_base64(src, p); *p=0; } burp-2.4.0/src/base64.h000066400000000000000000000004711404341324700145150ustar00rootroot00000000000000#ifndef BASE_64_H #define BASE_64_H #include "burp.h" extern void base64_init(void); extern int to_base64(int64_t value, char *where); extern int from_base64(int64_t *value, const char *where); extern uint64_t base64_to_uint64(const char *buf); extern void base64_from_uint64(uint64_t src, char *buf); #endif burp-2.4.0/src/berrno.c000066400000000000000000000046321404341324700147160ustar00rootroot00000000000000/* Bacula® - The Network Backup Solution Copyright (C) 2004-2009 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. 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 Affero 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. Bacula® is a registered trademark of Kern Sibbald. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ /* * Kern Sibbald, July MMIV */ /* * Originally from bacula-5.0.3:src/lib/berrno.h. * Converted to be C instead of a C++ class. * Graham Keeling, 2014. */ #include "burp.h" #include "berrno.h" void berrno_init(struct berrno *b) { b->m_berrno=errno; *(b->m_buf)=0; errno=b->m_berrno; } static int bstrerror(int errnum, char *buf, size_t bufsiz) { int stat=0; const char *msg; if(!(msg=strerror(errnum))) { msg="Bad errno"; stat=-1; } snprintf(buf, bufsiz, "%s", msg); return stat; } #ifdef HAVE_WIN32 static void format_win32_message(struct berrno *b) { LPVOID msg; FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&msg, 0, NULL); snprintf(b->m_buf, sizeof(b->m_buf), "%s", (const char *)msg); LocalFree(msg); } #endif const char *berrno_bstrerror(struct berrno *b, int errnum) { b->m_berrno=errnum; *(b->m_buf)=0; #ifdef HAVE_WIN32 if(b->m_berrno & b_errno_win32) { format_win32_message(b); return (const char *)(b->m_buf); } #endif // Normal errno. if(bstrerror(b->m_berrno, b->m_buf, sizeof(b->m_buf))<0) return "Invalid errno. No error message possible."; return b->m_buf; } burp-2.4.0/src/berrno.h000066400000000000000000000043141404341324700147200ustar00rootroot00000000000000/* Bacula® - The Network Backup Solution Copyright (C) 2004-2009 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. 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 Affero 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. Bacula® is a registered trademark of Kern Sibbald. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ /* * Kern Sibbald, July MMIV */ /* * Originally from bacula-5.0.3:src/lib/berrno.h. Heavily reduced in size and * converted to be C instead of a C++ class. * Graham Keeling, 2014. */ #ifndef _BERRNO_H #define _BERRNO_H #include // Extra bits set to interpret errno value differently from errno #ifdef HAVE_WIN32 #define b_errno_win32 (1<<29) // User reserved bit. #else #define b_errno_win32 0 // On Unix/Linix system. #endif /* * A more generalized way of handling errno that works with Unix and Windows. * * It works by picking up errno and creating a memory pool buffer * for editing the message. strerror() does the actual editing, and * it is thread safe. * * If bit 29 in m_berrno is set then it is a Win32 error, and we * must do a GetLastError() to get the error code for formatting. * If bit 29 in m_berrno is not set, then it is a Unix errno. * */ struct berrno { char m_buf[256]; int m_berrno; }; extern void berrno_init(struct berrno *b); extern const char *berrno_bstrerror(struct berrno *b, int errnum); #endif burp-2.4.0/src/bfile.c000066400000000000000000000277321404341324700145160ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "attribs.h" #include "berrno.h" #include "bfile.h" #include "log.h" #ifdef HAVE_DARWIN_OS #include #endif void bfile_free(struct BFILE **bfd) { free_v((void **)bfd); } #ifdef HAVE_WIN32 static ssize_t bfile_write_windows(struct BFILE *bfd, void *buf, size_t count); #endif #define min(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a < _b ? _a : _b; }) static void setup_vss_strip(struct BFILE *bfd) { memset(&bfd->mysid, 0, sizeof(struct mysid)); bfd->mysid.needed_s=bsidsize; } static ssize_t bfile_write_vss_strip(struct BFILE *bfd, void *buf, size_t count) { size_t mycount; struct mysid *mysid; struct bsid *sid; mysid=&bfd->mysid; sid=&mysid->sid; char *cp=(char *)buf; mycount=count; while(mycount) { if(mysid->needed_s) { size_t sidlen=bsidsize-mysid->needed_s; int got=min(mysid->needed_s, mycount); memcpy(sid+sidlen, cp, got); cp+=got; mycount-=got; mysid->needed_s-=got; if(!mysid->needed_s) mysid->needed_d=sid->Size+sid->dwStreamNameSize; } if(mysid->needed_d) { size_t wrote; int got=min(mysid->needed_d, mycount); if(sid->dwStreamId==1) { #ifdef HAVE_WIN32 if((wrote=bfile_write_windows(bfd, cp, got))<=0) return -1; #else if((wrote=write(bfd->fd, cp, got))<=0) return -1; #endif } else wrote=got; cp+=wrote; mycount-=wrote; mysid->needed_d-=wrote; if(!mysid->needed_d) mysid->needed_s=bsidsize; } } return count; } #ifdef HAVE_WIN32 char *unix_name_to_win32(char *name); extern "C" HANDLE get_osfhandle(int fd); static void bfile_set_win32_api(struct BFILE *bfd, int on) { if(have_win32_api() && on) bfd->use_backup_api=1; else bfd->use_backup_api=0; } int have_win32_api(void) { return p_BackupRead && p_BackupWrite; } // Windows flags for the OpenEncryptedFileRaw functions #define CREATE_FOR_EXPORT 0 // These are already defined //#define CREATE_FOR_IMPORT 1 //#define CREATE_FOR_DIR 2 //#define OVERWRITE_HIDDEN 4 // Return 0 for success, non zero for error. static int bfile_open_encrypted(struct BFILE *bfd, const char *fname, int flags, mode_t mode) { ULONG ulFlags=0; char *win32_fname=NULL; char *win32_fname_wchar=NULL; bfd->mode=BF_CLOSED; if(!(win32_fname_wchar=make_win32_path_UTF8_2_wchar_w(fname))) { logp("could not get widename!"); goto end; } if(!(win32_fname=unix_name_to_win32((char *)fname))) { logp("could not get win32_fname of %s!", fname); goto end; } if((flags & O_CREAT) /* Create */ || (flags & O_WRONLY)) /* Open existing for write */ { ulFlags |= CREATE_FOR_IMPORT; ulFlags |= OVERWRITE_HIDDEN; if(bfd->winattr & FILE_ATTRIBUTE_DIRECTORY) { mkdir(fname, 0777); ulFlags |= CREATE_FOR_DIR; } } else { /* Open existing for read */ ulFlags=CREATE_FOR_EXPORT; } if(p_OpenEncryptedFileRawW((LPCWSTR)win32_fname_wchar, ulFlags, &(bfd->pvContext))) bfd->mode=BF_CLOSED; else bfd->mode=BF_READ; end: free_w(&win32_fname_wchar); free_w(&win32_fname); return bfd->mode==BF_CLOSED; } static int bfile_error(struct BFILE *bfd) { if(bfd) { bfd->lerror=GetLastError(); bfd->berrno=b_errno_win32; } errno=b_errno_win32; return -1; } // Return 0 for success, non zero for error. static int bfile_open(struct BFILE *bfd, struct asfd *asfd, const char *fname, int flags, mode_t mode) { DWORD dwaccess; DWORD dwflags; DWORD dwshare; char *win32_fname=NULL; char *win32_fname_wchar=NULL; bfd->mode=BF_CLOSED; if(bfd->winattr & FILE_ATTRIBUTE_ENCRYPTED) return bfile_open_encrypted(bfd, fname, flags, mode); if(!(win32_fname=unix_name_to_win32((char *)fname))) { logp("could not get win32_fname of %s!\n", fname); goto end; } if(!(win32_fname_wchar=make_win32_path_UTF8_2_wchar_w(fname))) { logp("could not get widename!"); goto end; } if(flags & O_CREAT) { /* Create */ if(bfd->winattr & FILE_ATTRIBUTE_DIRECTORY) mkdir(fname, 0777); if(bfd->use_backup_api) { dwaccess=GENERIC_WRITE | FILE_ALL_ACCESS | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY; dwflags=FILE_FLAG_BACKUP_SEMANTICS; } else { dwaccess=GENERIC_WRITE; dwflags=0; } // unicode open for create write bfd->fh=p_CreateFileW((LPCWSTR)win32_fname_wchar, dwaccess, /* Requested access */ 0, /* Shared mode */ NULL, /* SecurityAttributes */ CREATE_ALWAYS, /* CreationDisposition */ dwflags, /* Flags and attributes */ NULL); /* TemplateFile */ bfd->mode=BF_WRITE; } else if(flags & O_WRONLY) { /* Open existing for write */ if(bfd->use_backup_api) { dwaccess=GENERIC_WRITE | WRITE_OWNER | WRITE_DAC; dwflags=FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT; } else { dwaccess=GENERIC_WRITE; dwflags=0; } // unicode open for open existing write bfd->fh=p_CreateFileW((LPCWSTR)win32_fname_wchar, dwaccess, /* Requested access */ 0, /* Shared mode */ NULL, /* SecurityAttributes */ OPEN_EXISTING, /* CreationDisposition */ dwflags, /* Flags and attributes */ NULL); /* TemplateFile */ bfd->mode=BF_WRITE; } else { /* Read */ if(bfd->use_backup_api) { dwaccess=GENERIC_READ|READ_CONTROL | ACCESS_SYSTEM_SECURITY; dwflags=FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OPEN_REPARSE_POINT; dwshare=FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; } else { dwaccess=GENERIC_READ; dwflags=0; dwshare=FILE_SHARE_READ | FILE_SHARE_WRITE; } // unicode open for open existing read bfd->fh=p_CreateFileW((LPCWSTR)win32_fname_wchar, dwaccess, /* Requested access */ dwshare, /* Share modes */ NULL, /* SecurityAttributes */ OPEN_EXISTING, /* CreationDisposition */ dwflags, /* Flags and attributes */ NULL); /* TemplateFile */ bfd->mode=BF_READ; } if(bfd->fh==INVALID_HANDLE_VALUE) { bfile_error(bfd); bfd->mode=BF_CLOSED; } else { free_w(&bfd->path); if(!(bfd->path=strdup_w(fname, __func__))) goto end; } end: bfd->lpContext=NULL; free_w(&win32_fname_wchar); free_w(&win32_fname); if(bfd->vss_strip) setup_vss_strip(bfd); return bfd->mode==BF_CLOSED; } static int bfile_close_encrypted(struct BFILE *bfd, struct asfd *asfd) { CloseEncryptedFileRaw(bfd->pvContext); if(bfd->mode==BF_WRITE && bfd->set_attribs_on_close) attribs_set(asfd, bfd->path, &bfd->statp, bfd->winattr, bfd->cntr); bfd->pvContext=NULL; bfd->mode=BF_CLOSED; free_w(&bfd->path); return 0; } // Return 0 on success, -1 on error. static int bfile_close(struct BFILE *bfd, struct asfd *asfd) { int ret=-1; if(!bfd) return 0; if(bfd->mode==BF_CLOSED) { ret=0; goto end; } if(bfd->winattr & FILE_ATTRIBUTE_ENCRYPTED) return bfile_close_encrypted(bfd, asfd); /* * We need to tell the API to release the buffer it * allocated in lpContext. We do so by calling the * API one more time, but with the Abort bit set. */ if(bfd->use_backup_api && bfd->mode==BF_READ) { BYTE buf[10]; if(bfd->lpContext && !p_BackupRead(bfd->fh, buf, /* buffer */ (DWORD)0, /* bytes to read */ &bfd->rw_bytes, /* bytes read */ 1, /* Abort */ 1, /* ProcessSecurity */ &bfd->lpContext)) /* Read context */ { bfile_error(NULL); goto end; } } else if(bfd->use_backup_api && bfd->mode==BF_WRITE) { BYTE buf[10]; if(bfd->lpContext && !p_BackupWrite(bfd->fh, buf, /* buffer */ (DWORD)0, /* bytes to read */ &bfd->rw_bytes, /* bytes written */ 1, /* Abort */ 1, /* ProcessSecurity */ &bfd->lpContext)) /* Write context */ { bfile_error(NULL); goto end; } } if(!CloseHandle(bfd->fh)) { bfile_error(NULL); goto end; } if(bfd->mode==BF_WRITE && bfd->set_attribs_on_close) attribs_set(asfd, bfd->path, &bfd->statp, bfd->winattr, bfd->cntr); bfd->lpContext=NULL; bfd->mode=BF_CLOSED; ret=0; end: free_w(&bfd->path); return ret; } // Returns: bytes read on success, or 0 on EOF, -1 on error. static ssize_t bfile_read(struct BFILE *bfd, void *buf, size_t count) { bfd->rw_bytes=0; if(bfd->use_backup_api) { if(!p_BackupRead(bfd->fh, (BYTE *)buf, count, &bfd->rw_bytes, 0, /* no Abort */ 1, /* Process Security */ &bfd->lpContext)) /* Context */ return bfile_error(bfd); } else { if(!ReadFile(bfd->fh, buf, count, &bfd->rw_bytes, NULL)) return bfile_error(bfd); } return (ssize_t)bfd->rw_bytes; } static ssize_t bfile_write_windows(struct BFILE *bfd, void *buf, size_t count) { bfd->rw_bytes = 0; if(bfd->use_backup_api) { if(!p_BackupWrite(bfd->fh, (BYTE *)buf, count, &bfd->rw_bytes, 0, /* No abort */ 1, /* Process Security */ &bfd->lpContext)) /* Context */ return bfile_error(bfd); } else { if(!WriteFile(bfd->fh, buf, count, &bfd->rw_bytes, NULL)) return bfile_error(bfd); } return (ssize_t)bfd->rw_bytes; } static ssize_t bfile_write(struct BFILE *bfd, void *buf, size_t count) { if(bfd->vss_strip) return bfile_write_vss_strip(bfd, buf, count); return bfile_write_windows(bfd, buf, count); } #else static int bfile_close(struct BFILE *bfd, struct asfd *asfd) { if(!bfd || bfd->mode==BF_CLOSED) return 0; if(!close(bfd->fd)) { if(bfd->mode==BF_WRITE && bfd->set_attribs_on_close) attribs_set(asfd, bfd->path, &bfd->statp, bfd->winattr, bfd->cntr); bfd->mode=BF_CLOSED; bfd->fd=-1; free_w(&bfd->path); return 0; } free_w(&bfd->path); return -1; } static int bfile_open(struct BFILE *bfd, struct asfd *asfd, const char *fname, int flags, mode_t mode) { if(!bfd) return 0; if(bfd->mode!=BF_CLOSED && bfd->close(bfd, asfd)) return -1; if((bfd->fd=open(fname, flags, mode))<0) return -1; if(flags & O_CREAT || flags & O_WRONLY) bfd->mode=BF_WRITE; else bfd->mode=BF_READ; free_w(&bfd->path); if(!(bfd->path=strdup_w(fname, __func__))) return -1; if(bfd->vss_strip) setup_vss_strip(bfd); return 0; } static ssize_t bfile_read(struct BFILE *bfd, void *buf, size_t count) { return read(bfd->fd, buf, count); } static ssize_t bfile_write(struct BFILE *bfd, void *buf, size_t count) { if(bfd->vss_strip) return bfile_write_vss_strip(bfd, buf, count); return write(bfd->fd, buf, count); } #endif static int bfile_open_for_send(struct BFILE *bfd, struct asfd *asfd, const char *fname, int64_t winattr, int atime, struct cntr *cntr, enum protocol protocol) { if(protocol==PROTO_1 && bfd->mode!=BF_CLOSED) { #ifdef HAVE_WIN32 if(bfd->path && !strcmp(bfd->path, fname)) { // Already open after reading the VSS data. // Time now for the actual file data. return 0; } else { #endif // Close the open bfd so that it can be // used again bfd->close(bfd, asfd); #ifdef HAVE_WIN32 } #endif } bfile_init(bfd, winattr, cntr); if(bfile_open(bfd, asfd, fname, O_RDONLY|O_BINARY #ifdef O_NOFOLLOW |O_NOFOLLOW #endif #ifdef O_NOATIME |(atime?0:O_NOATIME) #endif , 0)) { struct berrno be; berrno_init(&be); logw(asfd, cntr, "Could not open %s: %s\n", fname, berrno_bstrerror(&be, errno)); return -1; } return 0; } static void bfile_set_vss_strip(struct BFILE *bfd, int on) { bfd->vss_strip=on; } void bfile_setup_funcs(struct BFILE *bfd) { bfd->open=bfile_open; bfd->close=bfile_close; bfd->read=bfile_read; bfd->write=bfile_write; bfd->open_for_send=bfile_open_for_send; #ifdef HAVE_WIN32 bfd->set_win32_api=bfile_set_win32_api; #endif bfd->set_vss_strip=bfile_set_vss_strip; } void bfile_init(struct BFILE *bfd, int64_t winattr, struct cntr *cntr) { memset(bfd, 0, sizeof(struct BFILE)); bfd->mode=BF_CLOSED; bfd->winattr=winattr; bfd->cntr=cntr; if(!bfd->open) bfile_setup_funcs(bfd); #ifdef HAVE_WIN32 bfile_set_win32_api(bfd, 1); #else bfd->fd=-1; #endif } struct BFILE *bfile_alloc(void) { return (struct BFILE *)calloc_w(1, sizeof(struct BFILE), __func__); } burp-2.4.0/src/bfile.h000066400000000000000000000037551404341324700145220ustar00rootroot00000000000000#ifndef __BFILE_H #define __BFILE_H #include "burp.h" #include "conf.h" struct asfd; enum bf_mode { BF_CLOSED=0, BF_READ, /* BackupRead */ BF_WRITE /* BackupWrite */ }; struct mysid { struct bsid sid; size_t needed_s; size_t needed_d; }; struct BFILE { enum bf_mode mode; /* set if file is open */ uint64_t winattr; /* needed for deciding to open with encrypted functions or not */ struct stat statp; char *path; struct cntr *cntr; // Windows VSS headers tell us how much file data to expect. // Protocol1 only for now. size_t datalen; #ifdef HAVE_WIN32 uint8_t use_backup_api; /* set if using BackupRead/Write */ HANDLE fh; /* Win32 file handle */ LPVOID lpContext; /* BackupRead/Write context */ DWORD rw_bytes; /* Bytes read or written */ DWORD lerror; /* Last error code */ PVOID pvContext; /* also for the encrypted functions */ int berrno; /* errno */ #else int fd; #endif struct mysid mysid; int vss_strip; int set_attribs_on_close; // Let us try using function pointers. int (*open)(struct BFILE *bfd, struct asfd *asfd, const char *fname, int flags, mode_t mode); int (*close)(struct BFILE *bfd, struct asfd *asfd); ssize_t (*read)(struct BFILE *bfd, void *buf, size_t count); ssize_t (*write)(struct BFILE *bfd, void *buf, size_t count); int (*open_for_send)(struct BFILE *bfd, struct asfd *asfd, const char *fname, int64_t winattr, int atime, struct cntr *cntr, enum protocol protocol); #ifdef HAVE_WIN32 void (*set_win32_api)(struct BFILE *bfd, int on); #endif void (*set_vss_strip)(struct BFILE *bfd, int on); }; extern struct BFILE *bfile_alloc(void); extern void bfile_free(struct BFILE **bfd); // FIX THIS: should be possible to have this as a function pointer too. // Need to sort out the bfd in sbuf. extern void bfile_init(struct BFILE *bfd, int64_t winattr, struct cntr *cntr); extern void bfile_setup_funcs(struct BFILE *bfd); #ifdef HAVE_WIN32 extern int have_win32_api(void); #endif #endif burp-2.4.0/src/bu.c000066400000000000000000000034001404341324700140250ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "bu.h" #include "prepend.h" // balh struct bu *bu_alloc(void) { return (struct bu *)calloc_w(1, sizeof(struct bu), __func__); } int bu_init(struct bu *bu, char *fullpath, char *basename, char *timestampstr, uint16_t flags) { if(!(bu->data=prepend_s(fullpath, "data")) || !(bu->delta=prepend_s(fullpath, "deltas.reverse"))) goto error; bu->path=fullpath; bu->basename=basename; bu->timestamp=timestampstr; bu->flags=flags; bu->bno=strtoul(timestampstr, NULL, 10); return 0; error: free_w(&bu->data); free_w(&bu->delta); return -1; } static void bu_free_content(struct bu *bu) { if(!bu) return; free_w(&bu->path); free_w(&bu->basename); free_w(&bu->data); free_w(&bu->delta); free_w(&bu->timestamp); } void bu_free(struct bu **bu) { if(!bu || !*bu) return; bu_free_content(*bu); free_v((void **)bu); } void bu_list_free(struct bu **bu_list) { struct bu *bu; struct bu *next; struct bu *prev=NULL; if(*bu_list) prev=(*bu_list)->prev; for(bu=*bu_list; bu; bu=next) { next=bu->next; bu_free(&bu); } // Do it in both directions. for(bu=prev; bu; bu=prev) { prev=bu->prev; bu_free(&bu); } *bu_list=NULL; } static struct bu *bu_find(struct bu *bu, uint16_t flag) { struct bu *cbu=NULL; if(!bu) return NULL; if(bu->flags & flag) return bu; // Search in both directions. if(bu->next) for(cbu=bu; cbu; cbu=cbu->next) if(cbu->flags & flag) return cbu; if(bu->prev) for(cbu=bu; cbu; cbu=cbu->prev) if(cbu->flags & flag) return cbu; return cbu; } struct bu *bu_find_current(struct bu *bu) { return bu_find(bu, BU_CURRENT); } struct bu *bu_find_working_or_finishing(struct bu *bu) { struct bu *cbu=NULL; if((cbu=bu_find(bu, BU_WORKING))) return cbu; return bu_find(bu, BU_FINISHING); } burp-2.4.0/src/bu.h000066400000000000000000000025411404341324700140370ustar00rootroot00000000000000#ifndef _BU_H #define _BU_H #define BU_HARDLINKED 0x0001 #define BU_DELETABLE 0x0002 #define BU_WORKING 0x0004 #define BU_FINISHING 0x0008 #define BU_CURRENT 0x0010 #define BU_LIVE_COUNTERS 0x0020 // Only set in json_input. #define BU_MANIFEST 0x0040 // These are only set on a separate request. // Careful with the bit shifting in ncurses client with the UP/DOWN keys. #define BU_LOG_BACKUP 0x0080 #define BU_LOG_RESTORE 0x0100 #define BU_LOG_VERIFY 0x0200 #define BU_STATS_BACKUP 0x0400 #define BU_STATS_RESTORE 0x0800 #define BU_STATS_VERIFY 0x1000 // Representing backup directories on the server for a client. // Needed on the client side too, as the status monitor stuff uses it. struct bu { char *path; char *basename; char *data; char *delta; char *timestamp; uint16_t flags; // The number of the backup. uint64_t bno; // Transposed backup number - will set the oldest backup to 1. uint64_t trbno; // The position of this item in the array. uint64_t index; struct bu *next; struct bu *prev; }; extern int bu_init(struct bu *bu, char *fullpath, char *basename, char *timestampstr, uint16_t flags); extern void bu_list_free(struct bu **bu_list); extern struct bu *bu_alloc(void); extern void bu_free(struct bu **bu); extern struct bu *bu_find_current(struct bu *bu); extern struct bu *bu_find_working_or_finishing(struct bu *bu); #endif burp-2.4.0/src/burp.h000066400000000000000000000074401404341324700144040ustar00rootroot00000000000000#ifndef _BURP_H #define _BURP_H #if defined(HAVE_WIN32) #include "mingwconfig.h" #else #include "config.h" #endif #define _REENTRANT 1 #define _THREAD_SAFE 1 #define _POSIX_PTHREAD_SEMANTICS 1 #define __STDC_FORMAT_MACROS 1 // System includes. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_ALLOCA_H #include #endif #ifdef _MSC_VER #include #include #include #endif #ifdef _MSC_VER extern "C" { #include "getopt.h" } #endif #ifndef _SPLINT_ #include #endif #ifndef HAVE_WIN32 #include #include #include #endif // Fight OpenSSL namespace pollution. #define STORE OSSL_STORE #include #include #include #include #include #undef STORE // Local Burp includes. Be sure to put all the system includes before these. #ifdef HAVE_WIN32 #include #endif #include "burpconfig.h" #ifdef HAVE_WIN32 #include "win32/compat/compat.h" #include "win32/winapi.h" #include "winhost.h" #endif #if HAVE_STRUCT_UTIMBUF #include #else struct utimbuf { long actime; long modtime; }; #endif #ifdef HAVE_DIRENT_H #endif #ifdef HAVE_ENDIAN_H #include #elif HAVE_SYS_ENDIAN_H #include #elif HAVE_LIBKERN_OSBYTEORDER_H #include #define htobe64(x) OSSwapHostToBigInt64(x) #define htole64(x) OSSwapHostToLittleInt64(x) #define be64toh(x) OSSwapBigToHostInt64(x) #define le64toh(x) OSSwapLittleToHostInt64(x) #define __BYTE_ORDER BYTE_ORDER #define __BIG_ENDIAN BIG_ENDIAN #define __LITTLE_ENDIAN LITTLE_ENDIAN #define __PDP_ENDIAN PDP_ENDIAN #elif HAVE_SYS_BYTEORDER_H #include #define be64toh(x) BE_64(x) #define htobe64(x) BE_64(x) #define htole64(x) LE_64(x) #define le64toh(x) LE_64(x) #elif HAVE_WIN32 #include #include #if BYTE_ORDER == LITTLE_ENDIAN #define htobe64(x) __builtin_bswap64(x) #define htole64(x) (x) #define be64toh(x) __builtin_bswap64(x) #define le64toh(x) (x) #elif BYTE_ORDER == BIG_ENDIAN #define htobe64(x) (x) #define htole64(x) __builtin_bswap64(x) #define be64toh(x) (x) #define le64toh(x) __builtin_bswap64(x) #else #error byte order not supported #endif #define __BYTE_ORDER BYTE_ORDER #define __BIG_ENDIAN BIG_ENDIAN #define __LITTLE_ENDIAN LITTLE_ENDIAN #define __PDP_ENDIAN PDP_ENDIAN #elif _AIX && __GNUC__ /* AIX is always big endian */ #define htobe64(x) (x) #define htole64(x) __builtin_bswap64(x) #define be64toh(x) (x) #define le64toh(x) __builtin_bswap64(x) #endif #if !defined(htobe64) && defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 9 #include #include #if __BYTE_ORDER == __LITTLE_ENDIAN #define htobe64(x) bswap_64 (x) #define htole64(x) (x) #define be64toh(x) bswap_64 (x) #define le64toh(x) (x) #elif __BYTE_ORDER == __BIG_ENDIAN #define htobe64(x) (x) #define htole64(x) bswap_64 (x) #define be64toh(x) (x) #define le64toh(x) bswap_64 (x) #else #error byte order not supported #endif #endif // This is the shape of the Windows VSS header structure. // It is size 20 on disk. Using sizeof(struct bsid) gives 24 in memory. struct bsid { int32_t dwStreamId; int32_t dwStreamAttributes; int64_t Size; int32_t dwStreamNameSize; }; #define bsidsize 20 #endif burp-2.4.0/src/burpconfig.h000066400000000000000000000037121404341324700155700ustar00rootroot00000000000000#ifndef _BURPCONFIG_H #define _BURPCONFIG_H // Graham says: probably most of this stuff can be deleted - it is debris // left from Bacula. #define ASSERT(x) // MAX_PATH is Windows constatnt, usually 260, maybe changed in some Win10 update. // PATH_MAX is Posix constant // right method - it to call pathconf(), but such case lead to reworking lots of code; // and some buffers are better to allocate on stack, not on heap #ifndef MAX_PATH #ifdef PATH_MAX #define MAX_PATH PATH_MAX #else #define MAX_PATH 260 #endif #endif // unicode enabling of win 32 needs some defines and functions // using an average of 3 bytes per character is probably fine in // practice but I believe that Windows actually uses UTF-16 encoding // as opposed to UCS2 which means characters 0x10000-0x10ffff are // valid and result in 4 byte UTF-8 encodings. #define MAX_PATH_UTF8 MAX_PATH*4 // strict upper bound on UTF-16 to UTF-8 conversion // from // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/getfileattributesex.asp // In the ANSI version of this function, the name is limited to // MAX_PATH characters. To extend this limit to 32,767 wide // characters, call the Unicode version of the function and prepend // "\\?\" to the path. For more information, see Naming a File. #define MAX_PATH_W 32767 #ifdef HAVE_WIN32 #define WIN32_REPARSE_POINT 1 // Any odd dir except the next two. #define WIN32_MOUNT_POINT 2 // Directory link to Volume. #define WIN32_JUNCTION_POINT 3 // Directory link to a directory. void InitWinAPIWrapper(); #ifdef BUILDING_DLL #define DLL_IMP_EXP _declspec(dllexport) #elif defined(USING_DLL) #define DLL_IMP_EXP _declspec(dllimport) #endif #ifdef USING_CATS #define CATS_IMP_EXP _declspec(dllimport) #endif #endif #ifndef O_BINARY #define O_BINARY 0 #endif static inline uint8_t IsPathSeparator(int ch) { return #ifdef HAVE_WIN32 ch == '\\' || #endif ch == '/'; } #endif burp-2.4.0/src/client/000077500000000000000000000000001404341324700145345ustar00rootroot00000000000000burp-2.4.0/src/client/acl.c000066400000000000000000000112731404341324700154430ustar00rootroot00000000000000#include "../burp.h" #include "../alloc.h" #include "../cmd.h" #include "../log.h" #include "../prepend.h" #include "../sbuf.h" #include "acl.h" #include "extrameta.h" #ifdef HAVE_ACL #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_NETBSD_OS) #include "sys/acl.h" /* Linux can do shorter ACLs */ #if defined(HAVE_LINUX_OS) #include #define acl_to_text(acl,len) (acl_to_any_text((acl), NULL, ',', TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)) #endif // section of acl_is_trivial copied from bacula static int acl_is_trivial(acl_t acl) { #if defined(HAVE_LINUX_OS) \ || defined(HAVE_FREEBSD_OS) \ || defined(HAVE_NETBSD_OS) /* * acl is trivial if it has only the following entries: * "user::", * "group::", * "other::" */ acl_entry_t ace; acl_tag_t tag; int entry_available; entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace); while(entry_available==1) { /* * Get the tag type of this acl entry. * If we fail to get the tagtype we call the acl non-trivial. */ if (acl_get_tag_type(ace, &tag) < 0) return 0; /* * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ * or ACL_OTHER breaks the spell. */ if(tag!=ACL_USER_OBJ && tag!=ACL_GROUP_OBJ && tag!=ACL_OTHER) return 0; entry_available=acl_get_entry(acl, ACL_NEXT_ENTRY, &ace); } #endif return 1; } static int default_acl_contains_something(acl_t acl) { acl_entry_t ace; return acl_get_entry(acl, ACL_FIRST_ENTRY, &ace)==1; } static acl_t acl_contains_something(const char *path, int acl_type) { acl_t acl=NULL; if(!(acl=acl_get_file(path, acl_type))) return NULL; switch(acl_type) { case ACL_TYPE_ACCESS: if(acl_is_trivial(acl)) goto nothing; break; case ACL_TYPE_DEFAULT: if(!default_acl_contains_something(acl)) goto nothing; break; } return acl; nothing: acl_free(acl); return NULL; } int has_acl(const char *path, enum cmd cmd) { acl_t acl=NULL; if((acl=acl_contains_something(path, ACL_TYPE_ACCESS))) { acl_free(acl); return 1; } if(cmd==CMD_DIRECTORY && (acl=acl_contains_something(path, ACL_TYPE_DEFAULT))) { acl_free(acl); return 1; } return 0; } static int get_acl_string(struct asfd *asfd, acl_t acl, char **acltext, size_t *alen, const char *path, char type, struct cntr *cntr) { int ret=0; char pre[10]=""; char *tmp=NULL; uint32_t tlen=0; char *ourtext=NULL; char *oldtext=*acltext; uint32_t maxlen=0xFFFFFFFF/2; if(!(tmp=acl_to_text(acl, NULL))) { logw(asfd, cntr, "could not get ACL text of '%s'\n", path); goto end; // carry on } tlen=(uint32_t)strlen(tmp); if(tlen>maxlen) { logw(asfd, cntr, "ACL of '%s' too long: %d\n", path, tlen); goto end; // carry on } snprintf(pre, sizeof(pre), "%c%08X", type, tlen); if(!(ourtext=prepend(pre, tmp)) || !(*acltext=prepend_len(oldtext, *alen, ourtext, tlen+9, "", 0, alen))) ret=-1; free_w(&oldtext); end: if(tmp) acl_free(tmp); free_w(&ourtext); return ret; } int get_acl(struct asfd *asfd, const char *path, int isdir, char **acltext, size_t *alen, struct cntr *cntr) { int ret=-1; acl_t acl=NULL; if((acl=acl_contains_something(path, ACL_TYPE_ACCESS))) { if(get_acl_string(asfd, acl, acltext, alen, path, META_ACCESS_ACL, cntr)) goto end; } if(isdir) { if(acl) acl_free(acl); if((acl=acl_contains_something(path, ACL_TYPE_DEFAULT))) { if(get_acl_string(asfd, acl, acltext, alen, path, META_DEFAULT_ACL, cntr)) goto end; } } ret=0; end: if(acl) acl_free(acl); return ret; } static int do_set_acl(struct asfd *asfd, const char *path, const char *acltext, int acltype, struct cntr *cntr) { acl_t acl; int ret=-1; if(!(acl=acl_from_text(acltext))) { logp("acl_from_text error on %s (%s): %s\n", path, acltext, strerror(errno)); logw(asfd, cntr, "acl_from_text error on %s (%s): %s\n", path, acltext, strerror(errno)); goto end; } if(acl_valid(acl)) { logp("acl_valid error on %s: %s", path, strerror(errno)); logw(asfd, cntr, "acl_valid error on %s: %s\n", path, strerror(errno)); goto end; } if(acl_set_file(path, acltype, acl)) { logp("acl set error on %s: %s", path, strerror(errno)); logw(asfd, cntr, "acl set error on %s: %s\n", path, strerror(errno)); goto end; } ret=0; end: if(acl) acl_free(acl); return ret; } int set_acl(struct asfd *asfd, const char *path, const char *acltext, char metacmd, struct cntr *cntr) { switch(metacmd) { case META_ACCESS_ACL: return do_set_acl(asfd, path, acltext, ACL_TYPE_ACCESS, cntr); case META_DEFAULT_ACL: return do_set_acl(asfd, path, acltext, ACL_TYPE_DEFAULT, cntr); default: logp("unknown acl type: %c\n", metacmd); logw(asfd, cntr, "unknown acl type: %c\n", metacmd); return -1; } } #endif #endif burp-2.4.0/src/client/acl.h000066400000000000000000000007071404341324700154500ustar00rootroot00000000000000#ifndef _BURP_ACL_H #define _BURP_ACL_H #ifdef HAVE_ACL #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_NETBSD_OS) extern int has_acl(const char *path, enum cmd cmd); extern int get_acl(struct asfd *asfd, const char *path, int isdir, char **acltext, size_t *alen, struct cntr *cntr); extern int set_acl(struct asfd *asfd, const char *path, const char *acltext, char metacmd, struct cntr *cntr); #endif #endif #endif burp-2.4.0/src/client/auth.c000066400000000000000000000030611404341324700156410ustar00rootroot00000000000000#include "../burp.h" #include "../alloc.h" #include "../asfd.h" #include "../cmd.h" #include "../cntr.h" #include "../handy.h" #include "../iobuf.h" #include "../log.h" #include "auth.h" int authorise_client(struct asfd *asfd, char **server_version, const char *cname, const char *password, struct cntr *cntr) { int ret=-1; char hello[256]=""; struct iobuf *rbuf=asfd->rbuf; snprintf(hello, sizeof(hello), "hello:%s", PACKAGE_VERSION); if(asfd->write_str(asfd, CMD_GEN, hello)) { logp("problem with auth\n"); goto end; } if(asfd->read(asfd) || rbuf->cmd!=CMD_GEN || strncmp_w(rbuf->buf, "whoareyou")) { logp("problem with auth\n"); goto end; } if(rbuf->buf) { char *cp=NULL; if((cp=strchr(rbuf->buf, ':'))) { cp++; if(cp && !(*server_version=strdup_w(cp, __func__))) goto end; } iobuf_free_content(rbuf); } if(asfd->write_str(asfd, CMD_GEN, cname) || asfd_read_expect(asfd, CMD_GEN, "okpassword") || asfd->write_str(asfd, CMD_GEN, password) || asfd->read(asfd)) { logp("problem with auth\n"); goto end; } if(rbuf->cmd==CMD_WARNING) // special case for the version warning { //logw(conf->p1cntr, rbuf->buf); logp("WARNING: %s\n", iobuf_to_printable(rbuf)); cntr_add(cntr, rbuf->cmd, 0); iobuf_free_content(rbuf); if(asfd->read(asfd)) { logp("problem with auth\n"); goto end; } } if(rbuf->cmd==CMD_GEN && !strcmp(rbuf->buf, "ok")) { // It is OK. logp("auth ok\n"); } else { iobuf_log_unexpected(rbuf, __func__); goto end; } ret=0; end: iobuf_free_content(rbuf); return ret; } burp-2.4.0/src/client/auth.h000066400000000000000000000002761404341324700156530ustar00rootroot00000000000000#ifndef _AUTH_CLIENT_H #define _AUTH_CLIENT_H extern int authorise_client(struct asfd *asfd, char **server_version, const char *cname, const char *password, struct cntr *cntr); #endif burp-2.4.0/src/client/autoupgrade.c000066400000000000000000000064771404341324700172360ustar00rootroot00000000000000#include "../burp.h" #include "../asfd.h" #include "../async.h" #include "../cmd.h" #include "../fsops.h" #include "../iobuf.h" #include "../handy.h" #include "../log.h" #include "../prepend.h" #include "../run_script.h" #include "cvss.h" #include "autoupgrade.h" static int receive_file(struct asfd *asfd, const char *autoupgrade_dir, const char *file, struct cntr *cntr) { int ret=0; char *incoming=NULL; if(!(incoming=prepend_s(autoupgrade_dir, file))) return -1; ret=receive_a_file(asfd, incoming, cntr); free_w(&incoming); return ret; } static enum asl_ret autoupgrade_func(struct asfd *asfd, __attribute__ ((unused)) struct conf **confs, __attribute__ ((unused)) void *param) { if(!strcmp(asfd->rbuf->buf, "do not autoupgrade")) return ASL_END_OK; if(strcmp(asfd->rbuf->buf, "autoupgrade ok")) { iobuf_log_unexpected(asfd->rbuf, __func__); return ASL_END_ERROR; } return ASL_END_OK_RETURN_1; } int autoupgrade_client(struct async *as, struct conf **confs) { int a=0; int ret=-1; char *cp=NULL; char *copy=NULL; char *script_path=NULL; char script_name[32]=""; char package_name[32]=""; char write_str[256]=""; const char *args[2]; struct iobuf *rbuf=NULL; struct asfd *asfd; char *autoupgrade_dir=get_string(confs[OPT_AUTOUPGRADE_DIR]); const char *autoupgrade_os=get_string(confs[OPT_AUTOUPGRADE_OS]); struct cntr *cntr=get_cntr(confs); asfd=as->asfd; if(!autoupgrade_dir) { logp("autoupgrade_dir not set!\n"); goto end; } if(!autoupgrade_os) { logp("autoupgrade_os not set!\n"); goto end; } if(!(copy=strdup_w(autoupgrade_dir, __func__))) goto end; strip_trailing_slashes(©); if((cp=strchr(copy, '/'))) *cp='\0'; if(mkpath(&autoupgrade_dir, copy)) goto end; // Let the server know we are ready. snprintf(write_str, sizeof(write_str), "autoupgrade:%s", autoupgrade_os); if(asfd->write_str(asfd, CMD_GEN, write_str)) goto end; if(!(a=asfd->simple_loop(asfd, confs, NULL, __func__, autoupgrade_func))) { ret=0; // No autoupgrade. goto end; } else if(a<0) // Error. goto end; #ifdef HAVE_WIN32 win32_enable_backup_privileges(); snprintf(script_name, sizeof(script_name), "script.bat"); snprintf(package_name, sizeof(package_name), "package.exe"); #else snprintf(script_name, sizeof(script_name), "script"); snprintf(package_name, sizeof(package_name), "package"); #endif if(receive_file(asfd, autoupgrade_dir, script_name, cntr)) { logp("Problem receiving %s/%s\n", autoupgrade_dir, script_name); goto end; } if(receive_file(asfd, autoupgrade_dir, package_name, cntr)) { logp("Problem receiving %s/%s\n", autoupgrade_dir, package_name); goto end; } if(!(script_path=prepend_s(autoupgrade_dir, script_name))) goto end; chmod(script_path, 0755); /* Run the script here. */ a=0; args[a++]=script_path; args[a++]=NULL; run_script(asfd, args, NULL, confs, 0 /* do not wait */, 1 /* use logp */, 1 /* log_remote */); /* To get round Windows problems to do with installing over files that the current process is running from, I am forking the child, then immediately exiting the parent process. */ printf("\n"); logp("The server tried to upgrade your client.\n"); logp("You will need to try your command again.\n"); asfd_flush_asio(asfd); asfd_free(&as->asfd); exit(0); end: free_w(©); free_w(&script_path); iobuf_free(&rbuf); return ret; } burp-2.4.0/src/client/autoupgrade.h000066400000000000000000000002131404341324700172210ustar00rootroot00000000000000#ifndef _AUTOUPGRADE_CLIENT_H #define _AUTOUPGRADE_CLIENT_H extern int autoupgrade_client(struct async *as, struct conf **confs); #endif burp-2.4.0/src/client/backup.c000066400000000000000000000051771404341324700161570ustar00rootroot00000000000000#include "../burp.h" #include "../action.h" #include "../asfd.h" #include "../async.h" #include "../cntr.h" #include "../conf.h" #include "../handy.h" #include "../log.h" #include "backup_phase1.h" #include "cvss.h" #include "protocol1/backup_phase2.h" #include "protocol2/backup_phase2.h" #include "backup.h" #ifdef HAVE_WIN32 static void set_priority(int priority, const char *str) { if(SetThreadPriority(GetCurrentThread(), priority)) logp("Set %s\n", str); else logp("Failed to set %s\n", str); } static void set_low_priority(void) { // Run timed backups with lower priority. I found that this has to be // done after the snapshot, or the snapshot never finishes. At least, I // waited 30 minutes with nothing happening. #if defined(B_VSS_XP) || defined(B_VSS_W2K3) set_priority(THREAD_PRIORITY_LOWEST, "thread_priority_lowest"); #else set_priority(THREAD_MODE_BACKGROUND_BEGIN, "thread_mode_background_begin"); #endif } static void unset_low_priority(void) { set_priority(THREAD_MODE_BACKGROUND_END, "thread_mode_background_end"); } #endif // Return 0 for OK, -1 for error. int do_backup_client(struct asfd *asfd, struct conf **confs, enum action action, int resume) { int ret=-1; int breaking=get_int(confs[OPT_BREAKPOINT]); if(action==ACTION_ESTIMATE) logp("do estimate client\n"); else { logp("do backup client\n"); if(get_protocol(confs)==PROTO_1) logp("Using librsync hash %s\n", rshash_to_str(get_e_rshash(confs[OPT_RSHASH]))); } #ifdef HAVE_WIN32 win32_enable_backup_privileges(); #ifdef WIN32_VSS if(win32_start_vss(asfd, confs)) { log_and_send(asfd, "Problem with VSS\n"); return ret; } #endif if(action==ACTION_BACKUP_TIMED) set_low_priority(); #endif // Scan the file system and send the results to the server. // Skip phase1 if the server wanted to resume. if(!resume) { if(breaking==1) { breakpoint(breaking, __func__); goto end; } if(backup_phase1_client(asfd, confs)) goto end; } switch(action) { case ACTION_DIFF: case ACTION_DIFF_LONG: ret=1; goto end; case ACTION_ESTIMATE: cntr_print(get_cntr(confs), ACTION_ESTIMATE); break; default: // Now, the server will be telling us what data we need // to send. if(breaking==2) { breakpoint(breaking, __func__); goto end; } if(get_protocol(confs)==PROTO_1) ret=backup_phase2_client_protocol1(asfd, confs, resume); else ret=backup_phase2_client_protocol2(asfd, confs, resume); if(ret) goto end; break; } ret=0; end: #if defined(HAVE_WIN32) if(action==ACTION_BACKUP_TIMED) unset_low_priority(); #if defined(WIN32_VSS) win32_stop_vss(); #endif #endif return ret; } burp-2.4.0/src/client/backup.h000066400000000000000000000002361404341324700161530ustar00rootroot00000000000000#ifndef _BACKUP_CLIENT_H #define _BACKUP_CLIENT_H extern int do_backup_client(struct asfd *asfd, struct conf **confs, enum action act, int resume); #endif burp-2.4.0/src/client/backup_phase1.c000066400000000000000000000162521404341324700174140ustar00rootroot00000000000000/* Bacula® - The Network Backup Solution Copyright (C) 2000-2009 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. 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 Affero 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. Bacula® is a registered trademark of Kern Sibbald. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ /* * Traces of * bacula-5.0.3:src/filed/backup.c still exist in this file, hence * the copyright notice above. Specifically, FF_PKT and the FF_* types. At * some point, these will be removed in favour of the burp sbuf code. * Graham Keeling, 2014. */ #include "../burp.h" #include "../alloc.h" #include "../asfd.h" #include "../async.h" #include "../attribs.h" #include "../cmd.h" #include "../cntr.h" #include "../linkhash.h" #include "../log.h" #include "../strlist.h" #include "extrameta.h" #include "find.h" #include "backup_phase1.h" static int encryption=ENCRYPTION_NONE; static enum cmd filesymbol=CMD_FILE; static enum cmd dirsymbol=CMD_DIRECTORY; #ifdef HAVE_WIN32 static enum cmd metasymbol=CMD_VSS; static enum cmd vss_trail_symbol=CMD_VSS_T; #else static enum cmd metasymbol=CMD_METADATA; #endif static int enable_acl=0; static int enable_xattr=0; static int usual_stuff(struct asfd *asfd, struct cntr *cntr, const char *path, const char *link, struct sbuf *sb, enum cmd cmd) { // When run with ACTION_ESTIMATE, asfd is NULL. if(asfd) { if(asfd->write_str(asfd, CMD_ATTRIBS, sb->attr.buf) || asfd->write_str(asfd, cmd, path) || ((cmd==CMD_HARD_LINK || cmd==CMD_SOFT_LINK) && asfd->write_str(asfd, cmd, link))) return -1; } cntr_add_phase1(cntr, cmd, 1); return 0; } static int maybe_send_extrameta(struct asfd *asfd, const char *path, enum cmd cmd, struct sbuf *sb, struct cntr *cntr, enum cmd symbol) { // FIX THIS: should probably initialise extrameta with the desired // conf parameters so that they do not need to be passed in all the // time. if(!has_extrameta(path, cmd, enable_acl, enable_xattr)) return 0; return usual_stuff(asfd, cntr, path, NULL, sb, symbol); } static int ft_err(struct asfd *asfd, struct conf **confs, struct FF_PKT *ff, const char *msg) { int raise_error; const char *prefix=""; raise_error=get_int(confs[OPT_SCAN_PROBLEM_RAISES_ERROR]); if(raise_error) prefix="Err: "; if(logw(asfd, get_cntr(confs), "%s%s %s: %s\n", prefix, msg, ff->fname, strerror(errno))) return -1; if(raise_error) return -1; return 0; } static int do_to_server(struct asfd *asfd, struct conf **confs, struct FF_PKT *ff, struct sbuf *sb, enum cmd cmd, int compression) { #ifdef HAVE_WIN32 int split_vss=0; int strip_vss=0; if(get_protocol(confs)==PROTO_1) { split_vss=get_int(confs[OPT_SPLIT_VSS]); strip_vss=get_int(confs[OPT_STRIP_VSS]); } #endif struct cntr *cntr=get_cntr(confs); sb->compression=compression; sb->encryption=encryption; sb->statp=ff->statp; sb->winattr=ff->winattr; attribs_encode(sb); #ifdef HAVE_WIN32 if(split_vss && !strip_vss && cmd!=CMD_EFS_FILE && maybe_send_extrameta(asfd, ff->fname, cmd, sb, cntr, metasymbol)) return -1; #endif if(usual_stuff(asfd, cntr, ff->fname, ff->link, sb, cmd)) return -1; if(ff->type==FT_REG) cntr_add_val(cntr, CMD_BYTES_ESTIMATED, (uint64_t)ff->statp.st_size); #ifdef HAVE_WIN32 if(split_vss && !strip_vss && cmd!=CMD_EFS_FILE // FIX THIS: May have to check that it is not a directory here. && !S_ISDIR(sb->statp.st_mode) // does this work? && maybe_send_extrameta(asfd, ff->fname, cmd, sb, cntr, vss_trail_symbol)) return -1; return 0; #else return maybe_send_extrameta(asfd, ff->fname, cmd, sb, cntr, metasymbol); #endif } static int to_server(struct asfd *asfd, struct conf **confs, struct FF_PKT *ff, struct sbuf *sb, enum cmd cmd) { return do_to_server(asfd, confs, ff, sb, cmd, get_int(confs[OPT_COMPRESSION])); } static int my_send_file(struct asfd *asfd, struct FF_PKT *ff, struct conf **confs) { static struct sbuf *sb=NULL; struct cntr *cntr=get_cntr(confs); if(!sb && !(sb=sbuf_alloc(get_protocol(confs)))) return -1; #ifdef HAVE_WIN32 if(ff->winattr & FILE_ATTRIBUTE_ENCRYPTED) { if(ff->type==FT_REG || ff->type==FT_DIR) return to_server(asfd, confs, ff, sb, CMD_EFS_FILE); return logw(asfd, cntr, "EFS type %d not yet supported: %s\n", ff->type, ff->fname); } #endif switch(ff->type) { case FT_REG: case FT_RAW: case FT_FIFO: return do_to_server(asfd, confs, ff, sb, filesymbol, in_exclude_comp(get_strlist(confs[OPT_EXCOM]), ff->fname, get_int(confs[OPT_COMPRESSION]))); case FT_DIR: case FT_REPARSE: case FT_JUNCTION: return to_server(asfd, confs, ff, sb, dirsymbol); case FT_LNK_S: return to_server(asfd, confs, ff, sb, CMD_SOFT_LINK); case FT_LNK_H: return to_server(asfd, confs, ff, sb, CMD_HARD_LINK); case FT_SPEC: return to_server(asfd, confs, ff, sb, CMD_SPECIAL); case FT_NOFSCHG: return ft_err(asfd, confs, ff, "Will not descend: " "file system change not allowed"); case FT_NOFOLLOW: return ft_err(asfd, confs, ff, "Could not follow link"); case FT_NOSTAT: return ft_err(asfd, confs, ff, "Could not stat"); case FT_NOOPEN: return ft_err(asfd, confs, ff, "Could not open directory"); default: return logw(asfd, cntr, "Err: Unknown file type %d: %s\n", ff->type, ff->fname); } } int backup_phase1_client(struct asfd *asfd, struct conf **confs) { int ret=-1; struct FF_PKT *ff=NULL; struct strlist *l=NULL; enable_acl=get_int(confs[OPT_ACL]); enable_xattr=get_int(confs[OPT_XATTR]); // First, tell the server about everything that needs to be backed up. logp("Phase 1 begin (file system scan)\n"); // Encryption not yet supported in protocol2. if(get_protocol(confs)==PROTO_1 && get_string(confs[OPT_ENCRYPTION_PASSWORD])) { encryption=ENCRYPTION_KEY_DERIVED; filesymbol=CMD_ENC_FILE; metasymbol=CMD_ENC_METADATA; #ifdef HAVE_WIN32 metasymbol=CMD_ENC_VSS; vss_trail_symbol=CMD_ENC_VSS_T; #endif } #ifdef HAVE_WIN32 dirsymbol=filesymbol; if(get_protocol(confs)==PROTO_1 && get_int(confs[OPT_STRIP_VSS])) dirsymbol=CMD_DIRECTORY; #endif if(!(ff=find_files_init(my_send_file))) goto end; for(l=get_strlist(confs[OPT_STARTDIR]); l; l=l->next) if(l->flag) if(find_files_begin(asfd, ff, confs, l->path)) goto end; ret=0; end: cntr_print_end_phase1(get_cntr(confs)); if(ret) logp("Error in phase 1\n"); logp("Phase 1 end (file system scan)\n"); find_files_free(&ff); return ret; } burp-2.4.0/src/client/backup_phase1.h000066400000000000000000000002221404341324700174070ustar00rootroot00000000000000#ifndef _BACKUP_PHASE1_CLIENT_H #define _BACKUP_PHASE1_CLIENT_H extern int backup_phase1_client(struct asfd *asfd, struct conf **confs); #endif burp-2.4.0/src/client/ca.c000066400000000000000000000154011404341324700152640ustar00rootroot00000000000000#include "../burp.h" #include "../asfd.h" #include "../async.h" #include "../cmd.h" #include "../conf.h" #include "../conffile.h" #include "../fsops.h" #include "../handy.h" #include "../iobuf.h" #include "../log.h" #include "../run_script.h" #include "cvss.h" #include "ca.h" static int generate_key_and_csr(struct asfd *asfd, struct conf **confs, const char *csr_path) { int a=0; const char *args[12]; const char *ca_burp_ca=get_string(confs[OPT_CA_BURP_CA]); const char *cname=get_string(confs[OPT_CNAME]); const char *ssl_key=get_string(confs[OPT_SSL_KEY]); logp("Generating SSL key and certificate signing request\n"); logp("Running '%s --key --keypath %s --request --requestpath %s --name %s'\n", ca_burp_ca, ssl_key, csr_path, cname); #ifdef HAVE_WIN32 win32_enable_backup_privileges(); #else // FIX THIS signal(SIGPIPE, SIG_IGN); #endif args[a++]=ca_burp_ca; args[a++]="--key"; args[a++]="--keypath"; args[a++]=ssl_key; args[a++]="--request"; args[a++]="--requestpath"; args[a++]=csr_path; args[a++]="--name"; args[a++]=cname; args[a++]=NULL; if(run_script(asfd, args, NULL, confs, 1 /* wait */, 0, 0 /* do not use logp - stupid openssl prints lots of dots one at a time with no way to turn it off */)) { logp("error when running '%s --key --keypath %s --request --requestpath %s --name %s'\n", ca_burp_ca, ssl_key, csr_path, cname); return -1; } return 0; } /* Rewrite the conf file with the ssl_peer_cn value changed to what the server told us it should be. */ static int rewrite_client_conf(struct conf **confs) { int ret=-1; char p[32]=""; struct fzp *dp=NULL; struct fzp *sp=NULL; char *tmp=NULL; char buf[4096]=""; const char *conffile=get_string(confs[OPT_CONFFILE]); const char *ssl_peer_cn=get_string(confs[OPT_SSL_PEER_CN]); snprintf(p, sizeof(p), ".%d", getpid()); if(!(tmp=prepend(conffile, p))) goto end; if(!(sp=fzp_open(conffile, "rb")) || !(dp=fzp_open(tmp, "wb"))) goto end; while(fzp_gets(sp, buf, sizeof(buf))) { char *copy=NULL; char *field=NULL; char *value=NULL; int r=0; if(!(copy=strdup_w(buf, __func__))) goto end; if(conf_get_pair(buf, &field, &value, &r) || !field || !value || strcmp(field, "ssl_peer_cn")) { fzp_printf(dp, "%s", copy); free_w(©); continue; } free_w(©); #ifdef HAVE_WIN32 fzp_printf(dp, "ssl_peer_cn = %s\r\n", ssl_peer_cn); #else fzp_printf(dp, "ssl_peer_cn = %s\n", ssl_peer_cn); #endif } fzp_close(&sp); if(fzp_close(&dp)) { logp("error closing %s in %s\n", tmp, __func__); goto end; } if(files_equal(conffile, tmp, 0/*compressed*/)) { // No need to overwrite if there were no differences. ret=0; unlink(tmp); goto end; } logp("Rewriting conf file: %s\n", conffile); // Nasty race conditions going on here. However, the new config // file will get left behind, so at worse you will have to move // the new file into the correct place by hand. Or delete everything // and start again. #ifdef HAVE_WIN32 // Need to delete the destination, or Windows gets upset. unlink(conffile); #endif if(do_rename(tmp, conffile)) goto end; ret=0; end: fzp_close(&sp); fzp_close(&dp); if(ret) { logp("%s with %s failed\n", __func__, conffile); unlink(tmp); } free_w(&tmp); return ret; } static enum asl_ret csr_client_func(struct asfd *asfd, struct conf **confs, __attribute__((unused)) void *param) { if(strncmp_w(asfd->rbuf->buf, "csr ok:")) { iobuf_log_unexpected(asfd->rbuf, __func__); return ASL_END_ERROR; } // The server appends its name after 'csr ok:' if(set_string(confs[OPT_SSL_PEER_CN], asfd->rbuf->buf+strlen("csr ok:"))) return ASL_END_ERROR; return ASL_END_OK; } /* Return 1 for everything OK, signed and returned, -1 for error, 0 for nothing done. */ int ca_client_setup(struct asfd *asfd, struct conf **confs) { int ret=-1; struct stat statp; char csr_path[256]=""; char ssl_cert_tmp[512]=""; char ssl_cert_ca_tmp[512]=""; const char *ca_burp_ca=get_string(confs[OPT_CA_BURP_CA]); const char *ca_csr_dir=get_string(confs[OPT_CA_CSR_DIR]); const char *cname=get_string(confs[OPT_CNAME]); const char *ssl_key=get_string(confs[OPT_SSL_KEY]); const char *ssl_cert=get_string(confs[OPT_SSL_CERT]); const char *ssl_cert_ca=get_string(confs[OPT_SSL_CERT_CA]); struct cntr *cntr=get_cntr(confs); /* Store setting, compared later to decide whether to rewrite the config */ char *ssl_peer_cn_old=strdup_w(get_string(confs[OPT_SSL_PEER_CN]), __func__); if(!ssl_peer_cn_old) goto end; // Do not continue if we have one of the following things not set. if( !ca_burp_ca || !ca_csr_dir || !ssl_cert_ca || !ssl_cert || !ssl_key // Do not try to get a new certificate if we already have a key. || !lstat(ssl_key, &statp)) { if(asfd->write_str(asfd, CMD_GEN, "nocsr") || asfd_read_expect(asfd, CMD_GEN, "nocsr ok")) { logp("problem reading from server nocsr\n"); goto end; } logp("nocsr ok\n"); ret=0; goto end; } // Tell the server we want to do a signing request and store the servers name in ssl_peer_cn. if(asfd->write_str(asfd, CMD_GEN, "csr") || asfd->simple_loop(asfd, confs, NULL, __func__, csr_client_func)) goto end; logp("Server will sign a certificate request\n"); // First need to generate a client key and a certificate signing // request. snprintf(csr_path, sizeof(csr_path), "%s/%s.csr", ca_csr_dir, cname); if(generate_key_and_csr(asfd, confs, csr_path)) goto end_cleanup; // Then copy the csr to the server. if(send_a_file(asfd, csr_path, cntr)) goto end_cleanup; snprintf(ssl_cert_tmp, sizeof(ssl_cert_tmp), "%s.%d", ssl_cert, getpid()); snprintf(ssl_cert_ca_tmp, sizeof(ssl_cert_ca_tmp), "%s.%d", ssl_cert_ca, getpid()); // The server will then sign it, and give it back. if(receive_a_file(asfd, ssl_cert_tmp, cntr)) goto end_cleanup; // The server will also send the CA certificate. if(receive_a_file(asfd, ssl_cert_ca_tmp, cntr)) goto end_cleanup; // Possible race condition - the rename can delete the destination // and then fail. Worse case, the user has to rename them by hand. if(do_rename(ssl_cert_tmp, ssl_cert) || do_rename(ssl_cert_ca_tmp, ssl_cert_ca)) goto end_cleanup; // Need to rewrite our configuration file to contain the server // name (ssl_peer_cn) if the name differs from the config file. if(strncmp_w(ssl_peer_cn_old, get_string(confs[OPT_SSL_PEER_CN]))) { if(rewrite_client_conf(confs)) goto end_cleanup; } // My goodness, everything seems to have gone OK. Stand back! ret=1; end_cleanup: if(ret<0) { // On error, remove any possibly newly created files, so that // this function might run again on another go. unlink(csr_path); unlink(ssl_key); unlink(ssl_cert); unlink(ssl_cert_ca); unlink(ssl_cert_tmp); unlink(ssl_cert_ca_tmp); } end: if(ssl_peer_cn_old) free_w(&ssl_peer_cn_old); return ret; } burp-2.4.0/src/client/ca.h000066400000000000000000000001651404341324700152720ustar00rootroot00000000000000#ifndef CA_CLIENT_H #define CA_CLIENT_H extern int ca_client_setup(struct asfd *asfd, struct conf **confs); #endif burp-2.4.0/src/client/cvss.c000066400000000000000000000163471404341324700156710ustar00rootroot00000000000000#include "../burp.h" #include "../alloc.h" #include "../berrno.h" #include "../bfile.h" #include "../cmd.h" #include "../cntr.h" #include "../log.h" #include "../strlist.h" #include "cvss.h" #include "extrameta.h" #if defined(WIN32_VSS) #include "vss.h" // Attempt to stop VSS nicely if the client is interrupted by the user. BOOL CtrlHandler(DWORD fdwCtrlType) { switch(fdwCtrlType) { // Handle the CTRL-C signal. case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: win32_stop_vss(); return FALSE; default: return FALSE; } } int win32_start_vss(struct asfd *asfd, struct conf **confs) { int errors=0; struct cntr *cntr=get_cntr(confs); const char *drives_vss=get_string(confs[OPT_VSS_DRIVES]); if(SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) logp("Control handler registered.\n"); else { logw(asfd, cntr, "Could not register control handler.\n"); errors++; return errors; } if(g_pVSSClient->InitializeForBackup(asfd, cntr)) { char szWinDriveLetters[27]; // Tell vss which drives to snapshot. if(drives_vss) { unsigned int i=0; for(i=0; inext) { const char *path=NULL; if(!s->flag) continue; path=s->path; if(strlen(path)>2 && isalpha(path[0]) && path[1]==':') { int x=0; // Try not to add the same letter twice. for(x=0; xGetDriverName(), szWinDriveLetters); if(!g_pVSSClient->CreateSnapshots(szWinDriveLetters)) { berrno be; berrno_init(&be); logw(asfd, cntr, "Generate VSS snapshots failed.ERR=%s\n", berrno_bstrerror(&be, b_errno_win32)); errors++; } else { int i; for(i=0; i<(int)strlen(szWinDriveLetters); i++) { logp("VSS drive letters: %d\n", i); if(islower(szWinDriveLetters[i])) { logw(asfd, cntr, "Generate VSS snapshot of drive \"%c:\\\" failed.\n", szWinDriveLetters[i]); errors++; } } for(i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) { if(g_pVSSClient->GetWriterState(i)<1) { logw(asfd, cntr, "Start GetWriterState(%d)<1\n", i); errors++; } logp("VSS Writer (PrepareForBackup): %s\n", g_pVSSClient->GetWriterInfo(i)); } } } else { berrno be; berrno_init(&be); logw(asfd, cntr, "VSS was not initialized properly. ERR=%s", berrno_bstrerror(&be, b_errno_win32)); errors++; } return errors; } int win32_stop_vss(void) { int errors=0; if(g_pVSSClient->CloseBackup()) { int i=0; for(i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) { if(g_pVSSClient->GetWriterState(i)<1) { // Would be better to be a logw, but this gets // called by some weird handler thing above, so // it is hard to pass in asfd and cntr. logp("Stop GetWriterState(%d)<1\n", i); errors++; } logp("VSS Writer (BackupComplete): %s\n", g_pVSSClient->GetWriterInfo(i)); } } Win32ConvCleanupCache(); return errors; } #endif // WIN32_VSS #if defined(HAVE_WIN32) static int enable_priv(HANDLE hToken, const char *name) { TOKEN_PRIVILEGES tkp; DWORD lerror; if(!(p_LookupPrivilegeValue && p_AdjustTokenPrivileges)) return 0; /* not avail on this OS */ // Get the LUID for the security privilege. if(!p_LookupPrivilegeValue(NULL, name, &tkp.Privileges[0].Luid)) { logp("LookupPrivilegeValue: %lu\n", (unsigned long)GetLastError()); return 0; } /* Set the security privilege for this process. */ tkp.PrivilegeCount=1; tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED; p_AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL); lerror=GetLastError(); if(lerror==ERROR_SUCCESS) return 0; logp("Could not set privilege %s\n", name); return 1; } /* * Setup privileges we think we will need. We probably do not need * the SE_SECURITY_NAME, but since nothing seems to be working, * we get it hoping to fix the problems. */ int win32_enable_backup_privileges() { int ret=0; HANDLE hToken; HANDLE hProcess; if(!p_OpenProcessToken) return 0; /* No avail on this OS */ hProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); // Get a token for this process. if(!p_OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { logp("Could not OpenProcessToken\n"); /* Forge on anyway */ } if(enable_priv(hToken, SE_BACKUP_NAME)) ret=-1; if(enable_priv(hToken, SE_RESTORE_NAME)) ret=-1; if(enable_priv(hToken, SE_SECURITY_NAME)) ret=-1; /* enable_priv(hToken, SE_TAKE_OWNERSHIP_NAME); enable_priv(hToken, SE_ASSIGNPRIMARYTOKEN_NAME); enable_priv(hToken, SE_SYSTEM_ENVIRONMENT_NAME); enable_priv(hToken, SE_CREATE_TOKEN_NAME); enable_priv(hToken, SE_MACHINE_ACCOUNT_NAME); enable_priv(hToken, SE_TCB_NAME); enable_priv(hToken, SE_CREATE_PERMANENT_NAME); */ CloseHandle(hToken); CloseHandle(hProcess); if(ret) { logp("Some privileges were not enabled.\n\n"); logp("Are you running as Administrator?\n\n"); } return ret; } static int ensure_read(BFILE *bfd, char *buf, size_t s, int print_err) { ssize_t got=0; size_t offset=0; while((got=bfd->read(bfd, buf+offset, s-offset))>0) { offset+=got; if(offset>=s) break; } if(offset!=s) { if(print_err) logp("Error in read - got %lu, wanted %lu\n", (unsigned long)offset, (unsigned long)s); return -1; } return 0; } int get_vss(BFILE *bfd, char **vssdata, size_t *vlen) { bsid sid; char *tmp=NULL; *vlen=0; while(!ensure_read(bfd, (char *)&sid, bsidsize, 0)) { int64_t s=0; if(!(tmp=(char *)realloc_w(tmp, (*vlen)+bsidsize, __func__))) goto error; memcpy(tmp+(*vlen), &sid, bsidsize); (*vlen)+=bsidsize; // dwStreamId==1 means start of backup data, so finish. if(sid.dwStreamId==1) { // logp("\n%s: %d + %d\n", // path, (int)sid.Size, (int)sid.dwStreamNameSize); bfd->datalen=sid.Size; break; } // Otherwise, need to read in the rest of the VSS header. s=(sid.Size)+(sid.dwStreamNameSize); if(!(tmp=(char *)realloc_w(tmp, (*vlen)+s, __func__)) || ensure_read(bfd, tmp+(*vlen), s, 1)) { goto error; return -1; } (*vlen)+=s; } if(!(*vssdata=(char *)realloc_w(*vssdata, (*vlen)+9, __func__))) goto error; snprintf(*vssdata, 9, "%c%08X", META_VSS, (unsigned int)*vlen); memcpy((*vssdata)+9, tmp, *vlen); (*vlen)+=9; free_w(&tmp); return 0; error: free_w(&tmp); free_w(vssdata); *vlen=0; return -1; } static int ensure_write(BFILE *bfd, const char *buf, size_t got) { size_t wrote=0; while((wrote=bfd->write(bfd, (void *)buf, got))>0) { got-=wrote; if(got<=0) return 0; } logp("error when writing VSS data\n"); return -1; } int set_vss(BFILE *bfd, const char *vssdata, size_t vlen) { // Just need to write the VSS stuff to the file. if(!vlen || !vssdata) return 0; return ensure_write(bfd, vssdata, vlen); } #endif /* HAVE_WIN32 */ burp-2.4.0/src/client/cvss.h000066400000000000000000000006441404341324700156670ustar00rootroot00000000000000#ifndef _CLIENT_VSS_H #define _CLIENT_VSS_H #if defined(WIN32_VSS) extern int win32_start_vss(struct asfd *asfd, struct conf **confs); extern int win32_stop_vss(void); extern int get_vss(BFILE *bfd, char **vssdata, size_t *vlen); extern int set_vss(BFILE *bfd, const char *vssdata, size_t vlen); #endif // WIN32_VSS #if defined(HAVE_WIN32) extern int win32_enable_backup_privileges(); #endif /* HAVE_WIN32 */ #endif burp-2.4.0/src/client/delete.c000066400000000000000000000006701404341324700161450ustar00rootroot00000000000000#include "../burp.h" #include "../asfd.h" #include "../cmd.h" #include "../log.h" #include "delete.h" int do_delete_client(struct asfd *asfd, struct conf **confs) { char msg[128]=""; const char *backup=get_string(confs[OPT_BACKUP]); snprintf(msg, sizeof(msg), "Delete %s", backup?backup:""); if(asfd->write_str(asfd, CMD_GEN, msg) || asfd_read_expect(asfd, CMD_GEN, "ok")) return -1; logp("Deletion in progress\n"); return 0; } burp-2.4.0/src/client/delete.h000066400000000000000000000002001404341324700161370ustar00rootroot00000000000000#ifndef _DELETE_CLIENT_H #define _DELETE_CLIENT_H extern int do_delete_client(struct asfd *asfd, struct conf **confs); #endif burp-2.4.0/src/client/extra_comms.c000066400000000000000000000176531404341324700172350ustar00rootroot00000000000000#include "../burp.h" #include "../asfd.h" #include "../async.h" #include "../cmd.h" #include "../conf.h" #include "../conffile.h" #include "../handy.h" #include "../incexc_recv.h" #include "../incexc_send.h" #include "../iobuf.h" #include "../log.h" #include "autoupgrade.h" #include "extra_comms.h" #ifndef HAVE_WIN32 #include #endif static const char *server_supports(const char *feat, const char *wanted) { return strstr(feat, wanted); } static const char *server_supports_autoupgrade(const char *feat) { // 1.3.0 servers did not list the features, but the only feature // that was supported was autoupgrade. if(!strcmp(feat, "extra_comms_begin ok")) return "ok"; return server_supports(feat, ":autoupgrade:"); } #include int extra_comms_client(struct async *as, struct conf **confs, enum action *action, struct strlist *failover, char **incexc) { int ret=-1; char *feat=NULL; char *seed_src=NULL; char *seed_dst=NULL; struct asfd *asfd; struct iobuf *rbuf; const char *orig_client=NULL; asfd=as->asfd; rbuf=asfd->rbuf; if(asfd->write_str(asfd, CMD_GEN, "extra_comms_begin")) { logp("Problem requesting extra_comms_begin\n"); goto end; } // Servers greater than 1.3.0 will list the extra_comms // features they support. if(asfd->read(asfd)) { logp("Problem reading response to extra_comms_begin\n"); goto end; } if(rbuf->cmd!=CMD_GEN || strncmp_w(rbuf->buf, "extra_comms_begin ok")) { iobuf_log_unexpected(rbuf, __func__); goto end; } feat=rbuf->buf; rbuf->buf=NULL; logp("%s\n", feat); iobuf_init(rbuf); // Can add extra bits here. The first extra bit is the // autoupgrade stuff. if(server_supports_autoupgrade(feat) && get_string(confs[OPT_AUTOUPGRADE_DIR]) && get_string(confs[OPT_AUTOUPGRADE_OS]) && autoupgrade_client(as, confs)) goto end; // :srestore: means that the server wants to do a restore. if(server_supports(feat, ":srestore:")) { logp("Server wants to initiate a restore\n"); if(*action==ACTION_MONITOR) { logp("Client is in monitor mode, so ignoring\n"); } else if(get_int(confs[OPT_SERVER_CAN_RESTORE])) { logp("Client accepts.\n"); if(incexc_recv_client_restore(asfd, incexc, confs)) goto end; if(*incexc) { if(conf_parse_incexcs_srestore(confs, *incexc)) goto end; *action=ACTION_RESTORE; log_restore_settings(confs, 1); } } else { logp("Client configuration says no\n"); if(asfd->write_str(asfd, CMD_GEN, "srestore not ok")) goto end; } } // Needs to be after the srestore stuff, as the server may set // orig_client in the server-initiated restore file. if((orig_client=get_string(confs[OPT_ORIG_CLIENT]))) { char str[512]=""; snprintf(str, sizeof(str), "orig_client=%s", orig_client); if(!server_supports(feat, ":orig_client:")) { logp("Server does not support switching client.\n"); goto end; } if(asfd->write_str(asfd, CMD_GEN, str) || asfd_read_expect(asfd, CMD_GEN, "orig_client ok")) { logp("Problem requesting %s\n", str); goto end; } logp("Switched to client %s\n", orig_client); } // :sincexc: is for the server giving the client the // incexc config. if(*action==ACTION_BACKUP || *action==ACTION_BACKUP_TIMED || *action==ACTION_TIMER_CHECK) { if(!*incexc && server_supports(feat, ":sincexc:")) { logp("Server is setting includes/excludes.\n"); if(get_int(confs[OPT_SERVER_CAN_OVERRIDE_INCLUDES])) { logp("Client accepts.\n"); if(incexc_recv_client(asfd, incexc, confs)) goto end; if(*incexc && conf_parse_incexcs_buf(confs, *incexc)) goto end; } else { logp("Client configuration says no\n"); } } } if(server_supports(feat, ":counters_json:")) { if(asfd->write_str(asfd, CMD_GEN, "counters_json ok")) goto end; set_int(confs[OPT_SEND_CLIENT_CNTR], 1); } // :incexc: is for the client sending the server the // incexc conf so that it better knows what to do on // resume. if(server_supports(feat, ":incexc:") && incexc_send_client(asfd, confs)) goto end; if(server_supports(feat, ":uname:")) { const char *clientos=NULL; #ifdef HAVE_WIN32 #ifdef _WIN64 clientos="Windows 64bit"; #else clientos="Windows 32bit"; #endif #else struct utsname utsname; if(!uname(&utsname)) clientos=(const char *)utsname.sysname; #endif if(clientos) { char *msg=NULL; if(astrcat(&msg, "uname=", __func__) || astrcat(&msg, clientos, __func__)) goto end; if(asfd->write_str(asfd, CMD_GEN, msg)) { free_w(&msg); goto end; } free_w(&msg); } } if(server_supports(feat, ":csetproto:")) { char msg[128]=""; // Use protocol1 if no choice has been made on client side. if(get_protocol(confs)==PROTO_AUTO) { logp("Server has protocol=0 (auto)\n"); set_protocol(confs, PROTO_1); } // Send choice to server. snprintf(msg, sizeof(msg), "protocol=%d", get_protocol(confs)); if(asfd->write_str(asfd, CMD_GEN, msg)) goto end; logp("Using protocol=%d\n", get_protocol(confs)); } else if(server_supports(feat, ":forceproto=1:")) { logp("Server is forcing protocol 1\n"); if(get_protocol(confs)!=PROTO_AUTO && get_protocol(confs)!=PROTO_1) { logp("But client has set protocol=%d!\n", get_protocol(confs)); goto end; } set_protocol(confs, PROTO_1); } else if(server_supports(feat, ":forceproto=2:")) { logp("Server is forcing protocol 2\n"); if(get_protocol(confs)!=PROTO_AUTO && get_protocol(confs)!=PROTO_2) { logp("But client has set protocol=%d!\n", get_protocol(confs)); goto end; } set_protocol(confs, PROTO_2); } if(get_protocol(confs)==PROTO_2 && get_string(confs[OPT_ENCRYPTION_PASSWORD])) { char msg[64]=""; snprintf(msg, sizeof(msg), "%s is not supported in protocol 2", confs[OPT_ENCRYPTION_PASSWORD]->field); log_and_send(asfd, msg); goto end; } if(server_supports(feat, ":msg:")) { set_int(confs[OPT_MESSAGE], 1); if(asfd->write_str(asfd, CMD_GEN, "msg")) goto end; } #ifdef HAVE_BLAKE2 if(server_supports(feat, ":rshash=blake2:")) { set_e_rshash(confs[OPT_RSHASH], RSHASH_BLAKE2); // Send choice to server. if(asfd->write_str(asfd, CMD_GEN, "rshash=blake2")) goto end; } else #endif set_e_rshash(confs[OPT_RSHASH], RSHASH_MD4); if(server_supports(feat, ":failover:")) { if(*action==ACTION_BACKUP || *action==ACTION_BACKUP_TIMED) { char msg[64]=""; int left=0; struct strlist *f=NULL; for(f=failover; f; f=f->next) left++; snprintf(msg, sizeof(msg), "backup_failovers_left=%d", left); if(asfd->write_str(asfd, CMD_GEN, msg)) goto end; } } seed_src=get_string(confs[OPT_SEED_SRC]); seed_dst=get_string(confs[OPT_SEED_DST]); if(seed_src && *seed_src && seed_dst && *seed_dst && server_supports(feat, ":seed:")) { char *msg=NULL; logp("Seeding from %s\n", seed_src); if(astrcat(&msg, "seed_src=", __func__) || astrcat(&msg, seed_src, __func__) || asfd->write_str(asfd, CMD_GEN, msg)) { free_w(&msg); goto end; } free_w(&msg); logp("Seeding to %s\n", seed_dst); if(astrcat(&msg, "seed_dst=", __func__) || astrcat(&msg, seed_dst, __func__) || asfd->write_str(asfd, CMD_GEN, msg)) { free_w(&msg); goto end; } free_w(&msg); } if(server_supports(feat, ":vss_restore:")) { enum vss_restore vss_restore=(enum vss_restore) get_int(confs[OPT_VSS_RESTORE]); if(vss_restore==VSS_RESTORE_OFF && asfd->write_str(asfd, CMD_GEN, "vss_restore=off")) goto end; if(vss_restore==VSS_RESTORE_OFF_STRIP && asfd->write_str(asfd, CMD_GEN, "vss_restore=strip")) goto end; } if(server_supports(feat, ":regex_icase:")) { if(get_int(confs[OPT_REGEX_CASE_INSENSITIVE])) { if(asfd->write_str(asfd, CMD_GEN, "regex_icase=1")) goto end; } } if(asfd->write_str(asfd, CMD_GEN, "extra_comms_end") || asfd_read_expect(asfd, CMD_GEN, "extra_comms_end ok")) { logp("Problem requesting extra_comms_end\n"); goto end; } ret=0; end: free_w(&feat); return ret; } burp-2.4.0/src/client/extra_comms.h000066400000000000000000000003121404341324700172220ustar00rootroot00000000000000#ifndef _EXTRA_COMMS_CLIENT_H #define _EXTRA_COMMS_CLIENT_H extern int extra_comms_client(struct async *as, struct conf **confs, enum action *action, struct strlist *failover, char **incexc); #endif burp-2.4.0/src/client/extrameta.c000066400000000000000000000066341404341324700167030ustar00rootroot00000000000000#include "../burp.h" #include "../alloc.h" #include "../bfile.h" #include "../cmd.h" #include "../conf.h" #include "../log.h" #include "acl.h" #include "cvss.h" #include "extrameta.h" #include "xattr.h" int has_extrameta(const char *path, enum cmd cmd, int enable_acl, int enable_xattr) { #if defined(WIN32_VSS) return 1; #endif #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_NETBSD_OS) #ifdef HAVE_ACL if(enable_acl && has_acl(path, cmd)) return 1; #endif #endif #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_NETBSD_OS) || \ defined(HAVE_DARWIN_OS) #ifdef HAVE_XATTR if(enable_xattr && has_xattr(path)) return 1; #endif #endif return 0; } int get_extrameta(struct asfd *asfd, #ifdef HAVE_WIN32 struct BFILE *bfd, #endif const char *path, int isdir, char **extrameta, size_t *elen, struct cntr *cntr) { #if defined (WIN32_VSS) if(get_vss(bfd, extrameta, elen)) return -1; #endif // Important to do xattr directly after acl, because xattr is excluding // some entries if acls were set. #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_NETBSD_OS) #ifdef HAVE_ACL if(get_acl(asfd, path, isdir, extrameta, elen, cntr)) return -1; #endif #endif #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_NETBSD_OS) || \ defined(HAVE_DARWIN_OS) #ifdef HAVE_XATTR if(get_xattr(asfd, path, extrameta, elen, cntr)) return -1; #endif #endif return 0; } int set_extrameta(struct asfd *asfd, #ifdef HAVE_WIN32 struct BFILE *bfd, #endif const char *path, const char *extrameta, size_t metalen, struct cntr *cntr) { size_t l=0; char cmdtmp='\0'; uint32_t s=0; const char *metadata=NULL; int errors=0; metadata=extrameta; l=metalen; while(l>0) { char *m=NULL; if(l<9) { logw(asfd, cntr, "length of metadata '%s' %d is too short for %s\n", metadata, (uint32_t)l, path); return -1; } if((sscanf(metadata, "%c%08X", &cmdtmp, &s))!=2) { logw(asfd, cntr, "sscanf of metadata '%s' %d failed for %s\n", metadata, (uint32_t)l, path); return -1; } metadata+=9; l-=9; if(s>l) { logw(asfd, cntr, "requested length %d of metadata '%s' %d is too long for %s\n", s, metadata, (uint32_t)l, path); return -1; } if(!(m=(char *)malloc_w(s+1, __func__))) return -1; memcpy(m, metadata, s); m[s]='\0'; metadata+=s; l-=s; switch(cmdtmp) { #if defined(HAVE_WIN32) case META_VSS: if(set_vss(bfd, m, s)) errors++; break; #endif #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_NETBSD_OS) #ifdef HAVE_ACL case META_ACCESS_ACL: if(set_acl(asfd, path, m, cmdtmp, cntr)) errors++; break; case META_DEFAULT_ACL: if(set_acl(asfd, path, m, cmdtmp, cntr)) errors++; break; #endif #endif #if defined(HAVE_LINUX_OS) || \ defined(HAVE_DARWIN_OS) #ifdef HAVE_XATTR case META_XATTR: if(set_xattr(asfd, path, m, s, cmdtmp, cntr)) errors++; break; #endif #endif #if defined(HAVE_FREEBSD_OS) || \ defined(HAVE_NETBSD_OS) #ifdef HAVE_XATTR case META_XATTR_BSD: if(set_xattr(asfd, path, m, s, cmdtmp, cntr)) errors++; break; #endif #endif default: logp("unknown metadata: %c\n", cmdtmp); logw(asfd, cntr, "unknown metadata: %c\n", cmdtmp); errors++; break; } free_w(&m); } return errors; } burp-2.4.0/src/client/extrameta.h000066400000000000000000000012361404341324700167010ustar00rootroot00000000000000#ifndef _EXTRAMETA_H #define _EXTRAMETA_H #include "../bfile.h" #include "../sbuf.h" #define META_ACCESS_ACL 'A' #define META_DEFAULT_ACL 'D' #define META_XATTR 'X' #define META_XATTR_BSD 'B' #define META_VSS 'V' extern int has_extrameta(const char *path, enum cmd cmd, int enable_acl, int enable_xattr); extern int get_extrameta(struct asfd *asfd, #ifdef HAVE_WIN32 struct BFILE *bfd, #endif const char *path, int isdir, char **extrameta, size_t *elen, struct cntr *cntr); extern int set_extrameta(struct asfd *asfd, #ifdef HAVE_WIN32 struct BFILE *bfd, #endif const char *path, const char *extrameta, size_t metalen, struct cntr *cntr); #endif burp-2.4.0/src/client/find.c000066400000000000000000000430221404341324700156210ustar00rootroot00000000000000/* Bacula® - The Network Backup Solution Copyright (C) 2000-2010 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. 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 Affero 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. Bacula® is a registered trademark of Kern Sibbald. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ /* This file was derived from GNU TAR source code. Except for a few key ideas, it has been entirely rewritten for Bacula. Kern Sibbald, MM Thanks to the TAR programmers. */ /* This file was derived from the findlib code from bacula-5.0.3, and heavily modified. Therefore, I have retained the bacula copyright notice. The specific bacula files were: src/findlib/find.c src/findlib/find_one.c. The comment by Kern above, about TAR, was from find_one.c. Graham Keeling, 2014. */ #include "../burp.h" #include "../alloc.h" #include "../conf.h" #include "../fsops.h" #include "../linkhash.h" #include "../log.h" #include "../pathcmp.h" #include "../prepend.h" #include "../regexp.h" #include "../strlist.h" #include "find.h" #include "find_logic.h" #ifdef HAVE_LINUX_OS #include #endif #ifdef HAVE_SUN_OS #include #endif static int (*my_send_file)(struct asfd *, struct FF_PKT *, struct conf **); // Initialize the find files "global" variables struct FF_PKT *find_files_init( int callback(struct asfd *asfd, struct FF_PKT *ff, struct conf **confs)) { struct FF_PKT *ff; if(!(ff=(struct FF_PKT *)calloc_w(1, sizeof(struct FF_PKT), __func__)) || linkhash_init()) return NULL; my_send_file=callback; return ff; } void find_files_free(struct FF_PKT **ff) { linkhash_free(); free_v((void **)ff); } // Return 1 to include the file, 0 to exclude it. static int in_include_ext(struct strlist *incext, const char *fname) { int i=0; struct strlist *l; const char *cp=NULL; // If not doing include_ext, let the file get backed up. if(!incext) return 1; // The flag of the first item contains the maximum number of characters // that need to be checked. // FIX THIS: The next two functions do things very similar to this. for(cp=fname+strlen(fname)-1; iflag && cp>=fname; cp--, i++) { if(*cp!='.') continue; for(l=incext; l; l=l->next) if(!strcasecmp(l->path, cp+1)) return 1; // If file has no extension, it cannot be included. return 0; } return 0; } static int in_exclude_ext(struct strlist *excext, const char *fname) { int i=0; struct strlist *l; const char *cp=NULL; // If not doing exclude_ext, let the file get backed up. if(!excext) return 0; // The flag of the first item contains the maximum number of characters // that need to be checked. for(cp=fname+strlen(fname)-1; iflag && cp>=fname; cp--, i++) { if(*cp!='.') continue; for(l=excext; l; l=l->next) if(!strcasecmp(l->path, cp+1)) return 1; // If file has no extension, it is included. return 0; } return 0; } // Returns the level of compression. int in_exclude_comp(struct strlist *excom, const char *fname, int compression) { int i=0; struct strlist *l; const char *cp=NULL; // If not doing compression, or there are no excludes, return // straight away. if(!compression || !excom) return compression; // The flag of the first item contains the maximum number of characters // that need to be checked. for(cp=fname+strlen(fname)-1; iflag && cp>=fname; cp--, i++) { if(*cp!='.') continue; for(l=excom; l; l=l->next) if(!strcasecmp(l->path, cp+1)) return 0; return compression; } return compression; } /* Return 1 to include the file, 0 to exclude it. */ int in_include_regex(struct strlist *increg, const char *fname) { // If not doing include_regex, let the file get backed up. if(!increg) return 1; for(; increg; increg=increg->next) if(regex_check(increg->re, fname)) return 1; return 0; } static int in_exclude_regex(struct strlist *excreg, const char *fname) { // If not doing exclude_regex, let the file get backed up. for(; excreg; excreg=excreg->next) if(regex_check(excreg->re, fname)) return 1; return 0; } // When recursing into directories, do not want to check the include_ext list. #ifndef UTEST static #endif int file_is_included_no_incext(struct conf **confs, const char *fname) { int ret=0; int longest=0; int matching=0; struct strlist *l=NULL; struct strlist *best=NULL; if(in_exclude_ext(get_strlist(confs[OPT_EXCEXT]), fname) || in_exclude_regex(get_strlist(confs[OPT_EXCREG]), fname) || !in_include_regex(get_strlist(confs[OPT_INCREG]), fname)) return 0; // Check include/exclude directories. for(l=get_strlist(confs[OPT_INCEXCDIR]); l; l=l->next) { matching=is_subdir(l->path, fname); if(matching>=longest) { longest=matching; best=l; } } if(!best) ret=0; else ret=best->flag; return ret; } static int file_is_included(struct conf **confs, const char *fname, bool top_level) { // Always save the top level directory. // This will help in the simulation of browsing backups because it // will mean that there is always a directory before any files: // d /home/graham // f /home/graham/somefile.txt // This means that we can use the stats of the directory (/home/graham // in this example) as the stats of the parent directories (/home, // for example). Trust me on this. if(!top_level && !in_include_ext(get_strlist(confs[OPT_INCEXT]), fname)) return 0; return file_is_included_no_incext(confs, fname); } static int fs_change_is_allowed(struct conf **confs, const char *fname) { struct strlist *l; if(get_int(confs[OPT_CROSS_ALL_FILESYSTEMS])) return 1; for(l=get_strlist(confs[OPT_FSCHGDIR]); l; l=l->next) if(!strcmp(l->path, fname)) return 1; return 0; } static int need_to_read_fifo(struct conf **confs, const char *fname) { struct strlist *l; if(get_int(confs[OPT_READ_ALL_FIFOS])) return 1; for(l=get_strlist(confs[OPT_FIFOS]); l; l=l->next) if(!strcmp(l->path, fname)) return 1; return 0; } static int need_to_read_blockdev(struct conf **confs, const char *fname) { struct strlist *l; if(get_int(confs[OPT_READ_ALL_BLOCKDEVS])) return 1; for(l=get_strlist(confs[OPT_BLOCKDEVS]); l; l=l->next) if(!strcmp(l->path, fname)) return 1; return 0; } static int nobackup_directory(struct strlist *nobackup, const char *path) { struct stat statp; for(; nobackup; nobackup=nobackup->next) { char *fullpath=NULL; if(!(fullpath=prepend_s(path, nobackup->path))) return -1; if(!lstat(fullpath, &statp)) { free_w(&fullpath); return 1; } free_w(&fullpath); } return 0; } static int file_size_match(struct FF_PKT *ff_pkt, struct conf **confs) { uint64_t sizeleft; uint64_t min_file_size=get_uint64_t(confs[OPT_MIN_FILE_SIZE]); uint64_t max_file_size=get_uint64_t(confs[OPT_MAX_FILE_SIZE]); sizeleft=(uint64_t)ff_pkt->statp.st_size; if(min_file_size && sizeleftmax_file_size) return 0; return 1; } // Last checks before actually processing the file system entry. static int my_send_file_w(struct asfd *asfd, struct FF_PKT *ff, bool top_level, struct conf **confs) { if(!file_is_included(confs, ff->fname, top_level) || is_logic_excluded(confs, ff)) return 0; // Doing the file size match here also catches hard links. if(S_ISREG(ff->statp.st_mode) && !file_size_match(ff, confs)) return 0; /* * Handle hard linked files * Maintain a list of hard linked files already backed up. This * allows us to ensure that the data of each file gets backed * up only once. */ if(ff->statp.st_nlink > 1 && (S_ISREG(ff->statp.st_mode) || S_ISCHR(ff->statp.st_mode) || S_ISBLK(ff->statp.st_mode) || S_ISFIFO(ff->statp.st_mode) || S_ISSOCK(ff->statp.st_mode))) { struct f_link *lp; struct f_link **bucket=NULL; if((lp=linkhash_search(&ff->statp, &bucket))) { if(!strcmp(lp->name, ff->fname)) return 0; ff->link=lp->name; /* Handle link, file already saved */ ff->type=FT_LNK_H; } else { if(linkhash_add(ff->fname, &ff->statp, bucket)) return -1; } } return my_send_file(asfd, ff, confs); } static int found_regular_file(struct asfd *asfd, struct FF_PKT *ff_pkt, struct conf **confs, bool top_level) { ff_pkt->type=FT_REG; return my_send_file_w(asfd, ff_pkt, top_level, confs); } static int found_soft_link(struct asfd *asfd, struct FF_PKT *ff_pkt, struct conf **confs, char *fname, bool top_level) { ssize_t size; char *buffer=(char *)alloca(fs_full_path_max+102); if((size=readlink(fname, buffer, fs_full_path_max+101))<0) { /* Could not follow link */ ff_pkt->type=FT_NOFOLLOW; } else { buffer[size]=0; ff_pkt->link=buffer; /* point to link */ ff_pkt->type=FT_LNK_S; /* got a soft link */ } return my_send_file_w(asfd, ff_pkt, top_level, confs); } static int fstype_matches(struct asfd *asfd, struct conf **confs, const char *fname, int inex) { #if defined(HAVE_LINUX_OS) \ || defined(HAVE_SUN_OS) struct strlist *l; #if defined(HAVE_LINUX_OS) struct statfs buf; if(statfs(fname, &buf)) #elif defined(HAVE_SUN_OS) struct statvfs buf; if(statvfs(fname, &buf)) #endif { logw(asfd, get_cntr(confs), "Could not statfs %s: %s\n", fname, strerror(errno)); return -1; } for(l=get_strlist(confs[inex]); l; l=l->next) #if defined(HAVE_LINUX_OS) if(l->flag==buf.f_type) #elif defined(HAVE_SUN_OS) if(strcmp(l->path,buf.f_basetype)==0) #endif return -1; #elif defined(HAVE_WIN32) char filesystem_name[MAX_PATH_UTF8 + 1]; if (win32_getfsname(fname, filesystem_name, sizeof(filesystem_name))) return -1; for(strlist *l=get_strlist(confs[inex]); l; l=l->next) if(strcmp(l->path,filesystem_name)==0) return -1; #endif return 0; } #if defined(HAVE_WIN32) static void windows_reparse_point_fiddling(struct FF_PKT *ff_pkt) { /* * We have set st_rdev to 1 if it is a reparse point, otherwise 0, * if st_rdev is 2, it is a mount point. */ /* * A reparse point (WIN32_REPARSE_POINT) * is something special like one of the following: * IO_REPARSE_TAG_DFS 0x8000000A * IO_REPARSE_TAG_DFSR 0x80000012 * IO_REPARSE_TAG_HSM 0xC0000004 * IO_REPARSE_TAG_HSM2 0x80000006 * IO_REPARSE_TAG_SIS 0x80000007 * IO_REPARSE_TAG_SYMLINK 0xA000000C * * A junction point is a: * IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 * which can be either a link to a Volume (WIN32_MOUNT_POINT) * or a link to a directory (WIN32_JUNCTION_POINT) */ if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) { ff_pkt->type = FT_REPARSE; } else if (ff_pkt->statp.st_rdev == WIN32_JUNCTION_POINT) { ff_pkt->type = FT_JUNCTION; } } #endif // Prototype because process_entries_in_directory() recurses using find_files(). static int find_files(struct asfd *asfd, struct FF_PKT *ff_pkt, struct conf **confs, char *fname, dev_t parent_device, bool top_level); static int process_entries_in_directory(struct asfd *asfd, char **nl, int count, char **link, size_t len, size_t *link_len, struct conf **confs, struct FF_PKT *ff_pkt, dev_t our_device) { int m=0; int ret=0; for(m=0; m=*link_len) { *link_len=len+strlen(p)+1; if(!(*link=(char *) realloc_w(*link, (*link_len)+1, __func__))) return -1; } q=(*link)+len; plen=strlen(p); for(i=0; iflen=i; if(file_is_included_no_incext(confs, *link)) { ret=find_files(asfd, ff_pkt, confs, *link, our_device, false /*top_level*/); } else { struct strlist *x; // Excluded, but there might be a subdirectory that is // included. for(x=get_strlist(confs[OPT_INCEXCDIR]); x; x=x->next) { if(x->flag && is_subdir(*link, x->path)) { struct strlist *y; if((ret=find_files(asfd, ff_pkt, confs, x->path, our_device, false))) break; // Now need to skip subdirectories of // the thing that we just stuck in // find_one_file(), or we might get // some things backed up twice. for(y=x->next; y; y=y->next) if(y->next && is_subdir(x->path, y->path)) y=y->next; } } } free_w(&(nl[m])); if(ret) break; } return ret; } static int found_directory(struct asfd *asfd, struct FF_PKT *ff_pkt, struct conf **confs, char *fname, dev_t parent_device, bool top_level) { int ret=-1; char *link=NULL; size_t link_len; size_t len; int nbret=0; int count=0; dev_t our_device; char **nl=NULL; our_device=ff_pkt->statp.st_dev; /* Build a canonical directory name with a trailing slash in link var */ len=strlen(fname); link_len=len+200; if(!(link=(char *)malloc_w(link_len+2, __func__))) goto end; snprintf(link, link_len, "%s", fname); /* Strip all trailing slashes */ while(len >= 1 && IsPathSeparator(link[len - 1])) len--; /* add back one */ link[len++]='/'; link[len]=0; ff_pkt->link=link; ff_pkt->type=FT_DIR; #if defined(HAVE_WIN32) windows_reparse_point_fiddling(ff_pkt); #endif if(my_send_file_w(asfd, ff_pkt, top_level, confs)) goto end; // After my_send_file_w, so that we backup the directory itself. if((nbret=nobackup_directory(get_strlist(confs[OPT_NOBACKUP]), ff_pkt->fname))) { if(nbret<0) goto end; // Error. ret=0; // Do not back it up. goto end; } if(ff_pkt->type==FT_REPARSE || ff_pkt->type==FT_JUNCTION) { // Ignore. ret=0; goto end; } if(top_level || (parent_device!=ff_pkt->statp.st_dev #if defined(HAVE_WIN32) || ff_pkt->statp.st_rdev==WIN32_MOUNT_POINT #endif )) { if(fstype_matches(asfd, confs, ff_pkt->fname, OPT_EXCFS) || (get_strlist(confs[OPT_INCFS]) && !fstype_matches(asfd, confs, ff_pkt->fname, OPT_INCFS))) { if(top_level) logw(asfd, get_cntr(confs), "Skipping '%s' because of file system include or exclude.\n", fname); ret=my_send_file_w(asfd, ff_pkt, top_level, confs); goto end; } if(!top_level && !fs_change_is_allowed(confs, ff_pkt->fname)) { ff_pkt->type=FT_NOFSCHG; // Just backup the directory and return. ret=my_send_file_w(asfd, ff_pkt, top_level, confs); goto end; } } ff_pkt->link=ff_pkt->fname; errno=0; switch(entries_in_directory_alphasort(fname, &nl, &count, get_int(confs[OPT_ATIME]), /* follow_symlinks */ 0)) { case 0: break; case 1: ff_pkt->type=FT_NOOPEN; ret=my_send_file_w(asfd, ff_pkt, top_level, confs); default: goto end; } if(nl) { if(process_entries_in_directory(asfd, nl, count, &link, len, &link_len, confs, ff_pkt, our_device)) goto end; } ret=0; end: free_w(&link); free_v((void **)&nl); return ret; } static int found_other(struct asfd *asfd, struct FF_PKT *ff_pkt, struct conf **confs, bool top_level) { #ifdef HAVE_FREEBSD_OS /* * On FreeBSD, all block devices are character devices, so * to be able to read a raw disk, we need the check for * a character device. * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3 */ if((S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode)) && need_to_read_blockdev(confs, ff_pkt->fname)) { #else if(S_ISBLK(ff_pkt->statp.st_mode) && need_to_read_blockdev(confs, ff_pkt->fname)) { #endif /* raw partition */ ff_pkt->type=FT_RAW; } else if(S_ISFIFO(ff_pkt->statp.st_mode) && need_to_read_fifo(confs, ff_pkt->fname)) { ff_pkt->type=FT_FIFO; } else { /* The only remaining are special (character, ...) files */ ff_pkt->type=FT_SPEC; } return my_send_file_w(asfd, ff_pkt, top_level, confs); } static int find_files(struct asfd *asfd, struct FF_PKT *ff_pkt, struct conf **confs, char *fname, dev_t parent_device, bool top_level) { ff_pkt->fname=fname; ff_pkt->link=fname; #ifdef HAVE_WIN32 if(win32_lstat(fname, &ff_pkt->statp, &ff_pkt->winattr)) #else if(lstat(fname, &ff_pkt->statp)) #endif { ff_pkt->type=FT_NOSTAT; return my_send_file_w(asfd, ff_pkt, top_level, confs); } if(S_ISREG(ff_pkt->statp.st_mode)) return found_regular_file(asfd, ff_pkt, confs, top_level); else if(S_ISLNK(ff_pkt->statp.st_mode)) { #ifdef S_IFLNK /* A symlink. If they have specified the symlink in a read_blockdev argument, treat it as a block device. */ struct strlist *l; for(l=get_strlist(confs[OPT_BLOCKDEVS]); l; l=l->next) { if(!strcmp(l->path, fname)) { ff_pkt->statp.st_mode ^= S_IFLNK; ff_pkt->statp.st_mode |= S_IFBLK; return found_other(asfd, ff_pkt, confs, top_level); } } #endif return found_soft_link(asfd, ff_pkt, confs, fname, top_level); } else if(S_ISDIR(ff_pkt->statp.st_mode)) return found_directory(asfd, ff_pkt, confs, fname, parent_device, top_level); else return found_other(asfd, ff_pkt, confs, top_level); } int find_files_begin(struct asfd *asfd, struct FF_PKT *ff_pkt, struct conf **confs, char *fname) { return find_files(asfd, ff_pkt, confs, fname, (dev_t)-1, 1 /* top_level */); } burp-2.4.0/src/client/find.h000066400000000000000000000061621404341324700156320ustar00rootroot00000000000000/* Bacula® - The Network Backup Solution Copyright (C) 2001-2010 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. 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 Affero 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. Bacula® is a registered trademark of Kern Sibbald. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ /* * File types as returned by find_files() * * Kern Sibbald MMI */ /* * This file contains fragments from bacula-5.0.3:src/findlib/find.h, hence * retaining the copyright notice above. At some point, the fragments will be * removed because the burp sbuf code will take over completely. * Graham Keeling, 2014 */ #ifndef _FIND_H #define _FIND_H #include #include #define MODE_RALL (S_IRUSR|S_IRGRP|S_IROTH) #ifdef HAVE_REGEX_H #include #endif #define FT_LNK_H 1 // hard link to file already saved. #define FT_REG 3 // Regular file. #define FT_LNK_S 4 // Soft Link. #define FT_DIR 5 // Directory. #define FT_SPEC 6 // Special file -- chr, blk, fifo, sock. #define FT_NOFOLLOW 8 // Could not follow link. #define FT_NOSTAT 9 // Could not stat file. #define FT_NOFSCHG 14 // Different file system, prohibited. #define FT_NOOPEN 15 // Could not open directory. #define FT_RAW 16 // Raw block device. #define FT_FIFO 17 // Raw fifo device. #define FT_REPARSE 21 // Win NTFS reparse point. #define FT_JUNCTION 26 // Win32 Junction point. /* * Definition of the find_files packet passed as the * first argument to the find_files callback subroutine. */ struct FF_PKT { char *fname; /* full filename */ long flen; /* length of name component */ char *link; /* link if file linked */ struct stat statp; /* stat packet */ uint64_t winattr; /* windows attributes */ int type; /* FT_ type from above */ }; struct asfd; extern struct FF_PKT *find_files_init( int callback(struct asfd *asfd, struct FF_PKT *ff, struct conf **confs)); extern void find_files_free(struct FF_PKT **ff); extern int find_files_begin(struct asfd *asfd, struct FF_PKT *ff_pkt, struct conf **confs, char *fname); // Returns the level of compression. extern int in_exclude_comp(struct strlist *excom, const char *fname, int compression); #ifdef UTEST extern int file_is_included_no_incext(struct conf **confs, const char *fname); #endif #endif burp-2.4.0/src/client/find_logic.c000066400000000000000000000353011404341324700167770ustar00rootroot00000000000000#include "../burp.h" #include "../alloc.h" #include "../handy.h" #include "../conf.h" #include "../conffile.h" #include "../regexp.h" #include "../strlist.h" #include "../log.h" #include "find.h" #include "find_logic.h" #include #ifdef HAVE_LINUX_OS #include #endif #ifdef HAVE_SUN_OS #include #endif typedef struct _node node; typedef struct _dllist dllist; typedef int TOKENS; // define our tokens as 'flags' to ease the parsing #define EVAL_FALSE 0 // The evaluated function returned FALSE #define EVAL_TRUE 1 // The evaluated function returned TRUE #define LEFT_PARENS 2 // ( #define RIGHT_PARENS 3 // ) #define AND_FUNC 4 // and #define OR_FUNC 5 // or #define NOT 6 // not #define GTE 7 // >= #define GT 8 // > #define LTE 9 // <= #define LT 10 // < #define EQ 11 // = #define PLACEHOLDER 99 // placeholder value // a node contains a TOKEN with a reference to its successor and ancestor struct _node { TOKENS val; node *next; node *prev; }; // our expression will be converted in a doubly-linked list of nodes to ease // its evaluation struct _dllist { node *head; node *tail; size_t len; }; // the first parsing will split the string expression in string tokens // ie. 'file_size>10Kb and (file_ext=pst or file_ext=ost)' => // {"file_size>10Kb", "and", "(", "file_ext=pst", "or", "file_ext=ost", ")"} struct tokens { char **list; size_t size; int valid; }; // an expression is a hash record to retrieve already parsed records struct expression { char *id; struct tokens *tokens; UT_hash_handle hh; }; // a cregex is a pre-compiled regex struct cregex { char *id; regex_t *compiled; UT_hash_handle hh; }; // these are some caches struct expression *cache=NULL; struct cregex *regex_cache=NULL; static void free_tokens(struct tokens *ptr) { if(!ptr) return; free_list_w(&ptr->list, ptr->size); free_v((void **)&ptr); } static void free_expression(struct expression *ptr) { if(!ptr) return; free_tokens(ptr->tokens); free_v((void **)&ptr); } static void free_cregex(struct cregex *ptr) { if(!ptr) return; regex_free(&(ptr->compiled)); free_v((void **)&ptr); } static void free_node(node **ptr) { if(!*ptr) return; free_v((void **)ptr); } // append a node to a given list static void list_append(dllist **list, node *data) { dllist *l=*list; if(!l || !data) return; if(!l->tail) { l->head=data; l->tail=data; data->prev=NULL; data->next=NULL; } else { l->tail->next=data; data->prev=l->tail; l->tail=data; data->next=NULL; } l->len++; } // retrieve a node by its position in the list static node *list_get_node_by_id(dllist *list, int id) { node *ret=NULL; int cpt=0; if(!list || id<0 || id>(int)list->len) return ret; ret=list->head; for(cpt=0, ret=list->head; ret && cptnext); if(cptlen==0) return; tmp=l->tail; while(tmp) { node *buf=tmp->prev; l->tail=buf; if(l->tail) l->tail->next=NULL; l->len--; tmp->prev=NULL; free_node(&tmp); tmp=buf; } } static dllist *new_list(void) { dllist *ret; if(!(ret=(dllist *)malloc_w(sizeof(*ret), __func__))) return NULL; ret->len=0; ret->head=NULL; ret->tail=NULL; return ret; } static node *new_node(int value) { node *ret; if(!(ret=(node *)malloc_w(sizeof(*ret), __func__))) return NULL; ret->val=value; ret->next=NULL; ret->prev=NULL; return ret; } // here we actually convert our expression into a list of string tokens static struct tokens *create_token_list(char *expr) { char *n=NULL; char *n2=NULL; char **toks=NULL; size_t nb_elements=0; struct tokens *ret=NULL; int opened, closed; if(!(n=charreplace_noescaped_w(expr, '(', " ( ", &opened, __func__))) goto end; if(!(n2=charreplace_noescaped_w(n, ')', " ) ", &closed, __func__))) goto end; if(!(toks=charsplit_noescaped_w(n2, ' ', &nb_elements, __func__))) goto end; if(!(ret=(struct tokens *)malloc_w(sizeof(*ret), __func__))) goto end; ret->list=toks; ret->size=nb_elements; ret->valid=(opened==closed); end: free_w(&n); free_w(&n2); return ret; } // we create our "expression" record to be cached static struct expression *parse_expression(char *expr) { struct expression *ret=NULL; struct tokens *toks; if(!(toks=create_token_list(expr))) return ret; if(!(ret=(struct expression *)malloc_w(sizeof(*ret), __func__))) goto error; ret->tokens=toks; ret->id=expr; return ret; error: free_tokens(toks); return ret; } // search for the positions of the 'what' token in our tokens list static void find(dllist *toks, TOKENS what, int start, dllist **positions) { int i; node *tmp; for(i=0, tmp=toks->head; i<(int)toks->len; i++, tmp=tmp->next) { if(ival==what) list_append(positions, new_node(i)); } } // search for parentheses and return their positions // always return the deepest parentheses first // example: // false or ( false or ( true or false ) ) // 1 => ^ ^ (true, 5, 9) // 2 => ^ ^ (true, 2, 10) static void parens(dllist *toks, int *has, int *left, int *right) { dllist *positions; if(!(positions=new_list())) { *has=0; *left=-1; *right=-1; return; } find(toks, LEFT_PARENS, 0, &positions); if(positions->len==0) { *has=0; *left=-1; *right=-1; goto end; } *left=positions->tail->val; list_reset(&positions); find(toks, RIGHT_PARENS, *left+4, &positions); if(positions->len==0) { // special case (token) instead of ( token or/and token ) list_reset(&positions); find(toks, RIGHT_PARENS, *left+1, &positions); } *right=positions->head->val; *has=1; end: list_reset(&positions); free_v((void **)&positions); } // utility function static char *strip_quotes(char *src) { int len; char *strip=NULL; if(!(len=strlen(src))) goto end; if((*src=='\'' || *src=='"') && *src==src[len-1]) // strip the quotes { if(!(strip=(char *)malloc_w(len-1, __func__))) goto end; strip=strncpy(strip, src+1, len-2); strip[len-2]='\0'; } end: return strip; } // function 'file_ext' static int eval_file_ext(char *tok, const char *fname) { const char *cp; int len; char *strip=NULL; if(!(len=strlen(tok))) goto end; for(; *tok=='='; ++tok); if(!(len=strlen(tok))) goto end; // test again after we trimmed the '=' strip=strip_quotes(tok); for(cp=fname+strlen(fname)-1; cp>=fname; cp--) { if(*cp!='.') continue; if((strip && !strcasecmp(strip, cp+1)) || (!strip && !strcasecmp(tok, cp+1))) return EVAL_TRUE; } end: free_w(&strip); return EVAL_FALSE; } // function 'path_match' static int eval_path_match(char *tok, const char *fname) { int ret=EVAL_FALSE; struct cregex *reg; char *strip=NULL; if(strlen(tok)==0) goto end; for(; *tok=='='; ++tok); if(strlen(tok)==0) goto end; // test again after we trimmed the '=' if(regex_cache) HASH_FIND_STR(regex_cache, tok, reg); else reg=NULL; if(!reg) { regex_t *tmp; if((strip=strip_quotes(tok))) tmp=regex_compile_backup(strip); else tmp=regex_compile_backup(tok); if(!(reg=(struct cregex *)malloc_w(sizeof(*reg), __func__))) { regex_free(&tmp); goto end; } reg->id=strdup_w(tok, __func__); reg->compiled=tmp; HASH_ADD_KEYPTR(hh, regex_cache, reg->id, strlen(reg->id), reg); } if(regex_check(reg->compiled, fname)) ret=EVAL_TRUE; end: free_w(&strip); return ret; } // function 'file_match' static int eval_file_match(char *tok, const char *fname) { int len=strlen(fname); for(; len>0 && fname[len-1]!='/'; len--); return eval_path_match(tok, fname+len); } // function 'file_size' static int eval_file_size(char *tok, uint64_t filesize) { int ret=EVAL_FALSE; char *strip=NULL; TOKENS eval=PLACEHOLDER; uint64_t s=0; if(strlen(tok)==0) goto end; for(; ; tok++) { if(*tok!='>' && *tok!='<' && *tok!='=') break; switch(*tok) { case '<': eval=LT; break; case '>': eval=GT; break; case '=': switch(eval) { case LT: eval=LTE; break; case GT: eval=GTE; break; case PLACEHOLDER: case EQ: eval=EQ; break; default: eval=EVAL_FALSE; } break; } } if((strip=strip_quotes(tok))) get_file_size(strip, &s, NULL, -1); else get_file_size(tok, &s, NULL, -1); switch(eval) { case LT: ret=filesizes; break; case GTE: ret=filesize>=s; break; case EQ: ret=filesize==s; break; default: ret=EVAL_FALSE; } end: free_w(&strip); return ret; } // search what function to use static int eval_func(char *tok, const char *filename, uint64_t filesize) { int ret; if(!strncmp(tok, "file_ext", 8)) ret=eval_file_ext(tok+8, filename); else if(!strncmp(tok, "file_match", 10)) ret=eval_file_match(tok+10, filename); else if(!strncmp(tok, "path_match", 10)) ret=eval_path_match(tok+10, filename); else if(!strncmp(tok, "file_size", 9)) ret=eval_file_size(tok+9, filesize); else ret=EVAL_FALSE; return ret; } // convert a string token into a TOKENS static node *str_to_node(char *tok, const char *filename, uint64_t filesize) { int ret; if(!strncmp(tok, "and", 3)) ret=AND_FUNC; else if(!strncmp(tok, "or", 2)) ret=OR_FUNC; else if(!strncmp(tok, "(", 1)) ret=LEFT_PARENS; else if(!strncmp(tok, ")", 1)) ret=RIGHT_PARENS; else if(!strncmp(tok, "not", 3)) ret=NOT; else ret=eval_func(tok, filename, filesize); return new_node(ret); } // evaluate a trio of tokens like 'true or false' static int eval_triplet(node *head, int def) { TOKENS left, func, right; left=head->val; func=head->next->val; right=head->next->next->val; switch(func) { case AND_FUNC: return left && right; case OR_FUNC: return left || right; default: return def; } } // factorise tokens by recursively evaluating them static int bool_eval(dllist **tokens, int def) { dllist *toks=*tokens; if(toks->len==1) return toks->head->val; else if(toks->len==2) { switch(toks->head->val) { case NOT: return !toks->tail->val; default: return toks->tail->val; } } /* here we search for 'not' tokens */ if(toks->len>3) { dllist *new_tokens; node *tmp; int negate=0, is_negation=0; for(tmp=toks->head; tmp; tmp=tmp->next) { if(tmp->val==NOT) { is_negation=1; break; } } if(is_negation) { if(!(new_tokens=new_list())) return 0; for(tmp=toks->head; tmp; tmp=tmp->next) { if(tmp->val==NOT) negate=!negate; else { if(negate) { list_append(&new_tokens, new_node(!(tmp->val))); negate=0; } else { list_append(&new_tokens, new_node(tmp->val)); } } } list_reset(tokens); free_v((void **)tokens); *tokens=new_tokens; toks=*tokens; } } /* here we don't have any negations anymore, but we may have chains of * expressions to evaluate recursively */ if(toks->len>3) { node *tmp; dllist *new_tokens; int i; tmp=new_node(eval_triplet(toks->head, def)); if(!(new_tokens=new_list())) { free_node(&tmp); return 0; } list_append(&new_tokens, tmp); for(tmp=toks->head, i=0; tmp; tmp=tmp->next, i++) { if(i<3) continue; list_append(&new_tokens, new_node(tmp->val)); } list_reset(tokens); free_v((void **)tokens); *tokens=new_tokens; toks=*tokens; return bool_eval(tokens, def); } if(toks->len%3!=0) return def; return eval_triplet(toks->head, def); } // evaluate our list of tokens static int eval_parsed_expression(dllist **tokens, int def) { dllist *toks=*tokens, *sub; node *begin, *end, *tmp; int has, left, right, count; if(!toks || toks->len==0) return def; if(toks->len==1) return toks->head->val; parens(toks, &has, &left, &right); // we don't have parentheses, we can evaluate the tokens if(!has) return bool_eval(tokens, def); // we have parentheses // we retrieve the two nodes '(' and ')' begin=list_get_node_by_id(toks, left); end=list_get_node_by_id(toks, right); // then we capture only the tokens surrounded by the parentheses if(!(sub=new_list())) return def; tmp=begin->next; count=0; while(tmp && countval)); tmp=tmp->next; count++; } count++; // evaluate the inner expression tmp=new_node(bool_eval(&sub, def)); // we replace all the tokens parentheses included with the new computed node // first element of the list if(!begin->prev) (*tokens)->head=tmp; else if (!end->next) // last element of the list (*tokens)->tail=tmp; toks->len-=count; // decrement our list size tmp->prev=begin->prev; tmp->next=end->next; if(begin->prev) begin->prev->next=tmp; else if(end->next) end->next->prev=tmp; // cleanup "orphans" nodes tmp=begin; while(tmp && count>=0) { if(tmp) { node *buf=tmp->next; free_node(&tmp); tmp=buf; count--; } } list_reset(&sub); free_v((void **)&sub); return eval_parsed_expression(tokens, def); } static int eval_expression(char *expr, const char *filename, uint64_t filesize, int def) { int ret=def, i; struct expression *parsed; dllist *tokens=NULL; if(cache) HASH_FIND_STR(cache, expr, parsed); else parsed=NULL; if(!parsed) { if(!(parsed=parse_expression(expr))) return def; HASH_ADD_KEYPTR(hh, cache, parsed->id, strlen(parsed->id), parsed); } if(!parsed || !parsed->tokens->valid) goto end; if(!(tokens=new_list())) goto end; for(i=0; i<(int)parsed->tokens->size; i++) list_append(&tokens, str_to_node(parsed->tokens->list[i], filename, filesize)); ret=eval_parsed_expression(&tokens, def); end: list_reset(&tokens); free_v((void **)&tokens); return ret; } // cleanup our caches void free_logic_cache(void) { struct expression *parsed, *tmp; struct cregex *reg, *tmp2; if(cache) { HASH_ITER(hh, cache, parsed, tmp) { HASH_DEL(cache, parsed); free_expression(parsed); } } if(regex_cache) { HASH_ITER(hh, regex_cache, reg, tmp2) { HASH_DEL(regex_cache, reg); free_w(&(reg->id)); free_cregex(reg); } } } /* return 1 if there is a match, 'def' is there isn't any match, and 'miss' if * there are no rules to eval */ static int is_logic(struct strlist *list, struct FF_PKT *ff, int miss, int def) { if(!list) return miss; if(!S_ISREG(ff->statp.st_mode)) return def; // ignore directories for(; list; list=list->next) if(eval_expression(list->path, ff->fname, (uint64_t)ff->statp.st_size, miss)) return 1; return def; } int is_logic_excluded(struct conf **confs, struct FF_PKT *ff) { return is_logic(get_strlist(confs[OPT_EXCLOGIC]), ff, /* missing */ 0, /* default */ 0); } int is_logic_included(struct conf **confs, struct FF_PKT *ff) { return is_logic(get_strlist(confs[OPT_INCLOGIC]), ff, /* missing */ 1, /* default */ 0); } burp-2.4.0/src/client/find_logic.h000066400000000000000000000003701404341324700170020ustar00rootroot00000000000000#ifndef _FIND_LOGIC_H #define _FIND_LOGIC_H #include "find.h" extern void free_logic_cache(void); extern int is_logic_excluded(struct conf **confs, struct FF_PKT *ff); extern int is_logic_included(struct conf **confs, struct FF_PKT *ff); #endif burp-2.4.0/src/client/glob_windows.c000066400000000000000000000105201404341324700173730ustar00rootroot00000000000000#ifdef HAVE_WIN32 // Windows glob stuff originally by ziirish. #include "../burp.h" #include "../alloc.h" #include "../handy.h" #include "../log.h" #include "../strlist.h" static inline int xmin(int a, int b) { return ab?a:b; } static char *xstrsub(const char *src, int begin, int len) { int l; int ind; char *ret; size_t s_full; s_full=strlen(src); if(len==-1) l=(int)s_full; else l=len; if(!(ret=(char *)malloc_w((xmin(s_full, l)+1)*sizeof(char), __func__))) return NULL; ind=begin<0?xmax((int) s_full+begin, 0):xmin(s_full, begin); strncpy(ret, src+ind, xmin(s_full, l)); ret[xmin(s_full, l)] = '\0'; return ret; } static int process_entry(struct strlist *ig, struct conf **confs) { int ret=-1; size_t len1=0; char *sav=NULL; char **splitstr1=NULL; WIN32_FIND_DATA ffd; HANDLE hFind=INVALID_HANDLE_VALUE; char *tmppath=NULL; convert_backslashes(&ig->path); if(ig->path[strlen(ig->path)-1]!='*') { if(!(splitstr1=strsplit_w(ig->path, "*", &len1, __func__))) goto end; } if(len1>2) { logp("include_glob error: '%s' contains at least" " two '*' which is not currently supported\n", ig->path); goto end; } if(len1>1) { if(astrcat(&tmppath, splitstr1[0], __func__) || !(sav=strdup_w(tmppath, __func__)) || astrcat(&tmppath, "*", __func__)) goto end; } else { if(astrcat(&tmppath, ig->path, __func__)) goto end; } hFind=FindFirstFileA(tmppath, &ffd); if(hFind==INVALID_HANDLE_VALUE) { LPVOID lpMsgBuf; DWORD dw=GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL ); logp("include_glob error (%s): %s\n", tmppath, (char *)lpMsgBuf); LocalFree(lpMsgBuf); goto end; } do { if(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && strcmp(ffd.cFileName, ".") && strcmp(ffd.cFileName, "..")) { free_w(&tmppath); if(len1<2) { if(ig->path[strlen(ig->path)-1]=='*') { if(!(tmppath=xstrsub(ig->path, 0, strlen(ig->path)-1)) || astrcat(&tmppath, ffd.cFileName, __func__)) goto end; } else if(!(tmppath=strdup_w(ig->path, __func__))) goto end; } else { if(astrcat(&tmppath, sav, __func__) || astrcat(&tmppath, ffd.cFileName, __func__) || astrcat(&tmppath, splitstr1[1], __func__)) goto end; } if(add_to_strlist(confs[OPT_INCLUDE], tmppath, 1)) goto end; } } while(FindNextFileA(hFind, &ffd)!=0); FindClose(hFind); ret=0; end: free_w(&tmppath); if(splitstr1) { free_w(&sav); free_list_w(&splitstr1, len1); } return ret; } static int expand_windows_drives(struct conf **confs) { struct strlist *ig_o=NULL; struct strlist *ig_n=NULL; char *drives_detected=NULL; if(!(drives_detected=get_fixed_drives()) || !*drives_detected) { logp("Could not detect windows drives.\n"); return -1; } logp("windows drives detected: %s\n", drives_detected); for(ig_o=get_strlist(confs[OPT_INCGLOB]); ig_o; ig_o=ig_o->next) { if(!strncmp(ig_o->path, "*:", strlen("*:"))) { size_t d; for(d=0; dpath[0]=drives_detected[d]; if(strchr(ig_o->path, '*')) { // More to expand later. if(strlist_add(&ig_n, ig_o->path, 1)) return -1; } else { // Nothing else to expand, just add it // straight onto the includes - but // only if the expanded path actually // exists. char *rp; if(!(rp=realpath(ig_o->path, NULL))) { switch(errno) { case ENOENT: continue; case ENOMEM: return -1; case EACCES: default: // Add anyway, // for warnings // later. break; } } free_w(&rp); if(add_to_strlist(confs[OPT_INCLUDE], ig_o->path, 1)) return -1; } } continue; } if(strlist_add(&ig_n, ig_o->path, 1)) return -1; } set_strlist(confs[OPT_INCGLOB], ig_n); return 0; } int glob_windows(struct conf **confs) { struct strlist *ig=NULL; if(expand_windows_drives(confs)) return -1; for(ig=get_strlist(confs[OPT_INCGLOB]); ig; ig=ig->next) if(process_entry(ig, confs)) return -1; return 0; } #endif burp-2.4.0/src/client/glob_windows.h000066400000000000000000000002001404341324700173720ustar00rootroot00000000000000#ifndef _GLOB_WINDOWS_H #define _GLOB_WINDOWS_H #ifdef HAVE_WIN32 extern int glob_windows(struct conf **confs); #endif #endif burp-2.4.0/src/client/list.c000066400000000000000000000120741404341324700156570ustar00rootroot00000000000000#include "../burp.h" #include "../action.h" #include "../asfd.h" #include "../async.h" #include "../attribs.h" #include "../cmd.h" #include "../handy.h" #include "../log.h" #include "../times.h" #include "list.h" static int parseable_format=0; /* Note: The chars in this function are not the same as in the CMD_ set. These are for printing to the screen only. */ static char *encode_mode(mode_t mode, char *buf) { char *cp=buf; *cp++=S_ISDIR(mode)?'d':S_ISBLK(mode)?'b':S_ISCHR(mode)?'c': S_ISLNK(mode)?'l':S_ISFIFO(mode)?'p':S_ISSOCK(mode)?'s':'-'; *cp++=mode&S_IRUSR?'r':'-'; *cp++=mode&S_IWUSR?'w':'-'; *cp++=(mode&S_ISUID?(mode&S_IXUSR?'s':'S'):(mode&S_IXUSR?'x':'-')); *cp++=mode&S_IRGRP?'r':'-'; *cp++=mode&S_IWGRP?'w':'-'; *cp++=(mode&S_ISGID?(mode&S_IXGRP?'s':'S'):(mode&S_IXGRP?'x':'-')); *cp++=mode&S_IROTH?'r':'-'; *cp++=mode&S_IWOTH?'w':'-'; *cp++=(mode&S_ISVTX?(mode&S_IXOTH?'t':'T'):(mode&S_IXOTH?'x':'-')); *cp='\0'; return cp; } static void ls_to_buf(char *lsbuf, struct sbuf *sb) { int n; char *p; const char *f; struct stat *statp=&sb->statp; *lsbuf='\0'; p=encode_mode(statp->st_mode, lsbuf); n=sprintf(p, " %2d ", (uint32_t)statp->st_nlink); p+=n; n=sprintf(p, "%5d %5d", (uint32_t)statp->st_uid, (uint32_t)statp->st_gid); p+=n; n=sprintf(p, " %7lu ", (unsigned long)statp->st_size); p+=n; p=encode_time(statp->st_mtime, p); *p++=' '; for(f=sb->path.buf; *f; ) *p++=*f++; *p=0; } static int ls_long_output(struct sbuf *sb) { static size_t len=128; static char *lsbuf=NULL; while(sb->path.len + 128 > len) { len*=2; if(!(lsbuf=(char *)realloc_w(lsbuf, len, __func__))) return -1; } ls_to_buf(lsbuf, sb); printf("%s", lsbuf); if(sb->link.buf) printf(" -> %s", sb->link.buf); printf("\n"); return 0; } static void ls_short_output(struct sbuf *sb) { if(parseable_format) { // Just make everything a CMD_FILE, when reading in for // restore input, the type of file system entry will just // be ignored. printf("%c%04X%s\n", CMD_FILE, (unsigned int)sb->path.len, sb->path.buf); return; } printf("%s\n", sb->path.buf); } static int list_item(enum action act, struct sbuf *sb) { if(act==ACTION_LIST_LONG) return ls_long_output(sb); ls_short_output(sb); return 0; } int do_list_client(struct asfd *asfd, enum action act, struct conf **confs) { int ret=-1; char msg[512]=""; struct sbuf *sb=NULL; struct iobuf *rbuf=asfd->rbuf; const char *backup=get_string(confs[OPT_BACKUP]); const char *backup2=get_string(confs[OPT_BACKUP2]); const char *browsedir=get_string(confs[OPT_BROWSEDIR]); const char *regex=get_string(confs[OPT_REGEX]); parseable_format=act==ACTION_LIST_PARSEABLE; //logp("in do_list\n"); switch(act) { case ACTION_LIST: case ACTION_LIST_LONG: case ACTION_LIST_PARSEABLE: if(browsedir && regex) { logp("You cannot specify both a directory and a regular expression when listing.\n"); goto end; } if(browsedir) snprintf(msg, sizeof(msg), "listb %s:%s", backup?backup:"", browsedir); else snprintf(msg, sizeof(msg), "list %s:%s", backup?backup:"", regex?regex:""); break; case ACTION_DIFF: case ACTION_DIFF_LONG: snprintf(msg, sizeof(msg), "diff %s:%s", backup?backup:"", backup2?backup2:""); break; default: logp("unknown action %d\n", act); goto end; } if(asfd->write_str(asfd, CMD_GEN, msg) || asfd_read_expect(asfd, CMD_GEN, "ok")) goto end; if(!(sb=sbuf_alloc(get_protocol(confs)))) goto end; iobuf_init(&sb->path); iobuf_init(&sb->link); iobuf_init(&sb->attr); // This should probably should use the sbuf stuff. while(1) { sbuf_free_content(sb); iobuf_free_content(rbuf); if(asfd->read(asfd)) break; if(rbuf->cmd==CMD_MESSAGE) { if(!parseable_format) printf("%s\n", rbuf->buf); if(!strcmp(rbuf->buf, "no backups")) ret=0; goto end; } else if(rbuf->cmd==CMD_TIMESTAMP) { if(parseable_format) continue; // A backup timestamp, just print it. printf("Backup: %s\n", rbuf->buf); if(browsedir) printf("Listing directory: %s\n", browsedir); if(regex) printf("With regex: %s\n", regex); continue; } else if(rbuf->cmd!=CMD_ATTRIBS) { iobuf_log_unexpected(rbuf, __func__); goto end; } iobuf_copy(&sb->attr, rbuf); iobuf_init(rbuf); attribs_decode(sb); if(asfd->read(asfd)) { logp("got stat without an object\n"); goto end; } iobuf_copy(&sb->path, rbuf); iobuf_init(rbuf); if(sb->path.cmd==CMD_DIRECTORY || sb->path.cmd==CMD_FILE || sb->path.cmd==CMD_ENC_FILE || sb->path.cmd==CMD_EFS_FILE || sb->path.cmd==CMD_SPECIAL) { if(list_item(act, sb)) goto end; } else if(cmd_is_link(sb->path.cmd)) // symlink or hardlink { if(asfd->read(asfd) || rbuf->cmd!=sb->path.cmd) { logp("could not get link %s\n", iobuf_to_printable(&sb->path)); goto end; } iobuf_copy(&sb->link, rbuf); iobuf_init(rbuf); list_item(act, sb); } else { logp("unlistable %s\n", iobuf_to_printable(&sb->path)); } } ret=0; end: sbuf_free(&sb); if(!ret) logp("List finished ok\n"); return ret; } burp-2.4.0/src/client/list.h000066400000000000000000000002141404341324700156550ustar00rootroot00000000000000#ifndef _LIST_CLIENT_H #define _LIST_CLIENT_H extern int do_list_client(struct asfd *asfd, enum action act, struct conf **confs); #endif burp-2.4.0/src/client/main.c000066400000000000000000000353541404341324700156360ustar00rootroot00000000000000#include "../burp.h" #include "../conffile.h" #include "../action.h" #include "../asfd.h" #include "../async.h" #include "../cmd.h" #include "../cntr.h" #include "../fsops.h" #include "../handy.h" #include "../iobuf.h" #include "../log.h" #include "../run_script.h" #include "auth.h" #include "backup.h" #include "ca.h" #include "delete.h" #include "extra_comms.h" #include "list.h" #include "monitor.h" #include "find_logic.h" #include "monitor/status_client_ncurses.h" #include "protocol2/restore.h" #include "restore.h" #include "main.h" #ifndef HAVE_WIN32 #include #endif // These will also be used as the exit codes of the program and are therefore // unsigned integers. // Remember to update the man page if you update these. enum cliret { CLIENT_OK=0, CLIENT_ERROR=1, CLIENT_RESTORE_WARNINGS=2, CLIENT_SERVER_TIMER_NOT_MET=3, CLIENT_COULD_NOT_CONNECT=4, CLIENT_SERVER_MAX_PARALLEL_BACKUPS=5, // This one happens after a successful certificate signing request so // that it connects again straight away with the new key/certificate. CLIENT_RECONNECT=100 }; struct tchk { int resume; enum cliret ret; }; static enum asl_ret maybe_check_timer_func(struct asfd *asfd, struct conf **confs, void *param) { int complen=0; struct tchk *tchk=(struct tchk *)param; if(!strcmp(asfd->rbuf->buf, "max parallel backups")) { logp("Max parallel backups reached\n"); tchk->ret=CLIENT_SERVER_MAX_PARALLEL_BACKUPS; return ASL_END_OK; } else if(!strcmp(asfd->rbuf->buf, "timer conditions not met")) { logp("Timer conditions on the server were not met\n"); tchk->ret=CLIENT_SERVER_TIMER_NOT_MET; return ASL_END_OK; } else if(!strcmp(asfd->rbuf->buf, "timer conditions met")) { // Only happens on 'timer check only'. logp("Timer conditions on the server were met\n"); tchk->ret=CLIENT_OK; return ASL_END_OK; } if(!strncmp_w(asfd->rbuf->buf, "ok")) complen=3; else if(!strncmp_w(asfd->rbuf->buf, "resume")) { complen=7; tchk->resume=1; logp("server wants to resume previous backup.\n"); } else { iobuf_log_unexpected(asfd->rbuf, __func__); return ASL_END_ERROR; } // The server now tells us the compression level in the OK response. if(strlen(asfd->rbuf->buf)>3) set_int(confs[OPT_COMPRESSION], atoi(asfd->rbuf->buf+complen)); logp("Compression level: %d\n", get_int(confs[OPT_COMPRESSION])); return ASL_END_OK; } static enum cliret maybe_check_timer(struct asfd *asfd, const char *phase1str, struct conf **confs, int *resume) { struct tchk tchk; memset(&tchk, 0, sizeof(tchk)); if(asfd->write_str(asfd, CMD_GEN, phase1str)) return CLIENT_ERROR; if(asfd->simple_loop(asfd, confs, &tchk, __func__, maybe_check_timer_func)) return CLIENT_ERROR; *resume=tchk.resume; return tchk.ret; } static enum cliret backup_wrapper(struct asfd *asfd, enum action action, const char *phase1str, const char *incexc, struct conf **confs) { int resume=0; enum cliret ret=CLIENT_OK; const char *b_script_pre=get_string(confs[OPT_B_SCRIPT_PRE]); const char *b_script_post=get_string(confs[OPT_B_SCRIPT_POST]); // Set bulk packets quality of service flags on backup. if(incexc) { logp("Server is overriding the configuration\n"); logp("with the following settings:\n"); if(log_incexcs_buf(incexc)) goto error; } if(!get_strlist(confs[OPT_STARTDIR])) { logp("Found no include paths!\n"); goto error; } switch(maybe_check_timer(asfd, phase1str, confs, &resume)) { case CLIENT_OK: if(action==ACTION_TIMER_CHECK) goto end; break; case CLIENT_SERVER_TIMER_NOT_MET: goto timer_not_met; case CLIENT_SERVER_MAX_PARALLEL_BACKUPS: goto max_parallel_backups; default: goto error; } if(b_script_pre) { int a=0; const char *args[12]; args[a++]=b_script_pre; if(get_int(confs[OPT_B_SCRIPT_RESERVED_ARGS])) { args[a++]="pre"; args[a++]="reserved2"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; } args[a++]=NULL; if(run_script(asfd, args, get_strlist(confs[OPT_B_SCRIPT_PRE_ARG]), confs, 1, 1, 1)) ret=CLIENT_ERROR; if(get_int(confs[OPT_GLOB_AFTER_SCRIPT_PRE])) { if(reeval_glob(confs)) ret=CLIENT_ERROR; } } if(ret==CLIENT_OK && do_backup_client(asfd, confs, action, resume)) ret=CLIENT_ERROR; if((ret==CLIENT_OK || get_int(confs[OPT_B_SCRIPT_POST_RUN_ON_FAIL])) && b_script_post) { int a=0; const char *args[12]; args[a++]=b_script_post; if(get_int(confs[OPT_B_SCRIPT_RESERVED_ARGS])) { args[a++]="post"; // Tell post script whether the restore failed. args[a++]=ret?"1":"0"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; } args[a++]=NULL; // At this point, the server may have closed the connection, // so cannot log remotely. if(run_script(asfd, args, get_strlist(confs[OPT_B_SCRIPT_POST_ARG]), confs, 1, 1, /*log_remote*/ 0)) ret=CLIENT_ERROR; } if(ret==CLIENT_OK) logp("backup finished ok\n"); end: // The include_logic/exclude_logic cache may have been populated // during backup so we clean it here free_logic_cache(); return ret; error: logp("error in backup\n"); return CLIENT_ERROR; timer_not_met: return CLIENT_SERVER_TIMER_NOT_MET; max_parallel_backups: return CLIENT_SERVER_MAX_PARALLEL_BACKUPS; } static int s_server_session_id_context=1; static int ssl_setup(int *rfd, SSL **ssl, SSL_CTX **ctx, enum action action, struct conf **confs, const char *server, struct strlist *failover) { int ret=-1; int port=0; char portstr[8]=""; BIO *sbio=NULL; ssl_load_globals(); char *cp=NULL; char *server_copy=NULL; if(!(server_copy=strdup_w(server, __func__))) goto end; if(!(*ctx=ssl_initialise_ctx(confs))) { logp("error initialising ssl ctx\n"); goto end; } if((cp=strrchr(server_copy, ':'))) { *cp='\0'; port=atoi(cp+1); } SSL_CTX_set_session_id_context(*ctx, (const uint8_t *)&s_server_session_id_context, sizeof(s_server_session_id_context)); switch(action) { case ACTION_BACKUP: case ACTION_BACKUP_TIMED: case ACTION_TIMER_CHECK: if(get_int(confs[OPT_PORT_BACKUP])) port=get_int(confs[OPT_PORT_BACKUP]); break; case ACTION_RESTORE: if(get_int(confs[OPT_PORT_RESTORE])) port=get_int(confs[OPT_PORT_RESTORE]); break; case ACTION_VERIFY: if(get_int(confs[OPT_PORT_VERIFY])) port=get_int(confs[OPT_PORT_VERIFY]); break; case ACTION_LIST: case ACTION_LIST_LONG: case ACTION_LIST_PARSEABLE: case ACTION_DIFF: case ACTION_DIFF_LONG: if(get_int(confs[OPT_PORT_LIST])) port=get_int(confs[OPT_PORT_LIST]); break; case ACTION_DELETE: if(get_int(confs[OPT_PORT_DELETE])) port=get_int(confs[OPT_PORT_DELETE]); break; case ACTION_MONITOR: { struct strlist *s; if(!(s=get_strlist(confs[OPT_STATUS_PORT]))) { logp("%s not set\n", confs[OPT_STATUS_PORT]->field); goto end; } port=atoi(s->path); break; } case ACTION_CHAMP_CHOOSER: case ACTION_ESTIMATE: case ACTION_STATUS: case ACTION_STATUS_SNAPSHOT: case ACTION_UNSET: logp("Unexpected action in %s: %d\n", __func__, action); goto end; } snprintf(portstr, sizeof(portstr), "%d", port); if((*rfd=init_client_socket(server_copy, portstr))<0) goto end; if(!(*ssl=SSL_new(*ctx)) || !(sbio=BIO_new_socket(*rfd, BIO_NOCLOSE))) { logp_ssl_err("Problem joining SSL to the socket\n"); goto end; } SSL_set_bio(*ssl, sbio, sbio); if(SSL_connect(*ssl)<=0) { logp_ssl_err("SSL connect error\n"); goto end; } ret=0; end: free_w(&server_copy); return ret; } static enum cliret initial_comms(struct async *as, enum action *action, char **incexc, struct conf **confs, struct strlist *failover) { struct asfd *asfd; char *server_version=NULL; enum cliret ret=CLIENT_OK; asfd=as->asfd; if(authorise_client(asfd, &server_version, get_string(confs[OPT_CNAME]), get_string(confs[OPT_PASSWORD]), get_cntr(confs))) goto error; if(server_version) { logp("Server version: %s\n", server_version); // Servers before 1.3.2 did not tell us their versions. // 1.3.2 and above can do the automatic CA stuff that // follows. switch(ca_client_setup(asfd, confs)) { case 0: break; // All OK. case 1: // Certificate signed successfully. // Everything is OK, but we will reconnect now, // in order to use the new keys/certificates. goto reconnect; default: logp("Error with cert signing request\n"); goto error; } } if(ssl_check_cert(asfd->ssl, NULL, confs)) { logp("check cert failed\n"); goto error; } if(extra_comms_client(as, confs, action, failover, incexc)) { logp("extra comms failed\n"); goto error; } ret=CLIENT_OK; goto end; error: ret=CLIENT_ERROR; goto end; reconnect: ret=CLIENT_RECONNECT; goto end; end: free_w(&server_version); return ret; } static enum cliret restore_wrapper(struct asfd *asfd, enum action action, struct conf **confs) { enum cliret ret=CLIENT_OK; const char *r_script_pre=get_string(confs[OPT_R_SCRIPT_PRE]); const char *r_script_post=get_string(confs[OPT_R_SCRIPT_POST]); if(r_script_pre) { int a=0; const char *args[12]; args[a++]=r_script_pre; if(get_int(confs[OPT_R_SCRIPT_RESERVED_ARGS])) { args[a++]="pre"; args[a++]="reserved2"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; } args[a++]=NULL; if(run_script(asfd, args, get_strlist(confs[OPT_R_SCRIPT_PRE_ARG]), confs, 1, 1, 1)) ret=CLIENT_ERROR; } if(ret==CLIENT_OK) { if(do_restore_client(asfd, confs, action)) ret=CLIENT_ERROR; } if((ret==CLIENT_OK || get_int(confs[OPT_R_SCRIPT_POST_RUN_ON_FAIL])) && r_script_post) { int a=0; const char *args[12]; args[a++]=r_script_post; if(get_int(confs[OPT_R_SCRIPT_RESERVED_ARGS])) { args[a++]="post"; // Tell post script whether the restore failed. args[a++]=ret?"1":"0"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; } args[a++]=NULL; if(run_script(asfd, args, get_strlist(confs[OPT_R_SCRIPT_POST_ARG]), confs, 1, 1, /*log_remote*/ 0)) ret=CLIENT_ERROR; } // Return non-zero if there were warnings, // so that the test script can easily check. if(ret==CLIENT_OK && get_cntr(confs)->ent[CMD_WARNING]->count) ret=CLIENT_RESTORE_WARNINGS; return ret; } static enum cliret do_client(struct conf **confs, enum action action, const char *server, struct strlist *failover) { enum cliret ret=CLIENT_OK; int rfd=-1; SSL *ssl=NULL; SSL_CTX *ctx=NULL; struct cntr *cntr=NULL; char *incexc=NULL; enum action act=action; struct async *as=NULL; struct asfd *asfd=NULL; // as->settimers(0, 100); // logp("begin client\n"); // logp("action %d\n", action); // Status monitor forks a child process instead of connecting to // the server directly. if(action==ACTION_STATUS || action==ACTION_STATUS_SNAPSHOT) { #ifdef HAVE_WIN32 logp("Status mode not implemented on Windows.\n"); goto error; #endif if(status_client_ncurses_init(act) || status_client_ncurses(confs)) ret=CLIENT_ERROR; goto end; } if(!(cntr=cntr_alloc()) || cntr_init(cntr, get_string(confs[OPT_CNAME]), getpid())) goto error; set_cntr(confs[OPT_CNTR], cntr); if(act!=ACTION_ESTIMATE) { if(ssl_setup(&rfd, &ssl, &ctx, action, confs, server, failover)) goto could_not_connect; if(!(as=async_alloc()) || as->init(as, act==ACTION_ESTIMATE) || !(asfd=setup_asfd_ssl(as, "main socket", &rfd, ssl))) goto end; asfd->set_timeout(asfd, get_int(confs[OPT_NETWORK_TIMEOUT])); asfd->ratelimit=get_float(confs[OPT_RATELIMIT]); // Set quality of service bits on backup packets. if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED || act==ACTION_TIMER_CHECK) as->asfd->set_bulk_packets(as->asfd); if((ret=initial_comms(as, &act, &incexc, confs, failover))) goto end; } rfd=-1; switch(act) { case ACTION_BACKUP: ret=backup_wrapper(asfd, act, "backupphase1", incexc, confs); break; case ACTION_BACKUP_TIMED: ret=backup_wrapper(asfd, act, "backupphase1timed", incexc, confs); break; case ACTION_TIMER_CHECK: ret=backup_wrapper(asfd, act, "backupphase1timedcheck", incexc, confs); break; case ACTION_RESTORE: case ACTION_VERIFY: ret=restore_wrapper(asfd, act, confs); break; case ACTION_ESTIMATE: if(do_backup_client(asfd, confs, act, 0)) goto error; break; case ACTION_DELETE: if(do_delete_client(asfd, confs)) goto error; break; case ACTION_MONITOR: if(do_monitor_client(asfd)) goto error; break; case ACTION_DIFF: case ACTION_DIFF_LONG: /* if(!strcmp(get_string(confs[OPT_BACKUP2]), "n")) // Do a phase1 scan and diff that. ret=backup_wrapper(asfd, act, "backupphase1diff", incexc, confs); else */ // Diff two backups that already exist. // Fall through, the list code is all we need // for simple diffs on the client side. case ACTION_LIST: case ACTION_LIST_LONG: case ACTION_LIST_PARSEABLE: default: if(do_list_client(asfd, act, confs)) goto error; break; } if(asfd_flush_asio(asfd)) ret=CLIENT_ERROR; goto end; error: ret=CLIENT_ERROR; goto end; could_not_connect: ret=CLIENT_COULD_NOT_CONNECT; end: close_fd(&rfd); async_free(&as); asfd_free(&asfd); if(ctx) ssl_destroy_ctx(ctx); free_w(&incexc); set_cntr(confs[OPT_CNTR], NULL); cntr_free(&cntr); //logp("end client\n"); return ret; } int client(struct conf **confs, enum action action) { int finished=0; enum cliret ret=CLIENT_OK; const char *server=NULL; struct strlist *failover=NULL; if(!get_int(confs[OPT_ENABLED])) { logp("Client not enabled\n"); return ret; } #ifdef HAVE_WIN32 // prevent sleep when idle SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); #endif server=get_string(confs[OPT_SERVER]); failover=get_strlist(confs[OPT_SERVER_FAILOVER]); while(!finished) { ret=do_client(confs, action, server, failover); if(ret==CLIENT_RECONNECT) { logp("Re-opening connection to %s\n", server); sleep(5); ret=do_client(confs, action, server, failover); } switch(ret) { case CLIENT_OK: case CLIENT_SERVER_TIMER_NOT_MET: case CLIENT_SERVER_MAX_PARALLEL_BACKUPS: case CLIENT_RESTORE_WARNINGS: finished=1; break; case CLIENT_ERROR: if(action!=ACTION_BACKUP && action!=ACTION_BACKUP_TIMED) { finished=1; break; } if(!get_int( confs[OPT_FAILOVER_ON_BACKUP_ERROR])) { finished=1; break; } // Fall through to failover. case CLIENT_COULD_NOT_CONNECT: if(!failover) { finished=1; break; } logp("Failing over\n"); // Use a failover server. server=failover->path; failover=failover->next; break; case CLIENT_RECONNECT: logp("Multiple reconnect requests to %s- this should not happen!", server); finished=1; break; } } #ifdef HAVE_WIN32 // allow sleep when idle SetThreadExecutionState(ES_CONTINUOUS); #endif // See enum cliret for return codes. return (int)ret; } burp-2.4.0/src/client/main.h000066400000000000000000000001601404341324700156260ustar00rootroot00000000000000#ifndef _CLIENT_MAIN_H #define _CLIENT_MAIN_H extern int client(struct conf **confs, enum action act); #endif burp-2.4.0/src/client/monitor.c000066400000000000000000000026641404341324700163770ustar00rootroot00000000000000#include "../burp.h" #include "../asfd.h" #include "../async.h" #include "../conf.h" #include "../cmd.h" #include "../iobuf.h" #include "../log.h" #include "monitor.h" static int copy_input_to_output(struct asfd *in, struct asfd *out) { struct iobuf wbuf; iobuf_set(&wbuf, CMD_GEN, in->rbuf->buf, in->rbuf->len); return out->write(out, &wbuf); } #ifndef UTEST static #endif int monitor_client_main_loop(struct async *as) { struct asfd *asfd; struct asfd *sfd; // Server fd. struct asfd *sin; struct asfd *sout; sfd=as->asfd; sin=sfd->next; sout=sin->next; while(1) { if(as->read_write(as)) { logp("Exiting main loop\n"); break; } for(asfd=as->asfd; asfd; asfd=asfd->next) while(asfd->rbuf->buf) { if(asfd==sfd) { if(copy_input_to_output(sfd, sout)) goto error; } else if(asfd==sin) { if(copy_input_to_output(sin, sfd)) goto error; } iobuf_free_content(asfd->rbuf); if(asfd->parse_readbuf(asfd)) goto error; } //if(sel) logp("sel: %s\n", sel->name); } error: return -1; } int do_monitor_client(struct asfd *asfd) { int ret=-1; struct async *as=asfd->as; logp("in monitor\n"); // I tried to just printf to stdout, but the strings to print would be // so long that I would start to get printf errors. // Using the asfd stuff works well though. if(!setup_asfd_stdin(as) || !setup_asfd_stdout(as)) goto end; ret=monitor_client_main_loop(as); end: return ret; } burp-2.4.0/src/client/monitor.h000066400000000000000000000002721404341324700163750ustar00rootroot00000000000000#ifndef _MONITOR_CLIENT_H #define _MONITOR_CLIENT_H extern int do_monitor_client(struct asfd *asfd); #ifdef UTEST extern int monitor_client_main_loop(struct async *as); #endif #endif burp-2.4.0/src/client/monitor/000077500000000000000000000000001404341324700162235ustar00rootroot00000000000000burp-2.4.0/src/client/monitor/json_input.c000066400000000000000000000275001404341324700205630ustar00rootroot00000000000000#include "../../burp.h" #include "../../alloc.h" #include "../../asfd.h" #include "../../async.h" #include "../../bu.h" #include "../../cstat.h" #include "../../cntr.h" #include "../../handy.h" #include "../../iobuf.h" #include "../../log.h" #include "../../times.h" #include "json_input.h" #include "lline.h" #include "sel.h" #ifdef HAVE_WIN32 #include #else #include "../../yajl/api/yajl_parse.h" #endif static int map_depth=0; // FIX THIS: should pass around a ctx instead of keeping track of a bunch // of globals. static unsigned long number=0; static char *timestamp=NULL; static uint16_t flags=0; static struct cstat *cnew=NULL; static struct cstat *current=NULL; static struct cstat **cslist=NULL; static struct cntr_ent *cntr_ent=NULL; static char lastkey[32]=""; static int in_backups=0; static int in_flags=0; static int in_counters=0; static int in_logslist=0; static int in_log_content=0; static struct bu **sselbu=NULL; // For server side log files. static struct lline *ll_list=NULL; static struct lline **sllines=NULL; // For recording 'loglines' in json input. static struct lline *jsll_list=NULL; // For recording warnings in json input. static struct lline *warning_list=NULL; static pid_t pid=-1; static int bno=0; static enum cntr_status phase=CNTR_STATUS_UNSET; static int is_wrap(const char *val, const char *key, uint16_t bit) { if(!strcmp(val, key)) { flags|=bit; return 1; } return 0; } static int input_integer(__attribute__ ((unused)) void *ctx, long long val) { if(!strcmp(lastkey, "pid")) { pid=(pid_t)val; return 1; } else if(!strcmp(lastkey, "backup")) { bno=(int)val; return 1; } else if(in_counters) { if(!strcmp(lastkey, "count")) { if(!cntr_ent) goto error; cntr_ent->count=(uint64_t)val; } else if(!strcmp(lastkey, "changed")) { if(!cntr_ent) goto error; cntr_ent->changed=(uint64_t)val; } else if(!strcmp(lastkey, "same")) { if(!cntr_ent) goto error; cntr_ent->same=(uint64_t)val; } else if(!strcmp(lastkey, "deleted")) { if(!cntr_ent) goto error; cntr_ent->deleted=(uint64_t)val; } else if(!strcmp(lastkey, "scanned")) { if(!cntr_ent) goto error; cntr_ent->phase1=(uint64_t)val; } else { goto error; } return 1; } else if(in_backups && !in_flags && !in_counters && !in_logslist) { if(!current) goto error; if(!strcmp(lastkey, "number")) { number=(unsigned long)val; return 1; } else if(!strcmp(lastkey, "timestamp")) { time_t t; t=(unsigned long)val; free_w(×tamp); if(!(timestamp=strdup_w(getdatestr(t), __func__))) return 0; return 1; } } else { if(!strcmp(lastkey, "protocol")) { return 1; } } error: logp("Unexpected integer: '%s' %" PRIu64 "\n", lastkey, (uint64_t)val); return 0; } static int input_string(__attribute__ ((unused)) void *ctx, const unsigned char *val, size_t len) { char *str; if(!(str=(char *)malloc_w(len+1, __func__))) return 0; snprintf(str, len+1, "%s", val); str[len]='\0'; if(in_counters) { if(!strcmp(lastkey, "name")) { // Ignore 'name' in a counters object. We use 'type' // instead. } else if(!strcmp(lastkey, "type")) { if(!current || !current->cntrs) goto error; cntr_ent=current->cntrs->ent[(uint8_t)*str]; } else { goto error; } goto end; } else if(!strcmp(lastkey, "name")) { if(cnew) goto error; if((current=cstat_get_by_name(*cslist, str))) { cntrs_free(¤t->cntrs); } else { if(!(cnew=cstat_alloc()) || cstat_init(cnew, str, NULL)) goto error; current=cnew; } goto end; } else if(!strcmp(lastkey, "labels")) { if(!current) goto error; goto end; } else if(!strcmp(lastkey, "run_status")) { if(!current) goto error; current->run_status=run_str_to_status(str); goto end; } else if(!strcmp(lastkey, "action")) { // Ignore for now. goto end; } else if(!strcmp(lastkey, "phase")) { if(!current) goto error; phase=cntr_str_to_status((const char *)str); goto end; } else if(!strcmp(lastkey, "flags")) { if(!current) goto error; if(is_wrap(str, "hardlinked", BU_HARDLINKED) || is_wrap(str, "deletable", BU_DELETABLE) || is_wrap(str, "working", BU_WORKING) || is_wrap(str, "finishing", BU_FINISHING) || is_wrap(str, "current", BU_CURRENT) || is_wrap(str, "manifest", BU_MANIFEST)) goto end; } else if(!strcmp(lastkey, "counters")) // Do we need this? { goto end; } else if(!strcmp(lastkey, "list")) { if(is_wrap(str, "backup", BU_LOG_BACKUP) || is_wrap(str, "restore", BU_LOG_RESTORE) || is_wrap(str, "verify", BU_LOG_VERIFY) || is_wrap(str, "backup_stats", BU_STATS_BACKUP) || is_wrap(str, "restore_stats", BU_STATS_RESTORE) || is_wrap(str, "verify_stats", BU_STATS_VERIFY)) goto end; } else if(!strcmp(lastkey, "logs")) { goto end; } else if(!strcmp(lastkey, "logline")) { if(lline_add(&jsll_list, str)) goto error; free_w(&str); goto end; } else if(!strcmp(lastkey, "backup") || !strcmp(lastkey, "restore") || !strcmp(lastkey, "verify") || !strcmp(lastkey, "backup_stats") || !strcmp(lastkey, "restore_stats") || !strcmp(lastkey, "verify_stats")) { // Log file contents. if(lline_add(&ll_list, str)) goto error; free_w(&str); goto end; } else if(!strcmp(lastkey, "warning")) { if(lline_add(&warning_list, str)) goto error; free_w(&str); goto end; } error: logp("Unexpected string: '%s' '%s'\n", lastkey, str); free_w(&str); return 0; end: free_w(&str); return 1; } static int input_map_key(__attribute__((unused)) void *ctx, const unsigned char *val, size_t len) { snprintf(lastkey, len+1, "%s", val); lastkey[len]='\0'; // logp("mapkey: %s\n", lastkey); return 1; } static struct bu *bu_list=NULL; static int add_to_bu_list(void) { struct bu *bu; struct bu *last; if(!number) return 0; if(!(bu=bu_alloc())) return -1; bu->bno=number; bu->flags=flags; bu->timestamp=timestamp; // FIX THIS: Inefficient to find the end each time. for(last=bu_list; last && last->next; last=last->next) { } if(last) { last->next=bu; bu->prev=last; } else { bu_list=bu; bu_list->prev=NULL; } number=0; flags=0; timestamp=NULL; return 0; } static int input_start_map(__attribute__ ((unused)) void *ctx) { map_depth++; //logp("startmap: %d\n", map_depth); return 1; } static int input_end_map(__attribute__ ((unused)) void *ctx) { map_depth--; //logp("endmap: %d\n", map_depth); if(in_backups && !in_flags && !in_counters && !in_logslist) { if(add_to_bu_list()) return 0; } return 1; } static int input_start_array(__attribute__ ((unused)) void *ctx) { //logp("start arr\n"); if(!strcmp(lastkey, "backups")) { in_backups=1; } else if(!strcmp(lastkey, "flags")) { in_flags=1; } else if(!strcmp(lastkey, "counters")) { struct cntr *cntr; for(cntr=current->cntrs; cntr; cntr=cntr->next) if(cntr->pid==pid) break; if(!cntr) { if(!(cntr=cntr_alloc()) || cntr_init(cntr, current->name, pid)) return 0; cstat_add_cntr_to_list(current, cntr); } cntr->bno=bno; cntr->cntr_status=phase; in_counters=1; } else if(!strcmp(lastkey, "list")) { in_logslist=1; } else if(!strcmp(lastkey, "backup") || !strcmp(lastkey, "restore") || !strcmp(lastkey, "verify") || !strcmp(lastkey, "backup_stats") || !strcmp(lastkey, "restore_stats") || !strcmp(lastkey, "verify_stats")) { in_log_content=1; } return 1; } static void merge_bu_lists(void) { struct bu *n; struct bu *o; struct bu *lastn=NULL; struct bu *lasto=NULL; for(o=current->bu; o; ) { int found_in_new=0; lastn=NULL; for(n=bu_list; n; n=n->next) { if(o->bno==n->bno) { // Found o in new list. // Copy the fields from new to old. found_in_new=1; o->flags=n->flags; free_w(&o->timestamp); o->timestamp=n->timestamp; n->timestamp=NULL; // Remove it from new list. if(lastn) { lastn->next=n->next; if(n->next) n->next->prev=lastn; } else { bu_list=n->next; if(bu_list) bu_list->prev=NULL; } bu_free(&n); n=lastn; break; } lastn=n; } if(!found_in_new) { // Could not find o in new list. // Remove it from old list. if(lasto) { lasto->next=o->next; if(o->next) o->next->prev=lasto; } else { current->bu=o->next; if(current->bu) current->bu->prev=NULL; } // Need to reset if the one that was removed was // selected in ncurses. if(o==*sselbu) *sselbu=NULL; bu_free(&o); o=lasto; } lasto=o; if(o) o=o->next; } // Now, new list only has entries missing from old list. n=bu_list; lastn=NULL; while(n) { o=current->bu; lasto=NULL; while(o && n->bno < o->bno) { lasto=o; o=o->next; } // Found the place to insert it. if(lasto) { lasto->next=n; n->prev=lasto; } else { if(current->bu) current->bu->prev=n; current->bu=n; current->bu->prev=NULL; } lastn=n->next; n->next=o; n=lastn; } } static void update_live_counter_flag(void) { struct bu *bu; if(!current) return; for(bu=current->bu; bu; bu=bu->next) { struct cntr *cntr; for(cntr=current->cntrs; cntr; cntr=cntr->next) if(bu->bno==(uint64_t)cntr->bno) bu->flags|=BU_LIVE_COUNTERS; } } static int input_end_array(__attribute__ ((unused)) void *ctx) { if(in_backups && !in_flags && !in_counters && !in_logslist) { in_backups=0; if(add_to_bu_list()) return 0; // Now may have two lists. Want to keep the old one as intact // as possible, so that we can keep a pointer to its entries // in the ncurses stuff. // Merge the new list into the old. merge_bu_lists(); update_live_counter_flag(); bu_list=NULL; if(cnew) { cstat_add_to_list(cslist, cnew); cnew=NULL; } current=NULL; } else if(in_flags) { in_flags=0; } else if(in_counters) { in_counters=0; } else if(in_logslist) { in_logslist=0; } else if(in_log_content) { in_log_content=0; llines_free(sllines); *sllines=ll_list; ll_list=NULL; } return 1; } static yajl_callbacks callbacks = { NULL, NULL, input_integer, NULL, NULL, input_string, input_start_map, input_map_key, input_end_map, input_start_array, input_end_array }; static void do_yajl_error(yajl_handle yajl, struct asfd *asfd) { unsigned char *str; str=yajl_get_error(yajl, 1, (const unsigned char *)asfd->rbuf->buf, asfd->rbuf->len); logp("yajl error: %s\n", str?(const char *)str:"unknown"); if(str) yajl_free_error(yajl, str); } static yajl_handle yajl=NULL; int json_input_init(void) { if(!(yajl=yajl_alloc(&callbacks, NULL, NULL))) return -1; yajl_config(yajl, yajl_dont_validate_strings, 1); return 0; } void json_input_free(void) { if(!yajl) return; yajl_free(yajl); yajl=NULL; } struct lline *json_input_get_loglines(void) { return jsll_list; } struct lline *json_input_get_warnings(void) { return warning_list; } void json_input_clear_loglines(void) { llines_free(&jsll_list); } void json_input_clear_warnings(void) { llines_free(&warning_list); } // Client records will be coming through in alphabetical order. // FIX THIS: If a client is deleted on the server, it is not deleted from // clist. // return 0 for OK, -1 on error, 1 for json complete, 2 for json complete with // warnings. int json_input(struct asfd *asfd, struct sel *sel) { cslist=&sel->clist; sselbu=&sel->backup; sllines=&sel->llines; if(!yajl && json_input_init()) goto error; //printf("parse: '%s\n'", asfd->rbuf->buf); if(yajl_parse(yajl, (const unsigned char *)asfd->rbuf->buf, asfd->rbuf->len)!=yajl_status_ok) { do_yajl_error(yajl, asfd); goto error; } if(!map_depth) { // Got to the end of the JSON object. if(yajl_complete_parse(yajl)!=yajl_status_ok) { do_yajl_error(yajl, asfd); goto error; } json_input_free(); if(warning_list) return 2; return 1; } return 0; error: json_input_free(); return -1; } burp-2.4.0/src/client/monitor/json_input.h000066400000000000000000000005731404341324700205710ustar00rootroot00000000000000#ifndef _JSON_INPUT #define _JSON_INPUT struct sel; extern int json_input_init(void); void json_input_free(void); extern struct lline *json_input_get_loglines(void); extern struct lline *json_input_get_warnings(void); extern void json_input_clear_loglines(void); extern void json_input_clear_warnings(void); extern int json_input(struct asfd *asfd, struct sel *sel); #endif burp-2.4.0/src/client/monitor/lline.c000066400000000000000000000022251404341324700174730ustar00rootroot00000000000000#include "../../burp.h" #include "../../alloc.h" #include "../../log.h" #include "lline.h" static void lline_free_content(struct lline *lline) { free_w(&lline->line); } static void lline_free(struct lline **lline) { if(!lline || !*lline) return; lline_free_content(*lline); free_v((void **)lline); } void llines_free(struct lline **lline) { struct lline *l; struct lline *lhead=*lline; while(lhead) { l=lhead; lhead=lhead->next; lline_free(&l); } *lline=NULL; } static struct lline *lline_alloc(char *line) { struct lline *llnew=NULL; if(!line) { logp("%s called with NULL line!\n", __func__); return NULL; } if(!(llnew=(struct lline *) calloc_w(1, sizeof(struct lline), __func__)) || !(llnew->line=strdup_w(line, __func__))) return NULL; return llnew; } int lline_add(struct lline **lline, char *line) { struct lline *l=NULL; struct lline *llast=NULL; struct lline *llnew=NULL; if(!(llnew=lline_alloc(line))) return -1; for(l=*lline; l; l=l->next) { l->prev=llast; llast=l; } if(llast) { llnew->next=llast->next; llast->next=llnew; llnew->prev=llast; } else { *lline=llnew; llnew->next=l; } return 0; } burp-2.4.0/src/client/monitor/lline.h000066400000000000000000000004131404341324700174750ustar00rootroot00000000000000#ifndef _LLINE_H #define _LLINE_H // Reading in log lines. typedef struct lline lline_t; struct lline { char *line; lline_t *next; lline_t *prev; }; extern void llines_free(struct lline **lline); extern int lline_add(struct lline **lline, char *line); #endif burp-2.4.0/src/client/monitor/sel.c000066400000000000000000000010651404341324700171540ustar00rootroot00000000000000#include "../../burp.h" #include "../../alloc.h" #include "../../bu.h" #include "../../cstat.h" #include "lline.h" #include "sel.h" #include "status_client_ncurses.h" struct sel *sel_alloc(void) { return (struct sel *)calloc_w(1, sizeof(struct sel), __func__); } static void sel_free_content(struct sel *sel) { if(!sel) return; cstat_list_free(&sel->clist); llines_free(&sel->llines); // Will be freed elsewhere. //bu_list_free(&sel->backup); } void sel_free(struct sel **sel) { if(!sel || !*sel) return; sel_free_content(*sel); free_v((void **)sel); } burp-2.4.0/src/client/monitor/sel.h000066400000000000000000000005771404341324700171700ustar00rootroot00000000000000#ifndef _SEL_H #define _SEL_H enum page_e { PAGE_CLIENT_LIST=0, PAGE_BACKUP_LIST, PAGE_BACKUP_LOGS, PAGE_VIEW_LOG }; struct sel { struct cstat *clist; struct cstat *client; struct bu *backup; uint16_t logop; struct lline *llines; struct lline *lline; enum page_e page; int offset; }; extern struct sel *sel_alloc(void); extern void sel_free(struct sel **sel); #endif burp-2.4.0/src/client/monitor/status_client_ncurses.c000066400000000000000000001004531404341324700230150ustar00rootroot00000000000000#include "../../burp.h" #include "../../action.h" #include "../../alloc.h" #include "../../asfd.h" #include "../../async.h" #include "../../bu.h" #include "../../cmd.h" #include "../../cstat.h" #include "../../forkchild.h" #include "../../fsops.h" #include "../../fzp.h" #include "../../handy.h" #include "../../iobuf.h" #include "../../log.h" #include "../../times.h" #include "json_input.h" #include "lline.h" #include "sel.h" #include "status_client_ncurses.h" #ifdef HAVE_NCURSES_H #include #elif HAVE_NCURSES_NCURSES_H #include #endif // So that the sighandler can call endwin(): static enum action actg=ACTION_STATUS; #define LEFT_SPACE 3 #define TOP_SPACE 2 static struct fzp *lfzp=NULL; #ifdef HAVE_NCURSES static void print_line_ncurses(const char *string, int row, int col) { int k=0; const char *cp=NULL; while(kwrite_str(stdout_asfd, CMD_GEN, " "); k++; } stdout_asfd->write_str(stdout_asfd, CMD_GEN, string); stdout_asfd->write_str(stdout_asfd, CMD_GEN, "\n"); } static void print_line(const char *string, int row, int col) { #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { print_line_ncurses(string, row, col); return; } #endif print_line_stdout(string); } static char *get_bu_str(struct bu *bu) { static char ret[38]; if(!bu) snprintf(ret, sizeof(ret), "%07d never", 0); else if(!bu->bno) snprintf(ret, sizeof(ret), "%s", bu->timestamp); else snprintf(ret, sizeof(ret), "%07" PRIu64 " %s", bu->bno, bu->timestamp); return ret; } static void client_summary(struct cstat *cstat, int row, int col, int clientwidth) { char msg[1024]=""; char fmt[64]=""; struct bu *cbu=NULL; snprintf(fmt, sizeof(fmt), "%%-%d.%ds %%9s %%s%%s", clientwidth, clientwidth); // Find the current backup. cbu=bu_find_current(cstat->bu); snprintf(msg, sizeof(msg), fmt, cstat->name, run_status_to_str(cstat), " last backup: ", get_bu_str(cbu)); if(*msg) print_line(msg, row, col); } /* for the counters */ static void to_msg(char msg[], size_t s, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(msg, s, fmt, ap); va_end(ap); } static void print_cntr_ent(const char *field, uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, int *x, int col) { char msg[256]=""; uint64_t t=a+b+c; if(!field || (!t && !d && !e)) return; /* FIX THIS. if(phase==STATUS_RESTORING || phase==STATUS_VERIFYING) { to_msg(msg, sizeof(msg), "% 15s % 9s % 9" PRIu64 " % 9" PRIu64, field, "", t, e); } else { */ to_msg(msg, sizeof(msg), "% 15s % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 "", field, a, b, c, d, t, e); // } print_line(msg, (*x)++, col); /* FIX THIS if(percent && e) { uint64_t p; p=(t*100)/e; if(phase==STATUS_RESTORING || phase==STATUS_VERIFYING) { to_msg(msg, sizeof(msg), "% 15s % 9s % 9" PRIu64 "%% % 9s", "", "", p, ""); } else { to_msg(msg, sizeof(msg), "% 15s % 9s % 9s % 9s % 9s % 9" PRIu64 "%% % 9s", "", "", "", "", "", p, ""); print_line(msg, (*x)++, col); } */ } static void table_header(int *x, int col) { char msg[256]=""; /* FIX THIS if(phase==STATUS_RESTORING || phase==STATUS_VERIFYING) { to_msg(msg, sizeof(msg), "% 15s % 9s % 9s % 9s", "", "", "Attempted", "Expected"); } else { */ to_msg(msg, sizeof(msg), "% 15s % 9s % 9s % 9s % 9s % 9s % 9s", "", "New", "Changed", "Unchanged", "Deleted", "Total", "Scanned"); // } print_line(msg, (*x)++, col); } /* static void print_detail2(const char *field, uint64_t value1, const char *value2, int *x, int col) { char msg[256]=""; if(!field || !value1 || !value2 || !*value2) return; snprintf(msg, sizeof(msg), "%s: %" PRIu64 "%s", field, value1, value2); print_line(msg, (*x)++, col); } static void print_detail3(const char *field, const char *value, int *x, int col) { char msg[256]=""; if(!field || !value || !*value) return; snprintf(msg, sizeof(msg), "%s: %s", field, value); print_line(msg, (*x)++, col); } */ /* static void detail(const char *cntrclient, char status, char phase, const char *path, struct cntr *p1cntr, struct cntr *cntr, struct strlist *backups, int row, int col) { int x=0; char msg[1024]=""; print_line("", x++, col); table_header(phase, &x, col); print_detail(phase, "Files", cntr->file, cntr->file_changed, cntr->file_same, cntr->file_deleted, p1cntr->file, &x, col, 0); print_detail(phase, "Encrypted files", cntr->enc, cntr->enc_changed, cntr->enc_same, cntr->enc_deleted, p1cntr->enc, &x, col, 0); print_detail(phase, "Meta data", cntr->meta, cntr->meta_changed, cntr->meta_same, cntr->meta_deleted, p1cntr->meta, &x, col, 0); print_detail(phase, "Encrypted meta data", cntr->encmeta, cntr->encmeta_changed, cntr->encmeta_same, cntr->encmeta_deleted, p1cntr->encmeta, &x, col, 0); print_detail(phase, "Directories", cntr->dir, cntr->dir_changed, cntr->dir_same, cntr->dir_deleted, p1cntr->dir, &x, col, 0); print_detail(phase, "Soft links", cntr->slink, cntr->slink_changed, cntr->slink_same, cntr->slink_deleted, p1cntr->slink, &x, col, 0); print_detail(phase, "Hard links", cntr->hlink, cntr->hlink_changed, cntr->hlink_same, cntr->hlink_deleted, p1cntr->hlink, &x, col, 0); print_detail(phase, "Special files", cntr->special, cntr->special_changed, cntr->special_same, cntr->special_deleted, p1cntr->special, &x, col, 0); print_detail(phase, "Total", cntr->gtotal, cntr->gtotal_changed, cntr->gtotal_same, cntr->gtotal_deleted, p1cntr->gtotal, &x, col, 1); print_line("", x++, col); print_detail(phase, "Warnings", cntr->warning, 0, 0, 0, 0, &x, col, 1); if(p1cntr->byte) { tmp=bytes_to_human(p1cntr->byte); print_detail2("Bytes estimated", p1cntr->byte, tmp, &x, col); } if(cntr->byte) { const char *text=NULL; if(phase==STATUS_BACKUP) text="Bytes in backup"; else if(phase==STATUS_RESTORING) text="Bytes attempted"; else if(phase==STATUS_VERIFYING) text="Bytes checked"; tmp=bytes_to_human(cntr->byte); if(text) print_detail2(text, cntr->byte, tmp, &x, col); } if(cntr->recvbyte) { const char *text=NULL; tmp=bytes_to_human(cntr->recvbyte); if(phase==STATUS_BACKUP) text="Bytes received"; if(text) print_detail2(text, cntr->recvbyte, tmp, &x, col); } if(cntr->sentbyte) { const char *text=NULL; if(phase==STATUS_BACKUP) text="Bytes sent"; else if(phase==STATUS_RESTORING) text="Bytes sent"; tmp=bytes_to_human(cntr->sentbyte); print_detail2(text, cntr->sentbyte, tmp, &x, col); } if(p1cntr->start) { time_t now=0; time_t diff=0; now=time(NULL); diff=now-p1cntr->start; print_detail3("Start time", getdatestr(p1cntr->start), &x,col); print_detail3("Time taken", time_taken(diff), &x, col); if(diff>0) { uint64_t bytesleft=0; uint64_t byteswant=0; uint64_t bytesgot=0; float bytespersec=0; byteswant=p1cntr->byte; bytesgot=cntr->byte; bytespersec=(float)(bytesgot/diff); bytesleft=byteswant-bytesgot; if(bytespersec>0) { time_t timeleft=0; timeleft=(time_t)(bytesleft/bytespersec); print_detail3("Time left", time_taken(timeleft), &x, col); } } } if(path && *path) { char pathstr[256]=""; snprintf(pathstr, sizeof(pathstr), "\n%s\n", path); #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { printw("%s", pathstr); return; } #endif stdout_asfd->write_str(stdout_asfd, CMD_GEN, pathstr); } } */ #ifdef HAVE_NCURSES static void screen_header_ncurses(const char *date, int l, int col) { char v[32]=""; snprintf(v, sizeof(v), " %s monitor %s", PACKAGE_TARNAME, VERSION); print_line(v, 0-TOP_SPACE, col); mvprintw(0, col-l-1, date); } #endif static void screen_header_stdout(const char *date, int l, int col) { size_t c=0; char spaces[512]=""; char msg[64]=""; snprintf(msg, sizeof(msg), " %s status", PACKAGE_TARNAME); stdout_asfd->write_str(stdout_asfd, CMD_GEN, "\n"); stdout_asfd->write_str(stdout_asfd, CMD_GEN, msg); for(c=0; c<(col-strlen(msg)-l-1) && cwrite_str(stdout_asfd, CMD_GEN, spaces); stdout_asfd->write_str(stdout_asfd, CMD_GEN, date); stdout_asfd->write_str(stdout_asfd, CMD_GEN, "\n\n"); } static void screen_header(int col) { int l; const char *date=NULL; #ifdef UTEST date="1977-10-02 00:10:20"; #else date=gettimenow(); #endif l=strlen(date); #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { screen_header_ncurses(date, l, col); return; } #endif screen_header_stdout(date, l, col); } static int need_status(struct sel *sel) { static time_t lasttime=0; time_t now=0; time_t diff=0; if(sel->page==PAGE_VIEW_LOG && sel->llines) return 0; // Only ask for an update every second. now=time(NULL); diff=now-lasttime; if(diff<1) { // In case they fiddled their clock back in time. if(diff<0) lasttime=now; return 0; } lasttime=now; return 1; } static const char *logop_to_text(uint16_t logop) { switch(logop) { case BU_MANIFEST: return "Manifest"; case BU_LOG_BACKUP: return "Backup log"; case BU_LOG_RESTORE: return "Restore log"; case BU_LOG_VERIFY: return "Verify log"; case BU_STATS_BACKUP: return "Backup stats"; case BU_STATS_RESTORE: return "Restore stats"; case BU_STATS_VERIFY: return "Verify stats"; case BU_LIVE_COUNTERS: return "Live counters"; default: return ""; } } static void print_logs_list_line(struct sel *sel, uint16_t bit, int *x, int col) { char msg[64]=""; if(!sel->backup || !(sel->backup->flags & bit)) return; snprintf(msg, sizeof(msg), "%s%s", *x==3?"Browse: ":" ", logop_to_text(bit)); print_line(msg, (*x)++, col); if(!sel->logop) sel->logop=bit; #ifdef HAVE_NCURSES if(sel->logop==bit) mvprintw(*x+TOP_SPACE-1, 1, "*"); #endif } static void print_client(struct sel *sel, int *x, int col) { char msg[1024]=""; snprintf(msg, sizeof(msg), "Client: %s", sel->client->name); // sel->client->cntr->ent[CMD_FILE]->phase1, // sel->client->cntr->ent[CMD_FILE]->count); print_line(msg, (*x)++, col); } static void client_and_status(struct sel *sel, int *x, int col) { char msg[1024]=""; print_client(sel, x, col); snprintf(msg, sizeof(msg), "Status: %s", run_status_to_str(sel->client)); print_line(msg, (*x)++, col); } static void client_and_status_and_backup(struct sel *sel, int *x, int col) { char msg[1024]; client_and_status(sel, x, col); snprintf(msg, sizeof(msg), "Backup: %s", get_bu_str(sel->backup)); print_line(msg, (*x)++, col); } static void client_and_status_and_backup_and_log(struct sel *sel, int *x, int col) { char msg[1024]; client_and_status_and_backup(sel, x, col); snprintf(msg, sizeof(msg), "Browse: %s", logop_to_text(sel->logop)); print_line(msg, (*x)++, col); } #ifdef HAVE_NCURSES static int selindex_from_cstat(struct sel *sel) { int selindex=0; struct cstat *c; for(c=sel->clist; c; c=c->next) { selindex++; if(sel->client==c) break; } return selindex; } static int selindex_from_bu(struct sel *sel) { int selindex=0; struct bu *b; for(b=sel->client->bu; b; b=b->next) { selindex++; if(sel->backup==b) break; } return selindex; } static int selindex_from_lline(struct sel *sel) { int selindex=0; struct lline *l; for(l=sel->llines; l; l=l->next) { selindex++; if(sel->lline==l) break; } return selindex; } #endif static void print_logs_list(struct sel *sel, int *x, int col) { print_logs_list_line(sel, BU_LIVE_COUNTERS, x, col); print_logs_list_line(sel, BU_MANIFEST, x, col); print_logs_list_line(sel, BU_LOG_BACKUP, x, col); print_logs_list_line(sel, BU_LOG_RESTORE, x, col); print_logs_list_line(sel, BU_LOG_VERIFY, x, col); print_logs_list_line(sel, BU_STATS_BACKUP, x, col); print_logs_list_line(sel, BU_STATS_RESTORE, x, col); print_logs_list_line(sel, BU_STATS_VERIFY, x, col); } static void update_screen_clients(struct sel *sel, int *x, int col, int winmin, int winmax) { #ifdef HAVE_NCURSES int s=0; #endif struct cstat *c; int star_printed=0; int max_cname=23*((float)col/100); #ifdef HAVE_NCURSES if(actg==ACTION_STATUS_SNAPSHOT) #endif { size_t l; for(c=sel->clist; c; c=c->next) if((l=strlen(c->name))>(unsigned int)max_cname) max_cname=l; } for(c=sel->clist; c; c=c->next) { #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { s++; if(swinmax) break; } #endif client_summary(c, (*x)++, col, max_cname); #ifdef HAVE_NCURSES if(actg==ACTION_STATUS && sel->client==c) { mvprintw((*x)+TOP_SPACE-1, 1, "*"); star_printed=1; } #endif } if(!star_printed) sel->client=sel->clist; } static char *get_extradesc(struct bu *b, struct cntr *cntrs) { char *extradesc=NULL; struct cntr *cntr=NULL; if(b->flags & BU_CURRENT) { extradesc=strdup_w(" (current)", __func__); } else if(b->flags & BU_WORKING) { extradesc=strdup_w(" (working)", __func__); } else if(b->flags & BU_FINISHING) { extradesc=strdup_w(" (finishing)", __func__); } else { extradesc=strdup_w("", __func__); } for(cntr=cntrs; cntr; cntr=cntr->next) { char phase[32]=""; if(cntr->bno==b->bno) { snprintf(phase, sizeof(phase), " %s, pid: %d", cntr_status_to_str(cntr), cntr->pid); if(astrcat(&extradesc, phase, __func__)) return NULL; } } return extradesc; } static int update_screen_backups(struct sel *sel, int *x, int col, int winmin, int winmax) { #ifdef HAVE_NCURSES int s=0; #endif struct bu *b; char msg[1024]=""; int star_printed=0; for(b=sel->client->bu; b; b=b->next) { char *extradesc=NULL; #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { s++; if(swinmax) break; } #endif if(!(extradesc=get_extradesc(b, sel->client->cntrs))) return -1; snprintf(msg, sizeof(msg), "%s %s%s", b==sel->client->bu?"Backup list:": " ", get_bu_str(b), extradesc); free_w(&extradesc); print_line(msg, (*x)++, col); #ifdef HAVE_NCURSES if(actg==ACTION_STATUS && sel->backup==b) { mvprintw((*x)+TOP_SPACE-1, 1, "*"); star_printed=1; } #endif } if(!star_printed) sel->backup=sel->client->bu; return 0; } static void update_screen_live_counter_table(struct cntr_ent *e, int *x, int col) { if(!(e->flags & CNTR_TABULATE)) return; print_cntr_ent(e->label, e->count, e->changed, e->same, e->deleted, e->phase1, x, col); } static void update_screen_live_counter_single(struct cntr_ent *e, int *x, int col) { char msg[128]=""; const char *bytes_human=""; if(!(e->flags & CNTR_SINGLE_FIELD)) return; if(!e->count) return; switch(e->cmd) { case CMD_TIMESTAMP: case CMD_TIMESTAMP_END: return; case CMD_BYTES_ESTIMATED: case CMD_BYTES: case CMD_BYTES_RECV: case CMD_BYTES_SENT: bytes_human=bytes_to_human(e->count); break; default: break; } snprintf(msg, sizeof(msg), "%19s: %12" PRIu64 " %s", e->label, e->count, bytes_human); print_line(msg, (*x)++, col); } static void update_screen_live_counters(struct cntr *cntr, int *x, int col) { char msg[128]=""; struct cntr_ent *e; time_t start=(time_t)cntr->ent[(uint8_t)CMD_TIMESTAMP]->count; time_t end=(time_t)cntr->ent[(uint8_t)CMD_TIMESTAMP_END]->count; struct cntr_ent *gtotal=cntr->ent[(uint8_t)CMD_GRAND_TOTAL]; print_line("", (*x)++, col); snprintf(msg, sizeof(msg), " PID: %d (%s)", cntr->pid, cntr_status_to_str(cntr)); print_line(msg, (*x)++, col); snprintf(msg, sizeof(msg), "Start time: %s", getdatestr(start)); print_line(msg, (*x)++, col); snprintf(msg, sizeof(msg), " End time: %s", getdatestr(end)); print_line(msg, (*x)++, col); snprintf(msg, sizeof(msg), "Time taken: %s", time_taken(end-start)); print_line(msg, (*x)++, col); table_header(x, col); for(e=cntr->list; e; e=e->next) update_screen_live_counter_table(e, x, col); print_line("", (*x)++, col); if(gtotal->phase1) { snprintf(msg, sizeof(msg), "%19s: %" PRIu64 "%%", "Percentage complete", ((gtotal->count+gtotal->same+gtotal->changed)*100)/gtotal->phase1); print_line(msg, (*x)++, col); } print_line("", (*x)++, col); for(e=cntr->list; e; e=e->next) update_screen_live_counter_single(e, x, col); } static void update_screen_live_counters_w(struct sel *sel, int *x, int col) { struct cstat *client=sel->client; struct cntr *cntr=NULL; for(cntr=client->cntrs; cntr; cntr=cntr->next) { if(sel->backup && sel->backup->bno==cntr->bno) update_screen_live_counters(cntr, x, col); } } static void update_screen_view_log(struct sel *sel, int *x, int col, int winmin, int winmax) { #ifdef HAVE_NCURSES int s=0; #endif int o=0; struct lline *l; const char *cp=NULL; int star_printed=0; if(sel->client && sel->backup && (sel->logop & BU_LIVE_COUNTERS)) return update_screen_live_counters_w(sel, x, col); for(l=sel->llines; l; l=l->next) { #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { s++; if(swinmax) break; } #endif // Allow them to scroll log lines left and right. for(cp=l->line, o=0; *cp && ooffset; cp++, o++) { } print_line(cp, (*x)++, col); #ifdef HAVE_NCURSES if(actg==ACTION_STATUS && sel->lline==l) { mvprintw((*x)+TOP_SPACE-1, 1, "*"); star_printed=1; } #endif } if(!star_printed) sel->lline=sel->llines; } static int update_screen(struct sel *sel) { int x=0; int row=24; int col=80; #ifdef HAVE_NCURSES int selindex=0; static int selindex_last=0; #endif static int winmin=0; static int winmax=0; screen_header(col); if(!sel->client) return 0; #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { getmaxyx(stdscr, row, col); // Unit tests give -1 for row and column. // Hack around it so that the unit tests still work. if(row<0) row=24; if(col<0) col=80; //if(!winmax) winmax=row; switch(sel->page) { case PAGE_CLIENT_LIST: selindex=selindex_from_cstat(sel); break; case PAGE_BACKUP_LIST: selindex=selindex_from_bu(sel); break; case PAGE_BACKUP_LOGS: break; case PAGE_VIEW_LOG: selindex=selindex_from_lline(sel); break; } } #endif switch(sel->page) { case PAGE_CLIENT_LIST: break; case PAGE_BACKUP_LIST: client_and_status(sel, &x, col); break; case PAGE_BACKUP_LOGS: client_and_status_and_backup(sel, &x, col); break; case PAGE_VIEW_LOG: client_and_status_and_backup_and_log(sel, &x, col); break; } #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { // Adjust sliding window appropriately. if(selindex>selindex_last) { if(selindex>winmax-TOP_SPACE-1-x) { winmin+=selindex-selindex_last; winmax+=selindex-selindex_last; } } else if(selindexprev)? (*selbu)->prev->timestamp:""); print_line(msg, -1, col); } */ } #endif switch(sel->page) { case PAGE_CLIENT_LIST: update_screen_clients(sel, &x, col, winmin, winmax); break; case PAGE_BACKUP_LIST: if(update_screen_backups(sel, &x, col, winmin, winmax)) return -1; break; case PAGE_BACKUP_LOGS: print_logs_list(sel, &x, col); break; case PAGE_VIEW_LOG: update_screen_view_log(sel, &x, col, winmin, winmax); break; } #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { // Blank any remainder of the screen. for(; xpage) { case PAGE_CLIENT_LIST: snprintf(buf, sizeof(buf), "c:\n"); break; case PAGE_BACKUP_LIST: snprintf(buf, sizeof(buf), "c:%s\n", client); break; case PAGE_BACKUP_LOGS: if(sel->backup) snprintf(buf, sizeof(buf), "c:%s:b:%" PRIu64 "\n", client, sel->backup->bno); break; case PAGE_VIEW_LOG: { const char *lname=NULL; if(sel->logop & BU_LOG_BACKUP) lname="backup"; else if(sel->logop & BU_LOG_RESTORE) lname="restore"; else if(sel->logop & BU_LOG_VERIFY) lname="verify"; else if(sel->logop & BU_MANIFEST) lname="manifest"; else if(sel->logop & BU_STATS_RESTORE) lname="restore_stats"; else if(sel->logop & BU_STATS_VERIFY) lname="verify_stats"; else if(sel->logop & BU_STATS_BACKUP) lname="backup_stats"; else if(sel->logop & BU_LIVE_COUNTERS) { // Hack so that it does not request the logs // for live counters. if(!sel->backup || !sel->client || !sel->client->cntrs) break; // Make sure a request is sent, so that the // counters update. snprintf(buf, sizeof(buf), "c:%s:b:%" PRIu64 "\n", client, sel->backup->bno); break; } if(sel->backup && lname) snprintf(buf, sizeof(buf), "c:%s:b:%" PRIu64 ":l:%s\n", client, sel->backup->bno, lname); break; } } /* if(confs->browsedir) snprintf(buf, sizeof(buf), "c:%s:b:%s:p:%s\n", client, confs->backup, confs->browsedir); else if(confs->browsefile) snprintf(buf, sizeof(buf), "c:%s:b:%s:f:%s\n", client, confs->backup, confs->browsefile); */ if(*buf) { if(lfzp) logp("request: %s\n", buf); if(asfd->write_str(asfd, CMD_GEN /* ignored */, buf)) return -1; } return 0; } #ifdef HAVE_NCURSES static void ncurses_free(void) { endwin(); } #endif static void sighandler(int sig) { #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) ncurses_free(); #endif logp("got signal: %d\n", sig); if(sig==SIGPIPE) logp("Server may have too many active status clients.\n"); logp("exiting\n"); exit(1); } static void setup_signals(void) { signal(SIGABRT, &sighandler); signal(SIGTERM, &sighandler); signal(SIGINT, &sighandler); signal(SIGPIPE, &sighandler); } #ifdef HAVE_NCURSES static void left(struct sel *sel) { switch(sel->page) { case PAGE_CLIENT_LIST: break; case PAGE_BACKUP_LIST: sel->page=PAGE_CLIENT_LIST; break; case PAGE_BACKUP_LOGS: sel->page=PAGE_BACKUP_LIST; break; case PAGE_VIEW_LOG: if(sel->offset>0) { // Allow log lines to be scrolled left. sel->offset--; break; } sel->page=PAGE_BACKUP_LOGS; llines_free(&sel->llines); sel->lline=NULL; break; } } static void right(struct sel *sel) { switch(sel->page) { case PAGE_CLIENT_LIST: sel->page=PAGE_BACKUP_LIST; break; case PAGE_BACKUP_LIST: sel->page=PAGE_BACKUP_LOGS; break; case PAGE_BACKUP_LOGS: if(lfzp) logp("Option selected: 0x%04X\n", sel->logop); sel->page=PAGE_VIEW_LOG; break; case PAGE_VIEW_LOG: // Allow log lines to be scrolled right. sel->offset++; break; } } static void up_client(struct sel *sel) { if(sel->client && sel->client->prev) sel->client=sel->client->prev; } static void down_client(struct sel *sel) { if(sel->client && sel->client->next) sel->client=sel->client->next; } static void up_backup(struct sel *sel) { if(sel->backup && sel->backup->prev) sel->backup=sel->backup->prev; } static void down_backup(struct sel *sel) { if(sel->backup && sel->backup->next) sel->backup=sel->backup->next; } static void up_logs(struct sel *sel) { int i=0; uint16_t sh=sel->logop; for(i=0; sh>BU_LIVE_COUNTERS && i<16; i++) { sh=sh>>1; if(sh & sel->backup->flags) { sel->logop=sh; break; } } } static void down_logs(struct sel *sel) { int i=0; uint16_t sh=sel->logop; for(i=0; sh && i<16; i++) { sh=sh<<1; if(sh & sel->backup->flags) { sel->logop=sh; break; } } } static void up_view_log(struct sel *sel) { if(sel->lline && sel->lline->prev) sel->lline=sel->lline->prev; } static void down_view_log(struct sel *sel) { if(sel->lline && sel->lline->next) sel->lline=sel->lline->next; } static void up(struct sel *sel) { switch(sel->page) { case PAGE_CLIENT_LIST: up_client(sel); break; case PAGE_BACKUP_LIST: up_backup(sel); break; case PAGE_BACKUP_LOGS: up_logs(sel); break; case PAGE_VIEW_LOG: up_view_log(sel); break; } } static void down(struct sel *sel) { switch(sel->page) { case PAGE_CLIENT_LIST: down_client(sel); break; case PAGE_BACKUP_LIST: down_backup(sel); break; case PAGE_BACKUP_LOGS: down_logs(sel); break; case PAGE_VIEW_LOG: down_view_log(sel); break; } } static void page_up_client(struct sel *sel, int row) { struct cstat *c; for(c=sel->client; c; c=c->prev) { row--; if(!row) break; } sel->client=c; } static void page_down_client(struct sel *sel, int row) { struct cstat *c; for(c=sel->client; c; c=c->next) { row--; if(!row) break; if(!c->next) break; } sel->client=c; } static void page_up_backup(struct sel *sel, int row) { struct bu *b; for(b=sel->backup; b; b=b->prev) { row--; if(!row) break; } sel->backup=b; } static void page_down_backup(struct sel *sel, int row) { struct bu *b; for(b=sel->backup; b; b=b->next) { row--; if(!row) break; if(!b->next) break; } sel->backup=b; } static void page_up(struct sel *sel) { int row=getmaxy(stdscr); switch(sel->page) { case PAGE_CLIENT_LIST: page_up_client(sel, row); break; case PAGE_BACKUP_LIST: page_up_backup(sel, row); break; case PAGE_BACKUP_LOGS: break; case PAGE_VIEW_LOG: break; } } static void page_down(struct sel *sel) { int row=getmaxy(stdscr); switch(sel->page) { case PAGE_CLIENT_LIST: page_down_client(sel, row); break; case PAGE_BACKUP_LIST: page_down_backup(sel, row); break; case PAGE_BACKUP_LOGS: break; case PAGE_VIEW_LOG: break; } } static int parse_stdin_data(struct asfd *asfd, struct sel *sel) { static int ch; if(asfd->rbuf->len!=sizeof(ch)) { logp("Unexpected input length in %s: %lu!=%zu\n", __func__, (unsigned long)asfd->rbuf->len, sizeof(ch)); return -1; } memcpy(&ch, asfd->rbuf->buf, sizeof(ch)); switch(ch) { case 'q': case 'Q': return 1; case KEY_UP: case 'k': case 'K': up(sel); break; case KEY_DOWN: case 'j': case 'J': down(sel); break; case KEY_LEFT: case 'h': case 'H': left(sel); break; case KEY_RIGHT: case 'l': case 'L': case KEY_ENTER: case '\n': case ' ': right(sel); break; case KEY_PPAGE: page_up(sel); break; case KEY_NPAGE: page_down(sel); break; case -1: logp("Error on stdin\n"); return -1; } return 0; } #endif static int parse_data(struct asfd *asfd, struct sel *sel) { #ifdef HAVE_NCURSES if(actg==ACTION_STATUS && asfd->streamtype==ASFD_STREAM_NCURSES_STDIN) return parse_stdin_data(asfd, sel); #endif switch(json_input(asfd, sel)) { // 0 means carry on. // 1 means it got to the end of the JSON statement. // 2 means it got to the end but had warnings. // Anything else means an error. case 0: return 0; case 1: return 0; case 2: { // If we had a warning exit straight away. For example, // if they specified '-C non-existent-client'. return -1; } default: return -1; } } #ifndef UTEST static #endif int status_client_ncurses_main_loop(struct async *as, struct asfd *so_asfd, struct sel *sel, const char *orig_client) { int ret=-1; char *client=NULL; struct asfd *asfd=NULL; struct asfd *sfd=NULL; // Server asfd. int reqdone=0; int client_count=-1; if(!sel || !as || !(stdout_asfd=so_asfd) || !(sfd=as->asfd)) { logp("parameters not set up correctly in %s\n", __func__); goto error; } sel->page=PAGE_CLIENT_LIST; if(orig_client) { client=strdup_w(orig_client, __func__); sel->page=PAGE_BACKUP_LIST; } while(1) { if(need_status(sel) && !reqdone) { char *req=NULL; if(sel->page>PAGE_CLIENT_LIST) { if(client) req=client; else if(sel->client) req=sel->client->name; } if(request_status(sfd, req?req:"", sel)) goto error; // We only want to start on the client the user gave to // us. Freeing it will allow the user to browse other // clients thereafter. free_w(&client); if(actg==ACTION_STATUS_SNAPSHOT) reqdone=1; } if(as->read_write(as)) { // FIX THIS - an exception is thrown when the console // is resized. /* if(sfd->want_to_remove) { sfd->want_to_remove=0; continue; } */ logp("Exiting main loop\n"); goto error; } for(asfd=as->asfd; asfd; asfd=asfd->next) { while(asfd->rbuf->buf) { switch(parse_data(asfd, sel)) { case 0: break; case 1: goto end; default: goto error; } iobuf_free_content(asfd->rbuf); if(asfd->parse_readbuf(asfd)) goto error; } // Select things if they are not already selected. if(sel->client) { if(!sel->backup) sel->backup=sel->client->bu; } else sel->client=sel->clist; } #ifdef HAVE_NCURSES if(actg==ACTION_STATUS && update_screen(sel)) goto error; refresh(); #endif if(actg==ACTION_STATUS_SNAPSHOT) { int new_count=cstat_count(sel->clist); if(new_count==client_count && sel->client) { if(update_screen(sel)) goto error; stdout_asfd->write_str(stdout_asfd, CMD_GEN, "\n"); break; } client_count=new_count; } } end: ret=0; error: return ret; } #ifdef HAVE_NCURSES static void ncurses_init(void) { initscr(); start_color(); use_default_colors(); raw(); keypad(stdscr, TRUE); noecho(); curs_set(0); halfdelay(3); //nodelay(stdscr, TRUE); } #endif static pid_t fork_monitor(int *csin, int *csout, struct conf **confs) { int a=0; char *args[12]; char procpath[32]; char buf[PATH_MAX]; char *monitor_exe; monitor_exe=get_string(confs[OPT_MONITOR_EXE]); snprintf(procpath, sizeof(procpath), "/proc/%d/exe", getpid()); if(monitor_exe && is_reg_lstat(monitor_exe)>0) args[a++]=monitor_exe; else if(!readlink_w(procpath, buf, sizeof(buf))) args[a++]=(char *)buf; else if(is_reg_lstat(prog_long)>0) args[a++]=(char *)prog_long; else { static char p[64]=""; snprintf(p, sizeof(p), "/usr/sbin/%s", PACKAGE_TARNAME); logp("Using fallback monitor path: %s\n", p); args[a++]=p; } args[a++]=(char *)"-c"; args[a++]=get_string(confs[OPT_CONFFILE]); args[a++]=(char *)"-a"; args[a++]=(char *)"m"; args[a++]=NULL; return forkchild_fd(csin, csout, NULL, args[0], args); } int status_client_ncurses_init(enum action act) { actg=act; #ifndef HAVE_NCURSES if(act==ACTION_STATUS) { printf("To use the live status monitor, you need to recompile with ncurses support.\n"); return -1; } #endif return 0; } static void show_loglines(struct lline *llines, const char *prefix) { struct lline *l; for(l=llines; l; l=l->next) logp("%s%s\n", prefix, l->line); } int status_client_ncurses(struct conf **confs) { int ret=-1; int csin=-1; int csout=-1; pid_t childpid=-1; struct async *as=NULL; const char *monitor_logfile=get_string(confs[OPT_MONITOR_LOGFILE]); struct asfd *so_asfd=NULL; struct sel *sel=NULL; struct lline *llines=NULL; struct lline *wlines=NULL; if(json_input_init()) goto end; if(!(sel=sel_alloc())) goto end; setup_signals(); // Fork a burp child process that will contact the server over SSL. // We will read and write from and to its stdout and stdin. if((childpid=fork_monitor(&csin, &csout, confs))<0) goto end; //printf("childpid: %d\n", childpid); if(!(as=async_alloc()) || as->init(as, 0) || !setup_asfd_linebuf_write(as, "monitor stdin", &csin) || !setup_asfd_linebuf_read(as, "monitor stdout", &csout)) goto end; //printf("ml: %s\n", monitor_logfile); #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { if(!setup_asfd_ncurses_stdin(as)) goto end; ncurses_init(); } #endif if(!(so_asfd=setup_asfd_stdout(as))) goto end; if(monitor_logfile && !(lfzp=fzp_open(monitor_logfile, "wb"))) goto end; log_fzp_set_direct(lfzp); ret=status_client_ncurses_main_loop(as, so_asfd, sel, get_string(confs[OPT_ORIG_CLIENT])); end: #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) ncurses_free(); #endif llines=json_input_get_loglines(); wlines=json_input_get_warnings(); if(ret) { show_loglines(llines, ""); show_loglines(wlines, "WARNING: "); logp("%s exiting with error: %d\n", __func__, ret); } json_input_clear_loglines(); json_input_clear_warnings(); json_input_free(); fzp_close(&lfzp); async_asfd_free_all(&as); close_fd(&csin); close_fd(&csout); sel_free(&sel); return ret; } burp-2.4.0/src/client/monitor/status_client_ncurses.h000066400000000000000000000006031404341324700230160ustar00rootroot00000000000000#ifndef STATUS_CLIENT_NCURSES_H #define STATUS_CLIENT_NCURSES_H #include "../../action.h" struct async; struct sel; extern int status_client_ncurses_init(enum action act); extern int status_client_ncurses(struct conf **confs); #ifdef UTEST extern int status_client_ncurses_main_loop(struct async *as, struct asfd *so_asfd, struct sel *sel, const char *orig_client); #endif #endif burp-2.4.0/src/client/protocol1/000077500000000000000000000000001404341324700164565ustar00rootroot00000000000000burp-2.4.0/src/client/protocol1/backup_phase2.c000066400000000000000000000254151404341324700213400ustar00rootroot00000000000000#include "../../burp.h" #include "../../action.h" #include "../../alloc.h" #include "../../asfd.h" #include "../../async.h" #include "../../attribs.h" #include "../../cmd.h" #include "../../cntr.h" #include "../../conf.h" #include "../../log.h" #include "../../protocol1/handy.h" #include "../../protocol1/msg.h" #include "../extrameta.h" #include "../find.h" #include "backup_phase2.h" static int rs_loadsig_network_run(struct asfd *asfd, rs_job_t *job, struct cntr *cntr) { int ret=-1; rs_buffers_t buf; rs_result result; rs_filebuf_t *in_fb=NULL; memset(&buf, 0, sizeof(buf)); if(!(in_fb=rs_filebuf_new(NULL, NULL, asfd, ASYNC_BUF_LEN, -1))) goto end; while(1) { iobuf_free_content(asfd->rbuf); if(asfd->read(asfd)) goto end; if(asfd->rbuf->cmd==CMD_MESSAGE || asfd->rbuf->cmd==CMD_WARNING) { log_recvd(asfd->rbuf, cntr, 0); continue; } switch((result=rs_async(job, &buf, in_fb, NULL))) { case RS_BLOCKED: case RS_RUNNING: continue; case RS_DONE: ret=0; goto end; default: logp("error in rs_async for sig: %d\n", result); goto end; } } end: iobuf_free_content(asfd->rbuf); rs_filebuf_free(&in_fb); return ret; } static int load_signature(struct asfd *asfd, rs_signature_t **sumset, struct cntr *cntr) { rs_job_t *job; if(!(job=rs_loadsig_begin(sumset))) { logp("could not start sig job.\n"); return -1; } if(rs_loadsig_network_run(asfd, job, cntr)) return -1; if(rs_build_hash_table(*sumset)) return -1; rs_job_free(job); return 0; } static int load_signature_and_send_delta(struct asfd *asfd, struct BFILE *bfd, uint64_t *bytes, uint64_t *sentbytes, struct cntr *cntr) { int ret=-1; rs_job_t *job=NULL; rs_signature_t *sumset=NULL; uint8_t checksum[MD5_DIGEST_LENGTH]; rs_filebuf_t *infb=NULL; rs_filebuf_t *outfb=NULL; rs_buffers_t rsbuf; memset(&rsbuf, 0, sizeof(rsbuf)); if(load_signature(asfd, &sumset, cntr)) goto end; if(!(job=rs_delta_begin(sumset))) { logp("could not start delta job.\n"); goto end; } if(!(infb=rs_filebuf_new(bfd, NULL, NULL, ASYNC_BUF_LEN, bfd->datalen)) || !(outfb=rs_filebuf_new(NULL, NULL, asfd, ASYNC_BUF_LEN, -1))) { logp("could not rs_filebuf_new for delta\n"); goto end; } while(1) { rs_result result; switch((result=rs_async(job, &rsbuf, infb, outfb))) { case RS_DONE: *bytes=infb->bytes; *sentbytes=outfb->bytes; if(!MD5_Final(checksum, &(infb->md5))) { logp("MD5_Final() failed\n"); goto end; } if(write_endfile(asfd, *bytes, checksum)) goto end; ret=0; goto end; case RS_BLOCKED: case RS_RUNNING: // FIX ME: get it to read stuff here too. // (errors, for example) if(asfd->as->write(asfd->as)) goto end; continue; default: logp("error in rs_async for delta: %d\n", result); goto end; } } end: rs_filebuf_free(&infb); rs_filebuf_free(&outfb); if(job) rs_job_free(job); if(sumset) rs_free_sumset(sumset); return ret; } static enum send_e send_whole_file_w(struct asfd *asfd, struct sbuf *sb, const char *datapth, int quick_read, uint64_t *bytes, const char *encpassword, struct cntr *cntr, int compression, struct BFILE *bfd, const char *extrameta, size_t elen) { if((compression || encpassword) && sb->path.cmd!=CMD_EFS_FILE) { int key_deriv=sb->encryption==ENCRYPTION_KEY_DERIVED; return send_whole_file_gzl(asfd, datapth, quick_read, bytes, encpassword, cntr, compression, bfd, extrameta, elen, key_deriv, sb->protocol1->salt); } else return send_whole_filel(asfd, #ifdef HAVE_WIN32 sb->path.cmd, #endif datapth, quick_read, bytes, cntr, bfd, extrameta, elen); } static int forget_file(struct asfd *asfd, struct sbuf *sb, struct conf **confs) { // Tell the server to forget about this // file, otherwise it might get stuck // on a select waiting for it to arrive. if(asfd->write_str(asfd, CMD_INTERRUPT, sb->path.buf)) return 0; if(sb->path.cmd==CMD_FILE && sb->protocol1->datapth.buf) { rs_signature_t *sumset=NULL; // The server will be sending us a signature. // Munch it up then carry on. if(load_signature(asfd, &sumset, get_cntr(confs))) return -1; else rs_free_sumset(sumset); } return 0; } static int size_checks(struct asfd *asfd, struct sbuf *sb, struct conf **confs) { if(sb->path.cmd!=CMD_FILE && sb->path.cmd!=CMD_ENC_FILE && sb->path.cmd!=CMD_EFS_FILE) return 0; if(get_uint64_t(confs[OPT_MIN_FILE_SIZE]) && (uint64_t)sb->statp.st_sizepath)); return -1; } if(get_uint64_t(confs[OPT_MAX_FILE_SIZE]) && (uint64_t)sb->statp.st_size>get_uint64_t(confs[OPT_MAX_FILE_SIZE])) { logw(asfd, get_cntr(confs), "File size increased above max_file_size after initial scan: %s\n", iobuf_to_printable(&sb->path)); return -1; } return 0; } static int deal_with_data(struct asfd *asfd, struct sbuf *sb, struct BFILE *bfd, struct conf **confs) { int ret=-1; int forget=0; size_t elen=0; char *extrameta=NULL; uint64_t bytes=0; int conf_compression=get_int(confs[OPT_COMPRESSION]); struct cntr *cntr=get_cntr(confs); const char *enc_password=get_string(confs[OPT_ENCRYPTION_PASSWORD]); sb->compression=conf_compression; if(enc_password) { sb->encryption=ENCRYPTION_KEY_DERIVED; if(!RAND_bytes((uint8_t *)&sb->protocol1->salt, 8)) { logp("RAND_bytes() failed\n"); return -1; } } iobuf_copy(&sb->path, asfd->rbuf); iobuf_init(asfd->rbuf); #ifdef HAVE_WIN32 if(win32_lstat(sb->path.buf, &sb->statp, &sb->winattr)) #else if(lstat(sb->path.buf, &sb->statp)) #endif { logw(asfd, cntr, "Path has vanished: %s\n", iobuf_to_printable(&sb->path)); forget++; goto end; } if(size_checks(asfd, sb, confs)) { forget++; goto end; } sb->compression=in_exclude_comp(get_strlist(confs[OPT_EXCOM]), sb->path.buf, conf_compression); if(attribs_encode(sb)) goto error; if(sb->path.cmd!=CMD_METADATA && sb->path.cmd!=CMD_ENC_METADATA) { if(bfd->open_for_send(bfd, asfd, sb->path.buf, sb->winattr, get_int(confs[OPT_ATIME]), cntr, PROTO_1)) { forget++; goto end; } } if(sb->path.cmd==CMD_METADATA || sb->path.cmd==CMD_ENC_METADATA || sb->path.cmd==CMD_VSS || sb->path.cmd==CMD_ENC_VSS #ifdef HAVE_WIN32 || get_int(confs[OPT_STRIP_VSS]) #endif ) { if(get_extrameta(asfd, #ifdef HAVE_WIN32 bfd, #endif sb->path.buf, S_ISDIR(sb->statp.st_mode), &extrameta, &elen, cntr)) { logw(asfd, cntr, "Meta data error for %s\n", iobuf_to_printable(&sb->path)); forget++; goto end; } if(extrameta) { #ifdef HAVE_WIN32 if(get_int(confs[OPT_STRIP_VSS])) { free_w(&extrameta); elen=0; } #endif } else { logw(asfd, cntr, "No meta data after all: %s\n", iobuf_to_printable(&sb->path)); forget++; goto end; } } if(sb->path.cmd==CMD_FILE && sb->protocol1->datapth.buf) { uint64_t sentbytes=0; // Need to do sig/delta stuff. if(asfd->write(asfd, &(sb->protocol1->datapth)) || asfd->write(asfd, &sb->attr) || asfd->write(asfd, &sb->path) || load_signature_and_send_delta(asfd, bfd, &bytes, &sentbytes, cntr)) { logp("error in sig/delta for %s (%s)\n", iobuf_to_printable(&sb->path), iobuf_to_printable(&sb->protocol1->datapth)); forget++; goto end; } cntr_add(cntr, CMD_FILE_CHANGED, 1); } else { //logp("need to send whole file: %s\n", sb.path); // send the whole file. if(asfd->write(asfd, &sb->attr) || asfd->write(asfd, &sb->path)) goto end; switch(send_whole_file_w(asfd, sb, NULL, 0, &bytes, enc_password, cntr, sb->compression, bfd, extrameta, elen)) { case SEND_OK: break; case SEND_ERROR: forget++; break; case SEND_FATAL: default: goto error; } cntr_add(cntr, sb->path.cmd, 1); } cntr_add_bytes(cntr, bytes); end: ret=0; if(forget && forget_file(asfd, sb, confs)) ret=-1; error: #ifdef HAVE_WIN32 // If using Windows do not close bfd - it needs // to stay open to read VSS/file data/VSS. // It will get closed either when given a // different file path, or when this function // exits. #else bfd->close(bfd, asfd); #endif sbuf_free_content(sb); free_w(&extrameta); return ret; } static int parse_rbuf(struct asfd *asfd, struct sbuf *sb, struct BFILE *bfd, struct conf **confs) { static struct iobuf *rbuf; rbuf=asfd->rbuf; if(rbuf->cmd==CMD_DATAPTH) { iobuf_move(&(sb->protocol1->datapth), rbuf); } else if(rbuf->cmd==CMD_ATTRIBS) { // Ignore the stat data - we will fill it // in again. Some time may have passed by now, // and it is best to make it as fresh as // possible. } else if(iobuf_is_filedata(rbuf) || iobuf_is_vssdata(rbuf)) { if(deal_with_data(asfd, sb, bfd, confs)) return -1; } else if(rbuf->cmd==CMD_MESSAGE || rbuf->cmd==CMD_WARNING) { struct cntr *cntr=NULL; if(confs) cntr=get_cntr(confs); log_recvd(rbuf, cntr, 0); } else { iobuf_log_unexpected(rbuf, __func__); return -1; } return 0; } static int do_backup_phase2_client(struct asfd *asfd, struct conf **confs, int resume) { int ret=-1; // For efficiency, open Windows files for the VSS data, and do not // close them until another time around the loop, when the actual // data is read. struct BFILE *bfd=NULL; struct sbuf *sb=NULL; struct iobuf *rbuf=NULL; struct cntr *cntr=NULL; if(confs) cntr=get_cntr(confs); if(!asfd) { logp("%s() called without asfd!\n", __func__); goto end; } rbuf=asfd->rbuf; if(!(bfd=bfile_alloc()) || !(sb=sbuf_alloc(PROTO_1))) goto end; bfile_init(bfd, 0, cntr); if(!resume) { // Only do this bit if the server did not tell us to resume. if(asfd->write_str(asfd, CMD_GEN, "backupphase2") || asfd_read_expect(asfd, CMD_GEN, "ok")) goto end; } else { // On resume, the server might update the client with cntr. if(cntr_recv(asfd, confs)) goto end; } while(1) { iobuf_free_content(rbuf); if(asfd->read(asfd)) goto end; else if(!rbuf->buf) continue; if(rbuf->cmd==CMD_GEN && !strcmp(rbuf->buf, "backupphase2end")) { if(asfd->write_str(asfd, CMD_GEN, "okbackupphase2end")) goto end; ret=0; break; } if(parse_rbuf(asfd, sb, bfd, confs)) goto end; } end: // It is possible for a bfd to still be open. if(bfd) bfd->close(bfd, asfd); bfile_free(&bfd); iobuf_free_content(rbuf); sbuf_free(&sb); return ret; } int backup_phase2_client_protocol1(struct asfd *asfd, struct conf **confs, int resume) { int ret=0; struct cntr *cntr=NULL; if(confs) cntr=get_cntr(confs); logp("Phase 2 begin (send backup data)\n"); logfmt("\n"); ret=do_backup_phase2_client(asfd, confs, resume); cntr_print_end(cntr); cntr_set_bytes(cntr, asfd); cntr_print(cntr, ACTION_BACKUP); if(ret) logp("Error in phase 2\n"); logp("Phase 2 end (send file data)\n"); return ret; } burp-2.4.0/src/client/protocol1/backup_phase2.h000066400000000000000000000002751404341324700213420ustar00rootroot00000000000000#ifndef _BACKUP_PHASE2_CLIENT_PROTOCOL1_H #define _BACKUP_PHASE2_CLIENT_PROTOCOL1_H extern int backup_phase2_client_protocol1(struct asfd *asfd, struct conf **confs, int resume); #endif burp-2.4.0/src/client/protocol1/restore.c000066400000000000000000000142311404341324700203060ustar00rootroot00000000000000#include "../../burp.h" #include "../../action.h" #include "../../alloc.h" #include "../../asfd.h" #include "../../async.h" #include "../../attribs.h" #include "../../bfile.h" #include "../../cmd.h" #include "../../cntr.h" #include "../../fsops.h" #include "../../handy.h" #include "../../log.h" #include "../../protocol1/msg.h" #include "../extrameta.h" #include "../restore.h" #include "restore.h" static int do_restore_file_or_get_meta(struct asfd *asfd, struct BFILE *bfd, struct sbuf *sb, const char *fname, char **metadata, size_t *metalen, struct cntr *cntr, const char *rpath, const char *encryption_password) { int ret=-1; int enccompressed=0; uint64_t rcvdbytes=0; uint64_t sentbytes=0; const char *encpassword=NULL; int key_deriv=0; if(sbuf_is_encrypted(sb)) { encpassword=encryption_password; if(sb->encryption==ENCRYPTION_KEY_DERIVED) key_deriv=1; } enccompressed=dpth_protocol1_is_compressed(sb->compression, sb->protocol1->datapth.buf); /* if(encpassword && !enccompressed) printf("encrypted and not compressed\n"); else if(!encpassword && enccompressed) printf("not encrypted and compressed\n"); else if(!encpassword && !enccompressed) printf("not encrypted and not compressed\n"); else if(encpassword && enccompressed) printf("encrypted and compressed\n"); */ if(metadata) { ret=transfer_gzfile_inl(asfd, #ifdef HAVE_WIN32 sb, #endif NULL, &rcvdbytes, &sentbytes, encpassword, enccompressed, cntr, metadata, key_deriv, sb->protocol1->salt); *metalen=sentbytes; // skip setting cntr, as we do not actually // restore until a bit later } else { ret=transfer_gzfile_inl(asfd, #ifdef HAVE_WIN32 sb, #endif bfd, &rcvdbytes, &sentbytes, encpassword, enccompressed, cntr, NULL, key_deriv, sb->protocol1->salt); #ifndef HAVE_WIN32 if(bfd && bfd->close(bfd, asfd)) { logp("error closing %s in %s\n", fname, __func__); ret=-1; } // For Windows, only set the attribs when it closes the file, // so that trailing vss does not get blocked after having set // a read-only attribute. if(!ret) attribs_set(asfd, rpath, &sb->statp, sb->winattr, cntr); #endif } if(ret) { char msg[256]=""; snprintf(msg, sizeof(msg), "Could not transfer file in: %s", rpath); return restore_interrupt(asfd, sb, msg, cntr, PROTO_1); } return 0; } static int restore_file_or_get_meta(struct asfd *asfd, struct BFILE *bfd, struct sbuf *sb, const char *fname, enum action act, char **metadata, size_t *metalen, enum vss_restore vss_restore, struct cntr *cntr, const char *encyption_password) { int ret=0; char *rpath=NULL; if(act==ACTION_VERIFY) { cntr_add(cntr, sb->path.cmd, 1); goto end; } if(build_path(fname, "", &rpath, NULL)) { char msg[256]=""; // failed - do a warning snprintf(msg, sizeof(msg), "build path failed: %s", fname); if(restore_interrupt(asfd, sb, msg, cntr, PROTO_1)) ret=-1; goto end; } #ifndef HAVE_WIN32 // We always want to open the file if it is on Windows. Otherwise, // only open it if we are not doing metadata. if(!metadata) { #endif switch(open_for_restore(asfd, bfd, rpath, sb, vss_restore, cntr, PROTO_1)) { case OFR_OK: break; case OFR_CONTINUE: goto end; default: ret=-1; goto end; } #ifndef HAVE_WIN32 } #endif if(!(ret=do_restore_file_or_get_meta(asfd, bfd, sb, fname, metadata, metalen, cntr, rpath, encyption_password))) { // Only add to counters if we are not doing metadata. The // actual metadata restore comes a bit later. if(!metadata) cntr_add(cntr, sb->path.cmd, 1); } end: free_w(&rpath); if(ret) logp("restore_file error\n"); return ret; } static int restore_metadata(struct asfd *asfd, struct BFILE *bfd, struct sbuf *sb, const char *fname, enum action act, enum vss_restore vss_restore, struct cntr *cntr, const char *encryption_password) { int ret=-1; size_t metalen=0; char *metadata=NULL; // If it is directory metadata, try to make sure the directory // exists. Pass in NULL as the cntr, so no counting is done. // The actual directory entry will be coming after the metadata, // annoyingly. This is because of the way that the server is queuing // up directories to send after file data, so that the stat info on // them gets set correctly. if(act==ACTION_VERIFY) { cntr_add(cntr, sb->path.cmd, 1); ret=0; goto end; } // Create the directory, but do not add to the counts. if(S_ISDIR(sb->statp.st_mode) && restore_dir(asfd, sb, fname, act, /*cntr*/NULL, PROTO_1)) goto end; // Read in the metadata... if(restore_file_or_get_meta(asfd, bfd, sb, fname, act, &metadata, &metalen, vss_restore, cntr, encryption_password)) goto end; if(metadata) { if(!set_extrameta(asfd, #ifdef HAVE_WIN32 bfd, #endif fname, metadata, metalen, cntr)) { #ifndef HAVE_WIN32 // Set file times again, since we just diddled with the // file. Do not set all attributes, as it will wipe // out any security attributes (eg getcap /usr/bin/ping) if(attribs_set_file_times(asfd, fname, &sb->statp, cntr)) return -1; cntr_add(cntr, sb->path.cmd, 1); #endif } // Carry on if we could not set_extrameta. } ret=0; end: free_w(&metadata); return ret; } int restore_switch_protocol1(struct asfd *asfd, struct sbuf *sb, const char *fullpath, enum action act, struct BFILE *bfd, enum vss_restore vss_restore, struct cntr *cntr, const char *encryption_password) { switch(sb->path.cmd) { case CMD_FILE: case CMD_VSS_T: case CMD_ENC_FILE: case CMD_ENC_VSS_T: case CMD_EFS_FILE: if(!sb->protocol1->datapth.buf) { char msg[256]; snprintf(msg, sizeof(msg), "datapth not supplied for %s in %s\n", iobuf_to_printable(&sb->path), __func__); log_and_send(asfd, msg); return -1; } return restore_file_or_get_meta(asfd, bfd, sb, fullpath, act, NULL, NULL, vss_restore, cntr, encryption_password); case CMD_METADATA: case CMD_VSS: case CMD_ENC_METADATA: case CMD_ENC_VSS: return restore_metadata(asfd, bfd, sb, fullpath, act, vss_restore, cntr, encryption_password); default: // Other cases (dir/links/etc) are handled in the // calling function. logp("unknown cmd: %s\n", iobuf_to_printable(&sb->path)); return -1; } } burp-2.4.0/src/client/protocol1/restore.h000066400000000000000000000004421404341324700203120ustar00rootroot00000000000000#ifndef _RESTORE_CLIENT_PROTOCOL1_H #define _RESTORE_CLIENT_PROTOCOL1_H int restore_switch_protocol1(struct asfd *asfd, struct sbuf *sb, const char *fullpath, enum action act, struct BFILE *bfd, enum vss_restore vss_restore, struct cntr *cntr, const char *encryption_password); #endif burp-2.4.0/src/client/protocol2/000077500000000000000000000000001404341324700164575ustar00rootroot00000000000000burp-2.4.0/src/client/protocol2/backup_phase2.c000066400000000000000000000203571404341324700213410ustar00rootroot00000000000000#include "../../burp.h" #include "../../action.h" #include "../../asfd.h" #include "../../async.h" #include "../../base64.h" #include "../../cmd.h" #include "../../cntr.h" #include "../../iobuf.h" #include "../../log.h" #include "../../protocol2/blk.h" #include "../../protocol2/blist.h" #include "../../protocol2/rabin/rabin.h" #include "../../slist.h" #include "rabin_read.h" #include "backup_phase2.h" #define END_SIGS 0x01 #define END_BACKUP 0x02 #define END_REQUESTS 0x04 #define END_BLK_REQUESTS 0x08 static int add_to_file_requests(struct slist *slist, struct iobuf *rbuf) { static uint64_t file_no=1; struct sbuf *sb; if(!(sb=sbuf_alloc(PROTO_2))) return -1; iobuf_move(&sb->path, rbuf); // Give it a number to simplify tracking. sb->protocol2->index=file_no++; slist_add_sbuf(slist, sb); return 0; } static int add_to_data_requests(struct blist *blist, struct iobuf *rbuf) { uint64_t index; struct blk *blk; index=base64_to_uint64(rbuf->buf); //printf("last_requested: %d\n", blist->last_requested->index); // Find the matching entry. for(blk=blist->last_requested; blk; blk=blk->next) if(index==blk->index) break; if(!blk) { logp("Could not find requested block %" PRIu64 "\n", index); return -1; } blk->requested=1; blist->last_requested=blk; //printf("Found %lu\n", index); return 0; } static int deal_with_read(struct iobuf *rbuf, struct slist *slist, struct cntr *cntr, uint8_t *end_flags) { int ret=0; switch(rbuf->cmd) { /* Incoming file request. */ case CMD_FILE: case CMD_METADATA: if(add_to_file_requests(slist, rbuf)) goto error; return 0; /* Incoming data block request. */ case CMD_DATA_REQ: if(add_to_data_requests(slist->blist, rbuf)) goto error; goto end; /* Incoming control/message stuff. */ case CMD_WRAP_UP: { int64_t wrap_up; struct blk *blk; struct blist *blist=slist->blist; from_base64(&wrap_up, rbuf->buf); for(blk=blist->head; blk; blk=blk->next) { if(blk->index==(uint64_t)wrap_up) { blist->last_requested=blk; blist->last_sent=blk; break; } } if(!blk) { logp("Could not find wrap up index: %016" PRIX64 "\n", wrap_up); // goto error; } goto end; } case CMD_MESSAGE: case CMD_WARNING: { log_recvd(rbuf, cntr, 0); goto end; } case CMD_GEN: if(!strcmp(rbuf->buf, "requests_end")) { (*end_flags)|=END_REQUESTS; goto end; } else if(!strcmp(rbuf->buf, "blk_requests_end")) { (*end_flags)|=END_BLK_REQUESTS; goto end; } else if(!strcmp(rbuf->buf, "backup_end")) { (*end_flags)|=END_BACKUP; goto end; } break; default: break; } iobuf_log_unexpected(rbuf, __func__); error: ret=-1; end: iobuf_free_content(rbuf); return ret; } static int add_to_blks_list(struct asfd *asfd, struct conf **confs, struct slist *slist) { int just_opened=0; struct sbuf *sb=slist->last_requested; if(!sb) return 0; if(sb->protocol2->bfd.mode==BF_CLOSED) { char buf[32]; struct cntr *cntr=NULL; if(confs) cntr=get_cntr(confs); switch(rabin_open_file(sb, asfd, cntr, confs)) { case 1: // All OK. break; case 0: // Could not open file. Tell the server. base64_from_uint64(sb->protocol2->index, buf); if(asfd->write_str(asfd, CMD_INTERRUPT, buf)) return -1; if(slist_del_sbuf(slist, sb)) return -1; sbuf_free(&sb); return 0; default: return -1; } just_opened=1; } switch(blks_generate(sb, slist->blist, just_opened)) { case 0: // All OK. break; case 1: // File ended. if(rabin_close_file(sb, asfd)) { logp("Failed to close file %s\n", iobuf_to_printable(&sb->path)); return -1; } slist->last_requested=sb->next; break; default: return -1; } return 0; } static void free_stuff(struct slist *slist) { struct blk *blk; struct blist *blist=slist->blist; blk=blist->head; while(blk && blk!=blist->last_sent) { if(blk==slist->head->protocol2->bstart) slist->head->protocol2->bstart=NULL; if(blk==slist->head->protocol2->bend) { struct sbuf *sb; sb=slist->head; sb->protocol2->bend=NULL; slist->head=slist->head->next; if(!slist->head) slist->tail=NULL; sbuf_free(&sb); } blk=blk->next; blk_free(&blist->head); blist->head=blk; } } static void get_wbuf_from_data(struct conf **confs, struct iobuf *wbuf, struct slist *slist, uint8_t end_flags) { struct blk *blk; struct blist *blist=slist->blist; for(blk=blist->last_sent; blk; blk=blk->next) { if(blk->requested) { iobuf_set(wbuf, CMD_DATA, blk->data, blk->length); blk->requested=0; blist->last_sent=blk; cntr_add(get_cntr(confs), CMD_DATA, 1); break; } else { cntr_add_same(get_cntr(confs), CMD_DATA); if(end_flags&END_BLK_REQUESTS) { // Force onwards when the server has said that // there are no more blocks to request. blist->last_sent=blk; continue; } } if(blk==blist->last_requested) break; } // Need to free stuff that is no longer needed. free_stuff(slist); } static int iobuf_from_blk_data(struct iobuf *wbuf, struct blk *blk) { if(blk_md5_update(blk)) return -1; blk_to_iobuf_sig(blk, wbuf); return 0; } static int get_wbuf_from_blks(struct iobuf *wbuf, struct slist *slist, uint8_t *end_flags) { struct sbuf *sb=slist->blks_to_send; if(!sb) { if((*end_flags)&END_REQUESTS && !((*end_flags)&END_SIGS)) { iobuf_from_str(wbuf, CMD_GEN, (char *)"sigs_end"); (*end_flags)|=END_SIGS; } return 0; } if(!sb->protocol2->bsighead) return 0; if(!(sb->flags & SBUF_SENT_STAT)) { iobuf_copy(wbuf, &sb->attr); wbuf->cmd=CMD_ATTRIBS_SIGS; // hack sb->flags |= SBUF_SENT_STAT; return 0; } if(iobuf_from_blk_data(wbuf, sb->protocol2->bsighead)) return -1; // Move on. if(sb->protocol2->bsighead==sb->protocol2->bend) { slist->blks_to_send=sb->next; sb->protocol2->bsighead=sb->protocol2->bstart; } else { sb->protocol2->bsighead=sb->protocol2->bsighead->next; } return 0; } int backup_phase2_client_protocol2(struct asfd *asfd, struct conf **confs, int resume) { int ret=-1; uint8_t end_flags=0; struct slist *slist=NULL; struct iobuf *rbuf=NULL; struct iobuf *wbuf=NULL; struct cntr *cntr=NULL; if(confs) cntr=get_cntr(confs); if(!asfd || !asfd->as) { logp("%s() called without async structs!\n", __func__); goto end; } logp("Phase 2 begin (send backup data)\n"); logfmt("\n"); if(!(slist=slist_alloc()) || !(wbuf=iobuf_alloc()) || blks_generate_init()) goto end; rbuf=asfd->rbuf; if(!resume) { // Only do this bit if the server did not tell us to resume. if(asfd->write_str(asfd, CMD_GEN, "backupphase2") || asfd_read_expect(asfd, CMD_GEN, "ok")) goto end; } else { // On resume, the server might update the client with cntr. if(cntr_recv(asfd, confs)) goto end; } while(!(end_flags&END_BACKUP)) { if(!wbuf->len) { get_wbuf_from_data(confs, wbuf, slist, end_flags); if(!wbuf->len) { if(get_wbuf_from_blks(wbuf, slist, &end_flags)) goto end; } } if(wbuf->len) { if(asfd->append_all_to_write_buffer(asfd, wbuf) ==APPEND_ERROR) goto end; } if(asfd->as->read_write(asfd->as)) { logp("error in %s\n", __func__); goto end; } if(rbuf->buf && deal_with_read(rbuf, slist, cntr, &end_flags)) goto end; if(slist->head // Need to limit how many blocks are allocated at once. && (!slist->blist->head || slist->blist->tail->index - slist->blist->head->indexhead==slist->tail) { if(!slist->tail || slist->blist->last_sent== slist->tail->protocol2->bend) { if(!wbuf->len) break; } } } } if(asfd->write_str(asfd, CMD_GEN, "backup_end")) goto end; ret=0; end: slist_free(&slist); blks_generate_free(); if(wbuf) { // Write buffer did not allocate 'buf'. wbuf->buf=NULL; iobuf_free(&wbuf); } cntr_print_end(cntr); cntr_set_bytes(cntr, asfd); cntr_print(cntr, ACTION_BACKUP); if(ret) logp("Error in backup\n"); logp("End backup\n"); return ret; } burp-2.4.0/src/client/protocol2/backup_phase2.h000066400000000000000000000002511404341324700213350ustar00rootroot00000000000000#ifndef _BACKUP_CLIENT_PHASE2_H #define _BACKUP_CLIENT_PHASE2_H extern int backup_phase2_client_protocol2(struct asfd *asfd, struct conf **confs, int resume); #endif burp-2.4.0/src/client/protocol2/rabin_read.c000066400000000000000000000052561404341324700207210ustar00rootroot00000000000000#include "../../burp.h" #include "../../alloc.h" #include "../../attribs.h" #include "../../bfile.h" #include "../../cntr.h" #include "../../log.h" #include "../../sbuf.h" #include "../extrameta.h" #include "rabin_read.h" static char *meta_buffer=NULL; static size_t meta_buffer_len=0; static char *mp=NULL; static int rabin_close_file_extrameta(struct sbuf *sb) { free_w(&meta_buffer); meta_buffer_len=0; mp=NULL; sb->protocol2->bfd.mode=BF_CLOSED; return 0; } // Return -1 for error, 0 for could not get data, 1 for success. static int rabin_open_file_extrameta(struct sbuf *sb, struct asfd *asfd, struct cntr *cntr) { // Load all of the metadata into a buffer. rabin_close_file_extrameta(sb); if(get_extrameta(asfd, #ifdef HAVE_WIN32 NULL, #endif sb->path.buf, S_ISDIR(sb->statp.st_mode), &meta_buffer, &meta_buffer_len, cntr)) return -1; if(!meta_buffer) return 0; mp=meta_buffer; sb->protocol2->bfd.mode=BF_READ; return 1; } static ssize_t rabin_read_extrameta(char *buf, size_t bufsize) { // Place bufsize of the meta buffer contents into buf. size_t to_read=meta_buffer_len; if(!meta_buffer_len) return 0; if(bufsizeprotocol2->bfd; #ifdef HAVE_WIN32 if(win32_lstat(sb->path.buf, &sb->statp, &sb->winattr)) #else if(lstat(sb->path.buf, &sb->statp)) #endif { // This file is no longer available. logw(asfd, cntr, "%s has vanished\n", iobuf_to_printable(&sb->path)); return 0; } sb->compression=get_int(confs[OPT_COMPRESSION]); // Encryption not yet implemented in protocol2. //sb->encryption=conf->protocol2->encryption_password?1:0; sb->encryption=ENCRYPTION_NONE; if(attribs_encode(sb)) return -1; if(sbuf_is_metadata(sb)) return rabin_open_file_extrameta(sb, asfd, cntr); if(bfd->open_for_send(bfd, asfd, sb->path.buf, sb->winattr, get_int(confs[OPT_ATIME]), cntr, PROTO_2)) { logw(asfd, get_cntr(confs), "Could not open %s\n", iobuf_to_printable(&sb->path)); return 0; } return 1; } int rabin_close_file(struct sbuf *sb, struct asfd *asfd) { struct BFILE *bfd; if(sbuf_is_metadata(sb)) return rabin_close_file_extrameta(sb); bfd=&sb->protocol2->bfd; return bfd->close(bfd, asfd); } ssize_t rabin_read(struct sbuf *sb, char *buf, size_t bufsize) { struct BFILE *bfd; if(sbuf_is_metadata(sb)) return rabin_read_extrameta(buf, bufsize); bfd=&sb->protocol2->bfd; return (ssize_t)bfd->read(bfd, buf, bufsize); } burp-2.4.0/src/client/protocol2/rabin_read.h000066400000000000000000000004461404341324700207220ustar00rootroot00000000000000#ifndef _RABIN_READ_H #define _RABIN_READ_H extern int rabin_open_file(struct sbuf *sb, struct asfd *asfd, struct cntr *cntr, struct conf **confs); extern int rabin_close_file(struct sbuf *sb, struct asfd *asfd); extern ssize_t rabin_read(struct sbuf *sb, char *buf, size_t bufsize); #endif burp-2.4.0/src/client/protocol2/restore.c000066400000000000000000000134201404341324700203060ustar00rootroot00000000000000#include "../../burp.h" #include "../../alloc.h" #include "../../asfd.h" #include "../../async.h" #include "../../attribs.h" #include "../../bfile.h" #include "../../cmd.h" #include "../../cntr.h" #include "../../fsops.h" #include "../../log.h" #include "../../sbuf.h" #include "../extrameta.h" #include "../restore.h" #include "restore.h" int write_protocol2_data(struct asfd *asfd, struct BFILE *bfd, struct blk *blk, enum vss_restore vss_restore) { if(bfd->mode==BF_CLOSED) logp("Got data without an open file\n"); else { int w; if((w=bfd->write(bfd, blk->data, blk->length))<=0) { logp("%s(): error when appending %d: %d\n", __func__, blk->length, w); asfd->write_str(asfd, CMD_ERROR, "write failed"); return -1; } } return 0; } static int start_restore_file(struct asfd *asfd, struct BFILE *bfd, struct sbuf *sb, const char *fname, enum action act, enum vss_restore vss_restore, struct cntr *cntr) { int ret=-1; char *rpath=NULL; if(act==ACTION_VERIFY) { cntr_add(cntr, sb->path.cmd, 1); goto end; } if(build_path(fname, "", &rpath, NULL)) { char msg[256]=""; // Failed - do a warning. snprintf(msg, sizeof(msg), "build path failed: %s", fname); if(restore_interrupt(asfd, sb, msg, cntr, PROTO_2)) goto error; goto end; // Try to carry on with other files. } switch(open_for_restore(asfd, bfd, rpath, sb, vss_restore, cntr, PROTO_2)) { case OFR_OK: break; case OFR_CONTINUE: goto end; default: goto error; } cntr_add(cntr, sb->path.cmd, 1); end: ret=0; error: free_w(&rpath); return ret; } static int get_meta( struct asfd *asfd, struct cntr *cntr, char **metadata, size_t *metalen) { int ret=-1; struct iobuf *rbuf=asfd->rbuf; while(1) { iobuf_free_content(rbuf); if(asfd->read(asfd)) goto end; switch(rbuf->cmd) { case CMD_DATA: if(!(*metadata=(char *)realloc_w(*metadata, (*metalen)+rbuf->len, __func__))) goto end; memcpy((*metadata)+(*metalen), rbuf->buf, rbuf->len); *metalen+=rbuf->len; break; case CMD_END_FILE: ret=0; goto end; case CMD_MESSAGE: case CMD_WARNING: log_recvd(rbuf, cntr, 0); break; default: iobuf_log_unexpected(rbuf, __func__); goto end; } } end: iobuf_free_content(rbuf); return ret; } static int restore_metadata( struct asfd *asfd, struct sbuf *sb, const char *fname, enum action act, struct cntr *cntr) { // If it is directory metadata, try to make sure the directory // exists. Pass in NULL as the cntr, so no counting is done. // The actual directory entry will be coming after the metadata, // annoyingly. This is because of the way that the server is queuing // up directories to send after file data, so that the stat info on // them gets set correctly. if(act==ACTION_RESTORE) { size_t metalen=0; char *metadata=NULL; if(S_ISDIR(sb->statp.st_mode) && restore_dir(asfd, sb, fname, act, /*cntr*/NULL, PROTO_2)) return -1; // Read in the metadata... if(get_meta(asfd, cntr, &metadata, &metalen)) return -1; if(metadata) { if(set_extrameta(asfd, #ifdef HAVE_WIN32 NULL, #endif fname, metadata, metalen, cntr)) { free_w(&metadata); // carry on if we could not do it return 0; } free_w(&metadata); #ifndef HAVE_WIN32 // Set file times again, since we just diddled with the // file. Do not set all attributes, as it will wipe // out any security attributes (eg getcap /usr/bin/ping) if(attribs_set_file_times(asfd, fname, &sb->statp, cntr)) return -1; #endif cntr_add(cntr, sb->path.cmd, 1); } } else cntr_add(cntr, sb->path.cmd, 1); return 0; } static int unsupported_interrupt_and_warn( struct asfd *asfd, struct sbuf *sb, struct cntr *cntr, const char *fname, enum action act ) { char msg[256]=""; snprintf(msg, sizeof(msg), "restore not yet supported for %s: %s", cmd_to_text(sb->path.cmd), fname); switch(act) { case ACTION_RESTORE: if(restore_interrupt(asfd, sb, msg, cntr, PROTO_2)) return -1; break; default: if(cntr) { cntr_add(cntr, CMD_WARNING, 1); logp("WARNING: %s\n", msg); if(asfd->write_str(asfd, CMD_WARNING, msg)) return -1; } break; } return 0; // Try to carry on with other files. } int restore_switch_protocol2(struct asfd *asfd, struct sbuf *sb, const char *fullpath, enum action act, struct BFILE *bfd, enum vss_restore vss_restore, struct cntr *cntr) { switch(sb->path.cmd) { case CMD_FILE: // Have it a separate statement to the // encrypted version so that encrypted and not // encrypted files can be restored at the // same time. if(start_restore_file(asfd, bfd, sb, fullpath, act, vss_restore, cntr)) { logp("restore_file error\n"); goto error; } break; case CMD_ENC_FILE: /* FIX THIS: Encryption currently not working in protocol2 if(start_restore_file(asfd, bfd, sb, fullpath, act, vss_restore, confs)) { logp("restore_file error\n"); goto error; } */ if(unsupported_interrupt_and_warn( asfd, sb, cntr, fullpath, act)) return -1; break; case CMD_METADATA: if(restore_metadata(asfd, sb, fullpath, act, cntr)) goto error; break; case CMD_ENC_METADATA: /* FIX THIS: Encryption not supported yet. if(restore_metadata( bfd, sb, fullpath, act, confs)) goto error; */ if(unsupported_interrupt_and_warn( asfd, sb, cntr, fullpath, act)) return -1; break; case CMD_EFS_FILE: /* FIX THIS: EFS not supported yet. if(start_restore_file(asfd, bfd, sb, fullpath, act, vss_restore, confs)) { logp("restore_file error\n"); goto error; } */ if(unsupported_interrupt_and_warn( asfd, sb, cntr, fullpath, act)) return -1; break; default: logp("unknown cmd: %c\n", sb->path.cmd); goto error; } return 0; error: return -1; } burp-2.4.0/src/client/protocol2/restore.h000066400000000000000000000006651404341324700203220ustar00rootroot00000000000000#ifndef _RESTORE_CLIENT_PROTOCOL2_H #define _RESTORE_CLIENT_PROTOCOL2_H struct sbuf; #include "../../protocol2/blk.h" extern int write_protocol2_data(struct asfd *asfd, struct BFILE *bfd, struct blk *blk, enum vss_restore vss_restore); extern int restore_switch_protocol2(struct asfd *asfd, struct sbuf *sb, const char *fullpath, enum action act, struct BFILE *bfd, enum vss_restore vss_restore, struct cntr *cntr); #endif burp-2.4.0/src/client/restore.c000066400000000000000000000547131404341324700163750ustar00rootroot00000000000000#include "../burp.h" #include "../alloc.h" #include "../asfd.h" #include "../async.h" #include "../attribs.h" #include "../berrno.h" #include "../cmd.h" #include "../cntr.h" #include "../fsops.h" #include "../handy.h" #include "../pathcmp.h" #include "../log.h" #include "../prepend.h" #include "../protocol2/blk.h" #include "cvss.h" #include "protocol1/restore.h" #include "protocol2/restore.h" #include "restore.h" int restore_interrupt(struct asfd *asfd, struct sbuf *sb, const char *msg, struct cntr *cntr, enum protocol protocol) { int ret=-1; char *path=NULL; struct iobuf *rbuf=asfd->rbuf; if(cntr) { cntr_add(cntr, CMD_WARNING, 1); logp("WARNING: %s\n", msg); if(asfd->write_str(asfd, CMD_WARNING, msg)) goto end; } if(!iobuf_is_filedata(&sb->path) && !iobuf_is_vssdata(&sb->path)) { // Do not need to do anything. ret=0; goto end; } // If it is file data, get the server // to interrupt the flow and move on. if(protocol==PROTO_1) path=sb->protocol1->datapth.buf; else if(protocol==PROTO_2) path=sb->path.buf; if(!path) { ret=0; goto end; } if(asfd->write_str(asfd, CMD_INTERRUPT, path)) goto end; // Read to the end file marker. while(1) { iobuf_free_content(rbuf); if(asfd->read(asfd)) goto end; // Error. if(!rbuf->len) continue; switch(rbuf->cmd) { case CMD_APPEND: case CMD_DATA: continue; case CMD_END_FILE: ret=0; goto end; default: iobuf_log_unexpected(rbuf, __func__); goto end; } } end: iobuf_free_content(rbuf); return ret; } static int make_link( #ifdef HAVE_WIN32 struct asfd *asfd, struct cntr *cntr, #endif const char *fname, const char *lnk, enum cmd cmd, const char *restore_desired_dir) { int ret=-1; #ifdef HAVE_WIN32 logw(asfd, cntr, "windows seems not to support hardlinks or symlinks\n"); #else unlink(fname); if(cmd==CMD_HARD_LINK) { char *flnk=NULL; if(!(flnk=prepend_s(restore_desired_dir, lnk))) { log_out_of_memory(__func__); return -1; } //printf("%s -> %s\n", fname, flnk); ret=link(flnk, fname); free_w(&flnk); } else if(cmd==CMD_SOFT_LINK) { //printf("%s -> %s\n", fname, lnk); ret=symlink(lnk, fname); } else { logp("unexpected link command: %c\n", cmd); ret=-1; } #endif if(ret) logp("could not %slink %s -> %s: %s\n", cmd==CMD_HARD_LINK?"hard":"sym", fname, lnk, strerror(errno)); return ret; } // FIX THIS: Maybe should be in bfile.c. enum ofr_e open_for_restore(struct asfd *asfd, struct BFILE *bfd, const char *path, struct sbuf *sb, enum vss_restore vss_restore, struct cntr *cntr, enum protocol protocol) { static int flags; if(bfd->mode!=BF_CLOSED) { #ifdef HAVE_WIN32 if(bfd->path && !strcmp(bfd->path, path)) { // Already open after restoring the VSS data. // Time now for the actual file data. return OFR_OK; } else { #endif if(bfd->close(bfd, asfd)) { logp("error closing %s in %s()\n", path, __func__); return OFR_ERROR; } #ifdef HAVE_WIN32 } #endif } #ifdef HAVE_WIN32 // Some massive hacks to work around times that winattr was not // getting set correctly inside server side backups. // The EFS one will stop burp segfaulting when restoring affected // EFS files. if(sb->path.cmd==CMD_EFS_FILE) sb->winattr |= FILE_ATTRIBUTE_ENCRYPTED; if(S_ISDIR(sb->statp.st_mode)) sb->winattr |= FILE_ATTRIBUTE_DIRECTORY; #endif bfile_init(bfd, sb->winattr, cntr); bfd->set_attribs_on_close=1; switch(vss_restore) { case VSS_RESTORE_OFF: #ifdef HAVE_WIN32 bfd->set_win32_api(bfd, 0); #endif bfd->set_vss_strip(bfd, 0); break; case VSS_RESTORE_OFF_STRIP: #ifdef HAVE_WIN32 bfd->set_win32_api(bfd, 0); #endif bfd->set_vss_strip(bfd, 1); break; case VSS_RESTORE_ON: #ifdef HAVE_WIN32 bfd->set_win32_api(bfd, 1); #endif bfd->set_vss_strip(bfd, 0); break; } flags=O_WRONLY|O_BINARY #ifdef O_NOFOLLOW |O_NOFOLLOW #endif ; if(S_ISDIR(sb->statp.st_mode)) { // Windows directories are treated as having file data. mkdir(path, 0777); } else { flags|=O_CREAT|O_TRUNC; // Unlink first, so that a new file is created instead of // overwriting an existing file in place. Should be safer in // cases where the old file was hardlinked everywhere. if(unlink(path) && errno!=ENOENT) { char msg[256]=""; snprintf(msg, sizeof(msg), "Cannot unlink before restore: '%s': %s", path, strerror(errno)); if(restore_interrupt(asfd, sb, msg, cntr, protocol)) return OFR_ERROR; return OFR_CONTINUE; } } if(bfd->open(bfd, asfd, path, flags, S_IRUSR | S_IWUSR)) { struct berrno be; berrno_init(&be); char msg[256]=""; snprintf(msg, sizeof(msg), "Could not open for writing %s: %s", path, berrno_bstrerror(&be, errno)); if(restore_interrupt(asfd, sb, msg, cntr, protocol)) return OFR_ERROR; return OFR_CONTINUE; } // Add attributes to bfd so that they can be set when it is closed. bfd->winattr=sb->winattr; memcpy(&bfd->statp, &sb->statp, sizeof(struct stat)); return OFR_OK; } static char *build_msg(const char *text, const char *param) { static char msg[256]=""; snprintf(msg, sizeof(msg), text, param); return msg; } #ifndef HAVE_WIN32 static void do_logw(struct asfd *asfd, struct cntr *cntr, const char *text, const char *param) { logw(asfd, cntr, "%s", build_msg(text, param)); } #endif static int warn_and_interrupt(struct asfd *asfd, struct sbuf *sb, struct cntr *cntr, enum protocol protocol, const char *text, const char *param) { return restore_interrupt(asfd, sb, build_msg(text, param), cntr, protocol); } static int restore_special(struct asfd *asfd, struct sbuf *sb, const char *fname, enum action act, struct cntr *cntr, enum protocol protocol) { int ret=0; char *rpath=NULL; #ifdef HAVE_WIN32 logw(asfd, cntr, "Cannot restore special files to Windows: %s\n", fname); goto end; #else struct stat statp=sb->statp; if(act==ACTION_VERIFY) { cntr_add(cntr, CMD_SPECIAL, 1); return 0; } if(build_path(fname, "", &rpath, NULL)) { // failed - do a warning if(restore_interrupt(asfd, sb, build_msg("build path failed: %s", fname), cntr, protocol)) ret=-1; goto end; } if(S_ISFIFO(statp.st_mode)) { if(mkfifo(rpath, statp.st_mode) && errno!=EEXIST) do_logw(asfd, cntr, "Cannot make fifo: %s\n", strerror(errno)); else { attribs_set(asfd, rpath, &statp, sb->winattr, cntr); cntr_add(cntr, CMD_SPECIAL, 1); } } else if(S_ISSOCK(statp.st_mode)) { if(mksock(rpath)) do_logw(asfd, cntr, "Cannot make socket: %s\n", strerror(errno)); else { attribs_set(asfd, rpath, &statp, sb->winattr, cntr); cntr_add(cntr, CMD_SPECIAL, 1); } } #ifdef S_IFDOOR // Solaris high speed RPC mechanism else if (S_ISDOOR(statp.st_mode)) do_logw(asfd, cntr, "Skipping restore of door file: %s\n", fname); #endif #ifdef S_IFPORT // Solaris event port for handling AIO else if (S_ISPORT(statp.st_mode)) do_logw(asfd, cntr, "Skipping restore of event port file: %s\n", fname); #endif else if(mknod(fname, statp.st_mode, statp.st_rdev) && errno!=EEXIST) do_logw(asfd, cntr, "Cannot make node: %s\n", strerror(errno)); else { attribs_set(asfd, rpath, &statp, sb->winattr, cntr); cntr_add(cntr, CMD_SPECIAL, 1); } #endif end: free_w(&rpath); return ret; } int restore_dir(struct asfd *asfd, struct sbuf *sb, const char *dname, enum action act, struct cntr *cntr, enum protocol protocol) { int ret=0; char *rpath=NULL; if(act==ACTION_RESTORE) { if(build_path(dname, "", &rpath, NULL)) { ret=warn_and_interrupt(asfd, sb, cntr, protocol, "build path failed: %s", dname); goto end; } else if(is_dir_lstat(rpath)<=0) { if(mkdir(rpath, 0777)) { ret=warn_and_interrupt(asfd, sb, cntr, protocol, "mkdir error: %s", strerror(errno)); goto end; } } attribs_set(asfd, rpath, &(sb->statp), sb->winattr, cntr); if(!ret) cntr_add(cntr, sb->path.cmd, 1); } else cntr_add(cntr, sb->path.cmd, 1); end: free_w(&rpath); return ret; } static int restore_link(struct asfd *asfd, struct sbuf *sb, const char *fname, enum action act, struct cntr *cntr, enum protocol protocol, const char *restore_desired_dir) { int ret=0; if(act==ACTION_RESTORE) { char *rpath=NULL; if(build_path(fname, "", &rpath, NULL)) { ret=warn_and_interrupt(asfd, sb, cntr, protocol, "build path failed: %s", fname); goto end; } else if(make_link( #ifdef HAVE_WIN32 asfd, cntr, #endif fname, sb->link.buf, sb->link.cmd, restore_desired_dir)) { ret=warn_and_interrupt(asfd, sb, cntr, protocol, "could not create link", ""); goto end; } else if(!ret) { attribs_set(asfd, fname, &(sb->statp), sb->winattr, cntr); cntr_add(cntr, sb->path.cmd, 1); } free_w(&rpath); } else cntr_add(cntr, sb->path.cmd, 1); end: return ret; } static void strip_invalid_characters(char **path) { #ifdef HAVE_WIN32 char *ch = *path; if (ch[0] != 0 && ch[1] != 0) { ch += 2; while (*ch) { switch (*ch) { case ':': case '<': case '>': case '*': case '?': case '|': *ch = '_'; break; } ch++; } } #endif } static const char *act_str(enum action act) { static const char *ret=NULL; if(act==ACTION_RESTORE) ret="restore"; else ret="verify"; return ret; } #ifndef UTEST static #endif void strip_from_path(char *path, const char *strip) { char *p; char *src; size_t len; if(!path || !strip || strlen(path)<=strlen(strip) || !(p=strstr(path, strip))) return; len=strlen(p)-strlen(strip)+1; src=p+strlen(strip); memmove(p, src, len); } // Return 1 for ok, -1 for error, 0 for too many components stripped. static int strip_path_components(struct asfd *asfd, struct sbuf *sb, int strip, struct cntr *cntr, enum protocol protocol) { int s=0; char *tmp=NULL; char *cp=sb->path.buf; char *dp=NULL; for(s=0; cp && *cp && spath)); if(restore_interrupt(asfd, sb, msg, cntr, protocol)) return -1; return 0; } cp=dp+1; } if(!cp) { char msg[256]=""; snprintf(msg, sizeof(msg), "Stripped too many components: %s", iobuf_to_printable(&sb->path)); if(restore_interrupt(asfd, sb, msg, cntr, protocol)) return -1; return 0; } if(!(tmp=strdup_w(cp, __func__))) return -1; free_w(&sb->path.buf); sb->path.buf=tmp; return 1; } static int overwrite_ok(struct sbuf *sb, int overwrite, #ifdef HAVE_WIN32 struct BFILE *bfd, #endif const char *fullpath) { struct stat checkstat; // User specified overwrite is OK. #ifdef HAVE_WIN32 if(overwrite) return 1; #else // User specified overwrite is OK, // UNLESS we are trying to overwrite the file with trailing VSS data. if(overwrite) return (sb->path.cmd!=CMD_VSS_T && sb->path.cmd!=CMD_ENC_VSS_T); #endif if(!S_ISDIR(sb->statp.st_mode) && sb->path.cmd!=CMD_METADATA && sb->path.cmd!=CMD_ENC_METADATA && sb->path.cmd!=CMD_VSS && sb->path.cmd!=CMD_ENC_VSS) { #ifdef HAVE_WIN32 // If Windows previously got some VSS data, it needs to append // the file data to the already open bfd. // And trailing VSS data. if(bfd->mode!=BF_CLOSED && (sb->path.cmd==CMD_FILE || sb->path.cmd==CMD_ENC_FILE || sb->path.cmd==CMD_VSS_T || sb->path.cmd==CMD_ENC_VSS_T) && bfd->path && !strcmp(bfd->path, fullpath)) return 1; #endif // If we have file data and the destination is // a fifo, it is OK to write to the fifo. if((sb->path.cmd==CMD_FILE || sb->path.cmd==CMD_ENC_FILE) && S_ISFIFO(sb->statp.st_mode)) return 1; // File path exists. Do not overwrite. if(!lstat(fullpath, &checkstat)) return 0; } return 1; } #define RESTORE_STREAM "restore_stream" // Used to have "restore_spool". Removed for simplicity. static char *restore_style=NULL; static enum asl_ret restore_style_func(struct asfd *asfd, __attribute__ ((unused)) struct conf **confs, __attribute__ ((unused)) void *param) { char msg[32]=""; restore_style=NULL; if(strcmp(asfd->rbuf->buf, RESTORE_STREAM)) { iobuf_log_unexpected(asfd->rbuf, __func__); return ASL_END_ERROR; } snprintf(msg, sizeof(msg), "%s_ok", asfd->rbuf->buf); if(asfd->write_str(asfd, CMD_GEN, msg)) return ASL_END_ERROR; restore_style=asfd->rbuf->buf; iobuf_init(asfd->rbuf); return ASL_END_OK; } static char *get_restore_style(struct asfd *asfd, struct conf **confs) { if(get_protocol(confs)==PROTO_1) return strdup_w(RESTORE_STREAM, __func__); if(asfd->simple_loop(asfd, confs, NULL, __func__, restore_style_func)) return NULL; return restore_style; } #ifdef HAVE_WIN32 #ifndef PATH_MAX #define PATH_MAX _MAX_PATH #endif #endif static char *get_restore_desired_dir( const char *restoreprefix, struct asfd *asfd, struct cntr *cntr ) { char *ret=NULL; char *path=NULL; if( #ifdef HAVE_WIN32 isalpha(*restoreprefix) && *(restoreprefix+1)==':' #else *restoreprefix=='/' #endif ) { if(!(path=strdup_w(restoreprefix, __func__))) return NULL; } else { static char d[PATH_MAX]; if(!getcwd(d, sizeof(d))) { logw(asfd, cntr, "Could not get current working directory: %s\n", strerror(errno)); return NULL; } if(!(path=prepend_s(d, restoreprefix))) return NULL; } // Canonicalise the path so that we can protect against symlinks that // point outsired of the desired restore directory. if((ret=realpath(path, NULL))) goto end; if(errno!=ENOENT) goto realpath_error; // Try to create the directory if it did not exist, then try again. mkdir(path, 0777); if(!(ret=realpath(path, NULL))) goto realpath_error; end: free_w(&path); return ret; realpath_error: logp("%s: Could not get realpath in %s: %s\n", path, __func__, strerror(errno)); free_w(&path); return NULL; } // Seems that windows has no dirname(), so do something similar instead. static int strip_trailing_component(char **path) { char *cp=NULL; if(**path=='/' && !*((*path)+1)) return -1; if(!(cp=strrchr(*path, '/'))) return -1; if(*path==cp) *(cp+1)='\0'; // Deal with '/somepath' in root, gives '/'. else *cp='\0'; // Deal with '/some/path', gives '/some'. return 0; } static int canonicalise( struct asfd *asfd, struct sbuf *sb, struct cntr *cntr, enum protocol protocol, const char *restore_desired_dir, char **fullpath ) { int ret=-1; char *tmp=NULL; char *copy=NULL; char *canonical=NULL; if(!(copy=strdup_w(*fullpath, __func__))) goto end; // The realpath function does not work on entries that do not exist, // so we have to do complicated things. while(1) { if(strip_trailing_component(©)) { char msg[512]=""; snprintf(msg, sizeof(msg), "%s: Could not get dirname of '%s'", *fullpath, copy); if(restore_interrupt(asfd, sb, msg, cntr, protocol)) goto end; ret=1; goto end; } if((canonical=realpath(copy, NULL))) break; if(errno!=ENOENT) { char msg[512]=""; snprintf(msg, sizeof(msg), "%s: Could not get realpath of %s in %s: %s", *fullpath, copy, __func__, strerror(errno)); if(restore_interrupt(asfd, sb, msg, cntr, protocol)) goto end; ret=1; goto end; } } // Protect against malicious servers trying to install a symlink and // then files over the top of it to directories outside of the // desired directory. if(!is_subdir(restore_desired_dir, canonical)) { char msg[512]=""; snprintf(msg, sizeof(msg), "%s: Is not in a subdir of '%s'", *fullpath, restore_desired_dir); if(restore_interrupt(asfd, sb, msg, cntr, protocol)) goto end; ret=1; goto end; } // Add the trailing content back onto the canonical path. if(!(tmp=prepend_s(canonical, (*fullpath)+strlen(copy)))) goto end; free_w(fullpath); *fullpath=tmp; ret=0; end: // Cannot use free_w() because it was not allocated by alloc.c, and // I cannot implement realpath() it in alloc.c because I cannot get // Windows code to use alloc.c. if(canonical) free(canonical); free_w(©); return ret; } int do_restore_client(struct asfd *asfd, struct conf **confs, enum action act) { int ret=-1; char msg[512]=""; struct sbuf *sb=NULL; struct blk *blk=NULL; struct BFILE *bfd=NULL; char *fullpath=NULL; char *style=NULL; char *restore_desired_dir=NULL; struct cntr *cntr=get_cntr(confs); enum protocol protocol=get_protocol(confs); int strip=get_int(confs[OPT_STRIP]); int overwrite=get_int(confs[OPT_OVERWRITE]); const char *strip_path=get_string(confs[OPT_STRIP_FROM_PATH]); const char *backup=get_string(confs[OPT_BACKUP]); const char *regex=get_string(confs[OPT_REGEX]); const char *encryption_password= get_string(confs[OPT_ENCRYPTION_PASSWORD]); enum vss_restore vss_restore= (enum vss_restore)get_int(confs[OPT_VSS_RESTORE]); const char *restore_list=get_string(confs[OPT_RESTORE_LIST]); if(act==ACTION_RESTORE) { const char *restore_prefix=get_string(confs[OPT_RESTOREPREFIX]); if(!restore_prefix) { logw(NULL, cntr, "You must specify a restore directory (-d)!\n"); goto error; } if(!strcmp(restore_prefix, "/")) { // Special case to try to help Windows users that are // trying to do "bare metal" restores. Let them give // '/' as the restore prefix, and have it mean that // everything gets restored back to the original // locations (this would work on Linux *without* this // special case anyway, but hey-ho). } else if(!(restore_desired_dir=get_restore_desired_dir( restore_prefix, asfd, cntr))) goto error; } if(!(bfd=bfile_alloc())) goto error; bfile_init(bfd, 0, cntr); bfd->set_attribs_on_close=1; snprintf(msg, sizeof(msg), "%s%s %s:%s", act_str(act), restore_list?" restore_list":"", backup?backup:"", regex?regex:""); logp("Doing %s\n", msg); if(asfd->write_str(asfd, CMD_GEN, msg)) goto error; if(restore_list) { if(!strcmp(restore_list, "-")) restore_list="/dev/stdin"; logp("Reading from: '%s'\n", restore_list); if(asfd_read_expect(asfd, CMD_GEN, "ok restore_list")) goto error; if(send_a_file(asfd, restore_list, cntr)) goto error; } else { if(asfd_read_expect(asfd, CMD_GEN, "ok")) goto error; } logp("Doing %s confirmed\n", act_str(act)); if(act==ACTION_RESTORE) logp("Directory: '%s'\n", restore_desired_dir ? restore_desired_dir : "/"); #if defined(HAVE_WIN32) if(act==ACTION_RESTORE) win32_enable_backup_privileges(); #endif logfmt("\n"); if(cntr_recv(asfd, confs)) goto error; if(!(style=get_restore_style(asfd, confs))) goto error; if(!(sb=sbuf_alloc(protocol)) || (protocol==PROTO_2 && !(blk=blk_alloc()))) { log_and_send_oom(asfd); goto error; } while(1) { sbuf_free_content(sb); if(protocol==PROTO_1) sb->flags |= SBUF_CLIENT_RESTORE_HACK; switch(sbuf_fill_from_net(sb, asfd, blk, cntr)) { case 0: break; case 1: if(asfd->write_str(asfd, CMD_GEN, "restoreend ok")) goto error; goto end; // It was OK. default: case -1: goto error; } if(protocol==PROTO_2) { if(blk->data) { int wret=0; if(act==ACTION_VERIFY) cntr_add(cntr, CMD_DATA, 1); else wret=write_protocol2_data(asfd, bfd, blk, vss_restore); blk_free_content(blk); blk->data=NULL; if(wret) goto error; continue; } else if(sb->endfile.buf) { continue; } } switch(sb->path.cmd) { case CMD_DIRECTORY: case CMD_FILE: case CMD_ENC_FILE: case CMD_SOFT_LINK: case CMD_HARD_LINK: case CMD_SPECIAL: case CMD_METADATA: case CMD_ENC_METADATA: case CMD_VSS: case CMD_ENC_VSS: case CMD_VSS_T: case CMD_ENC_VSS_T: case CMD_EFS_FILE: if(strip) { int s; s=strip_path_components(asfd, sb, strip, cntr, protocol); if(s<0) goto error; if(s==0) { // Too many components stripped // - carry on. continue; } // It is OK, sb.path is now stripped. } if(strip_path) { strip_from_path(sb->path.buf, strip_path); // Strip links if their path is absolute if(sb->link.buf && !is_absolute(sb->link.buf)) strip_from_path(sb->link.buf, strip_path); } free_w(&fullpath); if(!(fullpath=prepend_s(restore_desired_dir, sb->path.buf))) { log_and_send_oom(asfd); goto error; } if(act==ACTION_RESTORE) { strip_invalid_characters(&fullpath); // canonicalise will fail on Windows split_vss // restores if we do not make sure bfd is // closed first. if(bfd && bfd->mode!=BF_CLOSED && bfd->path && strcmp(bfd->path, fullpath)) bfd->close(bfd, asfd); if(restore_desired_dir) { switch(canonicalise( asfd, sb, cntr, protocol, restore_desired_dir, &fullpath )) { case 0: break; case 1: continue; default: goto error; } } if(!overwrite_ok(sb, overwrite, #ifdef HAVE_WIN32 bfd, #endif fullpath)) { char msg[512]=""; // Something exists at that path. snprintf(msg, sizeof(msg), "Path exists: %s\n", fullpath); if(restore_interrupt(asfd, sb, msg, cntr, protocol)) goto error; continue; } } break; case CMD_MESSAGE: case CMD_WARNING: log_recvd(&sb->path, cntr, 1); logfmt("\n"); continue; default: break; } switch(sb->path.cmd) { // These are the same in both protocol1 and protocol2. case CMD_DIRECTORY: if(restore_dir(asfd, sb, fullpath, act, cntr, protocol)) goto error; continue; case CMD_SOFT_LINK: case CMD_HARD_LINK: if(restore_link(asfd, sb, fullpath, act, cntr, protocol, restore_desired_dir)) goto error; continue; case CMD_SPECIAL: if(restore_special(asfd, sb, fullpath, act, cntr, protocol)) goto error; continue; default: break; } if(protocol==PROTO_2) { if(restore_switch_protocol2(asfd, sb, fullpath, act, bfd, vss_restore, cntr)) goto error; } else { if(restore_switch_protocol1(asfd, sb, fullpath, act, bfd, vss_restore, cntr, encryption_password)) goto error; } } end: ret=0; error: // It is possible for a fd to still be open. if(bfd) { bfd->close(bfd, asfd); bfile_free(&bfd); } cntr_print_end(cntr); cntr_set_bytes(cntr, asfd); cntr_print(cntr, act); if(!ret) logp("%s finished\n", act_str(act)); else logp("ret: %d\n", ret); sbuf_free(&sb); free_w(&style); free_w(&fullpath); blk_free(&blk); // Cannot use free_w() because it was not allocated by alloc.c, and // I cannot implement realpath() it in alloc.c because I cannot get // Windows code to use alloc.c. if(restore_desired_dir) free(restore_desired_dir); return ret; } burp-2.4.0/src/client/restore.h000066400000000000000000000015071404341324700163730ustar00rootroot00000000000000#ifndef _RESTORE_CLIENT_H #define _RESTORE_CLIENT_H enum ofr_e { OFR_ERROR=-1, OFR_OK=0, OFR_CONTINUE }; extern enum ofr_e open_for_restore(struct asfd *asfd, struct BFILE *bfd, const char *path, struct sbuf *sb, enum vss_restore vss_restore, struct cntr *cntr, enum protocol protocol); extern int do_restore_client(struct asfd *asfd, struct conf **confs, enum action act); // These are for the protocol1 restore to use, until it is unified more fully // with protocol2. extern int restore_dir(struct asfd *asfd, struct sbuf *sb, const char *dname, enum action act, struct cntr *cntr, enum protocol protocol); extern int restore_interrupt(struct asfd *asfd, struct sbuf *sb, const char *msg, struct cntr *cntr, enum protocol protocol); #ifdef UTEST extern void strip_from_path(char *path, const char *strip); #endif #endif burp-2.4.0/src/client/xattr.c000066400000000000000000000312011404341324700160370ustar00rootroot00000000000000#include "../burp.h" #include "../alloc.h" #include "../asfd.h" #include "../async.h" #include "../cntr.h" #include "../log.h" #include "../prepend.h" #include "extrameta.h" #include "xattr.h" #ifdef HAVE_XATTR #ifdef HAVE_SYS_XATTR_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #ifdef HAVE_LIBUTIL_H #include #endif #ifdef HAVE_DARWIN_OS /* * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has * listxattr, getxattr and setxattr with an extra options argument * which mimics the l variants of the functions when we specify * XATTR_NOFOLLOW as the options value. */ #define llistxattr(path, list, size) \ listxattr((path), (list), (size), XATTR_NOFOLLOW) #define lgetxattr(path, name, value, size) \ getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW) #define lsetxattr(path, name, value, size, flags) \ setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW) static const char *acl_skiplist[2] = { "com.apple.system.Security", NULL }; #elif HAVE_LINUX_OS static const char *acl_skiplist[3] = { "system.posix_acl_access", "system.posix_acl_default", NULL }; #elif HAVE_FREEBSD_OS static const char *acl_skiplist[2] = { "system.posix1e.acl_access", NULL }; #else static const char *acl_skiplist[1] = { NULL }; #endif // Skip xattr entries that were already saved as ACLs. static int in_skiplist(const char *xattr) { for(int c=0; acl_skiplist[c]; c++) if(!strcmp(xattr, acl_skiplist[c])) return 1; return 0; } static int append_to_extrameta(const char *toappend, char metasymbol, char **xattrtext, size_t *xlen, uint32_t totallen) { char tmp3[10]; size_t newlen=0; snprintf(tmp3, sizeof(tmp3), "%c%08X", metasymbol, totallen); newlen=(*xlen)+9+totallen+1; if(!(*xattrtext=(char *) realloc_w(*xattrtext, newlen, __func__))) return -1; memcpy((*xattrtext)+(*xlen), tmp3, 9); (*xlen)+=9; memcpy((*xattrtext)+(*xlen), toappend, totallen); (*xlen)+=totallen; (*xattrtext)[*xlen]='\0'; return 0; } #ifndef UTEST static #endif char *get_next_xattr_str(struct asfd *asfd, char **data, size_t *l, struct cntr *cntr, uint32_t *s, const char *path) { char *ret=NULL; if(*l<8) { logw(asfd, cntr, "length of xattr '%s' %zd is too short for %s\n", *data, *l, path); return NULL; } if((sscanf(*data, "%08X", s))!=1) { logw(asfd, cntr, "sscanf of xattr '%s' %zd failed for %s\n", *data, *l, path); return NULL; } *data+=8; *l-=8; if(*s>*l) { logw(asfd, cntr, "requested length %d of xattr '%s' %zd is too long for %s\n", *s, *data, *l, path); return NULL; } if(!(ret=(char *)malloc_w((*s)+1, __func__))) return NULL; memcpy(ret, *data, *s); ret[*s]='\0'; *data+=*s; *l-=*s; return ret; } #ifdef HAVE_SYS_EXTATTR_H static int namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM }; int has_xattr(const char *path) { int i=0; for(i=0; i<(int)(sizeof(namespaces)/sizeof(int)); i++) { if(extattr_list_link(path, namespaces[i], NULL, 0)>0) return 1; } return 0; } #define BSD_BUF_SIZE 1024 int get_xattr(struct asfd *asfd, const char *path, char **xattrtext, size_t *xlen, struct cntr *cntr) { int i=0; uint32_t maxlen=0xFFFFFFFF/2; for(i=0; i<(int)(sizeof(namespaces)/sizeof(int)); i++) { int j=0; ssize_t len=0; int have_acl=0; char *xattrlist=NULL; char *cnamespace=NULL; uint32_t totallen=0; char *toappend=NULL; static char z[BSD_BUF_SIZE*2]=""; char cattrname[BSD_BUF_SIZE]=""; if((len=extattr_list_link(path, namespaces[i], NULL, 0))<0) { logw(asfd, cntr, "could not extattr_list_link of '%s': %zd\n", path, len); return 0; // carry on } if(!len) continue; if(xattrtext && *xattrtext) { // Already have some meta text, which means that some // ACLs were set. have_acl++; } if(!(xattrlist=(char *)calloc_w(1, len+1, __func__))) return -1; if((len=extattr_list_link(path, namespaces[i], xattrlist, len))<=0) { logw(asfd, cntr, "could not extattr_list_link '%s': %zd\n", path, len); free_w(&xattrlist); return 0; // carry on } xattrlist[len]='\0'; if(extattr_namespace_to_string(namespaces[i], &cnamespace)) { logp("Failed to convert %d into namespace on '%s'\n", namespaces[i], path); free_w(&xattrlist); return 0; // carry on } for(j=0; j<(int)len; j+=xattrlist[j]+1) { int cnt=0; char tmp1[9]; char tmp2[9]; size_t newlen=0; uint32_t zlen=0; ssize_t vlen=0; char *val=NULL; cnt=xattrlist[j]; if(cnt>((int)sizeof(cattrname)-1)) cnt=((int)sizeof(cattrname)-1); strncpy(cattrname, xattrlist+(j+1), cnt); cattrname[cnt]='\0'; snprintf(z, sizeof(z), "%s.%s", cnamespace, cattrname); if(have_acl && in_skiplist(z)) continue; zlen=(uint32_t)strlen(z); //printf("\ngot: %s (%s)\n", z, path); if((vlen=extattr_list_link(path, namespaces[i], xattrlist, len))<0) { logw(asfd, cntr, "could not extattr_list_link on %s for %s: %zd\n", path, cnamespace, vlen); continue; } if(vlen) { if(!(val=(char *)malloc_w(vlen+1, __func__))) { free_w(&xattrlist); free_w(&toappend); return -1; } if((vlen=extattr_get_link(path, namespaces[i], cattrname, val, vlen))<0) { logw(asfd, cntr, "could not extattr_list_link %s for %s: %zd\n", path, cnamespace, vlen); free_w(&val); continue; } val[vlen]='\0'; if(vlen>maxlen) { logw(asfd, cntr, "xattr value of '%s' too long: %zd\n", path, vlen); free_w(&toappend); free_w(&val); break; } } snprintf(tmp1, sizeof(tmp1), "%08X", zlen); snprintf(tmp2, sizeof(tmp2), "%08X", (uint32_t)vlen); newlen=totallen+8+zlen+8+vlen; if(!(toappend=(char *)realloc_w(toappend, newlen, __func__))) { free_w(&val); free_w(&xattrlist); return -1; } memcpy(toappend+totallen, tmp1, 8); totallen+=8; memcpy(toappend+totallen, z, zlen); totallen+=zlen; memcpy(toappend+totallen, tmp2, 8); totallen+=8; memcpy(toappend+totallen, val, vlen); totallen+=vlen; free_w(&val); if(totallen>maxlen) { logw(asfd, cntr, "xattr length of '%s' grew too long: %d\n", path, totallen); free_w(&val); free_w(&toappend); free_w(&xattrlist); return 0; // carry on } } if(toappend) { if(append_to_extrameta(toappend, META_XATTR_BSD, xattrtext, xlen, totallen)) { free_w(&toappend); free_w(&xattrlist); return -1; } } free_w(&toappend); free_w(&xattrlist); } return 0; } static int do_set_xattr_bsd(struct asfd *asfd, const char *path, const char *xattrtext, size_t xlen, struct cntr *cntr) { int ret=-1; size_t l=0; char *data=NULL; char *value=NULL; char *nspace=NULL; data=(char *)xattrtext; l=xlen; while(l>0) { ssize_t cnt; uint32_t vlen=0; int cnspace=0; char *name=NULL; if(!(nspace=get_next_xattr_str(asfd, &data, &l, cntr, &vlen, path)) || !(value=get_next_xattr_str(asfd, &data, &l, cntr, &vlen, path))) goto end; // Need to split the name into two parts. if(!(name=strchr(nspace, '.'))) { logw(asfd, cntr, "could not split %s into namespace and name on %s\n", nspace, path); goto end; } *name='\0'; name++; if(extattr_string_to_namespace(nspace, &cnspace)) { logw(asfd, cntr, "could not convert %s into namespace on %s\n", nspace, path); goto end; } //printf("set_link: %d %s %s %s\n", cnspace, nspace, name, value); if((cnt=extattr_set_link(path, cnspace, name, value, vlen))!=vlen) { logw(asfd, cntr, "extattr_set_link error on %s %zd!=%d: %s\n", path, cnt, vlen, strerror(errno)); goto end; } free_w(&nspace); free_w(&value); } ret=0; end: free_w(&nspace); free_w(&value); return ret; } int set_xattr(struct asfd *asfd, const char *path, const char *xattrtext, size_t xlen, char metacmd, struct cntr *cntr) { switch(metacmd) { case META_XATTR_BSD: return do_set_xattr_bsd(asfd, path, xattrtext, xlen, cntr); default: logp("unknown xattr type: %c\n", metacmd); logw(asfd, cntr, "unknown xattr type: %c\n", metacmd); break; } return -1; } #elif HAVE_SYS_XATTR_H int has_xattr(const char *path) { if(llistxattr(path, NULL, 0)>0) return 1; return 0; } static int get_toappend(struct asfd *asfd, const char *path, char **toappend, const char *xattrlist, ssize_t len, uint32_t *totallen, int have_acl, struct cntr *cntr) { char *val=NULL; const char *z=NULL; uint32_t maxlen=0xFFFFFFFF/2; for(z=xattrlist; z-xattrlist < len; z=strchr(z, '\0')+1) { char tmp1[9]; char tmp2[9]; ssize_t vlen; uint32_t zlen=0; uint32_t newlen=0; free_w(&val); if((zlen=(uint32_t)strlen(z))>maxlen) { logw(asfd, cntr, "xattr element of '%s' too long: %d\n", path, zlen); goto carryon; } if(have_acl && in_skiplist(z)) continue; if((vlen=lgetxattr(path, z, NULL, 0))<0) { logw(asfd, cntr, "could not lgetxattr on %s for %s: %zd %s\n", path, z, vlen, strerror(errno)); continue; } if(vlen) { if(!(val=(char *)malloc_w(vlen+1, __func__))) goto error; if((vlen=lgetxattr(path, z, val, vlen))<0) { logw(asfd, cntr, "could not lgetxattr %s for %s: %zd %s\n", path, z, vlen, strerror(errno)); continue; } val[vlen]='\0'; if(vlen>maxlen) { logw(asfd, cntr, "xattr value of '%s' too long: %zd\n", path, vlen); goto carryon; } } snprintf(tmp1, sizeof(tmp1), "%08X", zlen); snprintf(tmp2, sizeof(tmp2), "%08X", (uint32_t)vlen); newlen=(*totallen)+8+zlen+8+vlen; if(!(*toappend=(char *)realloc_w(*toappend, newlen, __func__))) goto error; memcpy((*toappend)+(*totallen), tmp1, 8); *totallen+=8; memcpy((*toappend)+(*totallen), z, zlen); *totallen+=zlen; memcpy((*toappend)+(*totallen), tmp2, 8); *totallen+=8; memcpy((*toappend)+(*totallen), val, vlen); *totallen+=vlen; if(*totallen>maxlen) { logw(asfd, cntr, "xattr length of '%s' grew too long: %d\n", path, *totallen); goto carryon; } } free_w(&val); return 0; error: free_w(&val); free_w(toappend); return -1; carryon: free_w(&val); free_w(toappend); return 0; } int get_xattr(struct asfd *asfd, const char *path, char **xattrtext, size_t *xlen, struct cntr *cntr) { int ret=0; ssize_t len; int have_acl=0; char *toappend=NULL; char *xattrlist=NULL; uint32_t totallen=0; if((len=llistxattr(path, NULL, 0))<0) { logw(asfd, cntr, "could not llistxattr '%s': %zd %s\n", path, len, strerror(errno)); goto end; // Carry on. } if(!(xattrlist=(char *)calloc_w(1, len, __func__))) { ret=-1; goto end; } if((len=llistxattr(path, xattrlist, len))<0) { logw(asfd, cntr, "could not llistxattr '%s': %zd %s\n", path, len, strerror(errno)); goto end; // Carry on. } if(xattrtext && *xattrtext) { // Already have some meta text, which means that some // ACLs were set. have_acl++; } if(get_toappend(asfd, path, &toappend, xattrlist, len, &totallen, have_acl, cntr)) { ret=-1; goto end; } if(toappend) ret=append_to_extrameta(toappend, META_XATTR, xattrtext, xlen, totallen); end: free_w(&toappend); free_w(&xattrlist); return ret; } static int do_set_xattr(struct asfd *asfd, const char *path, const char *xattrtext, size_t xlen, struct cntr *cntr) { size_t l=0; int ret=-1; char *data=NULL; char *name=NULL; char *value=NULL; data=(char *)xattrtext; l=xlen; while(l>0) { uint32_t s=0; free_w(&name); free_w(&value); if(!(name=get_next_xattr_str(asfd, &data, &l, cntr, &s, path)) || !(value=get_next_xattr_str(asfd, &data, &l, cntr, &s, path))) goto end; if(lsetxattr(path, name, value, s, 0)) { logw(asfd, cntr, "lsetxattr error on %s: %s\n", path, strerror(errno)); goto end; } } ret=0; end: free_w(&name); free_w(&value); return ret; } int set_xattr(struct asfd *asfd, const char *path, const char *xattrtext, size_t xlen, char metacmd, struct cntr *cntr) { switch(metacmd) { case META_XATTR: return do_set_xattr(asfd, path, xattrtext, xlen, cntr); default: logp("unknown xattr type: %c\n", metacmd); logw(asfd, cntr, "unknown xattr type: %c\n", metacmd); break; } return -1; } #endif #ifdef UTEST int fs_supports_xattr(void) { FILE *fp; int ret=-1; const char *fname="xattr_test_file"; if(!(fp=fopen(fname, "w"))) { printf("Could not open %s!\n", fname); return 0; } fclose(fp); #ifdef HAVE_SYS_EXTATTR_H ret=extattr_set_link(fname, EXTATTR_NAMESPACE_USER, "comment", "a", strlen("a")); #elif HAVE_SYS_XATTR_H ret=lsetxattr(fname, "user.comment", "a", strlen("a"), /*flags*/0); #else errno=ENOTSUP; #endif if(ret<0 && errno==ENOTSUP) { printf("File system does not support xattrs!\n"); unlink(fname); return 0; } unlink(fname); return 1; } #endif #endif burp-2.4.0/src/client/xattr.h000066400000000000000000000012341404341324700160470ustar00rootroot00000000000000#ifndef _BURP_XATTR_H #define _BURP_XATTR_H #ifdef HAVE_XATTR #if defined(HAVE_LINUX_OS) \ || defined(HAVE_FREEBSD_OS) \ || defined(HAVE_NETBSD_OS) \ || defined(HAVE_DARWIN_OS) extern int has_xattr(const char *path); extern int get_xattr(struct asfd *asfd, const char *path, char **xattrtext, size_t *xlen, struct cntr *cntr); extern int set_xattr(struct asfd *asfd, const char *path, const char *xattrtext, size_t xlen, char metacmd, struct cntr *cntr); #endif #ifdef UTEST extern char *get_next_xattr_str(struct asfd *asfd, char **data, size_t *l, struct cntr *cntr, uint32_t *s, const char *path); extern int fs_supports_xattr(void); #endif #endif #endif burp-2.4.0/src/cmd.c000066400000000000000000000103151404341324700141650ustar00rootroot00000000000000#include "burp.h" #include "cmd.h" char *cmd_to_text(enum cmd cmd) { static char buf[256]; size_t len=sizeof(buf); *buf='\0'; switch(cmd) { case CMD_ATTRIBS: snprintf(buf, len, "File attribute information"); break; case CMD_ATTRIBS_SIGS: snprintf(buf, len, "File attribute information preceding block signatures"); break; case CMD_SIG: snprintf(buf, len, "Block signature"); break; case CMD_DATA_REQ: snprintf(buf, len, "Request for block of data"); break; case CMD_DATA: snprintf(buf, len, "Block data"); break; case CMD_WRAP_UP: snprintf(buf, len, "Control packet"); break; case CMD_FILE: snprintf(buf, len, "Plain file"); break; case CMD_ENC_FILE: snprintf(buf, len, "Encrypted file"); break; case CMD_DIRECTORY: snprintf(buf, len, "Directory"); break; case CMD_SOFT_LINK: snprintf(buf, len, "Soft link"); break; case CMD_HARD_LINK: snprintf(buf, len, "Hard link"); break; case CMD_SPECIAL: snprintf(buf, len, "Special file - fifo, socket, device node"); break; case CMD_METADATA: snprintf(buf, len, "Extra meta data"); break; case CMD_GEN: snprintf(buf, len, "Generic command"); break; case CMD_ERROR: snprintf(buf, len, "Error message"); break; case CMD_APPEND: snprintf(buf, len, "Append to a file"); break; case CMD_INTERRUPT: snprintf(buf, len, "Interrupt"); break; case CMD_MESSAGE: snprintf(buf, len, "Message"); break; case CMD_WARNING: snprintf(buf, len, "Warning"); break; case CMD_END_FILE: snprintf(buf, len, "End of file transmission"); break; case CMD_ENC_METADATA: snprintf(buf, len, "Encrypted meta data"); break; case CMD_EFS_FILE: snprintf(buf, len, "Windows EFS file"); break; case CMD_FILE_CHANGED: snprintf(buf, len, "Plain file changed"); break; case CMD_TIMESTAMP: snprintf(buf, len, "Backup timestamp"); break; case CMD_TIMESTAMP_END: snprintf(buf, len, "Timestamp now/end"); break; case CMD_MANIFEST: snprintf(buf, len, "Path to a manifest"); break; case CMD_FINGERPRINT: snprintf(buf, len, "Fingerprint part of a signature"); break; case CMD_SAVE_PATH: snprintf(buf, len, "Save path part of a signature"); break; // For the status server/client */ case CMD_TOTAL: snprintf(buf, len, "Total counter"); break; case CMD_GRAND_TOTAL: snprintf(buf, len, "Grand total counter"); break; case CMD_BYTES_ESTIMATED: snprintf(buf, len, "Bytes estimated"); break; case CMD_BYTES: snprintf(buf, len, "Bytes"); break; case CMD_BYTES_RECV: snprintf(buf, len, "Bytes received"); break; case CMD_BYTES_SENT: snprintf(buf, len, "Bytes sent"); break; // Protocol1 only. case CMD_DATAPTH: snprintf(buf, len, "Path to data on the server"); break; case CMD_VSS: snprintf(buf, len, "Windows VSS header"); break; case CMD_ENC_VSS: snprintf(buf, len, "Encrypted windows VSS header"); break; case CMD_VSS_T: snprintf(buf, len, "Windows VSS footer"); break; case CMD_ENC_VSS_T: snprintf(buf, len, "Encrypted windows VSS footer"); break; // No default so that we get compiler warnings when we forget // to add new ones here. } if(!*buf) snprintf(buf, len, "----------------"); return buf; } void cmd_print_all(void) { int i=0; char cmds[256]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; printf("\nIndex of symbols\n\n"); for(i=0; cmds[i]; i++) printf(" %c: %s\n", cmds[i], cmd_to_text((enum cmd)cmds[i])); printf("\n"); } int cmd_is_filedata(enum cmd cmd) { return cmd==CMD_FILE || cmd==CMD_ENC_FILE || cmd==CMD_METADATA || cmd==CMD_ENC_METADATA || cmd==CMD_EFS_FILE; } int cmd_is_vssdata(enum cmd cmd) { return cmd==CMD_VSS || cmd==CMD_ENC_VSS || cmd==CMD_VSS_T || cmd==CMD_ENC_VSS_T; } int cmd_is_link(enum cmd cmd) { return cmd==CMD_SOFT_LINK || cmd==CMD_HARD_LINK; } int cmd_is_endfile(enum cmd cmd) { return cmd==CMD_END_FILE; } int cmd_is_encrypted(enum cmd cmd) { return cmd==CMD_ENC_FILE || cmd==CMD_ENC_METADATA || cmd==CMD_ENC_VSS || cmd==CMD_ENC_VSS_T || cmd==CMD_EFS_FILE; } int cmd_is_metadata(enum cmd cmd) { return cmd_is_vssdata(cmd) || cmd==CMD_METADATA || cmd==CMD_ENC_METADATA; } int cmd_is_estimatable(enum cmd cmd) { return cmd==CMD_FILE || cmd==CMD_ENC_FILE || cmd==CMD_EFS_FILE; } burp-2.4.0/src/cmd.h000066400000000000000000000047551404341324700142050ustar00rootroot00000000000000#ifndef _CMD_H #define _CMD_H #include enum cmd { /* These things appear at the beginning of each line of communication on the network, and in the manifest. */ // This comes before any file type entries. CMD_ATTRIBS='r', /* File stat information */ CMD_ATTRIBS_SIGS='R', /* File stat information preceding sigs */ CMD_SIG ='S', /* Signature of a block */ CMD_DATA_REQ ='D', /* Request for block data */ CMD_DATA ='B', /* Block data */ CMD_WRAP_UP ='W', /* Control packet - client can free blocks up to the given index. */ // File types CMD_FILE ='f', /* Plain file */ CMD_ENC_FILE ='y', /* Encrypted file */ CMD_DIRECTORY ='d', /* Directory */ CMD_SOFT_LINK ='l', /* Soft link */ CMD_HARD_LINK ='L', /* Hard link */ CMD_SPECIAL ='s', /* Fifo, socket, device node... */ CMD_METADATA ='m', /* Extra meta data */ CMD_ENC_METADATA='n', /* Encrypted extra meta data */ CMD_EFS_FILE ='k', /* Windows EFS file */ // Commands CMD_GEN ='c', /* Generic command */ CMD_ERROR ='e', /* Error message */ CMD_APPEND ='a', /* Append to a file */ CMD_INTERRUPT ='i', /* Please interrupt the current data flow */ CMD_MESSAGE ='p', /* A message */ CMD_WARNING ='w', /* A warning */ CMD_END_FILE ='x', /* End of file transmission - also appears at the end of the manifest and contains size/checksum info. */ // CMD_FILE_UNCHANGED only used in counting stats on the client, for humans CMD_FILE_CHANGED='z', CMD_TIMESTAMP ='b', /* Backup timestamp (in response to list) */ CMD_MANIFEST ='M', /* Path to a manifest */ CMD_FINGERPRINT ='F', /* Fingerprint part of a signature */ CMD_SAVE_PATH ='q', /* Save path part of a signature */ // These things are for the status server/client CMD_TOTAL ='Y', CMD_GRAND_TOTAL ='Z', CMD_BYTES_ESTIMATED='G', CMD_BYTES ='O', CMD_BYTES_RECV ='P', CMD_BYTES_SENT ='Q', CMD_TIMESTAMP_END='E', // Protocol1 only. CMD_DATAPTH ='t', /* Path to data on the server */ CMD_VSS ='v', /* Windows VSS metadata */ CMD_ENC_VSS ='V', /* Encrypted Windows VSS metadata */ CMD_VSS_T ='u', /* Windows VSS footer */ CMD_ENC_VSS_T ='U', /* Encrypted Windows VSS footer */ }; extern void cmd_print_all(void); extern char *cmd_to_text(enum cmd cmd); extern int cmd_is_filedata(enum cmd cmd); extern int cmd_is_vssdata(enum cmd cmd); extern int cmd_is_link(enum cmd cmd); extern int cmd_is_endfile(enum cmd cmd); extern int cmd_is_encrypted(enum cmd cmd); extern int cmd_is_metadata(enum cmd cmd); extern int cmd_is_estimatable(enum cmd cmd); #endif burp-2.4.0/src/cntr.c000066400000000000000000000610001404341324700143650ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "asfd.h" #include "async.h" #include "cmd.h" #include "cntr.h" #include "cstat.h" #include "fsops.h" #include "handy.h" #include "iobuf.h" #include "log.h" #include "times.h" #include "client/monitor/sel.h" #include "client/monitor/json_input.h" #include "server/bu_get.h" #include "server/monitor/json_output.h" #include #define CNTR_VERSION 3 #define CNTR_PATH_BUF_LEN 256 static void cntr_ent_free_content(struct cntr_ent *cntr_ent) { if(!cntr_ent) return; free_w(&cntr_ent->field); free_w(&cntr_ent->label); } static void cntr_ent_free(struct cntr_ent **cntr_ent) { if(!cntr_ent || !*cntr_ent) return; cntr_ent_free_content(*cntr_ent); free_v((void **)cntr_ent); } struct cntr *cntr_alloc(void) { return (struct cntr *)calloc_w(1, sizeof(struct cntr), __func__); } static int add_cntr_ent(struct cntr *cntr, int flags, enum cmd cmd, const char *field, const char *label) { struct cntr_ent *cenew=NULL; if(!(cenew=(struct cntr_ent *) calloc_w(1, sizeof(struct cntr_ent), __func__)) || !(cenew->field=strdup_w(field, __func__)) || !(cenew->label=strdup_w(label, __func__))) goto error; cenew->flags=flags; cenew->cmd=cmd; if(cntr->list) cenew->next=cntr->list; cntr->list=cenew; cntr->ent[(uint8_t)cmd]=cenew; return 0; error: cntr_ent_free(&cenew); return -1; } static size_t calc_max_str_len(struct cntr *cntr, const char *cname) { size_t slen=0; char ullmax[64]; struct cntr_ent *e=NULL; // See cntr_to_str(). // First section - name/version/status slen+=strlen(cname); slen+=32; // More than enough space. // Second section. snprintf(ullmax, sizeof(ullmax), " %" PRIu64 "\n", (uint64_t)ULLONG_MAX); for(e=cntr->list; e; e=e->next) { if(e->flags & CNTR_SINGLE_FIELD) // %c%llu\t slen+=strlen(ullmax)+2; else // %c%llu/%llu/%llu/%llu/%llu\t slen+=(strlen(ullmax)*5)+6; } // Fourth section - a path. Cannot know how long this might be. Guess. slen+=CNTR_PATH_BUF_LEN+3; // %c%s\t\n slen+=1; // Terminating character. return slen; } int cntr_init(struct cntr *cntr, const char *cname, pid_t pid) { if(!cname) { logp("%s called with no client name\n", __func__); return -1; } // Add in reverse order, so that iterating over from the beginning // comes out in the right order. if( add_cntr_ent(cntr, CNTR_SINGLE_FIELD, CMD_TIMESTAMP_END, "time_end", "End time") || add_cntr_ent(cntr, CNTR_SINGLE_FIELD, CMD_TIMESTAMP, "time_start", "Start time") || add_cntr_ent(cntr, CNTR_SINGLE_FIELD, CMD_BYTES_SENT, "bytes_sent", "Bytes sent") || add_cntr_ent(cntr, CNTR_SINGLE_FIELD, CMD_BYTES_RECV, "bytes_received", "Bytes received") || add_cntr_ent(cntr, CNTR_SINGLE_FIELD, CMD_BYTES, "bytes", "Bytes") || add_cntr_ent(cntr, CNTR_SINGLE_FIELD, CMD_BYTES_ESTIMATED, "bytes_estimated", "Bytes estimated") || add_cntr_ent(cntr, CNTR_SINGLE_FIELD, CMD_MESSAGE, "messages", "Messages") || add_cntr_ent(cntr, CNTR_SINGLE_FIELD, CMD_WARNING, "warnings", "Warnings") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_GRAND_TOTAL, "grand_total", "Grand total") || add_cntr_ent(cntr, 0, CMD_TOTAL, "total", "Total") || add_cntr_ent(cntr, CNTR_SINGLE_FIELD, CMD_ERROR, "errors", "Errors") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_DATA, "blocks", "Blocks") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_EFS_FILE, "efs_files", "EFS files") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_ENC_VSS_T, "vss_footers_encrypted", "VSS footers (enc)") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_VSS_T, "vss_footers", "VSS footers") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_ENC_VSS, "vss_headers_encrypted", "VSS headers (enc)") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_VSS, "vss_headers", "VSS headers") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_SPECIAL, "special_files", "Special files") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_SOFT_LINK, "hard_links", "Soft links") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_HARD_LINK, "soft_links", "Hard links") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_DIRECTORY, "directories", "Directories") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_ENC_METADATA, "meta_data_encrypted", "Meta data (enc)") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_METADATA, "meta_data", "Meta data") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_ENC_FILE, "files_encrypted", "Files (encrypted)") || add_cntr_ent(cntr, CNTR_TABULATE, CMD_FILE, "files", "Files") ) return -1; cntr->ent[(uint8_t)CMD_TIMESTAMP]->count=(uint64_t)time(NULL); cntr->str_max_len=calc_max_str_len(cntr, cname); if(!(cntr->str=(char *)calloc_w(1, cntr->str_max_len, __func__)) || !(cntr->cname=strdup_w(cname, __func__))) return -1; cntr->pid=pid; return 0; } static void cntr_free_content(struct cntr *cntr) { struct cntr_ent *e; struct cntr_ent *l=NULL; for(e=cntr->list; e; e=l) { l=e->next; cntr_ent_free(&e); } cntr->list=NULL; free_w(&cntr->str); free_w(&cntr->cname); } void cntr_free(struct cntr **cntr) { if(!cntr || !*cntr) return; cntr_free_content(*cntr); free_v((void **)cntr); } void cntrs_free(struct cntr **cntrs) { struct cntr *c; struct cntr *chead; if(!cntrs || !*cntrs) return; chead=*cntrs; while(chead) { c=chead; chead=chead->next; cntr_free(&c); } *cntrs=NULL; } const char *bytes_to_human(uint64_t counter) { static char ret[32]=""; float div=(float)counter; char units[3]=""; if(div<1024) return ""; if((div/=1024)<1024) snprintf(units, sizeof(units), "KB"); else if((div/=1024)<1024) snprintf(units, sizeof(units), "MB"); else if((div/=1024)<1024) snprintf(units, sizeof(units), "GB"); else if((div/=1024)<1024) snprintf(units, sizeof(units), "TB"); else if((div/=1024)<1024) snprintf(units, sizeof(units), "EB"); else { div/=1024; snprintf(units, sizeof(units), "PB"); } snprintf(ret, sizeof(ret), " (%.2f %s)", div, units); return ret; } static void border(void) { logc("--------------------------------------------------------------------------------\n"); } static void table_border(enum action act) { if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED) { logc("%18s ------------------------------------------------------------\n", ""); } if(act==ACTION_RESTORE || act==ACTION_VERIFY) { logc("%18s ------------------------------\n", ""); } } static void set_count_val(struct cntr *cntr, char ch, uint64_t val) { if(!cntr) return; if(cntr->ent[(uint8_t)ch]) cntr->ent[(uint8_t)ch]->count=val; } static void set_changed_val(struct cntr *cntr, char ch, uint64_t val) { if(!cntr) return; if(cntr->ent[(uint8_t)ch]) cntr->ent[(uint8_t)ch]->changed=val; } static void set_same_val(struct cntr *cntr, char ch, uint64_t val) { if(!cntr) return; if(cntr->ent[(uint8_t)ch]) cntr->ent[(uint8_t)ch]->same=val; } static void set_deleted_val(struct cntr *cntr, char ch, uint64_t val) { if(!cntr) return; if(cntr->ent[(uint8_t)ch]) cntr->ent[(uint8_t)ch]->deleted=val; } static void set_phase1_val(struct cntr *cntr, char ch, uint64_t val) { if(!cntr) return; if(cntr->ent[(uint8_t)ch]) cntr->ent[(uint8_t)ch]->phase1=val; } static void incr_count_val(struct cntr *cntr, char ch, uint64_t val) { if(!cntr) return; if(cntr->ent[(uint8_t)ch]) cntr->ent[(uint8_t)ch]->count+=val; } static void incr_same_val(struct cntr *cntr, char ch, uint64_t val) { if(!cntr) return; if(cntr->ent[(uint8_t)ch]) cntr->ent[(uint8_t)ch]->same+=val; } static void incr_changed_val(struct cntr *cntr, char ch, uint64_t val) { if(!cntr) return; if(cntr->ent[(uint8_t)ch]) cntr->ent[(uint8_t)ch]->changed+=val; } static void incr_deleted_val(struct cntr *cntr, char ch, uint64_t val) { if(!cntr) return; if(cntr->ent[(uint8_t)ch]) cntr->ent[(uint8_t)ch]->deleted+=val; } static void incr_phase1_val(struct cntr *cntr, char ch, uint64_t val) { if(!cntr) return; if(cntr->ent[(uint8_t)ch]) cntr->ent[(uint8_t)ch]->phase1+=val; } static void incr_count(struct cntr *cntr, char ch) { return incr_count_val(cntr, ch, 1); } static void incr_same(struct cntr *cntr, char ch) { return incr_same_val(cntr, ch, 1); } static void incr_changed(struct cntr *cntr, char ch) { return incr_changed_val(cntr, ch, 1); } static void incr_deleted(struct cntr *cntr, char ch) { return incr_deleted_val(cntr, ch, 1); } static void incr_phase1(struct cntr *cntr, char ch) { return incr_phase1_val(cntr, ch, 1); } static void print_end(uint64_t val) { if(val) logc(" %" PRIu64 "\n", val); } void cntr_add(struct cntr *c, char ch, int print) { struct cntr_ent *grand_total_ent; if(!c) return; if(!(grand_total_ent=c->ent[CMD_GRAND_TOTAL])) return; if(print) { if(ch!=CMD_MESSAGE && ch!=CMD_WARNING) logc("%c", ch); } if(ch==CMD_FILE_CHANGED) { incr_changed(c, CMD_FILE); incr_changed(c, CMD_TOTAL); incr_changed(c, CMD_GRAND_TOTAL); } else { incr_count(c, ch); if(ch==CMD_WARNING || ch==CMD_MESSAGE) return; incr_count(c, CMD_TOTAL); } if(!((++grand_total_ent->count)%64) && print) print_end(grand_total_ent->count); fflush(stdout); } void cntr_add_phase1(struct cntr *c, char ch, int print) { static struct cntr_ent *total; incr_phase1(c, ch); total=c->ent[(uint8_t)CMD_GRAND_TOTAL]; ++total->phase1; if(!print) return; if(total->phase1==1) logc("\n"); logc("%c", ch); if(!((total->phase1)%64)) print_end(total->phase1); fflush(stdout); } void cntr_add_val(struct cntr *c, char ch, uint64_t val) { incr_count_val(c, ch, val); } void cntr_add_new(struct cntr *c, char ch) { cntr_add(c, ch, 0); } void cntr_add_same(struct cntr *c, char ch) { incr_same(c, ch); incr_same(c, CMD_TOTAL); incr_same(c, CMD_GRAND_TOTAL); } void cntr_add_same_val(struct cntr *c, char ch, uint64_t val) { incr_same_val(c, ch, val); incr_same_val(c, CMD_TOTAL, val); incr_same_val(c, CMD_GRAND_TOTAL, val); } void cntr_add_changed(struct cntr *c, char ch) { incr_changed(c, ch); incr_changed(c, CMD_TOTAL); incr_changed(c, CMD_GRAND_TOTAL); } void cntr_add_changed_val(struct cntr *c, char ch, uint64_t val) { incr_changed_val(c, ch, val); incr_changed_val(c, CMD_TOTAL, val); incr_changed_val(c, CMD_GRAND_TOTAL, val); } void cntr_add_deleted(struct cntr *c, char ch) { incr_deleted(c, ch); incr_deleted(c, CMD_TOTAL); incr_deleted(c, CMD_GRAND_TOTAL); } void cntr_add_bytes(struct cntr *c, uint64_t bytes) { incr_count_val(c, CMD_BYTES, bytes); } static void cntr_set_sentbytes(struct cntr *c, uint64_t bytes) { set_count_val(c, CMD_BYTES_SENT, bytes); } static void cntr_set_recvbytes(struct cntr *c, uint64_t bytes) { set_count_val(c, CMD_BYTES_RECV, bytes); } void cntr_set_bytes(struct cntr *c, struct asfd *asfd) { if(!asfd) return; cntr_set_sentbytes(c, asfd->sent); cntr_set_recvbytes(c, asfd->rcvd); } static void quint_print(struct cntr_ent *ent, enum action act) { uint64_t a; uint64_t b; uint64_t c; uint64_t d; uint64_t e; if(!ent) return; a=ent->count; b=ent->changed; c=ent->same; d=ent->deleted; e=ent->phase1; if(!(ent->flags & CNTR_TABULATE)) return; if(!e && !a && !b && !c) return; logc("%18s:", ent->label); if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED) { logc("%9" PRIu64 " ", a); logc("%9" PRIu64 " ", b); logc("%9" PRIu64 " ", c); logc("%9" PRIu64 " ", d); } if(act==ACTION_RESTORE || act==ACTION_VERIFY) { logc("%9s ", ""); //logc("%9s ", ""); //logc("%9s ", ""); //logc("%9s ", ""); } if(act==ACTION_ESTIMATE) { logc("%9s ", ""); logc("%9s ", ""); logc("%9" PRIu64 "\n", e); } else { logc("%9" PRIu64 " |", a+b+c); logc("%9" PRIu64 "\n", e); } } static uint64_t get_count(struct cntr_ent **ent, enum cmd cmd) { if(!ent[(uint8_t)cmd]) return 0; return ent[(uint8_t)cmd]->count; } static void bottom_part(struct cntr *c, enum action act) { uint64_t l; struct cntr_ent **e=c->ent; logc("\n"); logc(" Messages: %11" PRIu64 "\n", get_count(e, CMD_MESSAGE)); logc(" Warnings: %11" PRIu64 "\n", get_count(e, CMD_WARNING)); logc("\n"); logc(" Bytes estimated: %11" PRIu64, get_count(e, CMD_BYTES_ESTIMATED)); logc("%s\n", bytes_to_human(get_count(e, CMD_BYTES_ESTIMATED))); if(act==ACTION_ESTIMATE) return; if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED) { l=get_count(e, CMD_BYTES); logc(" Bytes in backup: %11" PRIu64, l); logc("%s\n", bytes_to_human(l)); } if(act==ACTION_RESTORE) { l=get_count(e, CMD_BYTES); logc(" Bytes attempted: %11" PRIu64, l); logc("%s\n", bytes_to_human(l)); } if(act==ACTION_VERIFY) { l=get_count(e, CMD_BYTES); logc(" Bytes checked: %11" PRIu64, l); logc("%s\n", bytes_to_human(l)); } l=get_count(e, CMD_BYTES_RECV); logc(" Bytes received: %11" PRIu64, l); logc("%s\n", bytes_to_human(l)); l=get_count(e, CMD_BYTES_SENT); logc(" Bytes sent: %11" PRIu64, l); logc("%s\n", bytes_to_human(l)); } void cntr_print(struct cntr *cntr, enum action act) { struct cntr_ent *e; time_t now; time_t start; char time_start_str[32]; char time_end_str[32]; if(!cntr) return; now=time(NULL); start=(time_t)cntr->ent[(uint8_t)CMD_TIMESTAMP]->count; cntr->ent[(uint8_t)CMD_TIMESTAMP_END]->count=(uint64_t)now; border(); encode_time(start, time_start_str); encode_time(now, time_end_str); logc("Start time: %s\n", time_start_str); logc(" End time: %s\n", time_end_str); logc("Time taken: %s\n", time_taken(now-start)); if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED) { logc("%18s %9s %9s %9s %9s %9s |%9s\n", " ", "New", "Changed", "Duplicate", "Deleted", "Total", "Scanned"); } if(act==ACTION_RESTORE || act==ACTION_VERIFY) { logc("%18s %9s %9s |%9s\n", " ", "", "Attempted", "Expected"); } if(act==ACTION_ESTIMATE) { logc("%18s %9s %9s %9s\n", " ", "", "", "Scanned"); } table_border(act); for(e=cntr->list; e; e=e->next) quint_print(e, act); table_border(act); bottom_part(cntr, act); border(); } #ifndef HAVE_WIN32 int cntr_stats_to_file(struct cntr *cntr, const char *directory, enum action act) { int ret=-1; int fd=-1; char *path=NULL; char *pathtmp=NULL; const char *fname=NULL; struct async *as=NULL; struct asfd *wfd=NULL; if(!cntr) return 0; cntr->ent[(uint8_t)CMD_TIMESTAMP_END]->count =(uint64_t)time(NULL); if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED) fname="backup_stats"; else if(act==ACTION_RESTORE) fname="restore_stats"; else if(act==ACTION_VERIFY) fname="verify_stats"; else return 0; if(!(path=prepend_s(directory, fname)) || !(pathtmp=prepend(path, ".tmp"))) goto end; if((fd=open( pathtmp, #ifdef O_NOFOLLOW O_NOFOLLOW| #endif O_WRONLY|O_CREAT, 0666))<0) { logp("Could not open %s for writing in %s: %s\n", pathtmp, __func__, strerror(errno)); goto end; } if(!(as=async_alloc()) || as->init(as, 0) || !(wfd=setup_asfd_linebuf_write(as, "stats file", &fd))) { close_fd(&fd); goto end; } if(json_cntr(wfd, cntr)) goto end; ret=0; end: async_free(&as); asfd_free(&wfd); if(!ret && do_rename(pathtmp, path)) ret=-1; free_w(&path); free_w(&pathtmp); return ret; } #endif void cntr_print_end(struct cntr *cntr) { struct cntr_ent *grand_total_ent; if(!cntr) return; grand_total_ent=cntr->ent[CMD_GRAND_TOTAL]; if(grand_total_ent) { print_end(grand_total_ent->count); logc("\n"); } } void cntr_print_end_phase1(struct cntr *cntr) { struct cntr_ent *grand_total_ent; if(!cntr) return; grand_total_ent=cntr->ent[CMD_GRAND_TOTAL]; if(grand_total_ent) { print_end(grand_total_ent->phase1); logc("\n"); } } #ifndef HAVE_WIN32 // Return string length. size_t cntr_to_str(struct cntr *cntr, const char *path) { static char tmp[CNTR_PATH_BUF_LEN+3]=""; struct cntr_ent *e=NULL; char *str=cntr->str; cntr->ent[(uint8_t)CMD_TIMESTAMP_END]->count=time(NULL); snprintf(str, cntr->str_max_len-1, "cntr\t%s.%d.%d\t%d\t%d\t", cntr->cname, cntr->pid, cntr->bno, CNTR_VERSION, cntr->cntr_status); for(e=cntr->list; e; e=e->next) { if(e->flags & CNTR_SINGLE_FIELD) snprintf(tmp, sizeof(tmp), "%c%" PRIu64"\t", e->cmd, e->count); else snprintf(tmp, sizeof(tmp), "%c%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "\t", e->cmd, e->count, e->changed, e->same, e->deleted, e->phase1); strcat(str, tmp); } // Abuse CMD_DATAPTH. snprintf(tmp, sizeof(tmp), "%c%s\t\n", CMD_DATAPTH, path?path:""); strcat(str, tmp); return strlen(str); } #endif static int extract_ul(const char *value, struct cntr_ent *ent) { char *as=NULL; char *bs=NULL; char *cs=NULL; char *ds=NULL; char *es=NULL; char *copy=NULL; if(!value || !(copy=strdup_w(value, __func__))) return -1; // Do not want to use strtok, just in case I end up running more // than one at a time. as=copy; if((bs=strchr(as, '/'))) { *bs='\0'; ent->count=strtoull(as, NULL, 10); if((cs=strchr(++bs, '/'))) { *cs='\0'; ent->changed=strtoull(bs, NULL, 10); if((ds=strchr(++cs, '/'))) { *ds='\0'; ent->same=strtoull(cs, NULL, 10); if((es=strchr(++ds, '/'))) { ent->deleted=strtoull(ds, NULL, 10); *es='\0'; es++; ent->phase1=strtoull(es, NULL, 10); } } } } else { // Single field. ent->count=strtoull(as, NULL, 10); } free_w(©); return 0; } /* static char *get_backup_str(const char *s, int *deletable) { static char str[32]=""; const char *cp=NULL; const char *dp=NULL; if(!s || !*s) return NULL; if(!(cp=strchr(s, ' ')) || !(dp=strchr(cp+1, ' '))) snprintf(str, sizeof(str), "never"); else { uint64_t backupnum=0; backupnum=strtoul(s, NULL, 10); snprintf(str, sizeof(str), "%07lu %s", backupnum, getdatestr(atol(dp+1))); if(*(cp+1)=='1') *deletable=1; } return str; } */ /* static int add_to_backup_list(struct strlist **backups, const char *tok) { int deletable=0; const char *str=NULL; if(!(str=get_backup_str(tok, &deletable))) return 0; if(strlist_add(backups, (char *)str, deletable)) return -1; return 0; } */ static int extract_cntrs(struct cntr *cntr, char **path) { char *tok; while((tok=strtok(NULL, "\t\n"))) { switch(tok[0]) { case CMD_DATAPTH: free_w(path); if(!(*path=strdup_w(tok+1, __func__))) return -1; break; default: if(cntr->ent[(uint8_t)tok[0]] && extract_ul(tok+1, cntr->ent[(uint8_t)tok[0]])) return -1; break; } } return 0; } int extract_client_pid_bno(char *buf, char **cname, pid_t *pid, int *bno) { char *cp=NULL; char *pp=NULL; // Extract the client name. if((cp=strchr(buf, '\t'))) *cp='\0'; if(!(*cname=strdup_w(buf, __func__))) return -1; if(cp) *cp='\t'; // Extract the bno. if((pp=strrchr(*cname, '.'))) { *pp='\0'; *bno=(int)atoi(pp+1); // Extract the pid. if((pp=strrchr(*cname, '.'))) { *pp='\0'; *pid=(pid_t)atoi(pp+1); } } return 0; } int str_to_cntr(const char *str, struct cntr *cntr, char **path) { int ret=-1; char *tok=NULL; char *copy=NULL; if(!(copy=strdup_w(str, __func__))) return -1; if((tok=strtok(copy, "\t\n"))) { int bno=0; pid_t pid=-1; char *tmp=NULL; char *cname=NULL; // First token is 'cntr'. // Second is client name/pid/bno. if(!(tmp=strtok(NULL, "\t\n"))) { logp("Parsing problem in %s: null client\n", __func__); goto end; } if(extract_client_pid_bno(tmp, &cname, &pid, &bno)) goto end; free_w(&cname); cntr->pid=pid; cntr->bno=bno; // Third is the cntr version. if(!(tmp=strtok(NULL, "\t\n"))) { logp("Parsing problem in %s: null version\n", __func__); goto end; } if(atoi(tmp)!=CNTR_VERSION) { ret=0; goto end; } // Fourth is cntr_status. if(!(tmp=strtok(NULL, "\t\n"))) { logp("Parsing problem in %s: null cntr_status\n", __func__); goto end; } cntr->cntr_status=(enum cntr_status)atoi(tmp); if(extract_cntrs(cntr, path)) goto end; } ret=0; end: free_w(©); return ret; } #ifndef HAVE_WIN32 int cntr_send_bu(struct asfd *asfd, struct bu *bu, struct conf **confs, enum cntr_status cntr_status) { int ret=-1; uint16_t flags; struct cstat *clist=NULL; struct cstat *cstat=NULL; if(!get_int(confs[OPT_SEND_CLIENT_CNTR])) return 0; flags=bu->flags; // Want to setup a cstat and a bu so that we can piggy-back on the // status monitor cntr json code. if(!(cstat=cstat_alloc()) || cstat_init(cstat, get_string(confs[OPT_CNAME]), NULL/*clientconfdir*/)) goto end; cstat->cntrs=get_cntr(confs); cstat->protocol=get_protocol(confs); cstat->cntrs->cntr_status=cntr_status; cstat->run_status=RUN_STATUS_RUNNING; // Hacky provocation to get the json stuff to send counters in the // case where we are actually doing a restore. bu->flags|=BU_WORKING; cstat->bu=bu; clist=cstat; ret=json_send(asfd, clist, cstat, bu, NULL /* logfile */, NULL /* browse */, 0 /* use_cache */, version_to_long(get_string(confs[OPT_PEER_VERSION]))); end: cstat->bu=NULL; // 'bu' was not ours to mess with. cstat->cntrs=NULL; // 'cntrs' was not ours to mess with. bu->flags=flags; // Set flags back to what the were before. cstat_free(&cstat); return ret; } int cntr_send_sdirs(struct asfd *asfd, struct sdirs *sdirs, struct conf **confs, enum cntr_status cntr_status) { int ret=-1; struct bu *bu=NULL; struct bu *bu_list=NULL; // FIX THIS: // It would be better just to set up the correct 'bu' entry instead // of loading everything and then looking through the list. if(bu_get_list_with_working(sdirs, &bu_list)) goto end; for(bu=bu_list; bu; bu=bu->next) if((bu->flags & BU_WORKING) || (bu->flags & BU_FINISHING)) break; if(!bu) { logp("could not find working or finishing backup in %s\n", __func__); goto end; } ret=cntr_send_bu(asfd, bu, confs, cntr_status); end: bu_list_free(&bu_list); return ret; } #endif static enum asl_ret cntr_recv_func(struct asfd *asfd, struct conf **confs, void *param) { struct sel *sel=(struct sel *)param; switch(json_input(asfd, sel)) { case 0: return ASL_CONTINUE; case 1: case 2: return ASL_END_OK; default: return ASL_END_ERROR; } } int cntr_recv(struct asfd *asfd, struct conf **confs) { int ret=-1; struct sel *sel=NULL; struct cntr_ent *e; struct cntr *cntr=get_cntr(confs); if(!(sel=sel_alloc())) goto end; if(!get_int(confs[OPT_SEND_CLIENT_CNTR])) goto ok; if(json_input_init()) goto end; if(asfd->simple_loop(asfd, confs, sel, __func__, cntr_recv_func) || !sel->clist || !sel->clist->cntrs) goto end; for(e=sel->clist->cntrs->list; e; e=e->next) { set_count_val(cntr, e->cmd, e->count); set_changed_val(cntr, e->cmd, e->changed); set_same_val(cntr, e->cmd, e->same); set_deleted_val(cntr, e->cmd, e->deleted); set_phase1_val(cntr, e->cmd, e->phase1); } ok: ret=0; end: json_input_free(); sel_free(&sel); return ret; } const char *cntr_status_to_str(struct cntr *cntr) { switch(cntr->cntr_status) { case CNTR_STATUS_SCANNING: return CNTR_STATUS_STR_SCANNING; case CNTR_STATUS_BACKUP: return CNTR_STATUS_STR_BACKUP; case CNTR_STATUS_MERGING: return CNTR_STATUS_STR_MERGING; case CNTR_STATUS_SHUFFLING: return CNTR_STATUS_STR_SHUFFLING; case CNTR_STATUS_LISTING: return CNTR_STATUS_STR_LISTING; case CNTR_STATUS_RESTORING: return CNTR_STATUS_STR_RESTORING; case CNTR_STATUS_VERIFYING: return CNTR_STATUS_STR_VERIFYING; case CNTR_STATUS_DELETING: return CNTR_STATUS_STR_DELETING; case CNTR_STATUS_DIFFING: return CNTR_STATUS_STR_DIFFING; default: return "unknown"; } } enum cntr_status cntr_str_to_status(const char *str) { if(!strcmp(str, CNTR_STATUS_STR_SCANNING)) return CNTR_STATUS_SCANNING; else if(!strcmp(str, CNTR_STATUS_STR_BACKUP)) return CNTR_STATUS_BACKUP; else if(!strcmp(str, CNTR_STATUS_STR_MERGING)) return CNTR_STATUS_MERGING; else if(!strcmp(str, CNTR_STATUS_STR_SHUFFLING)) return CNTR_STATUS_SHUFFLING; else if(!strcmp(str, CNTR_STATUS_STR_LISTING)) return CNTR_STATUS_LISTING; else if(!strcmp(str, CNTR_STATUS_STR_RESTORING)) return CNTR_STATUS_RESTORING; else if(!strcmp(str, CNTR_STATUS_STR_VERIFYING)) return CNTR_STATUS_VERIFYING; else if(!strcmp(str, CNTR_STATUS_STR_DELETING)) return CNTR_STATUS_DELETING; else if(!strcmp(str, CNTR_STATUS_STR_DIFFING)) return CNTR_STATUS_DIFFING; return CNTR_STATUS_UNSET; } const char *cntr_status_to_action_str(struct cntr *cntr) { switch(cntr->cntr_status) { case CNTR_STATUS_SCANNING: case CNTR_STATUS_BACKUP: case CNTR_STATUS_MERGING: case CNTR_STATUS_SHUFFLING: return "backup"; case CNTR_STATUS_LISTING: return "list"; case CNTR_STATUS_RESTORING: return "restore"; case CNTR_STATUS_VERIFYING: return "verify"; case CNTR_STATUS_DELETING: return "delete"; case CNTR_STATUS_DIFFING: return "diff"; default: return "unknown"; } } int check_fail_on_warning(int fail_on_warning, struct cntr_ent *warn_ent) { if(!fail_on_warning || !warn_ent || !warn_ent->count) return 0; logp("fail_on_warning is set and warning count is %" PRIu64 "\n", warn_ent->count); return -1; } burp-2.4.0/src/cntr.h000066400000000000000000000072631404341324700144050ustar00rootroot00000000000000#ifndef _COUNTER_H #define _COUNTER_H #define CNTR_TABULATE 0x00000001 #define CNTR_SINGLE_FIELD 0x00000002 #include "burp.h" #include "cmd.h" #include "action.h" #include "bu.h" #include "server/sdirs.h" #define CNTR_ENT_SIZE 256 #define CNTR_STATUS_STR_SCANNING "scanning" #define CNTR_STATUS_STR_BACKUP "backup" #define CNTR_STATUS_STR_MERGING "merging" #define CNTR_STATUS_STR_SHUFFLING "shuffling" #define CNTR_STATUS_STR_LISTING "listing" #define CNTR_STATUS_STR_RESTORING "restoring" #define CNTR_STATUS_STR_VERIFYING "verifying" #define CNTR_STATUS_STR_DELETING "deleting" #define CNTR_STATUS_STR_DIFFING "diffing" struct asfd; struct conf; struct cstat; enum cntr_status { CNTR_STATUS_UNSET=0, CNTR_STATUS_SCANNING, CNTR_STATUS_BACKUP, CNTR_STATUS_MERGING, CNTR_STATUS_SHUFFLING, CNTR_STATUS_LISTING, CNTR_STATUS_RESTORING, CNTR_STATUS_VERIFYING, CNTR_STATUS_DELETING, CNTR_STATUS_DIFFING }; typedef struct cntr_ent cntr_ent_t; struct cntr_ent { enum cmd cmd; char *field; char *label; uint8_t flags; uint64_t count; uint64_t changed; uint64_t same; uint64_t deleted; uint64_t phase1; cntr_ent_t *next; }; struct cntr { // Want to be able to order the entries for output. struct cntr_ent *list; // Also want to be able to index each entry by a cmd, for fast // lookup when incrementing a counter. struct cntr_ent *ent[CNTR_ENT_SIZE]; // These should have their own individual cmd entries. uint64_t warning; uint64_t byte; uint64_t recvbyte; uint64_t sentbyte; // Buffer to use for the forked child to write statuses to the parent. size_t str_max_len; char *str; enum cntr_status cntr_status; char *cname; // For list management in status server. int pid; int found; int bno; struct cntr *next; }; extern struct cntr *cntr_alloc(void); extern int cntr_init(struct cntr *cntr, const char *cname, pid_t pid); extern void cntr_free(struct cntr **cntr); extern void cntrs_free(struct cntr **cntrs); extern const char *bytes_to_human(uint64_t counter); extern void cntr_set_bytes(struct cntr *cntr, struct asfd *asfd); extern void cntr_print(struct cntr *cntr, enum action act); extern int cntr_stats_to_file(struct cntr *cntr, const char *directory, enum action act); extern void cntr_print_end(struct cntr *c); extern void cntr_print_end_phase1(struct cntr *c); extern void cntr_add(struct cntr *c, char ch, int print); extern void cntr_add_new(struct cntr *c, char ch); extern void cntr_add_same(struct cntr *c, char ch); extern void cntr_add_changed(struct cntr *c, char ch); extern void cntr_add_deleted(struct cntr *c, char ch); extern void cntr_add_bytes(struct cntr *c, uint64_t bytes); extern void cntr_add_phase1(struct cntr *c, char ch, int print); extern void cntr_add_val(struct cntr *c, char ch, uint64_t val); extern void cntr_add_same_val(struct cntr *c, char ch, uint64_t val); extern void cntr_add_changed_val(struct cntr *c, char ch, uint64_t val); #ifndef HAVE_WIN32 extern size_t cntr_to_str(struct cntr *cntr, const char *path); extern int cntr_send_bu(struct asfd *asfd, struct bu *bu, struct conf **confs, enum cntr_status cntr_status); extern int cntr_send_sdirs(struct asfd *asfd, struct sdirs *sdirs, struct conf **confs, enum cntr_status cntr_status); #endif extern int str_to_cntr(const char *str, struct cntr *cntr, char **path); extern int cntr_recv(struct asfd *asfd, struct conf **conf); extern const char *cntr_status_to_str(struct cntr *cntr); extern enum cntr_status cntr_str_to_status(const char *str); extern const char *cntr_status_to_action_str(struct cntr *cntr); extern int extract_client_pid_bno(char *buf, char **cname, pid_t *pid, int *bno); extern int check_fail_on_warning( int fail_on_warning, struct cntr_ent *warn_ent ); #endif burp-2.4.0/src/conf.c000066400000000000000000000715131404341324700143560ustar00rootroot00000000000000#include "burp.h" #include "strlist.h" #include "conf.h" #include "log.h" #include "alloc.h" #include "cntr.h" #include "prepend.h" #include "server/dpth.h" #include enum burp_mode str_to_burp_mode(const char *str) { if(!strcmp(str, "server")) return BURP_MODE_SERVER; else if(!strcmp(str, "client")) return BURP_MODE_CLIENT; logp("Unknown mode setting: %s\n", str); return BURP_MODE_UNSET; } static const char *burp_mode_to_str(enum burp_mode bm) { switch(bm) { case BURP_MODE_UNSET: return "unset"; case BURP_MODE_SERVER: return "server"; case BURP_MODE_CLIENT: return "client"; default: return "unknown"; } } enum recovery_method str_to_recovery_method(const char *str) { if(!strcmp(str, "delete")) return RECOVERY_METHOD_DELETE; else if(!strcmp(str, "resume")) return RECOVERY_METHOD_RESUME; logp("Unknown working_dir_recovery_method setting: %s\n", str); return RECOVERY_METHOD_UNSET; } const char *recovery_method_to_str(enum recovery_method r) { switch(r) { case RECOVERY_METHOD_DELETE: return "delete"; case RECOVERY_METHOD_RESUME: return "resume"; default: return "unknown"; } } const char *rshash_to_str(enum rshash r) { switch(r) { case RSHASH_UNSET: return "unset"; case RSHASH_MD4: return "md4"; case RSHASH_BLAKE2: return "blake2"; default: return "unknown"; } } enum protocol str_to_protocol(const char *str) { if(!strcmp(str, "0")) return PROTO_AUTO; else if(!strcmp(str, "1")) return PROTO_1; else if(!strcmp(str, "2")) return PROTO_2; logp("Unknown protocol setting: %s\n", str); return PROTO_AUTO; } struct strlist *get_strlist(struct conf *conf) { assert(conf->conf_type==CT_STRLIST); return conf->data.sl; } char *get_string(struct conf *conf) { assert(conf->conf_type==CT_STRING); return conf->data.s; } int get_int(struct conf *conf) { assert(conf->conf_type==CT_UINT); return conf->data.i; } uint64_t get_uint64_t(struct conf *conf) { assert(conf->conf_type==CT_SSIZE_T); return conf->data.uint64; } float get_float(struct conf *conf) { assert(conf->conf_type==CT_FLOAT); return conf->data.f; } mode_t get_mode_t(struct conf *conf) { assert(conf->conf_type==CT_MODE_T); return conf->data.mode; } enum burp_mode get_e_burp_mode(struct conf *conf) { assert(conf->conf_type==CT_E_BURP_MODE); return conf->data.burp_mode; } enum protocol get_e_protocol(struct conf *conf) { assert(conf->conf_type==CT_E_PROTOCOL); return conf->data.protocol; } enum protocol get_protocol(struct conf **confs) { return get_e_protocol(confs[OPT_PROTOCOL]); } enum recovery_method get_e_recovery_method(struct conf *conf) { assert(conf->conf_type==CT_E_RECOVERY_METHOD); return conf->data.recovery_method; } enum rshash get_e_rshash(struct conf *conf) { assert(conf->conf_type==CT_E_RSHASH); return conf->data.rshash; } struct cntr *get_cntr(struct conf **confs) { return confs[OPT_CNTR]->data.cntr; } int set_string(struct conf *conf, const char *s) { assert(conf->conf_type==CT_STRING); if(conf->data.s) free_w(&(conf->data.s)); if(s && !(conf->data.s=strdup_w(s, __func__))) return -1; return 0; } int set_int(struct conf *conf, unsigned int i) { assert(conf->conf_type==CT_UINT); conf->data.i=i; return 0; } int set_strlist(struct conf *conf, struct strlist *s) { assert(conf->conf_type==CT_STRLIST); if(conf->data.sl) strlists_free(&conf->data.sl); conf->data.sl=s; return 0; } int set_float(struct conf *conf, float f) { assert(conf->conf_type==CT_FLOAT); conf->data.f=f; return 0; } int set_e_burp_mode(struct conf *conf, enum burp_mode bm) { assert(conf->conf_type==CT_E_BURP_MODE); conf->data.burp_mode=bm; return 0; } int set_e_protocol(struct conf *conf, enum protocol p) { assert(conf->conf_type==CT_E_PROTOCOL); conf->data.protocol=p; return 0; } int set_protocol(struct conf **confs, enum protocol p) { return set_e_protocol(confs[OPT_PROTOCOL], p); } int set_e_recovery_method(struct conf *conf, enum recovery_method r) { assert(conf->conf_type==CT_E_RECOVERY_METHOD); conf->data.recovery_method=r; return 0; } int set_e_rshash(struct conf *conf, enum rshash r) { assert(conf->conf_type==CT_E_RSHASH); conf->data.rshash=r; return 0; } int set_mode_t(struct conf *conf, mode_t m) { assert(conf->conf_type==CT_MODE_T); conf->data.mode=m; return 0; } int set_uint64_t(struct conf *conf, uint64_t s) { assert(conf->conf_type==CT_SSIZE_T); conf->data.uint64=s; return 0; } int set_cntr(struct conf *conf, struct cntr *cntr) { assert(conf->conf_type==CT_CNTR); conf->data.cntr=cntr; return 0; } int add_to_strlist(struct conf *conf, const char *value, int include) { assert(conf->conf_type==CT_STRLIST); if(conf->flags & CONF_FLAG_STRLIST_SORTED) return strlist_add_sorted(&(conf->data.sl), value, include); else return strlist_add(&(conf->data.sl), value, include); } int add_to_strlist_include_uniq(struct conf *conf, const char *value) { return strlist_add_sorted_uniq(&(conf->data.sl), value, 1); } void conf_free_content(struct conf *c) { if(!c) return; switch(c->conf_type) { case CT_STRING: free_w(&c->data.s); break; case CT_STRLIST: strlists_free(&c->data.sl); break; case CT_CNTR: cntr_free(&c->data.cntr); break; case CT_FLOAT: case CT_E_BURP_MODE: case CT_E_PROTOCOL: case CT_E_RECOVERY_METHOD: case CT_E_RSHASH: case CT_UINT: case CT_MODE_T: case CT_SSIZE_T: memset(&c->data, 0, sizeof(c->data)); break; } } void confs_memcpy(struct conf **dst, struct conf **src) { int i=0; for(i=0; iflags & CONF_FLAG_INCEXC) conf_free_content(confs[i]); } static void sc(struct conf *conf, uint8_t flags, enum conf_type conf_type, const char *field) { conf->conf_type=conf_type; conf->field=field; conf->flags=flags; memset(&conf->data, 0, sizeof(conf->data)); } static int sc_str(struct conf *conf, const char *def, uint8_t flags, const char *field) { sc(conf, flags, CT_STRING, field); return set_string(conf, def); } static int sc_int(struct conf *conf, unsigned int def, uint8_t flags, const char *field) { sc(conf, flags, CT_UINT, field); return set_int(conf, def); } static int sc_lst(struct conf *conf, struct strlist *def, uint8_t flags, const char *field) { sc(conf, flags, CT_STRLIST, field); return set_strlist(conf, def); } static int sc_flt(struct conf *conf, float def, uint8_t flags, const char *field) { sc(conf, flags, CT_FLOAT, field); return set_float(conf, def); } static int sc_ebm(struct conf *conf, enum burp_mode def, uint8_t flags, const char *field) { sc(conf, flags, CT_E_BURP_MODE, field); return set_e_burp_mode(conf, def); } static int sc_epr(struct conf *conf, enum protocol def, uint8_t flags, const char *field) { sc(conf, flags, CT_E_PROTOCOL, field); return set_e_protocol(conf, def); } static int sc_rec(struct conf *conf, enum recovery_method def, uint8_t flags, const char *field) { sc(conf, flags, CT_E_RECOVERY_METHOD, field); return set_e_recovery_method(conf, def); } static int sc_rsh(struct conf *conf, enum rshash def, uint8_t flags, const char *field) { sc(conf, flags, CT_E_RSHASH, field); return set_e_rshash(conf, def); } static int sc_mod(struct conf *conf, mode_t def, uint8_t flags, const char *field) { sc(conf, flags, CT_MODE_T, field); return set_mode_t(conf, def); } static int sc_u64(struct conf *conf, uint64_t def, uint8_t flags, const char *field) { sc(conf, flags, CT_SSIZE_T, field); return set_uint64_t(conf, def); } static int sc_cntr(struct conf *conf, struct cntr *def, uint8_t flags, const char *field) { sc(conf, flags, CT_CNTR, field); return set_cntr(conf, def); } static int reset_conf(struct conf **c, enum conf_opt o) { // Do this with a switch statement, so that we get compiler warnings // if anything is missed. switch(o) { case OPT_BURP_MODE: return sc_ebm(c[o], BURP_MODE_UNSET, 0, "mode"); case OPT_LOCKFILE: return sc_str(c[o], 0, 0, "lockfile"); case OPT_PIDFILE: return sc_str(c[o], 0, 0, "pidfile"); case OPT_SSL_CERT_CA: return sc_str(c[o], 0, 0, "ssl_cert_ca"); case OPT_SSL_CERT: return sc_str(c[o], 0, 0, "ssl_cert"); case OPT_SSL_KEY: return sc_str(c[o], 0, 0, "ssl_key"); case OPT_SSL_KEY_PASSWORD: // FIX THIS: synonym: ssl_cert_password return sc_str(c[o], 0, 0, "ssl_key_password"); case OPT_SSL_PEER_CN: return sc_str(c[o], 0, 0, "ssl_peer_cn"); case OPT_SSL_CIPHERS: return sc_str(c[o], 0, 0, "ssl_ciphers"); case OPT_SSL_COMPRESSION: return sc_int(c[o], 5, 0, "ssl_compression"); case OPT_SSL_VERIFY_PEER_EARLY: return sc_int(c[o], 0, 0, "ssl_verify_peer_early"); case OPT_RATELIMIT: return sc_flt(c[o], 0, 0, "ratelimit"); case OPT_NETWORK_TIMEOUT: return sc_int(c[o], 60*60*2, 0, "network_timeout"); case OPT_CLIENT_IS_WINDOWS: return sc_int(c[o], 0, 0, "client_is_windows"); case OPT_PEER_VERSION: return sc_str(c[o], 0, 0, "peer_version"); case OPT_PORT: return sc_lst(c[o], 0, 0, "port"); case OPT_STATUS_PORT: return sc_lst(c[o], 0, 0, "status_port"); case OPT_LISTEN: return sc_lst(c[o], 0, 0, "listen"); case OPT_LISTEN_STATUS: return sc_lst(c[o], 0, 0, "listen_status"); case OPT_NETWORK_ALLOW: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_REPLACE, "network_allow"); case OPT_NETWORK_ALLOW_STATUS: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_REPLACE, "network_allow_status"); case OPT_PORT_BACKUP: return sc_int(c[o], 0, 0, "port_backup"); case OPT_PORT_RESTORE: return sc_int(c[o], 0, 0, "port_restore"); case OPT_PORT_VERIFY: return sc_int(c[o], 0, 0, "port_verify"); case OPT_PORT_LIST: return sc_int(c[o], 0, 0, "port_list"); case OPT_PORT_DELETE: return sc_int(c[o], 0, 0, "port_delete"); case OPT_SSL_DHFILE: return sc_str(c[o], 0, 0, "ssl_dhfile"); case OPT_MAX_CHILDREN: return sc_lst(c[o], 0, 0, "max_children"); case OPT_MAX_STATUS_CHILDREN: return sc_lst(c[o], 0, 0, "max_status_children"); case OPT_MAX_PARALLEL_BACKUPS: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "max_parallel_backups"); case OPT_CLIENT_LOCKDIR: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "client_lockdir"); case OPT_UMASK: return sc_mod(c[o], 0022, 0, "umask"); case OPT_MAX_HARDLINKS: return sc_int(c[o], 10000, 0, "max_hardlinks"); case OPT_MAX_STORAGE_SUBDIRS: return sc_int(c[o], MAX_STORAGE_SUBDIRS, 0, "max_storage_subdirs"); case OPT_DAEMON: return sc_int(c[o], 1, 0, "daemon"); case OPT_CA_CONF: return sc_str(c[o], 0, 0, "ca_conf"); case OPT_CA_NAME: return sc_str(c[o], 0, 0, "ca_name"); case OPT_CA_SERVER_NAME: return sc_str(c[o], 0, 0, "ca_server_name"); case OPT_CA_BURP_CA: return sc_str(c[o], 0, 0, "ca_" PACKAGE_TARNAME "_ca"); case OPT_CA_CRL: return sc_str(c[o], 0, 0, "ca_crl"); case OPT_CA_CRL_CHECK: return sc_int(c[o], 0, 0, "ca_crl_check"); case OPT_RBLK_MEMORY_MAX: return sc_u64(c[o], 256*1024*1024, // 256 Mb. CONF_FLAG_CC_OVERRIDE, "rblk_memory_max"); case OPT_SPARSE_SIZE_MAX: return sc_u64(c[o], 256*1024*1024, // 256 Mb. CONF_FLAG_CC_OVERRIDE, "sparse_size_max"); case OPT_MONITOR_LOGFILE: return sc_str(c[o], 0, 0, "monitor_logfile"); case OPT_MONITOR_EXE: return sc_str(c[o], 0, 0, "monitor_exe"); case OPT_BACKUP_FAILOVERS_LEFT: return sc_int(c[o], 0, 0, ""); case OPT_CNAME: return sc_str(c[o], 0, 0, "cname"); case OPT_CNAME_LOWERCASE: return sc_int(c[o], 0, 0, "cname_lowercase"); case OPT_CNAME_FQDN: return sc_int(c[o], 1, 0, "cname_fqdn"); case OPT_PASSWORD: return sc_str(c[o], 0, 0, "password"); case OPT_PASSWD: return sc_str(c[o], 0, 0, "passwd"); case OPT_SERVER: return sc_str(c[o], 0, 0, "server"); case OPT_SERVER_FAILOVER: return sc_lst(c[o], 0, 0, "server_failover"); case OPT_FAILOVER_ON_BACKUP_ERROR: return sc_int(c[o], 0, 0, "failover_on_backup_error"); case OPT_ENCRYPTION_PASSWORD: return sc_str(c[o], 0, 0, "encryption_password"); case OPT_AUTOUPGRADE_OS: return sc_str(c[o], 0, 0, "autoupgrade_os"); case OPT_AUTOUPGRADE_DIR: return sc_str(c[o], 0, 0, "autoupgrade_dir"); case OPT_CA_CSR_DIR: return sc_str(c[o], 0, 0, "ca_csr_dir"); case OPT_RANDOMISE: return sc_int(c[o], 0, 0, "randomise"); case OPT_RESTORE_LIST: return sc_str(c[o], 0, 0, "restore_list"); case OPT_ENABLED: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "enabled"); case OPT_SERVER_CAN_OVERRIDE_INCLUDES: return sc_int(c[o], 1, 0, "server_can_override_includes"); case OPT_BACKUP: return sc_str(c[o], 0, CONF_FLAG_INCEXC_RESTORE, "backup"); case OPT_BACKUP2: return sc_str(c[o], 0, 0, "backup2"); case OPT_RESTOREPREFIX: return sc_str(c[o], 0, CONF_FLAG_INCEXC_RESTORE, "restoreprefix"); case OPT_STRIP_FROM_PATH: return sc_str(c[o], 0, CONF_FLAG_INCEXC_RESTORE, "stripfrompath"); case OPT_BROWSEFILE: return sc_str(c[o], 0, 0, "browsefile"); case OPT_BROWSEDIR: return sc_str(c[o], 0, 0, "browsedir"); case OPT_GLOB_AFTER_SCRIPT_PRE: return sc_int(c[o], 1, 0, "glob_after_script_pre"); case OPT_B_SCRIPT_PRE: return sc_str(c[o], 0, 0, "backup_script_pre"); case OPT_B_SCRIPT_PRE_ARG: return sc_lst(c[o], 0, 0, "backup_script_pre_arg"); case OPT_B_SCRIPT_POST: return sc_str(c[o], 0, 0, "backup_script_post"); case OPT_B_SCRIPT_POST_ARG: return sc_lst(c[o], 0, 0, "backup_script_post_arg"); case OPT_B_SCRIPT_POST_RUN_ON_FAIL: return sc_int(c[o], 0, 0, "backup_script_post_run_on_fail"); case OPT_B_SCRIPT_RESERVED_ARGS: return sc_int(c[o], 1, 0, "backup_script_reserved_args"); case OPT_R_SCRIPT_PRE: return sc_str(c[o], 0, 0, "restore_script_pre"); case OPT_R_SCRIPT_PRE_ARG: return sc_lst(c[o], 0, 0, "restore_script_pre_arg"); case OPT_R_SCRIPT_POST: return sc_str(c[o], 0, 0, "restore_script_post"); case OPT_R_SCRIPT_POST_ARG: return sc_lst(c[o], 0, 0, "restore_script_post_arg"); case OPT_R_SCRIPT_POST_RUN_ON_FAIL: return sc_int(c[o], 0, 0, "restore_script_post_run_on_fail"); case OPT_B_SCRIPT: return sc_str(c[o], 0, 0, "backup_script"); case OPT_B_SCRIPT_ARG: return sc_lst(c[o], 0, 0, "backup_script_arg"); case OPT_R_SCRIPT: return sc_str(c[o], 0, 0, "restore_script"); case OPT_R_SCRIPT_ARG: return sc_lst(c[o], 0, 0, "restore_script_arg"); case OPT_R_SCRIPT_RESERVED_ARGS: return sc_int(c[o], 1, 0, "restore_script_reserved_args"); case OPT_SEND_CLIENT_CNTR: return sc_int(c[o], 0, 0, "send_client_cntr"); case OPT_SUPER_CLIENT: return sc_str(c[o], 0, 0, ""); case OPT_RESTORE_PATH: return sc_str(c[o], 0, 0, "restore_path"); case OPT_ORIG_CLIENT: return sc_str(c[o], 0, CONF_FLAG_INCEXC_RESTORE, "orig_client"); case OPT_CONNECT_CLIENT: return sc_str(c[o], 0, 0, ""); case OPT_CNTR: return sc_cntr(c[o], 0, 0, ""); case OPT_VSS_RESTORE: return sc_int(c[o], VSS_RESTORE_ON, 0, ""); case OPT_READALL: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "readall"); case OPT_BREAKPOINT: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "breakpoint"); case OPT_CONFFILE: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "conffile"); case OPT_SYSLOG: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "syslog"); case OPT_STDOUT: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "stdout"); case OPT_PROGRESS_COUNTER: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "progress_counter"); case OPT_USER: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "user"); case OPT_GROUP: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "group"); case OPT_PROTOCOL: return sc_epr(c[o], PROTO_AUTO, CONF_FLAG_CC_OVERRIDE, "protocol"); case OPT_DIRECTORY: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "directory"); case OPT_TIMESTAMP_FORMAT: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "timestamp_format"); case OPT_CLIENTCONFDIR: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "clientconfdir"); case OPT_FORK: return sc_int(c[o], 1, 0, "fork"); case OPT_DIRECTORY_TREE: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "directory_tree"); case OPT_PASSWORD_CHECK: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "password_check"); case OPT_MANUAL_DELETE: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "manual_delete"); case OPT_MONITOR_BROWSE_CACHE: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "monitor_browse_cache"); case OPT_S_SCRIPT_PRE: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "server_script_pre"); case OPT_S_SCRIPT_PRE_ARG: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_REPLACE, "server_script_pre_arg"); case OPT_S_SCRIPT_PRE_NOTIFY: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "server_script_pre_notify"); case OPT_S_SCRIPT_POST: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "server_script_post"); case OPT_S_SCRIPT_POST_ARG: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_REPLACE, "server_script_post_arg"); case OPT_S_SCRIPT_POST_RUN_ON_FAIL: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "server_script_post_run_on_fail"); case OPT_S_SCRIPT_POST_NOTIFY: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "server_script_post_notify"); case OPT_S_SCRIPT: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "server_script"); case OPT_S_SCRIPT_ARG: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_REPLACE, "server_script_arg"); case OPT_S_SCRIPT_NOTIFY: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "server_script_notify"); case OPT_HARDLINKED_ARCHIVE: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "hardlinked_archive"); case OPT_KEEP: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_REPLACE, "keep"); case OPT_LIBRSYNC: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "librsync"); case OPT_LIBRSYNC_MAX_SIZE: return sc_u64(c[o], 0, CONF_FLAG_CC_OVERRIDE, "librsync_max_size"); case OPT_COMPRESSION: return sc_int(c[o], 9, CONF_FLAG_CC_OVERRIDE, "compression"); case OPT_VERSION_WARN: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "version_warn"); case OPT_PATH_LENGTH_WARN: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "path_length_warn"); case OPT_HARD_QUOTA: return sc_u64(c[o], 0, CONF_FLAG_CC_OVERRIDE, "hard_quota"); case OPT_SOFT_QUOTA: return sc_u64(c[o], 0, CONF_FLAG_CC_OVERRIDE, "soft_quota"); case OPT_TIMER_SCRIPT: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "timer_script"); case OPT_TIMER_ARG: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_REPLACE, "timer_arg"); case OPT_TIMER_REPEAT_INTERVAL: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "timer_repeat_interval"); case OPT_LABEL: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_REPLACE, "label"); case OPT_N_SUCCESS_SCRIPT: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "notify_success_script"); case OPT_N_SUCCESS_ARG: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_REPLACE, "notify_success_arg"); case OPT_N_SUCCESS_WARNINGS_ONLY: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "notify_success_warnings_only"); case OPT_N_SUCCESS_CHANGES_ONLY: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "notify_success_changes_only"); case OPT_N_FAILURE_SCRIPT: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "notify_failure_script"); case OPT_N_FAILURE_ARG: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_REPLACE, "notify_failure_arg"); case OPT_N_FAILURE_BACKUP_FAILOVERS_LEFT: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "notify_failure_on_backup_with_failovers_left"); case OPT_N_FAILURE_BACKUP_WORKING_DELETION: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "notify_failure_on_backup_working_dir_deletion"); case OPT_RESTORE_CLIENTS: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_SORTED, "restore_client"); case OPT_SUPER_CLIENTS: return sc_lst(c[o], 0, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_STRLIST_SORTED, "super_client"); case OPT_DEDUP_GROUP: return sc_str(c[o], 0, CONF_FLAG_CC_OVERRIDE, "dedup_group"); case OPT_CLIENT_CAN_DELETE: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "client_can_delete"); case OPT_CLIENT_CAN_DIFF: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "client_can_diff"); case OPT_CLIENT_CAN_FORCE_BACKUP: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "client_can_force_backup"); case OPT_CLIENT_CAN_LIST: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "client_can_list"); case OPT_CLIENT_CAN_MONITOR: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "client_can_monitor"); case OPT_CLIENT_CAN_RESTORE: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "client_can_restore"); case OPT_CLIENT_CAN_VERIFY: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "client_can_verify"); case OPT_SERVER_CAN_RESTORE: return sc_int(c[o], 1, CONF_FLAG_CC_OVERRIDE, "server_can_restore"); case OPT_WORKING_DIR_RECOVERY_METHOD: return sc_rec(c[o], RECOVERY_METHOD_DELETE, CONF_FLAG_CC_OVERRIDE, "working_dir_recovery_method"); case OPT_MAX_RESUME_ATTEMPTS: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "max_resume_attempts"); case OPT_FAIL_ON_WARNING: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, "fail_on_warning"); case OPT_RSHASH: return sc_rsh(c[o], RSHASH_UNSET, CONF_FLAG_CC_OVERRIDE, ""); case OPT_MESSAGE: return sc_int(c[o], 0, CONF_FLAG_CC_OVERRIDE, ""); case OPT_INCEXCDIR: // This is a combination of OPT_INCLUDE and OPT_EXCLUDE, so // no field name set for now. return sc_lst(c[o], 0, CONF_FLAG_STRLIST_SORTED, "incexcdir"); case OPT_STARTDIR: // This is a combination of OPT_INCLUDE and OPT_EXCLUDE, so // no field name set for now. // Deliberately not using CONF_FLAG_STRLIST_SORTED because of the // way finalise_start_dirs() works. return sc_lst(c[o], 0, 0, "startdir"); case OPT_INCLUDE: // Combines with OPT_EXCLUDE to make OPT_INCEXCDIR and OPT_STARTDIR. return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_INCEXC_RESTORE|CONF_FLAG_STRLIST_SORTED, "include"); case OPT_EXCLUDE: // Combines with OPT_INCLUDE to make OPT_INCEXCDIR and OPT_STARTDIR. return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_INCEXC_RESTORE|CONF_FLAG_STRLIST_SORTED, "exclude"); case OPT_FSCHGDIR: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "cross_filesystem"); case OPT_NOBACKUP: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "nobackup"); case OPT_INCEXT: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "include_ext"); case OPT_EXCEXT: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "exclude_ext"); case OPT_INCREG: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "include_regex"); case OPT_EXCREG: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "exclude_regex"); case OPT_INCLOGIC: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "include_logic"); case OPT_EXCLOGIC: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "exclude_logic"); case OPT_EXCFS: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "exclude_fs"); case OPT_INCFS: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "include_fs"); case OPT_EXCOM: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "exclude_comp"); case OPT_INCGLOB: return sc_lst(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_STRLIST_SORTED, "include_glob"); case OPT_SEED_SRC: return sc_str(c[o], 0, 0, "seed_src"); case OPT_SEED_DST: return sc_str(c[o], 0, 0, "seed_dst"); case OPT_CROSS_ALL_FILESYSTEMS: return sc_int(c[o], 0, CONF_FLAG_INCEXC, "cross_all_filesystems"); case OPT_READ_ALL_FIFOS: return sc_int(c[o], 0, CONF_FLAG_INCEXC, "read_all_fifos"); case OPT_FIFOS: return sc_lst(c[o], 0, CONF_FLAG_INCEXC, "read_fifo"); case OPT_READ_ALL_BLOCKDEVS: return sc_int(c[o], 0, CONF_FLAG_INCEXC, "read_all_blockdevs"); case OPT_BLOCKDEVS: return sc_lst(c[o], 0, CONF_FLAG_INCEXC, "read_blockdev"); case OPT_MIN_FILE_SIZE: return sc_u64(c[o], 0, CONF_FLAG_INCEXC, "min_file_size"); case OPT_MAX_FILE_SIZE: return sc_u64(c[o], 0, CONF_FLAG_INCEXC, "max_file_size"); case OPT_SPLIT_VSS: return sc_int(c[o], 0, CONF_FLAG_INCEXC, "split_vss"); case OPT_STRIP_VSS: return sc_int(c[o], 0, CONF_FLAG_INCEXC, "strip_vss"); case OPT_VSS_DRIVES: return sc_str(c[o], 0, CONF_FLAG_INCEXC, "vss_drives"); case OPT_ACL: return sc_int(c[o], 1, CONF_FLAG_INCEXC, "acl"); case OPT_XATTR: return sc_int(c[o], 1, CONF_FLAG_INCEXC, "xattr"); case OPT_ATIME: return sc_int(c[o], 0, CONF_FLAG_INCEXC, "atime"); case OPT_SCAN_PROBLEM_RAISES_ERROR: return sc_int(c[o], 0, CONF_FLAG_INCEXC, "scan_problem_raises_error"); case OPT_OVERWRITE: return sc_int(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_INCEXC_RESTORE, "overwrite"); case OPT_STRIP: return sc_int(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_INCEXC_RESTORE, "strip"); case OPT_REGEX: return sc_str(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_INCEXC_RESTORE, "regex"); case OPT_REGEX_CASE_INSENSITIVE: return sc_int(c[o], 0, CONF_FLAG_INCEXC|CONF_FLAG_INCEXC_RESTORE, "regex_case_insensitive"); case OPT_MAX: return 0; // No default, so we get compiler warnings if something was missed. } return -1; } static int set_conf(struct conf *c, const char *value) { switch(c->conf_type) { case CT_STRING: if(set_string(c, value)) return 1; break; case CT_FLOAT: if(set_float(c, atof(value))) return 1; break; case CT_E_BURP_MODE: { enum burp_mode bm; bm=str_to_burp_mode(value); if(bm==BURP_MODE_UNSET || set_e_burp_mode(c, bm)) return 1; break; } case CT_E_RECOVERY_METHOD: { enum recovery_method rm; rm=str_to_recovery_method(value); if(rm==RECOVERY_METHOD_UNSET || set_e_recovery_method(c, rm)) return 1; break; } // FIX THIS case CT_E_RSHASH: case CT_UINT: case CT_MODE_T: case CT_SSIZE_T: case CT_E_PROTOCOL: case CT_STRLIST: case CT_CNTR: break; } return 0; } int conf_set(struct conf **confs, const char *field, const char *value) { int i=0; int r=0; for(i=0; ifield, field)) continue; r+=set_conf(confs[i], value); } return r; } static char *conf_data_to_str(struct conf *conf) { size_t l=256; char *ret=NULL; if(!conf->field || !*conf->field) return NULL; if(!(ret=(char *)calloc_w(1, l, __func__))) return ret; *ret='\0'; switch(conf->conf_type) { case CT_STRING: snprintf(ret, l, "%32s: %s\n", conf->field, get_string(conf)?get_string(conf):""); break; case CT_FLOAT: snprintf(ret, l, "%32s: %g\n", conf->field, get_float(conf)); break; case CT_E_BURP_MODE: snprintf(ret, l, "%32s: %s\n", conf->field, burp_mode_to_str(get_e_burp_mode(conf))); break; case CT_E_PROTOCOL: snprintf(ret, l, "%32s: %d\n", conf->field, get_e_protocol(conf)); break; case CT_E_RECOVERY_METHOD: snprintf(ret, l, "%32s: %s\n", conf->field, recovery_method_to_str( get_e_recovery_method(conf))); break; case CT_E_RSHASH: snprintf(ret, l, "%32s: %s\n", conf->field, rshash_to_str(get_e_rshash(conf))); break; case CT_UINT: snprintf(ret, l, "%32s: %u\n", conf->field, get_int(conf)); break; case CT_STRLIST: { int count=0; char piece[256]=""; struct strlist *s; for(s=get_strlist(conf); s; s=s->next) { snprintf(piece, sizeof(piece), "%32s: %s\n", conf->field, s->path); if(astrcat(&ret, piece, __func__)) return ret; count++; } if(!count) snprintf(ret, l, "%32s:\n", conf->field); break; } case CT_MODE_T: snprintf(ret, l, "%32s: %o\n", conf->field, get_mode_t(conf)); break; case CT_SSIZE_T: snprintf(ret, l, "%32s: %" PRIu64 "\n", conf->field, get_uint64_t(conf)); break; case CT_CNTR: break; } return ret; } struct conf **confs_alloc(void) { int i=0; struct conf **confs=NULL; if(!(confs=(struct conf **) calloc_w(OPT_MAX, sizeof(struct conf *), __func__))) return NULL; for(i=0; iflags)) continue; // if(!*(confs[i]->field)) continue; str=conf_data_to_str(confs[i]); if(str && *str) printf("%s", str); free_w(&str); } return 0; } burp-2.4.0/src/conf.h000066400000000000000000000212341404341324700143560ustar00rootroot00000000000000#ifndef _CONF_H #define _CONF_H #define CONF_FLAG_CC_OVERRIDE 0x01 #define CONF_FLAG_INCEXC 0x02 #define CONF_FLAG_INCEXC_RESTORE 0x04 #define CONF_FLAG_STRLIST_SORTED 0x08 #define CONF_FLAG_STRLIST_REPLACE 0x10 enum burp_mode { BURP_MODE_UNSET=0, BURP_MODE_SERVER, BURP_MODE_CLIENT }; enum protocol { PROTO_AUTO=0, PROTO_1, PROTO_2 }; enum recovery_method { RECOVERY_METHOD_UNSET=0, RECOVERY_METHOD_DELETE, RECOVERY_METHOD_RESUME }; enum rshash { RSHASH_UNSET=0, RSHASH_MD4, RSHASH_BLAKE2 }; enum vss_restore { VSS_RESTORE_OFF=0, VSS_RESTORE_OFF_STRIP, VSS_RESTORE_ON }; enum conf_type { CT_STRING=0, CT_UINT, CT_FLOAT, CT_MODE_T, CT_SSIZE_T, CT_E_BURP_MODE, CT_E_PROTOCOL, CT_E_RECOVERY_METHOD, CT_E_RSHASH, CT_STRLIST, CT_CNTR, }; struct conf { enum conf_type conf_type; const char *field; union { char *s; float f; enum burp_mode burp_mode; enum recovery_method recovery_method; enum protocol protocol; enum rshash rshash; mode_t mode; uint64_t uint64; unsigned int i; struct strlist *sl; struct cntr *cntr; } data; int flags; }; enum conf_opt { OPT_CONFFILE=0, OPT_BURP_MODE, OPT_LOCKFILE, OPT_PIDFILE, OPT_SYSLOG, OPT_STDOUT, OPT_PROGRESS_COUNTER, OPT_SSL_CERT_CA, OPT_SSL_CERT, OPT_SSL_KEY, OPT_SSL_KEY_PASSWORD, OPT_SSL_PEER_CN, OPT_SSL_CIPHERS, OPT_SSL_COMPRESSION, OPT_SSL_VERIFY_PEER_EARLY, OPT_USER, OPT_GROUP, OPT_RATELIMIT, OPT_NETWORK_TIMEOUT, OPT_CLIENT_IS_WINDOWS, OPT_PEER_VERSION, OPT_PROTOCOL, OPT_RSHASH, OPT_MESSAGE, OPT_CNAME_LOWERCASE, // force lowercase cname, client or server option OPT_CNAME_FQDN, // use fqdn cname, client or server option OPT_VSS_RESTORE, // Server options. OPT_LISTEN, OPT_LISTEN_STATUS, OPT_NETWORK_ALLOW, OPT_NETWORK_ALLOW_STATUS, OPT_DIRECTORY, OPT_TIMESTAMP_FORMAT, OPT_CLIENTCONFDIR, OPT_SSL_DHFILE, OPT_MAX_CHILDREN, OPT_MAX_STATUS_CHILDREN, OPT_MAX_PARALLEL_BACKUPS, OPT_CLIENT_LOCKDIR, OPT_UMASK, OPT_MAX_HARDLINKS, OPT_MAX_STORAGE_SUBDIRS, OPT_FORK, OPT_DAEMON, OPT_DIRECTORY_TREE, OPT_CA_CONF, OPT_CA_NAME, OPT_CA_SERVER_NAME, OPT_CA_BURP_CA, OPT_CA_CRL_CHECK, OPT_CA_CRL, OPT_PASSWORD_CHECK, OPT_MANUAL_DELETE, OPT_RBLK_MEMORY_MAX, OPT_SPARSE_SIZE_MAX, OPT_MONITOR_LOGFILE, // An ncurses client option, from command line. OPT_MONITOR_BROWSE_CACHE, OPT_MONITOR_EXE, OPT_BACKUP_FAILOVERS_LEFT, // Client options. OPT_CNAME, // set on the server when client connects OPT_PORT, OPT_STATUS_PORT, OPT_PORT_BACKUP, OPT_PORT_RESTORE, OPT_PORT_VERIFY, OPT_PORT_LIST, OPT_PORT_DELETE, OPT_PASSWORD, // also a clientconfdir option OPT_PASSWD, // also a clientconfdir option OPT_ENABLED, // also a clientconfdir option OPT_SERVER, OPT_SERVER_FAILOVER, OPT_FAILOVER_ON_BACKUP_ERROR, OPT_ENCRYPTION_PASSWORD, OPT_AUTOUPGRADE_OS, OPT_AUTOUPGRADE_DIR, // also a server option OPT_CA_CSR_DIR, OPT_RANDOMISE, OPT_SERVER_CAN_OVERRIDE_INCLUDES, OPT_RESTORE_LIST, // This block of client stuff is all to do with what files to backup. OPT_STARTDIR, OPT_INCEXCDIR, OPT_INCLUDE, OPT_EXCLUDE, OPT_FSCHGDIR, OPT_NOBACKUP, OPT_INCEXT, // include extensions OPT_EXCEXT, // exclude extensions OPT_INCREG, // include (regular expression) OPT_EXCREG, // exclude (regular expression) OPT_INCLOGIC, // include logic expression OPT_EXCLOGIC, // exclude logic expression OPT_EXCFS, // exclude filesystems OPT_INCFS, // include filesystems OPT_EXCOM, // exclude from compression OPT_INCGLOB, // include (glob expression) OPT_SEED_SRC, OPT_SEED_DST, OPT_CROSS_ALL_FILESYSTEMS, OPT_READ_ALL_FIFOS, OPT_FIFOS, OPT_READ_ALL_BLOCKDEVS, OPT_BLOCKDEVS, OPT_MIN_FILE_SIZE, OPT_MAX_FILE_SIZE, OPT_SPLIT_VSS, OPT_STRIP_VSS, OPT_VSS_DRIVES, OPT_ACL, OPT_XATTR, OPT_ATIME, OPT_SCAN_PROBLEM_RAISES_ERROR, // These are to do with restore. OPT_OVERWRITE, OPT_STRIP, OPT_STRIP_FROM_PATH, OPT_BACKUP, OPT_BACKUP2, // For diffs. OPT_RESTOREPREFIX, OPT_REGEX, OPT_REGEX_CASE_INSENSITIVE, // To do with listing. OPT_BROWSEFILE, OPT_BROWSEDIR, // Backup/restore client scripts. OPT_B_SCRIPT_PRE, OPT_B_SCRIPT_PRE_ARG, OPT_B_SCRIPT_POST, OPT_B_SCRIPT_POST_ARG, OPT_B_SCRIPT_POST_RUN_ON_FAIL, OPT_B_SCRIPT_RESERVED_ARGS, OPT_R_SCRIPT_PRE, OPT_R_SCRIPT_PRE_ARG, OPT_R_SCRIPT_POST, OPT_R_SCRIPT_POST_ARG, OPT_R_SCRIPT_POST_RUN_ON_FAIL, OPT_R_SCRIPT_RESERVED_ARGS, // eval glob after script pre. OPT_GLOB_AFTER_SCRIPT_PRE, // Server scripts. OPT_S_SCRIPT_PRE, OPT_S_SCRIPT_PRE_ARG, OPT_S_SCRIPT_PRE_NOTIFY, OPT_S_SCRIPT_POST, OPT_S_SCRIPT_POST_ARG, OPT_S_SCRIPT_POST_RUN_ON_FAIL, OPT_S_SCRIPT_POST_NOTIFY, // Use these when you want to give the same args to both post and pre // scripts. // Backup/restore client scripts. OPT_B_SCRIPT, OPT_B_SCRIPT_ARG, OPT_R_SCRIPT, OPT_R_SCRIPT_ARG, // Server scripts. OPT_S_SCRIPT, OPT_S_SCRIPT_ARG, OPT_S_SCRIPT_NOTIFY, // Client options on the server. // They can be set globally in the server config, or for each client. OPT_HARDLINKED_ARCHIVE, OPT_KEEP, OPT_WORKING_DIR_RECOVERY_METHOD, OPT_FAIL_ON_WARNING, OPT_MAX_RESUME_ATTEMPTS, OPT_LIBRSYNC, OPT_LIBRSYNC_MAX_SIZE, OPT_COMPRESSION, OPT_VERSION_WARN, OPT_PATH_LENGTH_WARN, OPT_HARD_QUOTA, OPT_SOFT_QUOTA, OPT_TIMER_SCRIPT, OPT_TIMER_ARG, OPT_TIMER_REPEAT_INTERVAL, OPT_LABEL, // Notify scripts OPT_N_SUCCESS_SCRIPT, OPT_N_SUCCESS_ARG, OPT_N_SUCCESS_WARNINGS_ONLY, OPT_N_SUCCESS_CHANGES_ONLY, OPT_N_FAILURE_SCRIPT, OPT_N_FAILURE_ARG, OPT_N_FAILURE_BACKUP_FAILOVERS_LEFT, OPT_N_FAILURE_BACKUP_WORKING_DELETION, OPT_RESTORE_CLIENTS, OPT_SUPER_CLIENTS, OPT_DEDUP_GROUP, OPT_CLIENT_CAN_DELETE, OPT_CLIENT_CAN_DIFF, OPT_CLIENT_CAN_FORCE_BACKUP, OPT_CLIENT_CAN_LIST, OPT_CLIENT_CAN_MONITOR, OPT_CLIENT_CAN_RESTORE, OPT_CLIENT_CAN_VERIFY, OPT_SERVER_CAN_RESTORE, // Set to 1 on both client and server when the server is able to send // counters on resume/verify/restore. OPT_SEND_CLIENT_CNTR, // Set on the server to the super client name (the one that you // connected with) when the client has switched to a different set of // client backups. OPT_SUPER_CLIENT, // Path to the server initiated restore file. OPT_RESTORE_PATH, // Original client that backed up. Used when doing a server initiated // restore to an alternative client, OPT_ORIG_CLIENT, // The client that connected. OPT_CONNECT_CLIENT, OPT_CNTR, // For testing. OPT_BREAKPOINT, // readall capability OPT_READALL, OPT_MAX }; extern struct conf **confs_alloc(void); extern void confs_free(struct conf ***confs); extern void confs_free_content(struct conf **confs); extern int confs_init(struct conf **confs); extern void conf_free_content(struct conf *c); extern void confs_free_content(struct conf **confs); extern void confs_null(struct conf **confs); extern void confs_memcpy(struct conf **dst, struct conf **src); extern void free_incexcs(struct conf **confs); extern int conf_set(struct conf **confs, const char *field, const char *value); extern int confs_dump(struct conf **confs, int flags); extern struct strlist *get_strlist(struct conf *conf); extern char *get_string(struct conf *conf); extern int get_int(struct conf *conf); extern float get_float(struct conf *conf); extern uint64_t get_uint64_t(struct conf *conf); extern mode_t get_mode_t(struct conf *conf); extern enum burp_mode get_e_burp_mode(struct conf *conf); extern enum protocol get_e_protocol(struct conf *conf); extern enum protocol get_protocol(struct conf **confs); extern enum recovery_method get_e_recovery_method(struct conf *conf); extern enum rshash get_e_rshash(struct conf *conf); extern struct cntr *get_cntr(struct conf **confs); extern int set_cntr(struct conf *conf, struct cntr *cntr); extern int set_string(struct conf *conf, const char *s); extern int set_strlist(struct conf *conf, struct strlist *s); extern int set_int(struct conf *conf, unsigned int i); extern int set_e_burp_mode(struct conf *conf, enum burp_mode bm); extern int set_e_protocol(struct conf *conf, enum protocol p); extern int set_protocol(struct conf **confs, enum protocol p); extern int set_e_rshash(struct conf *conf, enum rshash r); extern int set_mode_t(struct conf *conf, mode_t m); extern int set_float(struct conf *conf, float f); extern int set_uint64_t(struct conf *conf, uint64_t s); extern int add_to_strlist(struct conf *conf, const char *value, int include); extern int add_to_strlist_include_uniq(struct conf *conf, const char *value); extern enum burp_mode str_to_burp_mode(const char *str); extern enum protocol str_to_protocol(const char *str); extern const char *recovery_method_to_str(enum recovery_method r); extern enum recovery_method str_to_recovery_method(const char *str); extern int set_e_recovery_method(struct conf *conf, enum recovery_method r); extern const char *rshash_to_str(enum rshash r); #endif burp-2.4.0/src/conffile.c000066400000000000000000001163501404341324700152150ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "conf.h" #include "fsops.h" #include "handy.h" #include "lock.h" #include "log.h" #include "msg.h" #include "pathcmp.h" #include "prepend.h" #include "strlist.h" #include "times.h" #include "client/glob_windows.h" #include "conffile.h" static struct strlist *cli_overrides=NULL; void conf_set_cli_overrides(struct strlist *overrides) { cli_overrides=overrides; } // This will strip off everything after the last quote. So, configs like this // should work: // exclude_regex = "[A-Z]:/pagefile.sys" # swap file (Windows XP, 7, 8) // Return 1 for quotes removed, -1 for error, 0 for OK. static int remove_quotes(const char *f, char **v, char quote) { char *dp=NULL; char *sp=NULL; char *copy=NULL; int ret=1; // If it does not start with a quote, leave it alone. if(**v!=quote) return 0; if(!(copy=strdup_w(*v, __func__))) { ret=-1; goto end; } for(dp=*v, sp=copy+1; *sp; sp++) { if(*sp==quote) { // Found a matching quote. Stop here. *dp='\0'; for(sp++; *sp && isspace(*sp); sp++) { } // Do not complain about trailing comments. if(*sp && *sp!='#') logp("ignoring trailing characters after quote in config '%s = %s'\n", f, copy); goto end; } else if(*sp=='\\') { sp++; *dp=*sp; dp++; if(*sp!=quote && *sp!='\\') logp("unknown escape sequence '\\%c' in config '%s = %s' - treating it as '%c'\n", *sp, f, copy, *sp); } else { *dp=*sp; dp++; } } logp("Did not find closing quote in config '%s = %s'\n", f, copy); *dp='\0'; end: free_w(©); return ret; } // Get field and value pair. int conf_get_pair(char buf[], char **f, char **v, int *r) { char *cp=NULL; char *eq=NULL; // strip leading space for(cp=buf; *cp && isspace(*cp); cp++) { } if(!*cp || *cp=='#') { *f=NULL; *v=NULL; return 0; } *f=cp; if(!(eq=strchr(*f, '='))) return -1; *eq='\0'; // Strip white space from before the equals sign. for(cp=eq-1; *cp && (isspace(*cp) || *cp == ':'); cp--) { if(*cp == ':') *r=1; *cp='\0'; } // Skip white space after the equals sign. for(cp=eq+1; *cp && isspace(*cp); cp++) { } *v=cp; // Strip white space at the end of the line. for(cp+=strlen(cp)-1; *cp && isspace(*cp); cp--) { *cp='\0'; } // FIX THIS: Make this more sophisticated - it should understand // escapes, for example. switch(remove_quotes(*f, v, '\'')) { case -1: return -1; case 1: break; default: // If single quotes were not removed, try to remove // double quotes. if(remove_quotes(*f, v, '\"')<0) return -1; break; } if(!*f || !**f || !*v || !**v) return -1; return 0; } static int conf_error(const char *conf_path, int line) { logp("%s: parse error on line %d\n", conf_path, line); return -1; } int get_file_size(const char *v, uint64_t *dest, const char *conf_path, int line) { // Store in bytes, allow k/m/g. const char *cp=NULL; *dest=strtoul(v, NULL, 10); for(cp=v; *cp && (isspace(*cp) || isdigit(*cp)); cp++) { } if(tolower(*cp)=='k') *dest*=1024; else if(tolower(*cp)=='m') *dest*=1024*1024; else if(tolower(*cp)=='g') *dest*=1024*1024*1024; else if(!*cp || *cp=='b') { } else { logp("Unknown file size type '%s' - please use b/kb/mb/gb\n", cp); return conf_error(conf_path, line); } return 0; } static int pre_post_override(struct conf *c, struct conf *pre, struct conf *post) { const char *override=get_string(c); if(!override) return 0; if(set_string(pre, override) || set_string(post, override)) return -1; return 0; } #ifdef HAVE_LINUX_OS struct fstype { const char *str; uint64_t flag; }; // Sorted in magic number order. static struct fstype fstypes[]={ { "devfs", 0x00001373 }, { "devpts", 0x00001CD1 }, { "smbfs", 0x0000517B }, { "nfs", 0x00006969 }, { "romfs", 0x00007275 }, { "iso9660", 0x00009660 }, { "devtmpfs", 0x00009FA0 }, { "proc", 0x00009FA0 }, { "usbdevfs", 0x00009FA2 }, { "ext2", 0x0000EF53 }, { "ext3", 0x0000EF53 }, { "ext4", 0x0000EF53 }, { "ecryptfs", 0x0000F15F }, { "cgroup", 0x0027E0EB }, { "ceph", 0x00C36400 }, { "tmpfs", 0x01021994 }, { "zfs", 0x2FC12FC1 }, { "jfs", 0x3153464A }, { "autofs", 0x42494E4D }, { "reiserfs", 0x52654973 }, { "ntfs", 0x5346544E }, { "xfs", 0x58465342 }, { "sysfs", 0x62656572 }, { "debugfs", 0x64626720 }, { "fusectl", 0x65735543 }, { "fuse.lxcfs", 0x65735546 }, { "securityfs", 0x73636673 }, { "ramfs", 0x858458F6 }, { "btrfs", 0x9123683E }, { "hugetlbfs", 0x958458F6 }, { "smb2", 0xFE534D42 }, { "cifs", 0xFF534D42 }, { NULL, 0 }, }; /* Use this C code to figure out what f_type gets set to. #include #include int main(int argc, char *argv[]) { int i=0; struct statfs buf; if(argc<1) { printf("not enough args\n"); return -1; } if(statfs(argv[1], &buf)) { printf("error\n"); return -1; } printf("0x%08X\n", buf.f_type); return 0; } */ #endif static int fstype_to_flag(const char *fstype, long *flag) { #ifdef HAVE_LINUX_OS int i=0; for(i=0; fstypes[i].str; i++) { if(!strcmp(fstypes[i].str, fstype)) { *flag=fstypes[i].flag; return 0; } } #else return 0; #endif return -1; } static int get_compression(const char *v) { const char *cp=v; if(!strncmp(v, "gzip", strlen("gzip")) || !(strncmp(v, "zlib", strlen("zlib")))) cp=v+strlen("gzip"); // Or "zlib". if(strlen(cp)==1 && isdigit(*cp)) return atoi(cp); return -1; } static int load_conf_field_and_value(struct conf **c, const char *f, // field const char *v, // value int reset, // reset flag const char *conf_path, int line) { if(!strcmp(f, "compression")) { int compression=get_compression(v); if(compression<0) return -1; set_int(c[OPT_COMPRESSION], compression); } else if(!strcmp(f, "ssl_compression")) { int compression=get_compression(v); if(compression<0) return -1; set_int(c[OPT_SSL_COMPRESSION], compression); } else if(!strcmp(f, "ratelimit")) { float f=0; f=atof(v); // User is specifying Mega bits per second. // Need to convert to bytes per second. f=(f*1024*1024)/8; if(!f) { logp("ratelimit should be greater than zero\n"); return -1; } set_float(c[OPT_RATELIMIT], f); } else { int i=0; for(i=0; ifield, f)) continue; switch(c[i]->conf_type) { case CT_STRING: return set_string(c[i], v); case CT_UINT: return set_int(c[i], atoi(v)); case CT_FLOAT: return set_float(c[i], atof(v)); break; case CT_MODE_T: return set_mode_t(c[i], strtol(v, NULL, 8)); case CT_SSIZE_T: { uint64_t s=0; return get_file_size(v, &s, conf_path, line) || set_uint64_t(c[i], s); } case CT_E_BURP_MODE: return set_e_burp_mode(c[i], str_to_burp_mode(v)); case CT_E_PROTOCOL: return set_e_protocol(c[i], str_to_protocol(v)); case CT_E_RECOVERY_METHOD: return set_e_recovery_method(c[i], str_to_recovery_method(v)); case CT_STRLIST: if (reset) set_strlist(c[i], 0); return add_to_strlist(c[i], v, !strcmp(c[i]->field, "include")); case CT_E_RSHASH: break; case CT_CNTR: break; // No default so we get a warning if something // was missed; } } } return 0; } // Recursing, so need to define this ahead of conf_parse_line. static int conf_load_lines_from_file(const char *conf_path, struct conf **confs); static int deal_with_dot_inclusion(const char *conf_path, char **extrafile, struct conf **confs) { int ret=-1; char *copy=NULL; #ifndef HAVE_WIN32 int i=0; glob_t globbuf; if(**extrafile!='/') #else if(strlen(*extrafile)>2 && (*extrafile)[1]!=':') #endif { // It is relative to the directory that the // current conf file is in. char *cp=NULL; char *tmp=NULL; if(!(copy=strdup_w(conf_path, __func__))) goto end; if((cp=strrchr(copy, '/'))) *cp='\0'; if(!(tmp=prepend_s(copy, *extrafile))) { log_out_of_memory(__func__); goto end; } free_w(extrafile); *extrafile=tmp; } #ifndef HAVE_WIN32 // Treat it is a glob expression. memset(&globbuf, 0, sizeof(globbuf)); glob(*extrafile, 0, NULL, &globbuf); for(i=0; (unsigned int)i #endif static char *extract_cn(X509_NAME *subj) { int nid; int index; ASN1_STRING *d; X509_NAME_ENTRY *e; nid=OBJ_txt2nid("CN"); if((index=X509_NAME_get_index_by_NID(subj, nid, -1))<0 || !(e=X509_NAME_get_entry(subj, index)) || !(d=X509_NAME_ENTRY_get_data(e))) return NULL; #if OPENSSL_VERSION_NUMBER < 0x1010000fL || defined(LIBRESSL_VERSION_NUMBER) return (char *)ASN1_STRING_data(d); #else return (char *)ASN1_STRING_get0_data(d); #endif } static void mangle_cname(char **cname, struct conf **c) { if(!get_int(c[OPT_CNAME_FQDN])) strip_fqdn(cname); if(get_int(c[OPT_CNAME_LOWERCASE])) strlwr(*cname); } static int get_cname_from_ssl_cert(struct conf **c) { int ret=-1; struct fzp *fzp=NULL; X509 *cert=NULL; X509_NAME *subj=NULL; char *path=get_string(c[OPT_SSL_CERT]); const char *cn=NULL; char *copy=NULL; if(!path || !(fzp=fzp_open(path, "rb"))) return 0; if(!(cert=fzp_PEM_read_X509(fzp))) { logp("unable to parse %s in: %s\n", path, __func__); goto end; } if(!(subj=X509_get_subject_name(cert))) { logp("unable to get subject from %s in: %s\n", path, __func__); goto end; } if(!(cn=extract_cn(subj))) { logp("could not get CN from %s\n", path); goto end; } if(!(copy=strdup_w(cn, __func__))) goto end; mangle_cname(©, c); if(set_string(c[OPT_CNAME], copy)) goto end; logp("cname from cert: %s\n", cn); if(strcmp(copy, cn)) logp("cname mangled to: %s\n", copy); ret=0; end: if(cert) X509_free(cert); fzp_close(&fzp); free_w(©); return ret; } #ifdef HAVE_WIN32 #include #include #endif static int get_fqdn(struct conf **c) { int ret=-1; int gai_result; struct addrinfo hints; struct addrinfo *info=NULL; char hostname[1024]=""; char *fqdn=NULL; hostname[1023] = '\0'; if(gethostname(hostname, 1023)) { logp("gethostname() failed: %s\n", strerror(errno)); goto end; } memset(&hints, 0, sizeof hints); hints.ai_family=AF_UNSPEC; hints.ai_socktype=SOCK_STREAM; hints.ai_flags=AI_CANONNAME; if((gai_result=getaddrinfo(hostname, NULL, &hints, &info))) { logp("getaddrinfo in %s: %s\n", __func__, gai_strerror(gai_result)); logp("Using %s\n", hostname); if(!(fqdn=strdup_w(hostname, __func__))) goto end; } else { //for(p=info; p; p=p->ai_next) // Just use the first one. if(!info) { logp("Got no hostname in %s\n", __func__); goto end; } if(!(fqdn=strdup_w(info->ai_canonname, __func__))) goto end; } mangle_cname(&fqdn, c); if(set_string(c[OPT_CNAME], fqdn)) goto end; logp("cname from hostname: %s\n", get_string(c[OPT_CNAME])); ret=0; end: if(info) freeaddrinfo(info); free_w(&fqdn); return ret; } const char *confs_get_lockfile(struct conf **confs) { const char *lockfile=get_string(confs[OPT_LOCKFILE]); if(!lockfile) lockfile=get_string(confs[OPT_PIDFILE]); return lockfile; } static int general_conf_checks(struct conf **c, const char *path, int *r) { if(!confs_get_lockfile(c)) conf_problem(path, "lockfile unset", r); if(!get_string(c[OPT_SSL_CERT])) conf_problem(path, "ssl_cert unset", r); if(!get_string(c[OPT_SSL_CERT_CA])) conf_problem(path, "ssl_cert_ca unset", r); return 0; } static int client_conf_checks(struct conf **c, const char *path, int *r) { int ret=-1; char *copy=NULL; const char *autoupgrade_os=get_string(c[OPT_AUTOUPGRADE_OS]); if(!get_string(c[OPT_CNAME])) { if(get_cname_from_ssl_cert(c)) goto end; // There was no error. This is probably a new install. // Try getting the fqdn and using that. if(!get_string(c[OPT_CNAME])) { if(get_fqdn(c)) goto end; if(!get_string(c[OPT_CNAME])) conf_problem(path, "client name unset", r); } } if(!get_string(c[OPT_PASSWORD])) { logp("password not set, falling back to \"password\"\n"); if(set_string(c[OPT_PASSWORD], "password")) goto end; } if(!get_string(c[OPT_SERVER])) conf_problem(path, "server unset", r); if(!get_string(c[OPT_SSL_PEER_CN])) { const char *server=get_string(c[OPT_SERVER]); logp("ssl_peer_cn unset\n"); if(server) { char *cp=NULL; if(!(copy=strdup_w(server, __func__))) goto end; if((cp=strchr(copy, ':'))) *cp='\0'; logp("falling back to '%s'\n", copy); if(set_string(c[OPT_SSL_PEER_CN], copy)) goto end; } } if(autoupgrade_os && strstr(autoupgrade_os, "..")) conf_problem(path, "autoupgrade_os must not contain a '..' component", r); if(get_string(c[OPT_CA_BURP_CA])) { if(!get_string(c[OPT_CA_CSR_DIR])) burp_ca_conf_problem(path, "ca_csr_dir", r); if(!get_string(c[OPT_SSL_CERT_CA])) burp_ca_conf_problem(path, "ssl_cert_ca", r); if(!get_string(c[OPT_SSL_CERT])) burp_ca_conf_problem(path, "ssl_cert", r); if(!get_string(c[OPT_SSL_KEY])) burp_ca_conf_problem(path, "ssl_key", r); } if(!r) { struct strlist *l; logp("Listing configured paths:\n"); for(l=get_strlist(c[OPT_INCEXCDIR]); l; l=l->next) logp("%s: %s\n", l->flag?"include":"exclude", l->path); logp("Listing starting paths:\n"); for(l=get_strlist(c[OPT_STARTDIR]); l; l=l->next) if(l->flag) logp("%s\n", l->path); } ret=0; end: free_w(©); return ret; } static int finalise_keep_args(struct conf **c) { struct strlist *k; struct strlist *last=NULL; uint64_t mult=1; for(k=get_strlist(c[OPT_KEEP]); k; k=k->next) { if(!(k->flag=atoi(k->path))) { logp("'keep' value cannot be set to '%s'\n", k->path); return -1; } mult*=k->flag; // An error if you try to keep backups every second // for 100 years. if(mult>52560000) { logp("Your 'keep' values are far too high. High enough to keep a backup every second for 10 years. Please lower them to something sensible.\n"); return -1; } last=k; } // If more than one keep value is set, add one to the last one. // This is so that, for example, having set 7, 4, 6, then // a backup of age 7*4*6=168 or more is guaranteed to be kept. // Otherwise, only 7*4*5=140 would be guaranteed to be kept. k=get_strlist(c[OPT_KEEP]); if(k && k->next) last->flag++; return 0; } static int incexc_munge(struct conf **c, struct strlist *s) { #ifdef HAVE_WIN32 convert_backslashes(&s->path); #endif if(!is_absolute(s->path)) { logp("ERROR: Please use absolute include/exclude paths.\n"); return -1; } if(add_to_strlist(c[OPT_INCEXCDIR], s->path, s->flag)) return -1; return 0; } static int finalise_incexc_dirs(struct conf **c) { struct strlist *s=NULL; for(s=get_strlist(c[OPT_INCLUDE]); s; s=s->next) if(incexc_munge(c, s)) return -1; for(s=get_strlist(c[OPT_EXCLUDE]); s; s=s->next) if(incexc_munge(c, s)) return -1; if(get_strlist(c[OPT_INCREG]) && !(get_strlist(c[OPT_INCLUDE]) || get_strlist(c[OPT_INCGLOB]))) { logp("Need at least one 'include' or 'include_glob' for the 'include_regex' to work.\n"); return -1; } return 0; } static int add_to_cross_filesystem(struct conf **c, const char *path) { if(strlist_find(get_strlist(c[OPT_FSCHGDIR]), path, 0)) return 0; return add_to_strlist(c[OPT_FSCHGDIR], path, 0); } static int check_start_dirs_and_seed(struct conf **c) { int errors=0; struct strlist *s=NULL; const char *src=get_string(c[OPT_SEED_SRC]); if(!src) return 0; for(s=get_strlist(c[OPT_STARTDIR]); s; s=s->next) { if(!is_subdir(src, s->path)) { logp("ERROR: Starting directories need to be within %s:%s: %s\n", c[OPT_SEED_SRC]->field, src, s->path); errors++; } } return errors; } // This decides which directories to start backing up, and which // are subdirectories which don't need to be started separately. static int finalise_start_dirs(struct conf **c) { struct strlist *s=NULL; struct strlist *last_ie=NULL; struct strlist *last_sd=NULL; // Make sure that the startdir list starts empty, or chaos will ensue. conf_free_content(c[OPT_STARTDIR]); for(s=get_strlist(c[OPT_INCLUDE]); s; s=s->next) { #ifdef HAVE_WIN32 convert_backslashes(&s->path); #endif if(!is_absolute(s->path)) { logp("ERROR: Please use absolute include/exclude paths.\n"); return -1; } // Ensure that we do not backup the same directory twice. if(last_ie && !strcmp(s->path, last_ie->path)) { logp("Directory appears twice in conf: %s\n", s->path); return -1; } // If it is not a subdirectory of the most recent start point, // we have found another start point. if(!get_strlist(c[OPT_STARTDIR]) || !last_sd || !is_subdir(last_sd->path, s->path)) { // Do not use strlist_add_sorted, because last_sd is // relying on incexcdir already being sorted. if(add_to_strlist(c[OPT_STARTDIR], s->path, s->flag)) return -1; last_sd=s; } else { // If it is not a starting directory, it should at // least be included as a cross_filesystem entry. if(add_to_cross_filesystem(c, s->path)) return -1; } last_ie=s; } if(check_start_dirs_and_seed(c)) return -1; return 0; } static int finalise_fschg_dirs(struct conf **c) { struct strlist *s; for(s=get_strlist(c[OPT_FSCHGDIR]); s; s=s->next) strip_trailing_slashes(&s->path); return 0; } // The glob stuff should only run on the client side. static int finalise_glob(struct conf **c) { int ret=-1; #ifdef HAVE_WIN32 if(glob_windows(c)) goto end; #else int i; glob_t globbuf; struct strlist *l; struct strlist *last=NULL; memset(&globbuf, 0, sizeof(globbuf)); for(l=get_strlist(c[OPT_INCGLOB]); l; l=l->next) { glob(l->path, last?GLOB_APPEND:0, NULL, &globbuf); last=l; } for(i=0; (unsigned int)inext) { int s=strlen(l->path); if(s>max) max=s; } if(list) list->flag=max+1; } static int finalise_fstypes(struct conf **c, int opt) { struct strlist *l; // Set the strlist flag for the excluded fstypes for(l=get_strlist(c[opt]); l; l=l->next) { l->flag=0; if(!strncasecmp(l->path, "0x", 2)) { l->flag=strtol((l->path)+2, NULL, 16); logp("Excluding file system type 0x%08lX\n", l->flag); } else { if(fstype_to_flag(l->path, &(l->flag))) { logp("Unknown exclude fs type: %s\n", l->path); l->flag=0; } } } return 0; } static int setup_script_arg_override(struct conf *c, struct conf *args) { struct strlist *s; set_strlist(args, NULL); for(s=get_strlist(c); s; s=s->next) if(add_to_strlist(args, s->path, s->flag)) return -1; return 0; } static int setup_script_arg_overrides(struct conf *c, struct conf *pre_args, struct conf *post_args) { if(!get_strlist(c)) return 0; return setup_script_arg_override(c, pre_args) || setup_script_arg_override(c, post_args); } static int listen_config_ok(const char *l) { int port; const char *c=NULL; const char *cp=NULL; for(c=l; *c; c++) if(!isalnum(*c) && *c!=':' && *c!='.') return 0; if(!(cp=strrchr(l, ':'))) return 0; if(l==cp) return 0; cp++; if(!strlen(cp) || strlen(cp)>5) return 0; port=atoi(cp); if(port<=0 || port>65535) return 0; return 1; } static int finalise_server_max_children(struct conf **c, enum conf_opt listen_opt, enum conf_opt max_children_opt) { struct strlist *l; struct strlist *mc; long max_children=5; for(l=get_strlist(c[listen_opt]), mc=get_strlist(c[max_children_opt]); l; l=l->next) { if(!listen_config_ok(l->path)) { logp("Could not parse %s config '%s'\n", c[listen_opt]->field, l->path); return -1; } if(mc) { if((max_children=atol(mc->path))<=0) { logp("%s too low for %s %s\n", c[max_children_opt]->field, c[listen_opt]->field, l->path); return -1; } l->flag=max_children; mc=mc->next; } else { logp("%s %s defaulting to %s %lu\n", c[listen_opt]->field, l->path, c[max_children_opt]->field, max_children); l->flag=max_children; } } if(mc) { logp("too many %s options\n", c[max_children_opt]->field); return -1; } return 0; } static int finalise_client_ports(struct conf **c) { int port=0; struct strlist *p; for(p=get_strlist(c[OPT_PORT]); p; p=p->next) port=atoi(p->path); if(!port) return 0; if(!get_int(c[OPT_PORT_BACKUP])) set_int(c[OPT_PORT_BACKUP], port); if(!get_int(c[OPT_PORT_RESTORE])) set_int(c[OPT_PORT_RESTORE], port); if(!get_int(c[OPT_PORT_VERIFY])) set_int(c[OPT_PORT_VERIFY], get_int(c[OPT_PORT_RESTORE])); if(!get_int(c[OPT_PORT_LIST])) set_int(c[OPT_PORT_LIST], port); if(!get_int(c[OPT_PORT_DELETE])) set_int(c[OPT_PORT_DELETE], port); return 0; } static int apply_cli_overrides(struct conf **confs) { int ret=-1; int line=0; char *opt=NULL; struct strlist *oo=NULL; for(oo=cli_overrides; oo; oo=oo->next) { line++; free_w(&opt); if(!(opt=strdup_w(oo->path, __func__))) goto end; if((ret=conf_parse_line(confs, "", opt, line))) { logp("Unable to parse cli option %d '%s'\n", line, oo->path); goto end; } } ret=0; end: free_w(&opt); return ret; } static int conf_finalise(struct conf **c) { enum burp_mode burp_mode; int s_script_notify=0; if(apply_cli_overrides(c)) return -1; burp_mode=get_e_burp_mode(c[OPT_BURP_MODE]); if(finalise_fstypes(c, OPT_EXCFS) || finalise_fstypes(c, OPT_INCFS)) return -1; strlist_compile_regexes(get_strlist(c[OPT_INCREG])); strlist_compile_regexes(get_strlist(c[OPT_EXCREG])); set_max_ext(get_strlist(c[OPT_INCEXT])); set_max_ext(get_strlist(c[OPT_EXCEXT])); set_max_ext(get_strlist(c[OPT_EXCOM])); if(burp_mode==BURP_MODE_CLIENT && finalise_glob(c)) return -1; if(finalise_incexc_dirs(c) || finalise_start_dirs(c) || finalise_fschg_dirs(c)) return -1; if(finalise_keep_args(c)) return -1; if(burp_mode==BURP_MODE_SERVER) { if(!get_strlist(c[OPT_LISTEN])) { logp("Need at least one 'listen' config.\n"); return -1; } if(finalise_server_max_children(c, OPT_LISTEN, OPT_MAX_CHILDREN) || finalise_server_max_children(c, OPT_LISTEN_STATUS, OPT_MAX_STATUS_CHILDREN)) return -1; } if(burp_mode==BURP_MODE_CLIENT) { if(finalise_client_ports(c)) return -1; } if((s_script_notify=get_int(c[OPT_S_SCRIPT_NOTIFY]))) { set_int(c[OPT_S_SCRIPT_PRE_NOTIFY], s_script_notify); set_int(c[OPT_S_SCRIPT_POST_NOTIFY], s_script_notify); } // These override the specific pre/post script paths with the general // one. For example, if 'server_script' is set, its value is used for // 'server_script_pre' and 'server_script_post'. if(pre_post_override(c[OPT_B_SCRIPT], c[OPT_B_SCRIPT_PRE], c[OPT_B_SCRIPT_POST]) || pre_post_override(c[OPT_R_SCRIPT], c[OPT_R_SCRIPT_PRE], c[OPT_R_SCRIPT_POST]) || pre_post_override(c[OPT_S_SCRIPT], c[OPT_S_SCRIPT_PRE], c[OPT_S_SCRIPT_POST]) // And these do the same for the script arguments. || setup_script_arg_overrides(c[OPT_B_SCRIPT_ARG], c[OPT_B_SCRIPT_PRE_ARG], c[OPT_B_SCRIPT_POST_ARG]) || setup_script_arg_overrides(c[OPT_R_SCRIPT_ARG], c[OPT_R_SCRIPT_PRE_ARG], c[OPT_R_SCRIPT_POST_ARG]) || setup_script_arg_overrides(c[OPT_S_SCRIPT_ARG], c[OPT_S_SCRIPT_PRE_ARG], c[OPT_S_SCRIPT_POST_ARG])) return -1; // We are now done with these. Clear them, otherwise they interfere. set_string(c[OPT_S_SCRIPT], NULL); set_strlist(c[OPT_S_SCRIPT_ARG], NULL); return 0; } static int conf_finalise_global_only(const char *conf_path, struct conf **confs) { int r=0; // Let the caller check the 'keep' value. if(!get_string(confs[OPT_SSL_KEY_PASSWORD]) && set_string(confs[OPT_SSL_KEY_PASSWORD], "")) r--; if(general_conf_checks(confs, conf_path, &r)) r--; switch(get_e_burp_mode(confs[OPT_BURP_MODE])) { case BURP_MODE_SERVER: if(server_conf_checks(confs, conf_path, &r)) r--; break; case BURP_MODE_CLIENT: if(client_conf_checks(confs, conf_path, &r)) r--; break; case BURP_MODE_UNSET: default: logp("%s: mode unset - need 'server' or 'client'\n", conf_path); r--; break; } return r; } static int conf_load_lines_from_file(const char *conf_path, struct conf **confs) { int ret=0; int line=0; FILE *fp=NULL; char buf[4096]=""; if(!(fp=fopen(conf_path, "r"))) { logp("could not open '%s' for reading.\n", conf_path); return -1; } while(fgets(buf, sizeof(buf), fp)) { line++; if(conf_parse_line(confs, conf_path, buf, line)) { conf_error(conf_path, line); ret=-1; } } if(fp) fclose(fp); return ret; } #ifndef UTEST static #endif int conf_load_lines_from_buf(const char *buf, struct conf **c) { int ret=0; int line=0; char *tok=NULL; char *copy=NULL; if(!buf) return 0; if(!(copy=strdup_w(buf, __func__))) return -1; if(!(tok=strtok(copy, "\n"))) { logp("unable to parse conf buffer\n"); free_w(©); return -1; } do { line++; if(conf_parse_line(c, "", tok, line)) { ret=-1; break; } } while((tok=strtok(NULL, "\n"))); free_w(©); return ret; } /* The server runs this when parsing a restore file on the server. Called elsewhere too. */ int conf_parse_incexcs_path(struct conf **c, const char *path) { free_incexcs(c); if(conf_load_lines_from_file(path, c) || conf_finalise(c)) return -1; return 0; } /* The client runs this when the server overrides the incexcs. */ int conf_parse_incexcs_buf(struct conf **c, const char *incexc) { free_incexcs(c); if(conf_load_lines_from_buf(incexc, c) || conf_finalise(c)) return -1; return 0; } /* The client runs this when the server overrides the incexcs for restore. */ int conf_parse_incexcs_srestore(struct conf **c, const char *incexc) { int ret=-1; char *rp=NULL; char *oldprefix=NULL; char *srvprefix=NULL; char *newprefix=NULL; const char *rpfield=c[OPT_RESTOREPREFIX]->field; if(!(rp=get_string(c[OPT_RESTOREPREFIX]))) { logp("The client side must specify a %s!\n", rpfield); goto end; } if(!(oldprefix=strdup_w(rp, __func__))) goto end; free_incexcs(c); set_string(c[OPT_RESTOREPREFIX], NULL); if(conf_load_lines_from_buf(incexc, c) || conf_finalise(c)) goto end; if((srvprefix=get_string(c[OPT_RESTOREPREFIX]))) { if(has_dot_component(srvprefix)) { logp("The server gave %s '%s', which is not allowed!", rpfield, srvprefix); goto end; } if(!strcmp(oldprefix, "/")) { // Avoid double slash. if(!(newprefix=prepend_s("", srvprefix))) goto end; } else { if(!(newprefix=prepend_s(oldprefix, srvprefix))) goto end; } if(set_string(c[OPT_RESTOREPREFIX], newprefix)) goto end; if(build_path_w(newprefix)) goto end; } else { if(set_string(c[OPT_RESTOREPREFIX], oldprefix)) goto end; } ret=0; end: free_w(&oldprefix); free_w(&newprefix); return ret; } static int conf_set_from_global(struct conf **globalc, struct conf **cc) { int i=0; for(i=0; iflags & CONF_FLAG_CC_OVERRIDE)) continue; switch(cc[i]->conf_type) { case CT_STRING: set_string(cc[i], get_string(globalc[i])); break; case CT_UINT: set_int(cc[i], get_int(globalc[i])); break; case CT_FLOAT: set_float(cc[i], get_float(globalc[i])); break; case CT_MODE_T: set_mode_t(cc[i], get_mode_t(globalc[i])); break; case CT_SSIZE_T: set_uint64_t(cc[i], get_uint64_t(globalc[i])); break; case CT_E_BURP_MODE: set_e_burp_mode(cc[i], get_e_burp_mode(globalc[i])); break; case CT_E_PROTOCOL: set_e_protocol(cc[i], get_e_protocol(globalc[i])); break; case CT_E_RECOVERY_METHOD: set_e_recovery_method(cc[i], get_e_recovery_method(globalc[i])); break; case CT_E_RSHASH: set_e_rshash(cc[i], get_e_rshash(globalc[i])); break; case CT_STRLIST: // Done later. break; case CT_CNTR: break; // No default so that there are warnings if anything // was missed. } } // If ssl_peer_cn is not set, default it to the client name. if(!get_string(globalc[OPT_SSL_PEER_CN]) && set_string(cc[OPT_SSL_PEER_CN], get_string(cc[OPT_CNAME]))) return -1; return 0; } static int append_strlist(struct conf *dst, struct conf *src) { struct strlist *s; for(s=get_strlist(src); s; s=s->next) if(add_to_strlist(dst, s->path, s->flag)) return -1; return 0; } // Instead of adding onto the end of the list, this replaces the list. static int conf_set_from_global_arg_list_overrides(struct conf **globalc, struct conf **cc) { int i=0; for(i=0; iconf_type!=CT_STRLIST) continue; if(!(cc[i]->flags & CONF_FLAG_CC_OVERRIDE)) continue; if(cc[i]->flags & CONF_FLAG_STRLIST_REPLACE) { // If there was no cc[i] strlist set, use the global. if(!get_strlist(cc[i]) && append_strlist(cc[i], globalc[i])) return -1; } else { struct conf tmpconf; // A bit painful. tmpconf.conf_type=cc[i]->conf_type; tmpconf.flags=cc[i]->flags; memset(&tmpconf.data, 0, sizeof(tmpconf.data)); if(append_strlist(&tmpconf, globalc[i]) || append_strlist(&tmpconf, cc[i])) return -1; set_strlist(cc[i], get_strlist(&tmpconf)); } } return 0; } static int conf_init_save_cname_and_version(struct conf **cconfs) { int ret=-1; char *cname=NULL; char *cversion=NULL; char *orig_cname=get_string(cconfs[OPT_CNAME]); char *orig_cversion=get_string(cconfs[OPT_PEER_VERSION]); if((orig_cname && !(cname=strdup_w(orig_cname, __func__))) || (orig_cversion && !(cversion=strdup_w(orig_cversion, __func__)))) goto end; set_string(cconfs[OPT_CNAME], NULL); set_string(cconfs[OPT_PEER_VERSION], NULL); if(confs_init(cconfs)) goto end; set_string(cconfs[OPT_CNAME], cname); set_string(cconfs[OPT_PEER_VERSION], cversion); ret=0; end: free_w(&cname); free_w(&cversion); return ret; } static int do_conf_load_overrides(struct conf **globalcs, struct conf **cconfs, const char *path, const char *buf) { // Some client settings can be globally set in the server conf and // overridden in the client specific conf. if(conf_set_from_global(globalcs, cconfs)) return -1; if(buf) { if(conf_load_lines_from_buf(buf, cconfs)) return -1; } else { if(conf_load_lines_from_file(path, cconfs)) return -1; } if(conf_set_from_global_arg_list_overrides(globalcs, cconfs) || conf_finalise(cconfs)) return -1; return 0; } #ifndef UTEST static #endif int conf_load_overrides(struct conf **globalcs, struct conf **cconfs, const char *path) { return do_conf_load_overrides(globalcs, cconfs, path, NULL); } int cname_valid(const char *cname) { if(!cname) return 0; if(cname[0]=='.' || strchr(cname, '/') // Avoid path attacks. || strchr(cname, '\\') // Be cautious of backslashes too. // I am told that emacs tmp files end with '~'. || cname[strlen(cname)-1]=='~') return 0; return 1; } int conf_load_clientconfdir(struct conf **globalcs, struct conf **cconfs) { int ret=-1; char *path=NULL; const char *cname=NULL; if(conf_init_save_cname_and_version(cconfs)) goto end; cname=get_string(cconfs[OPT_CNAME]); if(!cname_valid(cname)) { logp("client name '%s' is not valid\n", cname); goto end; } if(!(path=prepend_s(get_string(globalcs[OPT_CLIENTCONFDIR]), cname))) goto end; ret=conf_load_overrides(globalcs, cconfs, path); end: free_w(&path); return ret; } static int do_load_global_only(struct conf **globalcs, const char *path, const char *buf) { if(set_string(globalcs[OPT_CONFFILE], path)) return -1; if(buf) { if(conf_load_lines_from_buf(buf, globalcs)) return -1; } else { if(conf_load_lines_from_file(path, globalcs)) return -1; } if(conf_finalise(globalcs) || conf_finalise_global_only(path, globalcs)) return -1; return 0; } int conf_load_global_only(const char *path, struct conf **globalcs) { return do_load_global_only(globalcs, path, NULL); } static int restore_client_allowed(struct conf **cconfs, struct conf **sconfs) { struct strlist *r; for(r=get_strlist(sconfs[OPT_SUPER_CLIENTS]); r; r=r->next) if(!strcmp(r->path, get_string(cconfs[OPT_CNAME]))) return 2; for(r=get_strlist(sconfs[OPT_RESTORE_CLIENTS]); r; r=r->next) if(!strcmp(r->path, get_string(cconfs[OPT_CNAME]))) return 1; logp("Access to client is not allowed: %s\n", get_string(sconfs[OPT_CNAME])); return 0; } int conf_switch_to_orig_client(struct conf **globalcs, struct conf **cconfs, const char *orig_client) { int ret=-1; int is_super=0; struct conf **sconfs=NULL; // If we are already the wanted client, no need to switch. if(!strcmp(get_string(cconfs[OPT_CNAME]), orig_client)) return 0; if(!(sconfs=confs_alloc()) || confs_init(sconfs)) goto end; if(set_string(sconfs[OPT_CNAME], orig_client)) goto end; logp("Client wants to switch to client: %s\n", get_string(sconfs[OPT_CNAME])); if(conf_load_clientconfdir(globalcs, sconfs)) { logp("Could not load alternate config: %s", get_string(sconfs[OPT_CNAME])); goto end; } set_int(sconfs[OPT_SEND_CLIENT_CNTR], get_int(cconfs[OPT_SEND_CLIENT_CNTR])); switch(restore_client_allowed(cconfs, sconfs)) { case 1: break; case 2: is_super=1; break; default: goto end; } // Restore client can never force backup. set_int(sconfs[OPT_CLIENT_CAN_FORCE_BACKUP], 0); if(is_super) { set_int(sconfs[OPT_CLIENT_CAN_DELETE], get_int(cconfs[OPT_CLIENT_CAN_DELETE])); set_int(sconfs[OPT_CLIENT_CAN_DIFF], get_int(cconfs[OPT_CLIENT_CAN_DIFF])); set_int(sconfs[OPT_CLIENT_CAN_LIST], get_int(cconfs[OPT_CLIENT_CAN_LIST])); set_int(sconfs[OPT_CLIENT_CAN_MONITOR], get_int(cconfs[OPT_CLIENT_CAN_MONITOR])); set_int(sconfs[OPT_CLIENT_CAN_RESTORE], get_int(cconfs[OPT_CLIENT_CAN_RESTORE])); set_int(sconfs[OPT_CLIENT_CAN_VERIFY], get_int(cconfs[OPT_CLIENT_CAN_VERIFY])); } else { // For the rest of the client_can things, do not allow them on // orig_client if we do not have them ourselves. if(!get_int(cconfs[OPT_CLIENT_CAN_DELETE])) set_int(sconfs[OPT_CLIENT_CAN_DELETE], 0); if(!get_int(cconfs[OPT_CLIENT_CAN_DIFF])) set_int(sconfs[OPT_CLIENT_CAN_DIFF], 0); if(!get_int(cconfs[OPT_CLIENT_CAN_LIST])) set_int(sconfs[OPT_CLIENT_CAN_LIST], 0); if(!get_int(cconfs[OPT_CLIENT_CAN_MONITOR])) set_int(sconfs[OPT_CLIENT_CAN_MONITOR], 0); if(!get_int(cconfs[OPT_CLIENT_CAN_RESTORE])) set_int(sconfs[OPT_CLIENT_CAN_RESTORE], 0); if(!get_int(cconfs[OPT_CLIENT_CAN_VERIFY])) set_int(sconfs[OPT_CLIENT_CAN_VERIFY], 0); } if(set_string(sconfs[OPT_CONNECT_CLIENT], get_string(cconfs[OPT_CONNECT_CLIENT]))) goto end; if(set_string(sconfs[OPT_RESTORE_PATH], get_string(cconfs[OPT_RESTORE_PATH]))) goto end; if(set_string(cconfs[OPT_RESTORE_PATH], NULL)) goto end; set_cntr(sconfs[OPT_CNTR], get_cntr(cconfs)); set_cntr(cconfs[OPT_CNTR], NULL); confs_free_content(cconfs); confs_init(cconfs); confs_memcpy(cconfs, sconfs); confs_null(sconfs); if(set_string(cconfs[OPT_SUPER_CLIENT], get_string(cconfs[OPT_CNAME]))) goto end; if(set_string(cconfs[OPT_ORIG_CLIENT], get_string(cconfs[OPT_CNAME]))) goto end; logp("Switched to client %s\n", get_string(cconfs[OPT_CNAME])); ret=0; end: confs_free(&sconfs); return ret; } char *config_default_path(void) { static char path[256]=""; #ifdef HAVE_WIN32 char *pfenv=NULL; // Burp used to always install to 'C:/Program Files/Burp/', but as // of 1.3.11, it changed to %PROGRAMFILES%. Still want the old way // to work though. So check %PROGRAMFILES% first, then fall back. if((pfenv=getenv("PROGRAMFILES"))) { struct stat statp; snprintf(path, sizeof(path), "%s/%s/%s.conf", pfenv, PACKAGE_NAME, PACKAGE_TARNAME); if(!lstat(path, &statp) && !S_ISDIR(statp.st_mode)) return path; } snprintf(path, sizeof(path), "C:/Program Files/%s/%s.conf", PACKAGE_NAME, PACKAGE_TARNAME); #else snprintf(path, sizeof(path), "%s/%s.conf", SYSCONFDIR, PACKAGE_TARNAME); #endif return path; } burp-2.4.0/src/conffile.h000066400000000000000000000022631404341324700152170ustar00rootroot00000000000000#ifndef _CONFFILE_H #define _CONFFILE_H #include "cntr.h" #include "strlist.h" extern void conf_set_cli_overrides(struct strlist *overrides); extern int conf_get_pair(char buf[], char **field, char **value, int *reset); extern int get_file_size(const char *v, uint64_t *dest, const char *conf_path, int line); extern int conf_parse_incexcs_buf(struct conf **confs, const char *incexc); extern int conf_parse_incexcs_srestore(struct conf **confs, const char *incexc); extern int conf_parse_incexcs_path(struct conf **confs, const char *path); extern int conf_load_clientconfdir(struct conf **globalcs, struct conf **ccconfs); extern int conf_load_global_only(const char *path, struct conf **globalcs); extern const char *confs_get_lockfile(struct conf **confs); extern int conf_switch_to_orig_client(struct conf **globalcs, struct conf **cconfs, const char *orig_client); extern int reeval_glob(struct conf **c); extern char *config_default_path(void); extern int cname_valid(const char *cname); #ifdef UTEST extern int conf_load_lines_from_buf(const char *buf, struct conf **c); extern int conf_load_overrides(struct conf **globalcs, struct conf **cconfs, const char *path); #endif #endif burp-2.4.0/src/cstat.c000066400000000000000000000055001404341324700145400ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "bu.h" #include "cstat.h" #include "log.h" #include "prepend.h" #include "strlist.h" struct cstat *cstat_alloc(void) { return (struct cstat *)calloc_w(1, sizeof(struct cstat), __func__); } int cstat_init(struct cstat *cstat, const char *name, const char *clientconfdir) { if((clientconfdir && !(cstat->conffile=prepend_s(clientconfdir, name))) || !(cstat->name=strdup_w(name, __func__))) return -1; return 0; } static void cstat_free_content(struct cstat *c) { if(!c) return; bu_list_free(&c->bu); free_w(&c->name); free_w(&c->conffile); strlists_free(&c->labels); cntrs_free(&c->cntrs); if(c->sdirs) logp("%s() called without freeing sdirs\n", __func__); c->clientdir_mtime=0; } void cstat_add_cntr_to_list(struct cstat *c, struct cntr *cntr) { cntr->next=c->cntrs; c->cntrs=cntr; } void cstat_remove_cntr_from_list(struct cstat *c, struct cntr *cntr) { struct cntr *x; if(!c || !cntr) return; if(c->cntrs==cntr) { c->cntrs=cntr->next; return; } for(x=c->cntrs; x; x=x->next) { if(x->next==cntr) { x->next=cntr->next; return; } } } void cstat_free(struct cstat **cstat) { if(!cstat || !*cstat) return; cstat_free_content(*cstat); free_v((void **)cstat); } void cstat_add_to_list(struct cstat **clist, struct cstat *cnew) { struct cstat *c=NULL; struct cstat *clast=NULL; for(c=*clist; c; c=c->next) { if(strcmp(cnew->name, c->name)<0) { c->prev=cnew; break; } c->prev=clast; clast=c; } if(clast) { cnew->next=clast->next; clast->next=cnew; cnew->prev=clast; } else { *clist=cnew; cnew->next=c; } } void cstat_list_free(struct cstat **clist) { struct cstat *c; struct cstat *next; struct cstat *prev=NULL; if(*clist) prev=(*clist)->prev; for(c=*clist; c; c=next) { next=c->next; cstat_free(&c); } // Do it in both directions. for(c=prev; c; c=prev) { prev=c->prev; cstat_free(&c); } *clist=NULL; } const char *run_status_to_str(struct cstat *cstat) { switch(cstat->run_status) { case RUN_STATUS_IDLE: return RUN_STATUS_STR_IDLE; case RUN_STATUS_RUNNING: return RUN_STATUS_STR_RUNNING; default: return "unknown"; } } enum run_status run_str_to_status(const char *str) { if(!strcmp(str, RUN_STATUS_STR_IDLE)) return RUN_STATUS_IDLE; else if(!strcmp(str, RUN_STATUS_STR_RUNNING)) return RUN_STATUS_RUNNING; return RUN_STATUS_UNSET; } struct cstat *cstat_get_by_name(struct cstat *clist, const char *name) { struct cstat *c; for(c=clist; c; c=c->next) if(!strcmp(c->name, name)) return c; return NULL; } int cstat_count(struct cstat *clist) { int count=0; struct cstat *c; for(c=clist; c; c=c->next) count++; return count; } burp-2.4.0/src/cstat.h000066400000000000000000000030551404341324700145500ustar00rootroot00000000000000#ifndef _CSTAT_H #define _CSTAT_H #include "conf.h" #include "cntr.h" #define RUN_STATUS_STR_IDLE "idle" #define RUN_STATUS_STR_RUNNING "running" enum run_status { RUN_STATUS_UNSET=0, RUN_STATUS_IDLE, RUN_STATUS_RUNNING, }; struct cstat { char *name; char *conffile; struct strlist *labels; time_t conf_mtime; struct cntr *cntrs; // Set from the parent process. enum run_status run_status; // From the perspective of the server child, whether the connected // client is allowed to view this client. uint8_t permitted; // When the mtime of conffile changes, the following get reloaded. // Declared sdirs as void so that cstat can be declared for both // client and server. Server side will have to cast it. void *sdirs; time_t clientdir_mtime; struct bu *bu; // Backup list. enum protocol protocol; struct cstat *prev; struct cstat *next; }; extern struct cstat *cstat_alloc(void); extern int cstat_init(struct cstat *cstat, const char *name, const char *clientconfdir); extern void cstat_free(struct cstat **cstat); extern void cstat_list_free(struct cstat **clist); extern void cstat_add_to_list(struct cstat **clist, struct cstat *cnew); extern void cstat_add_cntr_to_list(struct cstat *c, struct cntr *cntr); extern void cstat_remove_cntr_from_list(struct cstat *c, struct cntr *cntr); extern const char *run_status_to_str(struct cstat *cstat); extern enum run_status run_str_to_status(const char *str); extern struct cstat *cstat_get_by_name(struct cstat *clist, const char *name); extern int cstat_count(struct cstat *clist); #endif burp-2.4.0/src/forkchild.c000066400000000000000000000046371404341324700154010ustar00rootroot00000000000000#include "burp.h" #include "forkchild.h" #include "fzp.h" #include "log.h" #ifdef HAVE_WIN32 // Windows version of forkchild is in src/win32/compat/compat.cpp #else static pid_t do_forkchild(int sin, int sout, int serr, const char *path, char * const argv[]) { pid_t pid; if((pid=fork())<0) return -1; else if(!pid) { int fd; if((sin>=0 && dup2(sin, STDIN_FILENO)<0) || (sout>=0 && dup2(sout, STDOUT_FILENO)<0) || (serr>=0 && dup2(serr, STDERR_FILENO)<0)) { logp("dup2: %s\n", strerror(errno)); return -1; } if(sout>=0) setbuf(stdout, NULL); if(serr>=0) setbuf(stderr, NULL); /* Close all unused file descriptors before exec. * FD_SETSIZE is not strictly the highest file descriptor-1, * but there does not appear to be a sensible way to find out * the true number, and FD_SETSIZE is a close approximation. * It would be a bit lame if you could open a file whose * descriptor could not be included in an fd_set. */ for(fd=3; fd<(int)FD_SETSIZE; ++fd) close(fd); if(execv(path, argv)) logp("execv %s: %s\n", path, strerror(errno)); exit(1); } return pid; } pid_t forkchild(struct fzp **sin, struct fzp **sout, struct fzp **serr, const char *path, char * const argv[]) { pid_t pid; int sinfds[2]; int soutfds[2]; int serrfds[2]; if((sin && pipe(sinfds)) || (sout && pipe(soutfds)) || (serr && pipe(serrfds))) return -1; if((sin && !(*sin=fzp_dopen(sinfds[1], "w"))) || (sout && !(*sout=fzp_dopen(soutfds[0], "r"))) || (serr && !(*serr=fzp_dopen(serrfds[0], "r")))) return -1; pid=do_forkchild(sin?sinfds[0]:-1, sout?soutfds[1]:-1, serr?serrfds[1]:-1, path, argv); if(sin) close(sinfds[0]); if(sout) close(soutfds[1]); if(serr) close(serrfds[1]); return pid; } pid_t forkchild_fd(int *sin, int *sout, int *serr, const char *path, char * const argv[]) { pid_t pid; int sinfds[2]; int soutfds[2]; int serrfds[2]; if((sin && pipe(sinfds)) || (sout && pipe(soutfds)) || (serr && pipe(serrfds))) return -1; if(sin) *sin=sinfds[1]; if(sout) *sout=soutfds[0]; if(serr) *serr=serrfds[0]; pid=do_forkchild(sin?sinfds[0]:-1, sout?soutfds[1]:-1, serr?serrfds[1]:-1, path, argv); if(sin) close(sinfds[0]); if(sout) close(soutfds[1]); if(serr) close(serrfds[1]); return pid; } pid_t forkchild_no_wait(struct fzp **sin, struct fzp **sout, struct fzp **serr, const char *path, char * const argv[]) { return forkchild(sin, sout, serr, path, argv); } #endif burp-2.4.0/src/forkchild.h000066400000000000000000000006451404341324700154010ustar00rootroot00000000000000#ifndef _FORKCHILD_H #define _FORKCHILD_H struct fzp; extern pid_t forkchild(struct fzp **sin, struct fzp **sout, struct fzp **serr, const char *path, char * const argv[]); extern pid_t forkchild_fd(int *sin, int *sout, int *serr, const char *path, char * const argv[]); extern pid_t forkchild_no_wait(struct fzp **sin, struct fzp **sout, struct fzp **serr, const char *path, char * const argv[]); #endif burp-2.4.0/src/fsops.c000066400000000000000000000263441404341324700145650ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "fsops.h" #include "fzp.h" #include "log.h" #include "pathcmp.h" #include "prepend.h" #ifndef HAVE_WIN32 #include #endif uint32_t fs_name_max=0; uint32_t fs_full_path_max=0; static uint32_t fs_path_max=0; void close_fd(int *fd) { if(!fd || *fd<0) return; //logp("closing %d\n", *fd); close(*fd); *fd=-1; } int is_dir_lstat(const char *path) { struct stat buf; if(lstat(path, &buf)) return -1; return S_ISDIR(buf.st_mode); } int is_reg_lstat(const char *path) { struct stat buf; if(lstat(path, &buf)) return -1; return S_ISREG(buf.st_mode); } int is_dir(const char *path, struct dirent *d) { #ifdef _DIRENT_HAVE_D_TYPE // Faster evaluation on most systems. switch(d->d_type) { case DT_DIR: return 1; case DT_UNKNOWN: break; default: return 0; } #endif return is_dir_lstat(path); } int mkpath(char **rpath, const char *limit) { int ret=-1; char *cp=NULL; struct stat buf; if((cp=strrchr(*rpath, '/'))) { *cp='\0'; #ifdef HAVE_WIN32 if(strlen(*rpath)==2 && (*rpath)[1]==':') { // We are down to the drive letter, which is OK. } else #endif if(!**rpath) { // We are down to the root, which is OK. } else if(lstat(*rpath, &buf)) { // does not exist - recurse further down, then come // back and try to mkdir it. if(mkpath(rpath, limit)) goto end; // Require that the user has set up the required paths // on the server correctly. I have seen problems with // part of the path being a temporary symlink that // gets replaced by burp with a proper directory. // Allow it to create the actual directory specified, // though. // That is, if limit is: // /var/spool/burp // and /var/spool exists, the directory will be // created. // If only /var exists, the directory will not be // created. // Caller can give limit=NULL to create the whole // path with no limit, as in a restore. if(limit && pathcmp(*rpath, limit)<0) { logp("will not mkdir %s\n", *rpath); goto end; } if(mkdir(*rpath, 0777)) { logp("could not mkdir %s: %s\n", *rpath, strerror(errno)); goto end; } } else if(S_ISDIR(buf.st_mode)) { // Is a directory - can put the slash back and return. } else if(S_ISLNK(buf.st_mode)) { // to help with the 'current' symlink } else { // something funny going on logp("warning: wanted '%s' to be a directory\n", *rpath); } } ret=0; end: if(cp) *cp='/'; return ret; } int build_path(const char *datadir, const char *fname, char **rpath, const char *limit) { if(!(*rpath=prepend_s(datadir, fname))) return -1; if(mkpath(rpath, limit)) { free_w(rpath); return -1; } return 0; } int do_rename(const char *oldpath, const char *newpath) { // Be careful, this is not actually atomic. Everything that uses this // needs to deal with the consequences. if(rename(oldpath, newpath)) { logp("could not rename '%s' to '%s': %s\n", oldpath, newpath, strerror(errno)); return -1; } return 0; } int build_path_w(const char *path) { int ret; char *rpath=NULL; ret=build_path(path, "", &rpath, NULL); free_w(&rpath); return ret; } #define RECDEL_ERROR -1 #define RECDEL_OK 0 #define RECDEL_ENTRIES_REMAINING 1 static void get_max(int32_t *max, int32_t default_max) { *max = pathconf(".", default_max); if(*max < 1024) *max = 1024; // Add for EOS. (*max)++; } static int do_recursive_delete(const char *d, const char *file, uint8_t delfiles, int32_t name_max, uint8_t ignore_not_empty_errors) { int ret=RECDEL_ERROR; DIR *dirp=NULL; struct dirent *entry=NULL; struct stat statp; char *directory=NULL; char *fullpath=NULL; if(!file) { if(!(directory=prepend_s(d, ""))) goto end; } else if(!(directory=prepend_s(d, file))) { log_out_of_memory(__func__); goto end; } if(lstat(directory, &statp)) { // path does not exist. ret=RECDEL_OK; goto end; } if(!(dirp=opendir(directory))) { logp("opendir %s in %s: %s\n", directory, __func__, strerror(errno)); goto end; } while(1) { errno=0; if(!(entry=readdir(dirp))) { if(errno) { logp("error in readdir in %s: %s\n", __func__, strerror(errno)); goto end; } // Got to the end of the directory. ret=RECDEL_OK; break; } if(!filter_dot(entry)) continue; free_w(&fullpath); if(!(fullpath=prepend_s(directory, entry->d_name))) goto end; if(is_dir(fullpath, entry)>0) { int r; if((r=do_recursive_delete(directory, entry->d_name, delfiles, name_max, ignore_not_empty_errors))==RECDEL_ERROR) goto end; // do not overwrite ret with OK if it previously // had ENTRIES_REMAINING if(r==RECDEL_ENTRIES_REMAINING) ret=r; } else if(delfiles) { if(unlink(fullpath)) { logp("unlink %s: %s\n", fullpath, strerror(errno)); ret=RECDEL_ENTRIES_REMAINING; } } else { ret=RECDEL_ENTRIES_REMAINING; } } if(ret==RECDEL_OK && rmdir(directory)) { if(errno!=ENOTEMPTY || !ignore_not_empty_errors) { logp("rmdir %s: %s\n", directory, strerror(errno)); ret=RECDEL_ERROR; } } end: if(dirp) closedir(dirp); free_w(&fullpath); free_w(&directory); return ret; } static int do_recursive_delete_w(const char *path, uint8_t delfiles, uint8_t ignore_not_empty_errors) { int32_t name_max; get_max(&name_max, _PC_NAME_MAX); return do_recursive_delete(path, NULL, delfiles, name_max, ignore_not_empty_errors); } int recursive_delete(const char *path) { struct stat statp; // We might have been given a file entry, instead of a directory. if(!lstat(path, &statp) && !S_ISDIR(statp.st_mode)) { if(unlink(path)) { logp("unlink %s: %s\n", path, strerror(errno)); return RECDEL_ENTRIES_REMAINING; } } return do_recursive_delete_w(path, 1, 0/*ignore_not_empty_errors*/); } int recursive_delete_dirs_only(const char *path) { return do_recursive_delete_w(path, 0, 0/*ignore_not_empty_errors*/); } int recursive_delete_dirs_only_no_warnings(const char *path) { return do_recursive_delete_w(path, 0, 1/*ignore_not_empty_errors*/); } int unlink_w(const char *path, const char *func) { if(unlink(path)) { logp("unlink(%s) called from %s(): %s\n", path, func, strerror(errno)); return -1; } return 0; } static void init_max(const char *path, uint32_t *max, int what, uint32_t default_max) { *max=pathconf(path?path:".", what); if(*maxd_name, __func__))) goto error; } if(*nl && compar) qsort(*nl, *count, sizeof(**nl), compar); return 0; error: if(*nl) { int i; for(i=0; i<*count; i++) free_w(&((*nl)[i])); free_v((void **)nl); } return -1; } static int entries_in_directory(const char *path, char ***nl, int *count, int atime, int follow_symlinks, int (*compar)(const char **, const char **)) { int ret=0; DIR *directory=NULL; if(!fs_name_max) { // Get system path and filename maximum lengths. // FIX THIS: maybe this should be done every time a file system // is crossed? if(init_fs_max(path)) return -1; } #if defined(O_DIRECTORY) && defined(O_NOATIME) int dfd=-1; if((dfd=open(path, O_RDONLY|O_DIRECTORY|(atime?0:O_NOATIME) #ifdef O_NOFOLLOW |(follow_symlinks?0:O_NOFOLLOW) #endif ))<0 || !(directory=fdopendir(dfd))) #else // Mac OS X appears to have no O_NOATIME and no fdopendir(), so it should // end up using opendir() here. if(!(directory=opendir(path))) #endif { #if defined(O_DIRECTORY) && defined(O_NOATIME) close_fd(&dfd); #endif ret=1; } else { if(do_get_entries_in_directory(directory, nl, count, (int (*)(const void *, const void *))compar)) ret=-1; } if(directory) closedir(directory); return ret; } int filter_dot(const struct dirent *d) { if(!d || !strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) return 0; return 1; } static int my_alphasort(const char **a, const char **b) { return pathcmp(*a, *b); } int entries_in_directory_alphasort(const char *path, char ***nl, int *count, int atime, int follow_symlinks) { return entries_in_directory(path, nl, count, atime, follow_symlinks, my_alphasort); } #define FULL_CHUNK 4096 int files_equal(const char *opath, const char *npath, int compressed) { int ret=0; size_t ogot; size_t ngot; unsigned int i=0; struct fzp *ofp=NULL; struct fzp *nfp=NULL; static char obuf[FULL_CHUNK]; static char nbuf[FULL_CHUNK]; if(compressed) { ofp=fzp_gzopen(opath, "rb"); nfp=fzp_gzopen(npath, "rb"); } else { ofp=fzp_open(opath, "rb"); nfp=fzp_open(npath, "rb"); } if(!ofp && !nfp) { ret=1; goto end; } if(!ofp && nfp) goto end; if(!nfp && ofp) goto end; while(1) { ogot=fzp_read(ofp, obuf, FULL_CHUNK); ngot=fzp_read(nfp, nbuf, FULL_CHUNK); if(ogot!=ngot) goto end; for(i=0; i=0) close(fd); return ret; } int is_lnk_lstat(const char *path) { struct stat buf; if(lstat(path, &buf)) return -1; return S_ISLNK(buf.st_mode); } int is_lnk_valid(const char *path) { struct stat buf; if(stat(path, &buf)) return 0; return 1; } int do_symlink(const char *oldpath, const char *newpath) { if(!symlink(oldpath, newpath)) return 0; logp("could not symlink '%s' to '%s': %s\n", newpath, oldpath, strerror(errno)); return -1; } static int do_readlink(const char *path, char buf[], size_t buflen) { ssize_t len; if((len=readlink(path, buf, buflen-1))<0) return -1; buf[len]='\0'; return 0; } int readlink_w(const char *path, char buf[], size_t buflen) { struct stat statp; if(lstat(path, &statp)) return -1; if(S_ISLNK(statp.st_mode)) return do_readlink(path, buf, buflen); return -1; } int readlink_w_in_dir(const char *dir, const char *lnk, char buf[], size_t buflen) { char *tmp=NULL; if(!(tmp=prepend_s(dir, lnk))) return -1; readlink_w(tmp, buf, buflen); free_w(&tmp); return 0; } #endif burp-2.4.0/src/fsops.h000066400000000000000000000026611404341324700145660ustar00rootroot00000000000000#ifndef _FSOPS_H #define _FSOPS_H #include extern uint32_t fs_name_max; extern uint32_t fs_full_path_max; extern void close_fd(int *fd); extern int is_dir(const char *path, struct dirent *d); extern int is_dir_lstat(const char *path); extern int is_reg_lstat(const char *path); extern int mkpath(char **rpath, const char *limit); extern int build_path(const char *datadir, const char *fname, char **rpath, const char *limit); extern int do_rename(const char *oldpath, const char *newpath); extern int build_path_w(const char *path); extern int recursive_delete(const char *path); extern int recursive_delete_dirs_only(const char *path); extern int recursive_delete_dirs_only_no_warnings(const char *path); extern int unlink_w(const char *path, const char *func); extern int init_fs_max(const char *path); extern int entries_in_directory_alphasort(const char *path, char ***nl, int *count, int atime, int follow_symlinks); extern int filter_dot(const struct dirent *d); extern int files_equal(const char *opath, const char *npath, int compressed); #ifndef HAVE_WIN32 extern int mksock(const char *path); extern int is_lnk_lstat(const char *path); extern int is_lnk_valid(const char *path); extern int do_symlink(const char *oldpath, const char *newpath); extern int readlink_w(const char *path, char buf[], size_t buflen); extern int readlink_w_in_dir(const char *dir, const char *lnk, char buf[], size_t buflen); #endif #endif burp-2.4.0/src/fzp.c000066400000000000000000000232201404341324700142200ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "cmd.h" #include "fsops.h" #include "fzp.h" #include "log.h" #include "prepend.h" #ifndef HAVE_WIN32 #include "server/compress.h" #include "server/protocol1/zlibio.h" #endif static struct fzp *fzp_alloc(void) { return (struct fzp *)calloc_w(1, sizeof(struct fzp), __func__); } static void fzp_free(struct fzp **fzp) { if(!fzp || !*fzp) return; free_w(&(*fzp)->buf); free_v((void **)fzp); } static FILE *open_fp(const char *fname, const char *mode) { FILE *fp=NULL; if(!(fp=fopen(fname, mode))) logp("could not open %s: %s\n", fname, strerror(errno)); return fp; } static gzFile open_zp(const char *fname, const char *mode) { gzFile zp=NULL; if(!(zp=gzopen(fname, mode))) logp("could not open %s: %s\n", fname, strerror(errno)); return zp; } static int close_fp(FILE **fp) { int ret=0; if(!*fp) return ret; if(fclose(*fp)) { logp("fclose failed: %s\n", strerror(errno)); ret=-1; } *fp=NULL; return ret; } static int close_zp(gzFile *zp) { int e; int ret=0; if(!*zp) return ret; if((e=gzclose(*zp)) // Can return Z_BUF_ERROR if the last read ended in the middle // of a gzip stream. I saw this happening in utests on OpenBSD. && e!=Z_BUF_ERROR) { const char *str=NULL; if(e==Z_ERRNO) str=strerror(errno); logp("gzclose failed: %d (%s)\n", e, str?:""); ret=-1; } return ret; } static void unknown_type(enum fzp_type type, const char *func) { logp("unknown type in %s: %d\n", func, type); } static void not_open(const char *func) { logp("File pointer not open in %s\n", func); } static struct fzp *fzp_do_open(const char *path, const char *mode, enum fzp_type type) { struct fzp *fzp=NULL; if(!(fzp=fzp_alloc())) goto error; fzp->type=type; switch(type) { case FZP_FILE: if(!(fzp->fp=open_fp(path, mode))) goto error; return fzp; case FZP_COMPRESSED: if(!(fzp->zp=open_zp(path, mode))) goto error; return fzp; default: unknown_type(fzp->type, __func__); goto error; } error: fzp_close(&fzp); return NULL; } struct fzp *fzp_open(const char *path, const char *mode) { return fzp_do_open(path, mode, FZP_FILE); } struct fzp *fzp_gzopen(const char *path, const char *mode) { return fzp_do_open(path, mode, FZP_COMPRESSED); } int fzp_close(struct fzp **fzp) { int ret=-1; if(!fzp || !*fzp) return 0; switch((*fzp)->type) { case FZP_FILE: ret=close_fp(&((*fzp)->fp)); break; case FZP_COMPRESSED: ret=close_zp(&((*fzp)->zp)); break; default: unknown_type((*fzp)->type, __func__); break; } fzp_free(fzp); return ret; } int fzp_read(struct fzp *fzp, void *ptr, size_t nmemb) { if(fzp) switch(fzp->type) { case FZP_FILE: return (int)fread(ptr, 1, nmemb, fzp->fp); case FZP_COMPRESSED: return gzread(fzp->zp, ptr, (unsigned)nmemb); default: unknown_type(fzp->type, __func__); goto error; } not_open(__func__); error: return 0; } size_t fzp_write(struct fzp *fzp, const void *ptr, size_t nmemb) { if(fzp) switch(fzp->type) { case FZP_FILE: return fwrite(ptr, 1, nmemb, fzp->fp); case FZP_COMPRESSED: return gzwrite(fzp->zp, ptr, (unsigned)nmemb); default: unknown_type(fzp->type, __func__); goto error; } not_open(__func__); error: return 0; } int fzp_eof(struct fzp *fzp) { if(fzp) switch(fzp->type) { case FZP_FILE: return feof(fzp->fp); case FZP_COMPRESSED: return gzeof(fzp->zp); default: unknown_type(fzp->type, __func__); goto error; } not_open(__func__); error: // Non-zero means end of file. Should be OK to use -1 here. return -1; } int fzp_flush(struct fzp *fzp) { if(fzp) switch(fzp->type) { case FZP_FILE: return fflush(fzp->fp); case FZP_COMPRESSED: return gzflush(fzp->zp, Z_FINISH); default: unknown_type(fzp->type, __func__); goto error; } not_open(__func__); error: return EOF; } int fzp_seek(struct fzp *fzp, off_t offset, int whence) { if(fzp) switch(fzp->type) { case FZP_FILE: return fseeko(fzp->fp, offset, whence); case FZP_COMPRESSED: // Notice that gzseek returns the new offset. if(gzseek(fzp->zp, offset, whence)==offset) return 0; goto error; default: unknown_type(fzp->type, __func__); goto error; } not_open(__func__); error: return -1; } off_t fzp_tell(struct fzp *fzp) { if(fzp) switch(fzp->type) { case FZP_FILE: return ftello(fzp->fp); case FZP_COMPRESSED: return gztell(fzp->zp); default: unknown_type(fzp->type, __func__); goto error; } not_open(__func__); error: return -1; } #ifndef HAVE_WIN32 // There is no zlib gztruncate. Inflate it, truncate it, recompress it. static int gztruncate(const char *path, off_t length, int compression) { int ret=1; char tmp[16]; char *dest=NULL; char *dest2=NULL; snprintf(tmp, sizeof(tmp), ".%d", getpid()); if(!(dest=prepend(path, tmp)) || !(dest2=prepend(dest, "-2")) || zlib_inflate(NULL, path, dest, NULL)) goto end; if(truncate(dest, length)) { logp("truncate of %s failed in %s\n", dest, __func__); goto end; } if(compress_file(dest, dest2, compression)) goto end; unlink(dest); ret=do_rename(dest2, path); end: if(dest) unlink(dest); if(dest2) unlink(dest2); free_w(&dest); free_w(&dest2); return ret; } int fzp_truncate(const char *path, enum fzp_type type, off_t length, int compression) { if(!path) { // Avoids a valgrind complaint in one of the tests. errno=ENOENT; goto error; } switch(type) { case FZP_FILE: return truncate(path, length); case FZP_COMPRESSED: return gztruncate(path, length, compression); default: unknown_type(type, __func__); goto error; } error: return -1; } #endif int fzp_printf(struct fzp *fzp, const char *format, ...) { int ret=-1; int n; if(!fzp) { not_open(__func__); return ret; } if(!fzp->buf) { fzp->s=128; if(!(fzp->buf=(char *)malloc_w(fzp->s, __func__))) return ret; } // Avoid fixed size buffer. while(1) { va_list ap; va_start(ap, format); n=vsnprintf(fzp->buf, fzp->s, format, ap); va_end(ap); if(n<0) { logp("Failed to vsnprintf in %s: %s\n", __func__, strerror(errno)); return ret; } if(fzp->s<(size_t)n) { fzp->s*=2; if(!(fzp->buf=(char *) realloc_w(fzp->buf, fzp->s, __func__))) return ret; continue; } break; } switch(fzp->type) { case FZP_FILE: ret=fprintf(fzp->fp, "%s", fzp->buf); break; case FZP_COMPRESSED: ret=gzprintf(fzp->zp, "%s", fzp->buf); break; default: unknown_type(fzp->type, __func__); break; } return ret; } void fzp_setlinebuf(struct fzp *fzp) { #ifndef HAVE_WIN32 if(fzp) switch(fzp->type) { case FZP_FILE: setlinebuf(fzp->fp); return; case FZP_COMPRESSED: logp("gzsetlinebuf() does not exist in %s\n", __func__); return; default: unknown_type(fzp->type, __func__); return; } not_open(__func__); #endif } char *fzp_gets(struct fzp *fzp, char *s, int size) { if(fzp) switch(fzp->type) { case FZP_FILE: return fgets(s, size, fzp->fp); case FZP_COMPRESSED: return gzgets(fzp->zp, s, size); default: unknown_type(fzp->type, __func__); goto error; } not_open(__func__); error: return NULL; } extern int fzp_fileno(struct fzp *fzp) { if(fzp) switch(fzp->type) { case FZP_FILE: return fileno(fzp->fp); case FZP_COMPRESSED: logp("gzfileno() does not exist in %s\n", __func__); goto error; default: unknown_type(fzp->type, __func__); goto error; } not_open(__func__); error: return -1; } static struct fzp *fzp_do_dopen(int fd, const char *mode, enum fzp_type type) { struct fzp *fzp=NULL; if(!(fzp=fzp_alloc())) goto error; fzp->type=type; switch(type) { case FZP_FILE: if(!(fzp->fp=fdopen(fd, mode))) goto error; return fzp; case FZP_COMPRESSED: if(!(fzp->zp=gzdopen(fd, mode))) goto error; return fzp; default: unknown_type(fzp->type, __func__); goto error; } error: fzp_close(&fzp); return NULL; } struct fzp *fzp_dopen(int fd, const char *mode) { return fzp_do_dopen(fd, mode, FZP_FILE); } struct fzp *fzp_gzdopen(int fd, const char *mode) { return fzp_do_dopen(fd, mode, FZP_COMPRESSED); } void fzp_ERR_print_errors_fp(struct fzp *fzp) { if(fzp) switch(fzp->type) { case FZP_FILE: ERR_print_errors_fp(fzp->fp); break; case FZP_COMPRESSED: logp("ERR_print_errors_zp() does not exist in %s\n", __func__); break; default: unknown_type(fzp->type, __func__); break; } } X509 *fzp_PEM_read_X509(struct fzp *fzp) { if(fzp) switch(fzp->type) { case FZP_FILE: return PEM_read_X509(fzp->fp, NULL, NULL, NULL); case FZP_COMPRESSED: logp("PEM_read_X509() does not exist in %s\n", __func__); goto error; default: unknown_type(fzp->type, __func__); goto error; } not_open(__func__); error: return NULL; } static void pass_msg(size_t nmemb, size_t got, int pass) { logp("Tried to read %lu bytes, got %lu by pass %d\n", (unsigned long)nmemb, (unsigned long)got, pass); } int fzp_read_ensure(struct fzp *fzp, void *ptr, size_t nmemb, const char *func) { static int f; static int r; static size_t got; static int pass; for(r=0, got=0, pass=0; got!=nmemb; pass++) { r=fzp_read(fzp, ((char *)ptr)+got, nmemb-got); if(r>0) { got+=r; continue; } if(r<0) { pass_msg(nmemb, got, pass); logp("Error in %s, called from %s: %s\n", __func__, func, strerror(errno)); return -1; } f=fzp_eof(fzp); if(!f) continue; // Not yet end of file, keep trying. if(f>0) { // End of file. if(!got) return 1; pass_msg(nmemb, got, pass); logp("Error in %s, called from %s: %lu bytes, eof\n", __func__, func, (unsigned long)got); return -1; } else { pass_msg(nmemb, got, pass); logp("Error in %s by fzp_feof, called from %s: %s\n", __func__, func, strerror(errno)); return -1; } } return 0; } burp-2.4.0/src/fzp.h000066400000000000000000000024741404341324700142350ustar00rootroot00000000000000#ifndef _FZP_H #define _FZP_H #include enum fzp_type { FZP_FILE=0, FZP_COMPRESSED }; struct fzp { enum fzp_type type; union { FILE *fp; gzFile zp; }; char *buf; size_t s; }; extern struct fzp *fzp_open(const char *path, const char *mode); extern struct fzp *fzp_gzopen(const char *path, const char *mode); extern int fzp_close(struct fzp **fzp); extern int fzp_read(struct fzp *fzp, void *ptr, size_t nmemb); extern size_t fzp_write(struct fzp *fzp, const void *ptr, size_t nmemb); extern int fzp_eof(struct fzp *fzp); extern int fzp_flush(struct fzp *fzp); extern int fzp_seek(struct fzp *fzp, off_t offset, int whence); extern off_t fzp_tell(struct fzp *fzp); #ifndef HAVE_WIN32 extern int fzp_truncate(const char *path, enum fzp_type type, off_t length, int compression); #endif extern int fzp_printf(struct fzp *fzp, const char *format, ...); extern void fzp_setlinebuf(struct fzp *fzp); extern char *fzp_gets(struct fzp *fzp, char *s, int size); extern int fzp_fileno(struct fzp *fzp); extern struct fzp *fzp_dopen(int fd, const char *mode); extern struct fzp *fzp_gzdopen(int fd, const char *mode); extern void fzp_ERR_print_errors_fp(struct fzp *fzp); extern X509 *fzp_PEM_read_X509(struct fzp *fzp); extern int fzp_read_ensure(struct fzp *fzp, void *ptr, size_t nmemb, const char *func); #endif burp-2.4.0/src/handy.c000066400000000000000000000447131404341324700145360ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "asfd.h" #include "async.h" #include "berrno.h" #include "cmd.h" #include "fsops.h" #include "fzp.h" #include "handy.h" #include "hexmap.h" #include "iobuf.h" #include "log.h" #include "msg.h" #include "prepend.h" #include "protocol1/handy.h" #include "protocol2/blk.h" #include #include #ifdef HAVE_WIN32 #include #include #endif // return -1 for error, 0 for OK, 1 if the client wants to interrupt the // transfer. int do_quick_read(struct asfd *asfd, const char *datapth, struct cntr *cntr) { int r=0; struct iobuf *rbuf; if(asfd->as->read_quick(asfd->as)) return -1; rbuf=asfd->rbuf; if(rbuf->buf) { if(rbuf->cmd==CMD_MESSAGE || rbuf->cmd==CMD_WARNING) { log_recvd(rbuf, cntr, 0); } else if(rbuf->cmd==CMD_INTERRUPT) { // Client wants to interrupt - double check that // it is still talking about the file that we are // sending. if(datapth && !strcmp(rbuf->buf, datapth)) r=1; } else { iobuf_log_unexpected(rbuf, __func__); r=-1; } iobuf_free_content(rbuf); } return r; } static int send_whole_file_gz(struct asfd *asfd, const char *datapth, int quick_read, uint64_t *bytes, struct cntr *cntr, int compression, struct fzp *fzp) { int ret=0; int zret=0; unsigned have; z_stream strm; int flush=Z_NO_FLUSH; uint8_t in[ZCHUNK]; uint8_t out[ZCHUNK]; struct iobuf wbuf; /* allocate deflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; if((zret=deflateInit2(&strm, compression, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY))!=Z_OK) return -1; do { strm.avail_in=fzp_read(fzp, in, ZCHUNK); if(!compression && !strm.avail_in) break; *bytes+=strm.avail_in; if(strm.avail_in) flush=Z_NO_FLUSH; else flush=Z_FINISH; strm.next_in=in; // Run deflate() on input until output buffer not full, finish // compression if all of source has been read in. do { if(compression) { strm.avail_out=ZCHUNK; strm.next_out=out; zret=deflate(&strm, flush); if(zret==Z_STREAM_ERROR) { logp("z_stream_error\n"); ret=-1; break; } have=ZCHUNK-strm.avail_out; } else { have=strm.avail_in; memcpy(out, in, have); } wbuf.cmd=CMD_APPEND; wbuf.buf=(char *)out; wbuf.len=have; if(asfd->write(asfd, &wbuf)) { ret=-1; break; } if(quick_read && datapth) { int qr; if((qr=do_quick_read(asfd, datapth, cntr))<0) { ret=-1; break; } if(qr) // Client wants to interrupt. { goto cleanup; } } if(!compression) break; } while(!strm.avail_out); if(ret) break; if(!compression) continue; if(strm.avail_in) /* all input will be used */ { ret=-1; logp("strm.avail_in=%d\n", strm.avail_in); break; } } while(flush!=Z_FINISH); if(!ret) { if(compression && zret!=Z_STREAM_END) { logp("ret OK, but zstream not finished: %d\n", zret); ret=-1; } } cleanup: deflateEnd(&strm); if(!ret) { return write_endfile(asfd, *bytes, NULL); } //logp("end of send\n"); return ret; } int set_non_blocking(int fd) { int flags; if((flags = fcntl(fd, F_GETFL, 0))<0) flags = 0; return fcntl(fd, F_SETFL, flags | O_NONBLOCK); } int set_blocking(int fd) { int flags; if((flags = fcntl(fd, F_GETFL, 0))<0) flags = 0; return fcntl(fd, F_SETFL, flags | ~O_NONBLOCK); } char *get_tmp_filename(const char *basis) { return prepend(basis, ".tmp"); } void add_fd_to_sets(int fd, fd_set *read_set, fd_set *write_set, fd_set *err_set, int *max_fd) { if(read_set) FD_SET((unsigned int) fd, read_set); if(write_set) FD_SET((unsigned int) fd, write_set); if(err_set) FD_SET((unsigned int) fd, err_set); if(fd > *max_fd) *max_fd = fd; } #ifndef HAVE_WIN32 int get_address_and_port(struct sockaddr_storage *addr, char *addrstr, size_t len, uint16_t *port) { struct sockaddr_in *s4; struct sockaddr_in6 *s6; switch(addr->ss_family) { case AF_INET: s4=(struct sockaddr_in *)addr; inet_ntop(AF_INET, &s4->sin_addr, addrstr, len); *port=ntohs(s4->sin_port); break; case AF_INET6: s6=(struct sockaddr_in6 *)addr; inet_ntop(AF_INET6, &s6->sin6_addr, addrstr, len); *port=ntohs(s6->sin6_port); break; default: logp("unknown addr.ss_family: %d\n", addr->ss_family); return -1; } return 0; } #endif int set_peer_env_vars(struct sockaddr_storage *addr) { #ifndef HAVE_WIN32 uint16_t port=0; char portstr[16]=""; char addrstr[INET6_ADDRSTRLEN]=""; if(get_address_and_port(addr, addrstr, INET6_ADDRSTRLEN, &port)) return -1; if(setenv("REMOTE_ADDR", addrstr, 1)) { logp("setenv REMOTE_ADDR to %s failed: %s\n", addrstr, strerror(errno)); return -1; } snprintf(portstr, sizeof(portstr), "%d", port); if(setenv("REMOTE_PORT", portstr, 1)) { logp("setenv REMOTE_PORT failed: %s\n", strerror(errno)); return -1; } #endif return 0; } int set_keepalive(int fd, int value) { int keepalive=value; if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof(keepalive))) { logp("setsockopt keepalive=%d failed: %s\n", value, strerror(errno)); return -1; } return 0; } int init_client_socket(const char *host, const char *port) { int rfd=-1; int gai_ret; struct addrinfo hints; struct addrinfo *result; struct addrinfo *rp; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = 0; logp("Connecting to %s:%s\n", host?host:"loopback", port); if((gai_ret=getaddrinfo(host, port, &hints, &result))) { logp("getaddrinfo: %s\n", gai_strerror(gai_ret)); return -1; } for(rp=result; rp; rp=rp->ai_next) { rfd=socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if(rfd<0) continue; set_keepalive(rfd, 1); if(connect(rfd, rp->ai_addr, rp->ai_addrlen) != -1) break; close_fd(&rfd); } freeaddrinfo(result); if(!rp) { // host==NULL and AI_PASSIVE not set -> loopback logp("Could not connect to %s:%s\n", host?host:"loopback", port); close_fd(&rfd); return -1; } reuseaddr(rfd); #ifdef HAVE_WIN32 setmode(rfd, O_BINARY); #endif return rfd; } void reuseaddr(int fd) { int optval=1; #ifdef HAVE_OLD_SOCKOPT #define sockopt_val_t char * #else #define sockopt_val_t void * #endif if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&optval, sizeof(optval))<0) logp("Error: setsockopt SO_REUSEADDR: %s", strerror(errno)); } void setup_signal(int sig, void handler(int sig)) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler=handler; sigaction(sig, &sa, NULL); } /* Function based on src/lib/priv.c from bacula. */ int chuser_and_or_chgrp(const char *user, const char *group, int readall) { #ifdef HAVE_WIN32 return 0; #else struct passwd *passw = NULL; struct group *grp = NULL; gid_t gid; uid_t uid; char *username=NULL; // Allow setting readall=1 without setting user if(readall && !user) user="nobody"; if(!user && !group) return 0; if(user) { if(!(passw=getpwnam(user))) { logp("could not find user '%s': %s\n", user, strerror(errno)); return -1; } } else { if(!(passw=getpwuid(getuid()))) { logp("could not find password entry: %s\n", strerror(errno)); return -1; } user=passw->pw_name; } // Any OS uname pointer may get overwritten, so save name, uid, and gid if(!(username=strdup_w(user, __func__))) return -1; uid=passw->pw_uid; gid=passw->pw_gid; if(group) { if(!(grp=getgrnam(group))) { logp("could not find group '%s': %s\n", group, strerror(errno)); goto err; } gid=grp->gr_gid; } else { // Resolve gid to group name for logp() if (!(grp=getgrgid(gid))) { logp("could not find group for gid %d: %s\n", gid, strerror(errno)); goto err; } group=grp->gr_name; grp=NULL; } if(gid!=getgid() // do not do it if we already have the same gid. && initgroups(username, gid)) { if(grp) logp("could not initgroups for group '%s', user '%s': %s\n", group, username, strerror(errno)); else logp("could not initgroups for user '%s': %s\n", username, strerror(errno)); goto err; } if(grp) { if(gid!=getgid() // do not do it if we already have the same gid && setgid(gid)) { logp("could not set group '%s': %s\n", group, strerror(errno)); goto err; } } if (readall) { #ifdef ENABLE_KEEP_READALL_CAPS_SUPPORT cap_t caps; // Make capabilities pass through setreuid if(prctl(PR_SET_KEEPCAPS, 1)) { logp("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno)); goto err; } if(setreuid(uid, uid)) { logp("Could not switch to user=%s (uid=%u): %s\n", username, uid, strerror(errno)); goto err; } // `ep' is Effective and Permitted caps=cap_from_text("cap_dac_read_search=ep"); if(!caps) { logp("cap_from_text() failed: %s\n", strerror(errno)); goto err; } if(cap_set_proc(caps) < 0) { logp("cap_set_proc() failed: %s\n", strerror(errno)); goto err; } cap_free(caps); logp("Privileges switched to %s keeping readall capability.\n", username); #else logp("Keep readall capabilities is not implemented on this platform yet\n"); goto err; #endif } else if(uid!=getuid() // do not do it if we already have the same uid && setuid(uid)) { logp("could not set specified user '%s': %s\n", username, strerror(errno)); goto err; } return 0; err: free_w(&username); return -1; #endif } // Not in dpth.c so that Windows client can see it. int dpth_protocol1_is_compressed(int compressed, const char *datapath) { const char *dp=NULL; if(compressed>0) return compressed; if(compressed==0) return 0; /* Legacy - if the compressed value is -1 - that is, it is not set in the manifest, deduce the value from the datapath. */ if((dp=strrchr(datapath, '.')) && !strcmp(dp, ".gz")) return 1; return 0; } long version_to_long(const char *version) { long ret=0; char *copy=NULL; char *tok1=NULL; char *tok2=NULL; char *tok3=NULL; if(!version || !*version) return 0; if(!(copy=strdup_w(version, __func__))) return -1; if(!(tok1=strtok(copy, ".")) || !(tok2=strtok(NULL, ".")) || !(tok3=strtok(NULL, "."))) { free_w(©); return -1; } ret+=atol(tok3); ret+=atol(tok2)*100; ret+=atol(tok1)*100*100; free_w(©); return ret; } /* These receive_a_file() and send_a_file() functions are for use by extra_comms and the CA stuff, rather than backups/restores. */ int receive_a_file(struct asfd *asfd, const char *path, struct cntr *cntr) { int ret=-1; struct BFILE *bfd=NULL; uint64_t rcvdbytes=0; uint64_t sentbytes=0; if(!(bfd=bfile_alloc())) goto end; bfile_init(bfd, 0, cntr); #ifdef HAVE_WIN32 bfd->set_win32_api(bfd, 0); #else bfd->set_vss_strip(bfd, 0); #endif if(bfd->open(bfd, asfd, path, #ifdef O_NOFOLLOW O_NOFOLLOW | #endif O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) { struct berrno be; berrno_init(&be); logp("Could not open for writing %s: %s\n", path, berrno_bstrerror(&be, errno)); goto end; } ret=transfer_gzfile_in(asfd, bfd, &rcvdbytes, &sentbytes); if(bfd->close(bfd, asfd)) { logp("error closing %s in %s\n", path, __func__); goto end; } logp("Received: %s\n", path); ret=0; end: bfd->close(bfd, asfd); bfile_free(&bfd); return ret; } /* Windows will use this function, when sending a certificate signing request. It is not using the Windows API stuff because it needs to arrive on the server side without any junk in it. */ int send_a_file(struct asfd *asfd, const char *path, struct cntr *cntr) { int ret=0; struct fzp *fzp=NULL; uint64_t bytes=0; if(!(fzp=fzp_open(path, "rb")) || send_whole_file_gz(asfd, "datapth", 0, &bytes, cntr, 9 /*compression*/, fzp)) { ret=-1; goto end; } logp("Sent %s\n", path); end: fzp_close(&fzp); return ret; } int strncmp_w(const char *s1, const char *s2) { return strncmp(s1, s2, strlen(s2)); } char *strreplace_w(char *orig, char *search, char *replace, const char *func) { char *result=NULL; // the return string char *ins; // the next insert point char *tmp; // varies int len_rep; // length of replace (the string to replace search with) int len_search; // length of search (the string to look for) int len_front; // distance between rep and end of last rep int count; // number of replacements // sanity checks and initialization if(!orig || !search) goto end; len_search = strlen(search); if(len_search==0) goto end; if(!replace) len_rep=0; else len_rep=strlen(replace); // count the number of replacements needed ins=orig; for(count=0; (tmp=strstr(ins, search)); ++count) ins=tmp+len_search; tmp=result=(char *)malloc_w(strlen(orig)+(len_rep-len_search)*count+1, func); if(!result) goto end; while(count--) { ins=strstr(orig, search); len_front=ins-orig; tmp=strncpy(tmp, orig, len_front)+len_front; tmp=strcpy(tmp, replace)+len_rep; orig+=len_front+len_search; // move to next "end of rep" } strcpy(tmp, orig); end: return result; } static int charcount_noescaped(const char *orig, char search, int repeat) { int count=0; int len; int i; char quote='\0'; char prev='\0'; if(!orig) return count; len=strlen(orig); for(count=0, i=0; i0 && orig[i-1]=='\\') goto loop_tail; quote='\0'; } else if(quote=='\0' && orig[i]==search) { // ignore escaped char if(i>0 && orig[i-1]=='\\') goto loop_tail; if(repeat || prev!=orig[i]) count++; } loop_tail: prev=orig[i]; } return count; } char *charreplace_noescaped_w(const char *orig, char search, const char *replace, int *count, const char *func) { char *result=NULL; char *tmp; char quote='\0'; int nb_repl=0; // number of replacement int i; int len; int len_replace; int len_dest; if(!orig || !search) goto end; len=strlen(orig); len_replace=strlen(replace); if(!(nb_repl=charcount_noescaped(orig, search, 1))) { result=strdup_w(orig, func); goto end; } len_dest=len+((len_replace-1)*nb_repl)+1; tmp=result=(char *)malloc_w(len_dest, func); if(!result) goto end; quote='\0'; for(i=0; i0) buf++; if(!(tmp=(char *)malloc_w( tmp_len, func))) goto error; tmp=strncpy(tmp, buf, tmp_len); tmp[tmp_len-1]='\0'; ret[k]=tmp; buf+=j; j=0; k++; } goto loop_tail; } } j++; loop_tail: prev=ptr[i]; } while(*buf==delimiter && *(buf-1)!='\\') buf++; if(!(end=(char *)malloc_w(j+1, func))) goto error; end=strncpy(end, buf, j+1); end[j]='\0'; ret[k]=end; ret[k+1]=NULL; end: free_w(&ptr); return ret; error: free_w(&ptr); free_list_w(&ret, *size); return NULL; } void free_list_w(char ***list, size_t size) { char **l=*list; if(!l) return; size_t i; for(i=0; i #include #include #include "bfile.h" #undef ENABLE_KEEP_READALL_CAPS_SUPPORT #if defined(HAVE_SYS_PRCTL_H) && defined(HAVE_SYS_CAPABILITY_H) && \ defined(HAVE_PRCTL) && defined(HAVE_SETREUID) && defined(HAVE_LIBCAP) # include # include # if defined(PR_SET_KEEPCAPS) # define ENABLE_KEEP_READALL_CAPS_SUPPORT # endif #endif #define min(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a < _b ? _a : _b; }) extern int set_non_blocking(int fd); extern int set_blocking(int fd); extern char *get_tmp_filename(const char *basis); extern void add_fd_to_sets(int fd, fd_set *read_set, fd_set *write_set, fd_set *err_set, int *max_fd); extern int set_peer_env_vars(struct sockaddr_storage *addr); extern int set_keepalive(int fd, int value); extern int init_client_socket(const char *host, const char *port); extern void reuseaddr(int fd); extern int chuser_and_or_chgrp(const char *user, const char *group, int readall); extern int dpth_protocol1_is_compressed(int compressed, const char *datapath); #ifndef HAVE_WIN32 extern void setup_signal(int sig, void handler(int sig)); #endif extern long version_to_long(const char *version); /* These receive_a_file() and send_a_file() functions are for use by extra_comms and the CA stuff, rather than backups/restores. */ extern int receive_a_file(struct asfd *asfd, const char *path, struct cntr *cntr); extern int send_a_file(struct asfd *asfd, const char *path, struct cntr *cntr); extern int do_quick_read(struct asfd *asfd, const char *datapth, struct cntr *cntr); extern int strncmp_w(const char *s1, const char *s2); extern char **strsplit_w(const char *src, const char *delimiters, size_t *size, const char *func); extern char **charsplit_noescaped_w(const char *src, char delimiter, size_t *size, const char *func); extern char *strreplace_w(char *orig, char *search, char *replace, const char *func); extern char *charreplace_noescaped_w(const char *orig, char search, const char *replace, int *count, const char *func); extern void free_list_w(char ***list, size_t size); extern char *strdup_w(const char *s, const char *func); extern void *realloc_w(void *ptr, size_t size, const char *func); extern void *malloc_w(size_t size, const char *func); extern void *calloc_w(size_t nmem, size_t size, const char *func); extern void free_v(void **ptr); extern void free_w(char **str); extern char *strlwr(char *s); extern void strip_fqdn(char **fqdn); extern void strip_trailing_slashes(char **str); extern int breakpoint(int breaking, const char *func); #ifdef HAVE_WIN32 extern void convert_backslashes(char **path); #else extern int get_address_and_port(struct sockaddr_storage *addr, char *addrstr, size_t len, uint16_t *port); #endif #endif burp-2.4.0/src/hexmap.c000066400000000000000000000054071404341324700147120ustar00rootroot00000000000000#include "burp.h" #include "hexmap.h" #include "protocol2/blk.h" // FIX THIS: Should be set in configure somewhere. #include #define HEXMAP_SIZE 256 static uint8_t hexmap1[HEXMAP_SIZE]; static uint8_t hexmap2[HEXMAP_SIZE]; uint8_t md5sum_of_empty_string[MD5_DIGEST_LENGTH]; static void do_hexmap_init(uint8_t *hexmap, uint8_t shift) { uint8_t i; uint8_t h; memset(hexmap, 0, HEXMAP_SIZE); for(i='0', h=0x00; i<='9'; i++, h++) hexmap[i]=h<rbuf->buf, endreqstrf)) { if(asfd->write_str(asfd, CMD_GEN, endrepstrf)) return ASL_END_ERROR; return ASL_END_OK; } if(add_to_incexc(incexc, asfd->rbuf->buf, asfd->rbuf->len)) return ASL_END_ERROR; return ASL_CONTINUE; } static int incexc_recv(struct asfd *asfd, char **incexc, const char *repstr, const char *endreqstr, const char *endrepstr, struct conf **confs) { free_w(incexc); if(asfd->write_str(asfd, CMD_GEN, repstr)) return -1; endreqstrf=endreqstr; endrepstrf=endrepstr; if(asfd->simple_loop(asfd, confs, incexc, __func__, incexc_recv_func)) return -1; // Need to put another new line at the end. return add_to_incexc(incexc, "\n", 1); } int incexc_recv_client(struct asfd *asfd, char **incexc, struct conf **confs) { return incexc_recv(asfd, incexc, "sincexc ok", "sincexc end", "sincexc end ok", confs); } int incexc_recv_client_restore(struct asfd *asfd, char **incexc, struct conf **confs) { return incexc_recv(asfd, incexc, "srestore ok", "srestore end", "srestore end ok", confs); } int incexc_recv_server(struct asfd *asfd, char **incexc, struct conf **confs) { return incexc_recv(asfd, incexc, "incexc ok", "incexc end", "incexc end ok", confs); } burp-2.4.0/src/incexc_recv.h000066400000000000000000000005041404341324700157160ustar00rootroot00000000000000#ifndef _INCEXC_RECV_H #define _INCEXC_RECV_H extern int incexc_recv_client(struct asfd *asfd, char **incexc, struct conf **confs); extern int incexc_recv_server(struct asfd *asfd, char **incexc, struct conf **confs); extern int incexc_recv_client_restore(struct asfd *asfd, char **incexc, struct conf **confs); #endif burp-2.4.0/src/incexc_send.c000066400000000000000000000060241404341324700157060ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "asfd.h" #include "cmd.h" #include "log.h" #include "prepend.h" #include "strlist.h" #include "incexc_send.h" static int send_incexc_string(struct asfd *asfd, const char *field, const char *str) { char *tosend=NULL; int ret=-1; if(!str) return 0; if(!(tosend=prepend_n(field, str, strlen(str), " = "))) goto end; if(asfd->write_str(asfd, CMD_GEN, tosend)) { logp("Error in async_write_str when sending incexc\n"); goto end; } ret=0; end: free_w(&tosend); return ret; } static int send_incexc_str(struct asfd *asfd, struct conf *conf) { return send_incexc_string(asfd, conf->field, get_string(conf)); } static int send_incexc_uint(struct asfd *asfd, struct conf *conf) { char tmp[64]=""; snprintf(tmp, sizeof(tmp), "%d", get_int(conf)); return send_incexc_string(asfd, conf->field, tmp); } static int send_incexc_uint64(struct asfd *asfd, struct conf *conf) { char tmp[32]=""; snprintf(tmp, sizeof(tmp), "%" PRIu64, get_uint64_t(conf)); return send_incexc_string(asfd, conf->field, tmp); } static int send_incexc_strlist(struct asfd *asfd, struct conf *conf) { struct strlist *l; for(l=get_strlist(conf); l; l=l->next) if(send_incexc_string(asfd, conf->field, l->path)) return -1; return 0; } static int do_sends(struct asfd *asfd, struct conf **confs, int flag) { int i=0; int r=-1; for(i=0; iflags & flag)) continue; switch(confs[i]->conf_type) { case CT_STRING: if(send_incexc_str(asfd, confs[i])) goto end; break; case CT_STRLIST: if(send_incexc_strlist(asfd, confs[i])) goto end; break; case CT_UINT: if(send_incexc_uint(asfd, confs[i])) goto end; break; case CT_SSIZE_T: if(send_incexc_uint64(asfd, confs[i])) goto end; break; case CT_FLOAT: case CT_MODE_T: case CT_E_BURP_MODE: case CT_E_PROTOCOL: case CT_E_RECOVERY_METHOD: case CT_E_RSHASH: case CT_CNTR: break; } } r=0; end: return r; } static int do_request_response(struct asfd *asfd, const char *reqstr, const char *repstr) { return (asfd->write_str(asfd, CMD_GEN, reqstr) || asfd_read_expect(asfd, CMD_GEN, repstr)); } int incexc_send_client(struct asfd *asfd, struct conf **confs) { if(do_request_response(asfd, "incexc", "incexc ok") || do_sends(asfd, confs, CONF_FLAG_INCEXC) || do_request_response(asfd, "incexc end", "incexc end ok")) return -1; return 0; } int incexc_send_server(struct asfd *asfd, struct conf **confs) { /* 'sincexc' and 'sincexc ok' have already been exchanged, so go straight into doing the sends. */ if(do_sends(asfd, confs, CONF_FLAG_INCEXC) || do_request_response(asfd, "sincexc end", "sincexc end ok")) return -1; return 0; } int incexc_send_server_restore(struct asfd *asfd, struct conf **confs) { /* 'srestore' and 'srestore ok' have already been exchanged, so go straight into doing the sends. */ if(do_sends(asfd, confs, CONF_FLAG_INCEXC_RESTORE) || do_request_response(asfd, "srestore end", "srestore end ok")) return -1; return 0; } burp-2.4.0/src/incexc_send.h000066400000000000000000000004241404341324700157110ustar00rootroot00000000000000#ifndef _INCEXC_SEND_H #define _INCEXC_SEND_H extern int incexc_send_client(struct asfd *asfd, struct conf **confs); extern int incexc_send_server(struct asfd *asfd, struct conf **confs); extern int incexc_send_server_restore(struct asfd *asfd, struct conf **confs); #endif burp-2.4.0/src/iobuf.c000066400000000000000000000105331404341324700145300ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "cmd.h" #include "iobuf.h" #include "log.h" #include "msg.h" #include "pathcmp.h" struct iobuf *iobuf_alloc(void) { struct iobuf *iobuf; if(!(iobuf=(struct iobuf *)calloc_w(1, sizeof(struct iobuf), __func__))) return NULL; iobuf_init(iobuf); return iobuf; } void iobuf_set(struct iobuf *iobuf, enum cmd cmd, char *buf, size_t len) { iobuf->cmd=cmd; iobuf->buf=buf; iobuf->len=len; } void iobuf_init(struct iobuf *iobuf) { iobuf_set(iobuf, CMD_ERROR, NULL, 0); } void iobuf_free_content(struct iobuf *iobuf) { if(!iobuf) return; free_w(&iobuf->buf); iobuf_init(iobuf); } void iobuf_free(struct iobuf **iobuf) { if(!iobuf || !*iobuf) return; iobuf_free_content(*iobuf); free_v((void **)iobuf); } void iobuf_log_unexpected(struct iobuf *iobuf, const char *func) { logp("unexpected command in %s(): %s\n", func, iobuf_to_printable(iobuf)); } void iobuf_copy(struct iobuf *dst, struct iobuf *src) { iobuf_set(dst, src->cmd, src->buf, src->len); } void iobuf_move(struct iobuf *dst, struct iobuf *src) { iobuf_copy(dst, src); src->buf=NULL; } void iobuf_from_str(struct iobuf *iobuf, enum cmd cmd, char *str) { iobuf_set(iobuf, cmd, str, strlen(str)); } int iobuf_send_msg_fzp(struct iobuf *iobuf, struct fzp *fzp) { return send_msg_fzp(fzp, iobuf->cmd, iobuf->buf, iobuf->len); } int iobuf_pathcmp(struct iobuf *a, struct iobuf *b) { int r; if((r=pathcmp(a->buf, b->buf))) return r; if(a->cmd==CMD_METADATA || a->cmd==CMD_ENC_METADATA) { if(b->cmd==CMD_METADATA || b->cmd==CMD_ENC_METADATA) return 0; else return 1; } else if(a->cmd==CMD_VSS || a->cmd==CMD_ENC_VSS) { if(b->cmd==CMD_VSS || b->cmd==CMD_ENC_VSS) return 0; else return -1; } else if(a->cmd==CMD_VSS_T || a->cmd==CMD_ENC_VSS_T) { if(b->cmd==CMD_VSS_T || b->cmd==CMD_ENC_VSS_T) return 0; else return 1; } else { if(b->cmd==CMD_METADATA || b->cmd==CMD_ENC_METADATA) return -1; else if(b->cmd==CMD_VSS || b->cmd==CMD_ENC_VSS) return 1; else if(b->cmd==CMD_VSS_T || b->cmd==CMD_ENC_VSS_T) return -1; else return 0; } } int iobuf_is_filedata(struct iobuf *iobuf) { return cmd_is_filedata(iobuf->cmd); } int iobuf_is_vssdata(struct iobuf *iobuf) { return cmd_is_vssdata(iobuf->cmd); } int iobuf_is_link(struct iobuf *iobuf) { return cmd_is_link(iobuf->cmd); } int iobuf_is_encrypted(struct iobuf *iobuf) { return cmd_is_encrypted(iobuf->cmd); } int iobuf_is_metadata(struct iobuf *iobuf) { return cmd_is_metadata(iobuf->cmd); } int iobuf_is_estimatable(struct iobuf *iobuf) { return cmd_is_estimatable(iobuf->cmd); } static int do_iobuf_fill_from_fzp(struct iobuf *iobuf, struct fzp *fzp, int extra_bytes) { unsigned int s; char lead[6]=""; char command; int r; r=fzp_read_ensure(fzp, lead, sizeof(lead)-1, __func__); lead[5]='\0'; switch(r) { case 0: break; // OK. case 1: return 1; // Finished OK. default: { logp("Error reading lead in %s\n", __func__); return -1; // Error. } } if((sscanf(lead, "%c%04X", &command, &s))!=2) { logp("sscanf failed reading manifest: %s\n", lead); return -1; } iobuf->cmd=(enum cmd)command; iobuf->len=(size_t)s; if(!(iobuf->buf=(char *)malloc_w( iobuf->len+extra_bytes+1, __func__))) return -1; switch(fzp_read_ensure(fzp, iobuf->buf, iobuf->len+extra_bytes, __func__)) { case 0: break; // OK. case 1: return 1; // Finished OK. default: logp("Error attempting to read after %s in %s (%c:%u)\n", lead, __func__, iobuf->cmd, s); return -1; } iobuf->buf[iobuf->len]='\0'; return 0; } int iobuf_fill_from_fzp(struct iobuf *iobuf, struct fzp *fzp) { return do_iobuf_fill_from_fzp(iobuf, fzp, 1 /*newline*/); } int iobuf_fill_from_fzp_data(struct iobuf *iobuf, struct fzp *fzp) { return do_iobuf_fill_from_fzp(iobuf, fzp, 0 /*no newline*/); } static int is_printable(struct iobuf *iobuf) { size_t l; for(l=0; llen; l++) if(!isprint(iobuf->buf[l]) && iobuf->buf[l]!='\n') return 0; return 1; } const char *iobuf_to_printable(struct iobuf *iobuf) { static char str[256]=""; if(is_printable(iobuf)) snprintf(str, sizeof(str), "%c:%04X:%s", iobuf->cmd, (int)iobuf->len, iobuf->buf); else snprintf(str, sizeof(str), "%c:%04X:(binary data)", iobuf->cmd, (int)iobuf->len); return str; } int iobuf_relative_path_attack(struct iobuf *iobuf) { if(!has_dot_component(iobuf->buf)) return 0; iobuf_log_unexpected(iobuf, __func__); return 1; } burp-2.4.0/src/iobuf.h000066400000000000000000000025441404341324700145400ustar00rootroot00000000000000#ifndef _IOBUF_H #define _IOBUF_H #include "cmd.h" #include "fzp.h" struct iobuf { enum cmd cmd; char *buf; size_t len; }; extern struct iobuf *iobuf_alloc(void); extern void iobuf_init(struct iobuf *iobuf); extern void iobuf_free_content(struct iobuf *iobuf); extern void iobuf_free(struct iobuf **iobuf); extern void iobuf_log_unexpected(struct iobuf *iobuf, const char *func); extern void iobuf_set(struct iobuf *iobuf, enum cmd cmd, char *buf, size_t len); extern void iobuf_copy(struct iobuf *dst, struct iobuf *src); extern void iobuf_move(struct iobuf *dst, struct iobuf *src); extern void iobuf_from_str(struct iobuf *iobuf, enum cmd cmd, char *str); extern int iobuf_send_msg_fzp(struct iobuf *iobuf, struct fzp *fzp); extern int iobuf_pathcmp(struct iobuf *a, struct iobuf *b); extern int iobuf_is_filedata(struct iobuf *iobuf); extern int iobuf_is_vssdata(struct iobuf *iobuf); extern int iobuf_is_link(struct iobuf *iobuf); extern int iobuf_is_encrypted(struct iobuf *iobuf); extern int iobuf_is_metadata(struct iobuf *iobuf); extern int iobuf_is_estimatable(struct iobuf *iobuf); extern int iobuf_fill_from_fzp(struct iobuf *iobuf, struct fzp *fzp); extern int iobuf_fill_from_fzp_data(struct iobuf *iobuf, struct fzp *fzp); extern const char *iobuf_to_printable(struct iobuf *iobuf); extern int iobuf_relative_path_attack(struct iobuf *iobuf); #endif burp-2.4.0/src/ipacl.c000066400000000000000000000331421404341324700145150ustar00rootroot00000000000000#include "ipacl.h" #ifdef USE_IPACL #include #include #include #include #include #define E(a, b, c, d) \ {.ip6 = { \ cpu_to_be32(a), cpu_to_be32(b), \ cpu_to_be32(c), cpu_to_be32(d), \ }} /* This table works for both IPv4 and IPv6; * just use prefixlen_netmask_map[prefixlength].ip. */ static const union ipacl_inet_addr ipacl_netmask_map[]= { E(0x00000000, 0x00000000, 0x00000000, 0x00000000), E(0x80000000, 0x00000000, 0x00000000, 0x00000000), E(0xC0000000, 0x00000000, 0x00000000, 0x00000000), E(0xE0000000, 0x00000000, 0x00000000, 0x00000000), E(0xF0000000, 0x00000000, 0x00000000, 0x00000000), E(0xF8000000, 0x00000000, 0x00000000, 0x00000000), E(0xFC000000, 0x00000000, 0x00000000, 0x00000000), E(0xFE000000, 0x00000000, 0x00000000, 0x00000000), E(0xFF000000, 0x00000000, 0x00000000, 0x00000000), E(0xFF800000, 0x00000000, 0x00000000, 0x00000000), E(0xFFC00000, 0x00000000, 0x00000000, 0x00000000), E(0xFFE00000, 0x00000000, 0x00000000, 0x00000000), E(0xFFF00000, 0x00000000, 0x00000000, 0x00000000), E(0xFFF80000, 0x00000000, 0x00000000, 0x00000000), E(0xFFFC0000, 0x00000000, 0x00000000, 0x00000000), E(0xFFFE0000, 0x00000000, 0x00000000, 0x00000000), E(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000), E(0xFFFF8000, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFC000, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFE000, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFF000, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFF800, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFC00, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFE00, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFF00, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFF80, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFFC0, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFFE0, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFFF0, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFFF8, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFFFC, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFFFE, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0x80000000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xC0000000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xE0000000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xF0000000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xF8000000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFC000000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFE000000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFF800000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFC00000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFE00000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFF00000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFF80000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFC0000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFE0000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFF0000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFF8000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFC000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFE000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFF000, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFF800, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFC00, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFE00, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFF00, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFF80, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFC0, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFE0, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFF0, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFF8, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFC, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFE, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE), E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF), }; #undef E __always_inline static __be32 ipacl_netmask(__u8 pfxlen) { return ipacl_netmask_map[pfxlen].ip; } __always_inline static const __be32 *ipacl_netmask6(__u8 pfxlen) { return &ipacl_netmask_map[pfxlen].ip6[0]; } __always_inline static void ip6_netmask(union ipacl_inet_addr *ip, __u8 prefix) { ip->ip6[0]&=ipacl_netmask6(prefix)[0]; ip->ip6[1]&=ipacl_netmask6(prefix)[1]; ip->ip6[2]&=ipacl_netmask6(prefix)[2]; ip->ip6[3]&=ipacl_netmask6(prefix)[3]; } __always_inline static void ipacl_net4_data_netmask(struct ipacl_net4_elem *elem, __u8 cidr) { elem->ip&=ipacl_netmask(cidr); elem->cidr=cidr; } __always_inline static void ipacl_net6_data_netmask(struct ipacl_net6_elem *elem, __u8 cidr) { ip6_netmask(&elem->ip, cidr); elem->cidr=cidr; } __always_inline static bool ipv_prefix_equal(const __be32 addr1, const __be32 addr2, unsigned int prefixlen) { return !((addr1 ^ addr2) & ipacl_netmask(prefixlen)); } __always_inline static bool __ipv6_prefix_equal64_half(const __be64 *a1, const __be64 *a2, unsigned int len) { return !(len && ((*a1 ^ *a2) & htobe64((~0UL) << (64-len)))); } __always_inline static bool ipv6_prefix_equal(const struct in6_addr *addr1, const struct in6_addr *addr2, unsigned int prefixlen) { const __be64 *a1 = (const __be64 *)addr1; const __be64 *a2 = (const __be64 *)addr2; if(prefixlen >= 64) { if(a1[0] ^ a2[0]) return false; return __ipv6_prefix_equal64_half(a1+1, a2+1, prefixlen-64); } return __ipv6_prefix_equal64_half(a1, a2, prefixlen); } __always_inline static bool ipacl_net4_do_match(struct ipacl_net4_elem *elem, const struct sockaddr_in *in) { return ipv_prefix_equal(in->sin_addr.s_addr, elem->ip, elem->cidr); } __always_inline static bool ipacl_net6_do_match(struct ipacl_net6_elem *elem, const struct sockaddr_in6 *in6) { return ipv6_prefix_equal(&in6->sin6_addr, &elem->ip.in6, elem->cidr); } static ipacl_entity_t *ipacl_create(hipacl_t *acl, struct sockaddr_storage *ss, __u8 cidr) { ipacl_entity_t *elem=malloc_w(sizeof(ipacl_entity_t), __func__); if (!elem) return NULL; elem->ss_family=ss->ss_family; switch(ss->ss_family) { case AF_INET: memcpy(&elem->in, &((struct sockaddr_in *)ss)->sin_addr, sizeof(struct in_addr)); ipacl_net4_data_netmask(&elem->in, cidr); break; case AF_INET6: memcpy(&elem->in6, &(((struct sockaddr_in6 *)ss))->sin6_addr, sizeof(struct in6_addr)); ipacl_net6_data_netmask(&elem->in6, cidr); break; } SLIST_INSERT_HEAD(acl, elem, node); return elem; } static bool parse_prefix(const char *str, long *prefix) { char *endptr; long val; errno=0; /* To distinguish success/failure after call */ val=strtol(str, &endptr, 10); if((errno==ERANGE && (val==LONG_MAX || val==LONG_MIN)) || (errno!=0 && val==0) || endptr==str) // No digits were found return false; if(prefix) *prefix = val; return true; } static bool check_prefix(long *prefix, sa_family_t ss_family) { switch(ss_family) { case AF_INET: *prefix=(*prefix == -1) ? 32 : *prefix; return (*prefix>=0 && *prefix<=32); case AF_INET6: *prefix=(*prefix == -1) ? 128 : *prefix; return (*prefix>=0 && *prefix<=128); default: return false; } } const char *ipacl_strerror(ipacl_res_t res) { switch(res) { case IPACL_OK: return "success"; case IPACL_INVALID_PREFIX: return "invalid prefix"; case IPACL_UNPARSABLE_PREFIX: return "unparsable prefix"; case IPACL_UNPARSABLE_ADDR: return "unparsable address"; case IPACL_NOMEM: return "no memory"; default: return "unknown"; } } static char *trim(char *str) { char *end; if(!str) return NULL; while(isspace(*str)) str++; if(*str==0) return str; end=str+strlen(str)-1; while(end>str && isspace(*end)) end--; *(end+1)=0; return str; } ipacl_res_t ipacl_emplace(hipacl_t *hacl, const char *ipacl_str) { struct sockaddr_storage ss={}; size_t acl_str_length; if(!hacl || !ipacl_str || !(acl_str_length=strlen(ipacl_str))) return IPACL_OK; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" char __acl_str[++acl_str_length], *p; #pragma GCC diagnostic pop memcpy(__acl_str, ipacl_str, acl_str_length); if((p = strrchr(__acl_str, '/'))) *(p++)=0; if(inet_pton(AF_INET, __acl_str, &(((struct sockaddr_in *)&ss)->sin_addr))==1) ss.ss_family = AF_INET; else if(inet_pton(AF_INET6, __acl_str, &((struct sockaddr_in6 *)&ss)->sin6_addr)==1) ss.ss_family = AF_INET6; else return IPACL_UNPARSABLE_ADDR; long prefix=-1; if(p && !parse_prefix(p, &prefix)) return IPACL_UNPARSABLE_PREFIX; if(!check_prefix(&prefix, ss.ss_family)) return IPACL_INVALID_PREFIX; if(ipacl_create(hacl, &ss, prefix)==NULL) return IPACL_NOMEM; return IPACL_OK; } /** Parse ipacl_str and add elements to dst acl */ ipacl_res_t ipacl_append(hipacl_t *hacl, const char *ipacl_str, int *size) { size_t ipacl_str_length; ipacl_res_t rc=IPACL_OK; char *token; int _size=0; if(!hacl || !ipacl_str || !(ipacl_str_length=strlen(ipacl_str))) return IPACL_OK; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" char *context=NULL, __ipacl_str[++ipacl_str_length]; #pragma GCC diagnostic pop memcpy(__ipacl_str, ipacl_str, ipacl_str_length); for(token=strtok_r(__ipacl_str," ,;", &context); token; token=strtok_r(NULL," ,;", &context), ++_size) { if((rc=ipacl_emplace(hacl, trim(token)))!=IPACL_OK) break; } if(size) *size = _size; return rc; } void ipacl_free(hipacl_t *hacl) { if(!hacl) return; while(!SLIST_EMPTY(hacl)) { ipacl_entity_t *e=SLIST_FIRST(hacl); SLIST_REMOVE_HEAD(hacl, node); free_v((void **)&e); } } bool ipacl_test_saddr_storage(const hipacl_t *hacl, const struct sockaddr_storage *ss) { if(!ss || !hacl) return false; ipacl_entity_t *e; SLIST_FOREACH(e, hacl, node) { if (e->ss_family!=ss->ss_family) continue; bool match=e->ss_family==AF_INET ? ipacl_net4_do_match(&e->in, (const struct sockaddr_in *)ss) : ipacl_net6_do_match(&e->in6, (const struct sockaddr_in6 *)ss); if(match) return true; } return false; } bool ipacl_test_saddr(const hipacl_t *hacl, const struct sockaddr *saddr) { return ipacl_test_saddr_storage(hacl, (const struct sockaddr_storage*)saddr); } bool ipacl_test_ip(const hipacl_t *hacl, const char *ip) { struct sockaddr_storage ss; if(inet_pton(AF_INET, ip, &(((struct sockaddr_in *)&ss)->sin_addr)) == 1) ss.ss_family=AF_INET; else if(inet_pton(AF_INET6, ip, &(((struct sockaddr_in6 *)&ss)->sin6_addr)) == 1) ss.ss_family=AF_INET6; else ss.ss_family=AF_UNSPEC; return ss.ss_family!=AF_UNSPEC ? ipacl_test_saddr(hacl, (struct sockaddr *)&ss) : false; } bool ipacl_is_empty(const hipacl_t *hacl) { return SLIST_EMPTY(hacl); } #endif /* USE_IPACL */ burp-2.4.0/src/ipacl.h000066400000000000000000000043011404341324700145150ustar00rootroot00000000000000#ifndef _IPACL_H #define _IPACL_H #include "burp.h" #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_NETBSD_OS) #define USE_IPACL #endif #ifdef USE_IPACL #include "alloc.h" #include #include #include #include #include #include #ifdef HAVE_LINUX_OS #include #else typedef unsigned char __u8; typedef unsigned int __u32; #ifdef __GNUC__ __extension__ typedef unsigned long long __u64; #else typedef unsigned long long __u64; #endif typedef __u32 __be32; typedef __u64 __be64; #endif #if BYTE_ORDER == BIG_ENDIAN #define cpu_to_be32(x) (x) #elif BYTE_ORDER == LITTLE_ENDIAN #define cpu_to_be32(x) ( __builtin_bswap32(x)) #else #error byte order not supported #endif #ifdef __cplusplus extern "C" { #endif typedef enum { IPACL_OK=0, IPACL_INVALID_PREFIX, IPACL_UNPARSABLE_PREFIX, IPACL_UNPARSABLE_ADDR, IPACL_NOMEM, } ipacl_res_t; union ipacl_inet_addr { __u32 all[4]; __be32 ip; __be32 ip6[4]; struct in_addr in; struct in6_addr in6; }; /** IPv4 variant */ typedef struct ipacl_net4_elem { __be32 ip; __u8 cidr; } ipacl_net4_elem_t; /** IPv6 variant */ typedef struct ipacl_net6_elem { union ipacl_inet_addr ip; __u8 cidr; } ipacl_net6_elem_t; struct ipacl_entity { SLIST_ENTRY(ipacl_entity ) node; sa_family_t ss_family; union { ipacl_net4_elem_t in; ipacl_net6_elem_t in6; }; }; typedef struct ipacl_entity ipacl_entity_t; SLIST_HEAD(hipacl, ipacl_entity); typedef struct hipacl hipacl_t; #define IPACL_HEAD_INITIALIZER(head) SLIST_HEAD_INITIALIZER(head) extern ipacl_res_t ipacl_emplace(hipacl_t *hacl, const char *ipacl_str); extern ipacl_res_t ipacl_append(hipacl_t *hacl, const char *ipacl_str, int *size); extern const char *ipacl_strerror(ipacl_res_t res); extern bool ipacl_is_empty(const hipacl_t *hacl); extern bool ipacl_test_saddr(const hipacl_t *hacl, const struct sockaddr *saddr); extern bool ipacl_test_saddr_storage(const hipacl_t *hacl, const struct sockaddr_storage *ss); extern bool ipacl_test_ip(const hipacl_t *hacl, const char *ip); extern void ipacl_free(hipacl_t *hacl); #ifdef __cplusplus } #endif #endif /* USE_IPACL */ #endif /* _IPACL_H */ burp-2.4.0/src/linkhash.c000066400000000000000000000056461404341324700152360ustar00rootroot00000000000000/* Bacula® - The Network Backup Solution Copyright (C) 2000-2010 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. 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 Affero 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. Bacula® is a registered trademark of Kern Sibbald. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ /* Code extracted from findlib from bacula-5.0.3, and heavily modified. Therefore, I have retained the bacula copyright notice. Graham Keeling, 2015. */ #include "burp.h" #include "handy.h" #include "linkhash.h" // List of all hard linked files found. struct f_link **linkhash=NULL; #define LINK_HASHTABLE_BITS 16 #define LINK_HASHTABLE_SIZE (1<next; if(lc) { free_w(&lc->name); free_v((void **)&lc); } } linkhash[i]=NULL; } free_v((void **)&linkhash); } static inline int get_hash(struct stat *statp) { int hash=statp->st_dev; uint64_t i=statp->st_ino; hash ^= i; i >>= 16; hash ^= i; i >>= 16; hash ^= i; i >>= 16; hash ^= i; return hash & LINK_HASHTABLE_MASK; } struct f_link *linkhash_search(struct stat *statp, struct f_link ***bucket) { struct f_link *lp; *bucket=&linkhash[get_hash(statp)]; for(lp=**bucket; lp; lp=lp->next) if(lp->ino==(ino_t)statp->st_ino && lp->dev==(dev_t)statp->st_dev) return lp; return NULL; } int linkhash_add(char *fname, struct stat *statp, struct f_link **bucket) { struct f_link *new_fl; if(!(new_fl=(struct f_link *)malloc_w(sizeof(struct f_link), __func__)) || !(new_fl->name=strdup_w(fname, __func__))) return -1; new_fl->ino=statp->st_ino; new_fl->dev=statp->st_dev; new_fl->next=*bucket; *bucket=new_fl; return 0; } burp-2.4.0/src/linkhash.h000066400000000000000000000012731404341324700152330ustar00rootroot00000000000000#ifndef _LINKHASH_H #define _LINKHASH_H /* * Structure for keeping track of hard linked files, we * keep an entry for each hardlinked file that we save, * which is the first one found. For all the other files that * are linked to this one, we save only the directory * entry so we can link it. */ struct f_link { struct f_link *next; // Device plus inode is unique. dev_t dev; ino_t ino; char *name; }; extern struct f_link **linkhash; extern int linkhash_init(void); extern void linkhash_free(void); extern struct f_link *linkhash_search(struct stat *statp, struct f_link ***bucket); extern int linkhash_add(char *fname, struct stat *statp, struct f_link **bucket); #endif burp-2.4.0/src/lock.c000066400000000000000000000074571404341324700143670ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "lock.h" #include "log.h" struct lock *lock_alloc(void) { return (struct lock *)calloc_w(1, sizeof(struct lock), __func__); } int lock_init(struct lock *lock, const char *path) { free_w(&lock->path); if(!(lock->path=strdup_w(path, __func__))) return -1; return 0; } struct lock *lock_alloc_and_init(const char *path) { struct lock *lock; if(!(lock=lock_alloc()) || lock_init(lock, path)) lock_free(&lock); return lock; } static void lock_free_content(struct lock *lock) { free_w(&lock->path); } void lock_free(struct lock **lock) { if(!lock || !*lock) return; lock_free_content(*lock); free_v((void **)lock); } void lock_get_quick(struct lock *lock) { #if defined(HAVE_WIN32) || !defined(HAVE_LOCKF) // Would somebody please tell me how to get a lock on Windows?! lock->status=GET_LOCK_GOT; return; #else if((lock->fd=open( lock->path, #ifdef O_NOFOLLOW O_NOFOLLOW| #endif O_WRONLY|O_CREAT, 0666 ))<0) { logp("Could not open lock file %s: %s\n", lock->path, strerror(errno)); goto error; } if(lockf(lock->fd, F_TLOCK, 0)) { if(errno==EACCES || errno==EAGAIN) goto notgot; logp("Could not get lock %s: %s\n", lock->path, strerror(errno)); goto error; // Some other error. } if(lock_write_pid(lock)) goto error; lock->status=GET_LOCK_GOT; return; error: lock->status=GET_LOCK_ERROR; return; notgot: lock->status=GET_LOCK_NOT_GOT; return; #endif } int lock_write_pid(struct lock *lock) { char text[64]=""; if(ftruncate(lock->fd, 0)) { logp("Could not ftruncate lock %s: %s\n", lock->path, strerror(errno)); return -1; } if(lseek(lock->fd, 0, SEEK_SET)<0) { logp("Could not seek to start of lock %s: %s\n", lock->path, strerror(errno)); return -1; } snprintf(text, sizeof(text), "%d\n", (int)getpid()); if(write(lock->fd, text, strlen(text))!=(ssize_t)strlen(text)) { logp("Could not write pid/progname to %s: %s\n", lock->path, strerror(errno)); return -1; } return 0; } // Return 0 for lock got, 1 for lock not got, -1 for error. void lock_get(struct lock *lock) { #if defined(HAVE_WIN32) || !defined(HAVE_LOCKF) // Would somebody please tell me how to get a lock on Windows?! lock->status=GET_LOCK_GOT; return; #else char *cp=NULL; char *copy=NULL; // Try to make sure the lock directory exists. if(!(copy=strdup_w(lock->path, __func__))) { lock->status=GET_LOCK_ERROR; return; } if((cp=strrchr(copy, '/'))) { *cp='\0'; if(*copy) mkdir(copy, 0777); } free_w(©); lock_get_quick(lock); // Try to make sure the pid gets onto the disk. if(lock->status==GET_LOCK_GOT) fsync(lock->fd); return; #endif } int lock_test(const char *path) { #if defined(HAVE_WIN32) || !defined(HAVE_LOCKF) // Would somebody please tell me how to test a lock on Windows?! return 0; #else int r=0; int fdlock; if((fdlock=open(path, O_WRONLY, 0666))<0) return 0; // file does not exist - could have got the lock errno=0; if((r=lockf(fdlock, F_TLOCK, 0)) && (errno==EAGAIN || errno==EACCES)) { // could not have got the lock close(fdlock); return -1; } close(fdlock); // could have got the lock return 0; #endif } int lock_release(struct lock *lock) { int ret=0; if(!lock || lock->status!=GET_LOCK_GOT) return 0; if(lock->path) unlink(lock->path); if(lock->fd>=0) { if((ret=close(lock->fd))) logp("Could not close %s: %s\n", lock->path, strerror(errno)); lock->fd=-1; } lock->status=GET_LOCK_NOT_GOT; return ret; } void lock_add_to_list(struct lock **locklist, struct lock *lock) { if(*locklist) lock->next=*locklist; *locklist=lock; } void locks_release_and_free(struct lock **locklist) { struct lock *l; struct lock *head; if(!locklist) return; head=*locklist; while(head) { l=head; head=head->next; lock_release(l); lock_free(&l); } *locklist=NULL; } burp-2.4.0/src/lock.h000066400000000000000000000014661404341324700143660ustar00rootroot00000000000000#ifndef LOCK_H #define LOCK_H enum lockstat { GET_LOCK_NOT_GOT=0, GET_LOCK_ERROR, GET_LOCK_GOT }; struct lock { int fd; enum lockstat status; char *path; struct lock *next; }; extern struct lock *lock_alloc(void); extern int lock_init(struct lock *lock, const char *path); extern struct lock *lock_alloc_and_init(const char *path); extern void lock_free(struct lock **lock); extern int lock_write_pid(struct lock *lock); // Need to test lock->status to find out what happened when calling these. extern void lock_get_quick(struct lock *lock); extern void lock_get(struct lock *lock); extern int lock_test(const char *path); extern int lock_release(struct lock *lock); extern void lock_add_to_list(struct lock **locklist, struct lock *lock); extern void locks_release_and_free(struct lock **locklist); #endif burp-2.4.0/src/log.c000066400000000000000000000147361404341324700142160ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "asfd.h" #include "async.h" #include "cmd.h" #include "cntr.h" #include "iobuf.h" #include "log.h" #include "strlist.h" #include "times.h" const char *prog="unknown"; const char *prog_long="unknown"; static struct fzp *logfzp=NULL; // Start with all logging on, so that something is said when initial startup // goes wrong - for example, reading the conf file. static int do_syslog=1; static int do_stdout=1; static int do_progress_counter=1; static int syslog_opened=0; static int json=0; void log_init(char *progname) { prog_long=progname; if((prog=strrchr(progname, '/'))) prog++; else prog=progname; } void logp(const char *fmt, ...) { #ifndef UTEST int pid; char buf[512]=""; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); pid=(int)getpid(); if(logfzp) fzp_printf(logfzp, "%s: %s[%d] %s", gettimenow(), prog, pid, buf); else { if(do_syslog) syslog(LOG_INFO, "%s", buf); if(do_stdout) { if(json) { char *cp; // To help programs parsing the monitor output, // log things with simple JSON. // So do simple character substitution to have // a better chance of valid JSON. for(cp=buf; *cp; cp++) { if(*cp=='"') *cp='\''; else if(!isprint(*cp)) *cp='.'; } fprintf(stdout, "{ \"logline\": \"%s\" }\n", buf); } else fprintf(stdout, "%s: %s[%d] %s", gettimenow(), prog, pid, buf); } } va_end(ap); #endif } void logp_ssl_err(const char *fmt, ...) { char buf[512]=""; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); logp("%s", buf); if(logfzp) fzp_ERR_print_errors_fp(logfzp); else { if(do_syslog) { // FIX THIS: How to send to syslog? static BIO *bio_err=NULL; if(!bio_err) bio_err=BIO_new_fp(stderr, BIO_NOCLOSE); ERR_print_errors(bio_err); } if(do_stdout) { if(!json) { static BIO *bio_err=NULL; if(!bio_err) bio_err=BIO_new_fp(stdout, BIO_NOCLOSE); ERR_print_errors(bio_err); } } } } // For the counters. void logc(const char *fmt, ...) { char buf[512]=""; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); if(logfzp) fzp_printf(logfzp, "%s", buf); // for the server side else { if(do_progress_counter && do_stdout) fprintf(stdout, "%s", buf); } va_end(ap); } void logfmt(const char *fmt, ...) { #ifndef UTEST if(do_stdout) { char buf[512]=""; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); fprintf(stdout, "%s", buf); } #endif } const char *progname(void) { return prog; } int log_fzp_set(const char *path, struct conf **confs) { fzp_close(&logfzp); if(path) { logp("Logging to %s\n", path); if(!(logfzp=fzp_open(path, "ab"))) return -1; } if(logfzp) fzp_setlinebuf(logfzp); do_syslog=get_int(confs[OPT_SYSLOG]); do_stdout=get_int(confs[OPT_STDOUT]); do_progress_counter=get_int(confs[OPT_PROGRESS_COUNTER]); if(syslog_opened) { closelog(); syslog_opened=0; } if(do_syslog) { openlog(prog, LOG_PID, LOG_USER); syslog_opened++; } return 0; } void log_fzp_set_direct(struct fzp *fzp) { fzp_close(&logfzp); logfzp=fzp; } void log_out_of_memory(const char *function) { if(function) logp("out of memory in %s()\n", function); else logp("out of memory in unknown function\n"); } void log_restore_settings(struct conf **cconfs, int srestore) { struct strlist *l; logp("Restore settings:\n"); if(get_string(cconfs[OPT_ORIG_CLIENT])) logp("orig_client = '%s'\n", get_string(cconfs[OPT_ORIG_CLIENT])); if(get_string(cconfs[OPT_BACKUP])) logp("backup = '%s'\n", get_string(cconfs[OPT_BACKUP])); logp("restore_list = %s\n", get_string(cconfs[OPT_RESTORE_LIST])?"true":"false"); if(srestore) { // This are unknown unless doing a server initiated restore. logp("overwrite = %d\n", get_int(cconfs[OPT_OVERWRITE])); logp("strip = %d\n", get_int(cconfs[OPT_STRIP])); } if(get_string(cconfs[OPT_RESTOREPREFIX])) logp("restoreprefix = '%s'\n", get_string(cconfs[OPT_RESTOREPREFIX])); if(get_string(cconfs[OPT_STRIP_FROM_PATH])) logp("stripfrompath = '%s'\n", get_string(cconfs[OPT_STRIP_FROM_PATH])); if(get_string(cconfs[OPT_REGEX])) logp("regex = '%s'\n", get_string(cconfs[OPT_REGEX])); for(l=get_strlist(cconfs[OPT_INCLUDE]); l; l=l->next) logp("include = '%s'\n", l->path); } int logm(struct asfd *asfd, struct conf **confs, const char *fmt, ...) { int r=0; char buf[512]=""; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); if(asfd && asfd->as->doing_estimate) printf("\nMESSAGE: %s", buf); else { if(asfd && get_int(confs[OPT_MESSAGE])) // Backwards compatibility r=asfd->write_str(asfd, CMD_MESSAGE, buf); logp("MESSAGE: %s", buf); } va_end(ap); if(confs) cntr_add(get_cntr(confs), CMD_MESSAGE, 1); return r; } int logw(struct asfd *asfd, struct cntr *cntr, const char *fmt, ...) { int r=0; char buf[512]=""; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); if(asfd && asfd->as && asfd->as->doing_estimate) printf("\nWARNING: %s", buf); else { if(asfd) r=asfd->write_str(asfd, CMD_WARNING, buf); logp("WARNING: %s", buf); } va_end(ap); cntr_add(cntr, CMD_WARNING, 1); return r; } void log_and_send(struct asfd *asfd, const char *msg) { logp("%s\n", msg); if(asfd) asfd->write_str(asfd, CMD_ERROR, msg); } void log_and_send_oom(struct asfd *asfd) { char m[256]=""; snprintf(m, sizeof(m), "out of memory in %s()\n", __func__); logp("%s", m); if(asfd) asfd->write_str(asfd, CMD_ERROR, m); } void log_set_json(int value) { json=value; } void log_oom_w(const char *func, const char *orig_func) { logp("out of memory in %s, called from %s\n", func, orig_func); } int log_incexcs_buf(const char *incexc) { char *tok=NULL; char *copy=NULL; if(!incexc || !*incexc) return 0; if(!(copy=strdup_w(incexc, __func__))) return -1; if(!(tok=strtok(copy, "\n"))) { logp("unable to parse server incexc\n"); free_w(©); return -1; } do { logp("%s\n", tok); } while((tok=strtok(NULL, "\n"))); free_w(©); return 0; } void log_recvd(struct iobuf *iobuf, struct cntr *cntr, int print) { int l; const char *prefix="unset"; switch(iobuf->cmd) { case CMD_MESSAGE: prefix="MESSAGE"; break; case CMD_WARNING: prefix="WARNING"; break; default: break; } // Strip any trailing newlines. for(l=iobuf->len-1; l>=0; l--) { if(iobuf->buf[l]!='\n') break; iobuf->buf[l]='\0'; } logp("%s: %s\n", prefix, iobuf->buf); cntr_add(cntr, iobuf->cmd, print); } burp-2.4.0/src/log.h000066400000000000000000000026371404341324700142200ustar00rootroot00000000000000#ifndef _LOG_ROUTINES #define _LOG_ROUTINES struct asfd; struct conf; struct cntr; struct fzp; struct iobuf; extern const char *prog; extern const char *prog_long; extern void log_init(char *progname); extern const char *progname(void); extern int log_fzp_set(const char *path, struct conf **confs); extern void log_fzp_set_direct(struct fzp *fzp); extern void log_out_of_memory(const char *function); extern void log_restore_settings(struct conf **cconfs, int srestore); extern void log_and_send(struct asfd *asfd, const char *msg); extern void log_and_send_oom(struct asfd *asfd); extern void log_set_json(int value); extern void log_oom_w(const char *func, const char *orig_func); extern int log_incexcs_buf(const char *incexc); extern void log_recvd(struct iobuf *iobuf, struct cntr *cntr, int print); #ifndef __GNUC__ #define __attribute__(x) /*NOTHING*/ #endif extern void logp(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern void logp_ssl_err(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern void logc(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern void logfmt(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern int logm(struct asfd *asfd, struct conf **confs, const char *fmt, ...) __attribute__((format (printf, 3, 4))); extern int logw(struct asfd *asfd, struct cntr *cntr, const char *fmt, ...) __attribute__((format (printf, 3, 4))); #endif burp-2.4.0/src/msg.c000066400000000000000000000053211404341324700142110ustar00rootroot00000000000000#include "burp.h" #include "asfd.h" #include "async.h" #include "bfile.h" #include "cmd.h" #include "fzp.h" #include "iobuf.h" #include "log.h" #include "msg.h" int send_msg_fzp(struct fzp *fzp, enum cmd cmd, const char *buf, size_t s) { if(fzp_printf(fzp, "%c%04X", cmd, (unsigned int)s)!=5 || fzp_write(fzp, buf, s)!=s || fzp_printf(fzp, "\n")!=1) { logp("Unable to write message to file: %s\n", strerror(errno)); return -1; } return 0; } static int do_write(struct asfd *asfd, struct BFILE *bfd, uint8_t *out, size_t outlen, uint64_t *sent) { int ret=0; if((ret=bfd->write(bfd, out, outlen))<=0) { logp("%s: error when appending %lu: %d\n", __func__, (unsigned long)outlen, ret); asfd->write_str(asfd, CMD_ERROR, "write failed"); return -1; } *sent+=outlen; return 0; } static int do_inflate(struct asfd *asfd, z_stream *zstrm, struct BFILE *bfd, uint8_t *out, uint64_t *sent) { int zret=Z_OK; unsigned have=0; struct iobuf *rbuf=asfd->rbuf; zstrm->avail_in=rbuf->len; zstrm->next_in=(uint8_t *)rbuf->buf; do { zstrm->avail_out=ZCHUNK; zstrm->next_out=out; zret=inflate(zstrm, Z_NO_FLUSH); switch(zret) { case Z_NEED_DICT: zret=Z_DATA_ERROR; case Z_DATA_ERROR: case Z_MEM_ERROR: logp("zstrm inflate error: %d\n", zret); return -1; } have=ZCHUNK-zstrm->avail_out; if(!have) continue; if(do_write(asfd, bfd, out, have, sent)) return -1; } while(!zstrm->avail_out); return 0; } int transfer_gzfile_in(struct asfd *asfd, struct BFILE *bfd, uint64_t *rcvd, uint64_t *sent) { int quit=0; int ret=-1; uint8_t out[ZCHUNK]; struct iobuf *rbuf=asfd->rbuf; z_stream zstrm; zstrm.zalloc=Z_NULL; zstrm.zfree=Z_NULL; zstrm.opaque=Z_NULL; zstrm.avail_in=0; zstrm.next_in=Z_NULL; if(inflateInit2(&zstrm, (15+16))) { logp("unable to init inflate\n"); goto end; } while(!quit) { iobuf_free_content(rbuf); if(asfd->read(asfd)) goto end_inflate; (*rcvd)+=rbuf->len; //logp("transfer in: %s\n", iobuf_to_printable(rbuf)); switch(rbuf->cmd) { case CMD_APPEND: // append if(!bfd) { logp("given append, but no file to write to\n"); asfd->write_str(asfd, CMD_ERROR, "append with no file"); goto end_inflate; } else { if(do_inflate(asfd, &zstrm, bfd, out, sent)) goto end_inflate; } break; case CMD_END_FILE: // finish up goto end_ok; case CMD_MESSAGE: case CMD_WARNING: { struct cntr *cntr=NULL; log_recvd(rbuf, cntr, 0); break; } default: iobuf_log_unexpected(rbuf, __func__); goto end_inflate; } } end_ok: ret=0; end_inflate: inflateEnd(&zstrm); end: if(ret) logp("transfer file returning: %d\n", ret); iobuf_free_content(rbuf); return ret; } burp-2.4.0/src/msg.h000066400000000000000000000004671404341324700142240ustar00rootroot00000000000000#ifndef _MSG_ROUTINES #define _MSG_ROUTINES #include #include "bfile.h" #include "cmd.h" #include "fzp.h" extern int send_msg_fzp(struct fzp *fzp, enum cmd cmd, const char *buf, size_t s); extern int transfer_gzfile_in(struct asfd *asfd, struct BFILE *bfd, uint64_t *rcvd, uint64_t *sent); #endif burp-2.4.0/src/pathcmp.c000066400000000000000000000043771404341324700150710ustar00rootroot00000000000000#include "burp.h" #include "pathcmp.h" // Return a number indicating the number of directories matched. // 0 if it is not a sub-directory. // Two paths the same counts as a subdirectory. int is_subdir(const char *dir, const char *sub) { int count=1; const char *d=NULL; const char *s=NULL; const char *dl=NULL; const char *sl=NULL; if(!sub || !dir) return 0; for(s=sl=sub, dl=d=dir; *s && *d; s++, d++) { if(*s!=*d) break; sl=s; dl=d; if(*s=='/') count++; } if(!*d && !*s) return count; // Paths were exactly the same. if(!*d && *s=='/') return count; // 'dir' ended without a slash, for example: // dir=/bin sub=/bin/bash if(*dl=='/' && *sl=='/' && *(sl+1) && !*(dl+1)) return count; return 0; } int pathcmp(const char *a, const char *b) { // This should have used 'unsigned chars', but now its too late and // everybody has backups with odd sorting. Will have to live with it. const char *x=NULL; const char *y=NULL; if(!a && !b) return 0; // equal if( a && !b) return 1; // a is longer if(!a && b) return -1; // b is longer for(x=a, y=b; *x && *y ; x++, y++) { if(*x==*y) continue; if(*x=='/' && *y!='/') return -1; if(*x!='/' && *y=='/') return 1; // Need to make sure the comparisons are signed. // Not doing this caused problems on raspberry pis. if((int8_t)*x<(int8_t)*y) return -1; if((int8_t)*x>(int8_t)*y) return 1; } if(!*x && !*y) return 0; // equal if( *x && !*y) return 1; // x is longer return -1; // y is longer } // Not really pathcmp functions, but there is nowhere better to put them. int has_dot_component(const char *path) { const char *p=NULL; for(p=path; *p; p++) { if(*p!='.') continue; // Check for single dot. if((p==path || *(p-1)=='/') && (*(p+1)=='/' || !*(p+1))) return 1; // Check for double dot. if(*(p+1)=='.' && (p==path || *(p-1)=='/') && (*(p+2)=='/' || !*(p+2))) return 1; } return 0; } int is_absolute(const char *path) { if(has_dot_component(path)) return 0; // This is being run on the server too, where you can enter paths for the // clients, so need to allow windows style paths for windows and unix. return (isalpha(*path) && *(path+1)==':') #ifndef HAVE_WIN32 // Windows does not need to check for unix style paths. || *path=='/' #endif ; } burp-2.4.0/src/pathcmp.h000066400000000000000000000003641404341324700150660ustar00rootroot00000000000000#ifndef _PATHCMP_H #define _PATHCMP_H extern int is_subdir(const char *dir, const char *sub); extern int pathcmp(const char *a, const char *b); extern int has_dot_component(const char *path); extern int is_absolute(const char *path); #endif burp-2.4.0/src/prepend.c000066400000000000000000000032751404341324700150660ustar00rootroot00000000000000#include "burp.h" #include "alloc.h" #include "log.h" #include "prepend.h" /* This may be given binary data, of which we need to already know the length */ char *prepend_len(const char *prep, size_t plen, const char *fname, size_t flen, const char *sep, size_t slen, size_t *newlen) { size_t l=0; char *rpath=NULL; l+=plen; l+=flen; l+=slen; l+=1; if(!(rpath=(char *)malloc_w(l, __func__))) return NULL; if(plen) memcpy(rpath, prep, plen); if(slen) memcpy(rpath+plen, sep, slen); if(flen) memcpy(rpath+plen+slen, fname, flen); rpath[plen+slen+flen]='\0'; if(newlen) (*newlen)+=slen+flen; return rpath; } char *prepend_n(const char *prep, const char *fname, size_t len, const char *sep) { return prepend_len(prep, prep?strlen(prep):0, fname, len, sep, (sep && *fname)?strlen(sep):0, NULL); } char *prepend(const char *prep, const char *fname) { return prepend_n(prep, fname, strlen(fname), ""); } char *prepend_slash(const char *prep, const char *fname, size_t len) { if(!prep || !*prep) return strdup_w(fname, __func__); // Try to avoid getting a double slash in the path. if(fname && fname[0]=='/') { fname++; len--; } return prepend_n(prep, fname, len, "/"); } char *prepend_s(const char *prep, const char *fname) { return prepend_slash(prep, fname, strlen(fname)); } int astrcat(char **buf, const char *append, const char *func) { int l=0; char *copy=NULL; if(append) l+=strlen(append); if(*buf) l+=strlen(*buf); l++; if((*buf && !(copy=strdup_w(*buf, __func__))) || !(*buf=(char *)realloc_w(*buf, l, __func__))) { log_oom_w(__func__, func); return -1; } snprintf(*buf, l, "%s%s", copy?copy:"", append?append:""); free_w(©); return 0; } burp-2.4.0/src/prepend.h000066400000000000000000000010431404341324700150620ustar00rootroot00000000000000#ifndef _PREPEND_H #define _PREPEND_H extern char *prepend_len(const char *prep, size_t plen, const char *fname, size_t flen, const char *sep, size_t slen, size_t *newlen); extern char *prepend_n(const char *prep, const char *fname, size_t len, const char *sep); extern char *prepend(const char *prep, const char *fname); extern char *prepend_slash(const char *prep, const char *fname, size_t len); extern char *prepend_s(const char *prep, const char *fname); extern int astrcat(char **buf, const char *append, const char *func); #endif burp-2.4.0/src/prog.c000066400000000000000000000373261404341324700144040ustar00rootroot00000000000000#include "burp.h" #include "base64.h" #include "cmd.h" #include "conf.h" #include "conffile.h" #include "client/main.h" #include "handy.h" #include "hexmap.h" #include "lock.h" #include "log.h" #include "strlist.h" #include "server/main.h" #include "server/protocol1/bedup.h" #include "server/protocol2/bsigs.h" #include "server/protocol2/bsparse.h" #include "server/protocol2/champ_chooser/champ_server.h" static void usage_server(void) { #ifndef HAVE_WIN32 printf("\nThe configuration file specifies whether %s runs in server or client mode.\n", PACKAGE_TARNAME); printf("\nServer usage: %s [options]\n", progname()); printf("\n"); printf(" Options:\n"); printf(" -a c Run as a stand-alone champion chooser.\n"); printf(" -c Path to conf file (default: %s).\n", config_default_path()); printf(" -d a single client in the status monitor.\n"); printf(" -o