Compare commits

...

95 Commits

Author SHA1 Message Date
Harald Welte 260f4786c0 HACK: create install-client.sh as 'install.sh client' is broken 2017-10-25 03:41:00 +02:00
Harald Welte 73d86aeb20 client: don't exclude "*" by default. This belongs in config file. 2017-10-25 03:39:55 +02:00
Janek Bevendorff 0cc469f2b5 Add logger to DSM dependencies 2017-07-20 00:29:39 +02:00
Janek Bevendorff a8b36864e6 Replace ipkg with opkg 2017-07-20 00:21:40 +02:00
Janek Bevendorff c7427cf61d Add comment explaining default rsync settings better 2017-06-14 01:11:16 +02:00
Janek Bevendorff 0a3d0cff18 Add --block-size setting to default config to avoid hangs when backing up large files 2017-06-14 01:05:16 +02:00
Janek Bevendorff 8416d9a088 Bump version to 0.3.0 2016-11-18 02:44:29 +01:00
Janek Bevendorff 4f35991986 Implement pre and post hooks, resolves #10 2016-11-18 02:39:14 +01:00
Janek Bevendorff 51f514506c Add note about logging and chroot 2016-11-18 01:29:48 +01:00
Janek Bevendorff 5f03542d07 Log backup success and failure 2016-11-18 01:29:48 +01:00
Janek Bevendorff 1a0c1b984f Add logging 2016-11-18 01:29:48 +01:00
Janek Bevendorff 78853b7703 Fix /tmp logfile fallback path 2016-11-17 03:41:09 +01:00
Janek Bevendorff fbe3702c90 Remove LOG_FILE and USER_LOG_FILE config directives and allow user to force logging into specific file instead of syslog by using the --log-file parameter 2016-11-17 03:32:56 +01:00
Janek Bevendorff f22b634607 Add color formatting to console output 2016-11-17 02:55:18 +01:00
Janek Bevendorff 0f1b35e57c Use syslog for logging if available 2016-11-17 02:45:22 +01:00
Janek Bevendorff 4b84ad5997 Make rs-rm command more generic 2016-11-17 02:12:28 +01:00
Janek Bevendorff 2c4d2915e5 Fix regexp 2016-11-17 02:05:17 +01:00
Janek Bevendorff 52d0a17cae Add upgrade progress status message 2016-11-17 01:58:44 +01:00
Janek Bevendorff 9c9eb7b98a Update rm and cp paths in global rsnapshot config when upgrading from older version 2016-11-17 01:57:25 +01:00
Janek Bevendorff 3fae0fa63a Use /usr/bin/rm also on Synology, add wrapper for /usr/bin/rm for deleting files in read-only directories 2016-11-17 01:47:46 +01:00
Janek Bevendorff e68fed9277 Fix desktop notification not shown when DBUS_SESSION_BUS_ADDRESS environment variable is not set 2016-11-17 00:14:21 +01:00
Janek Bevendorff 18a2cc2774 Update version to 0.2.6 2016-11-16 22:42:59 +01:00
Janek Bevendorff 2ac6c3fb6c util-linux-ng still needed on DSM 6.x for logger binary 2016-11-16 22:28:59 +01:00
Janek Bevendorff fe570455cf Update dates in license headers 2016-11-16 22:15:30 +01:00
Janek Bevendorff 6ba2d541fa Use bash instead of POSIX shell, since scripts aren't POSIX-compliant anyway. Fixes issue #7 2016-11-16 22:10:30 +01:00
Janek Bevendorff 585cfa4a81 Also try /usr/lib/openssh/sftp-server
Suggested by kmyid <591fdb4a16>
2016-11-16 22:04:05 +01:00
Janek Bevendorff d6e66ec303 Add chroot SSHD config 2016-11-16 21:57:59 +01:00
Janek Bevendorff 0f0f43fc47 Add bind option to remounts 2016-11-16 21:47:57 +01:00
Janek Bevendorff 71a5983d27 Update mount listing 2016-11-16 21:41:55 +01:00
Janek Bevendorff 5da9c7f6a7 Update README for Synology DSM 6.x 2016-11-16 21:40:36 +01:00
Janek Bevendorff e9dc07e3e2 Bump version to 0.2.5 2015-10-16 13:31:58 +02:00
Janek Bevendorff 0f1cb0be0d Update README to include documentation about systemd timer 2015-10-16 13:31:42 +02:00
Janek Bevendorff c502982cac Add systemd service and timer for client 2015-10-16 13:22:04 +02:00
Janek Bevendorff 785c0245f0 Simplify code 2015-10-14 20:35:55 +02:00
Janek Bevendorff a87b975a72 Bump version to 0.2.4 2015-10-14 19:50:51 +02:00
Janek Bevendorff fea3b8e4cd Fix SFTP not working for non-standard sftp-server locations 2015-10-14 19:50:23 +02:00
Janek Bevendorff 8b7a244c27 Share version information between client and server scripts and update to 0.2.3 2015-10-14 19:11:46 +02:00
Janek Bevendorff 876a1968da Copy updated config file to /etc/rs-backup/client-config.new if contents differ 2015-10-14 18:37:18 +02:00
Janek Bevendorff 0e70386f81 Change default user include file location to .rs-backup/include 2015-10-14 18:29:46 +02:00
Janek Bevendorff c02c79108e Ignore log and runfiles 2015-10-14 18:16:01 +02:00
Janek Bevendorff 62a83a2a6c Only log error messages from rsync, not the whole file list 2015-10-14 17:58:33 +02:00
Janek Bevendorff d70dda84e3 Handle external interrupts through SIGHUP, SIGINT and SIGTERM 2015-10-14 17:41:09 +02:00
Janek Bevendorff 3ee4d4b71e Fix inconsistent path in rsnapshot config template, resolves issue #9 2015-03-22 11:59:51 +01:00
Janek Bevendorff 5f85b35a22 Avoid using BusyBox stat on Synology DSM 2015-03-19 20:06:03 +01:00
Janek Bevendorff f3182973d9 Replace parsed `ls` with `stat` - less portable, but safe 2015-03-19 19:27:59 +01:00
Janek Bevendorff 75d2c11ace Fix error when no display number present in janek :0 2014-11-10 15:56
janek    pts/0        2014-11-10 15:57 (:0)
janek    pts/1        2014-11-10 15:57 (:0) output, closes #8
2014-11-10 19:07:55 +01:00
Janek Bevendorff 181b05d116 Make read-only bind mount work with unpatched kernels (i.e. uglify mount commands by a lot) 2014-10-15 01:47:28 +02:00
Janek Bevendorff 755f2c62a0 Add ro mount option to bind mounts as extra security measure 2014-10-15 00:59:12 +02:00
Janek Bevendorff 801366d781 Re-add /bin to chroot mounts 2014-10-15 00:34:20 +02:00
Janek Bevendorff b5cd13472f Update README.md
Remove trailing slash from /etc/fstab
2014-10-15 00:27:23 +02:00
Janek Bevendorff ad633b262a Replace spaces with tabs in fstab and move mounts to /etc/rc on Synology DSM 2014-10-15 00:21:53 +02:00
Janek Bevendorff 1c2933e48c Reload crond on Synology DSM after installing crontab entries 2014-10-08 01:31:39 +02:00
Janek Bevendorff 6baf317f41 Fix typo in variable name 2014-10-07 01:37:28 +02:00
Janek Bevendorff 64a7c12886 Add newlines to the end 2014-10-07 01:29:25 +02:00
Janek Bevendorff 715c1d393c Separate entries by tabs instead of spaces and replace @ directives with classic cron fields 2014-10-07 01:26:32 +02:00
Janek Bevendorff 8b41ff5c86 Add sudo to commands that need to be run as root 2014-10-07 01:00:01 +02:00
Janek Bevendorff 62e4dc4c99 Add sudo to rs-setquota command 2014-10-07 00:55:28 +02:00
Janek Bevendorff 15d6f110ca Add documentation for quota support 2014-10-07 00:50:37 +02:00
Janek Bevendorff 39680df809 Re-implement quota support 2014-10-07 00:11:49 +02:00
Janek Bevendorff 1d1a09f89a Use absolute paths to rsnapshot if possible 2014-10-06 13:55:06 +02:00
Janek Bevendorff 917966b2af Add short note for usage on Cygwin 2014-10-06 13:17:31 +02:00
Janek Bevendorff e8dd2d3ed9 Don't show notifications on Cygwin 2014-10-06 13:05:38 +02:00
Janek Bevendorff 15356c8ce5 Don't check for UID 0 on Cygwin 2014-10-06 12:44:48 +02:00
Janek Bevendorff a2b5ac8d87 Add short guide for installing rs-backup-suite on Synology DSM 2014-10-06 02:28:18 +02:00
Janek Bevendorff a7d5ef2f06 Update old path 2014-10-06 02:13:26 +02:00
Janek Bevendorff 0984a79fee Bump version number to 0.2.2 2014-10-06 02:03:57 +02:00
Janek Bevendorff d1196fa3db Remove vim fail. :-) 2014-10-06 01:56:14 +02:00
Janek Bevendorff 3bd8fbc511 Create (commented) mount options in /etc/fstab automatically 2014-10-06 01:52:59 +02:00
Janek Bevendorff 65ec9170de Create all necessary directories on Synology DSM, skip the rest 2014-10-06 00:57:32 +02:00
Janek Bevendorff bfcdf48381 Fix rsync session terminating 2014-10-06 00:49:39 +02:00
Janek Bevendorff 66e3839f55 Make sftp access compatible with Synology DSM and apply some general optimization 2014-10-05 23:52:40 +02:00
Janek Bevendorff b81371afca Add support for libnotify / notify-send 2014-10-05 21:57:48 +02:00
Janek Bevendorff f8c24cb457 Improve script compatibility by replacing PCRE grep with awk (resulting in overall nicer code) 2014-10-05 19:38:21 +02:00
Janek Bevendorff 9705fa78dc Do not overwrite any existing global rsnapshot config 2014-10-05 18:51:48 +02:00
Janek Bevendorff dc8a35e78d Replace incompatible busybox /usr/bin/logger with /opt/bin/logger on Synology DSM (now requires util-linux-ng to be installed via ipkg) 2014-10-05 18:41:15 +02:00
Janek Bevendorff c68fbedb26 Create correct perl5 mount directory on Synology DSM 2014-10-05 18:09:03 +02:00
Janek Bevendorff 031c437f5b Fix typo in variable name preventing rsync options to be loaded from config file and add (commented) example config that also works on Synology DSM 2014-10-05 18:03:50 +02:00
Janek Bevendorff 86f5ccdef3 Move --acls option to config file since not all servers support it (e.g. Synology DSM doesn't) 2014-10-05 17:54:53 +02:00
Janek Bevendorff 0a7ba16686 Make Synology OS detection work in chroot 2014-10-05 17:32:21 +02:00
Janek Bevendorff 8221e5acb3 Fix script not working in chroot due to missing /etc/profile 2014-10-05 17:18:00 +02:00
Janek Bevendorff b719226c73 Make passwd parsing more generic to improve compatibility 2014-10-05 16:43:57 +02:00
Janek Bevendorff 33f85d5d93 Correct initial rsnapshot config adjustment (again) 2014-10-05 16:43:57 +02:00
Janek Bevendorff 460d545a40 Minor formatting fix 2014-10-05 16:43:57 +02:00
Janek Bevendorff 1b03f13769 Fix UID acquisition for systems that don't set $UID 2014-10-05 16:43:46 +02:00
Janek Bevendorff 7075faeb7a Fix initial rsnapshot config correction 2014-10-05 16:19:54 +02:00
Janek Bevendorff cf078a123c Don't ignore client-config and server-config 2014-10-05 16:11:50 +02:00
Janek Bevendorff 2eae6e45de Remove xattr retention to improve compatibility with Synology DSM 2014-10-05 16:10:10 +02:00
Janek Bevendorff 22f454657d Fix detection of ald backup root config when no server-config available 2014-10-05 16:07:16 +02:00
Janek Bevendorff dca81553b6 Also remove skeleton folder in /etc 2014-10-05 15:32:27 +02:00
Janek Bevendorff b82fb3d80b Update client config guide 2014-10-05 15:27:47 +02:00
Janek Bevendorff 86803bf206 Also uninstall crontab entries 2014-10-05 15:17:31 +02:00
Janek Bevendorff fafe3d8275 Formatting fix 2014-10-05 01:22:44 +02:00
Janek Bevendorff 840e6db3c6 Update out-of date information 2014-10-05 01:20:47 +02:00
Janek Bevendorff 49001dd638 Fix typo 2014-10-05 01:19:28 +02:00
Janek Bevendorff aedc33c88d Formatting fix 2014-10-05 01:18:46 +02:00
29 changed files with 1037 additions and 166 deletions

2
.gitignore vendored
View File

@ -6,6 +6,4 @@ desktop.ini
Desktop.ini
.DS_Store
server/etc/rs-backup/server-config
client/etc/rs-backup/client-config
client/etc/rs-backup/include-files

132
README.md
View File

@ -7,11 +7,13 @@ rs-backup-suite is designed for push backups, which means the client pushes its
It is also a user-centric backup system. That means each user creates his own backup on the NAS instead of root backing up the whole machine at once (although this is possible). That also means that each user has a UNIX account on the NAS. The NAS username is usually `<hostname>-<local user name>` (e.g. `mymachine-johndoe`).
On the client machine(s) each user can create a file called `.rs-backup-include` (name is configurable) inside his home directory which includes the list of files that should be considered by the backup. Additionally root can maintain a similar file located at `/usr/local/etc/rs-backup/include-files` for the system files.
On the client machine(s) each user can create a file called `.rs-backup/include` (name is configurable) inside his home directory which includes the list of files that should be considered by the backup. Additionally root can maintain a similar file located at `/etc/rs-backup/include-files` for the system files.
## Setup (please read this carefully before performing any actions!)
rs-backup-suite is split into two parts: a client part for pushing the backup to the NAS and a server part which runs on the NAS itself.
**NOTE:** Any command that necessarily needs to be run as root is preceded by `sudo` in this document. If `sudo` is not available on your system, make sure you are running the command in a root shell (e.g. using `su`).
### Server
For installing the server component run
@ -32,38 +34,46 @@ If you need to tweak the server settings, simply edit `/etc/rs-backup/server-con
#### Adding a backup user
A backup user is an unprivileged UNIX account on the server. Normally each user on each client has one corresponding backup user which he uses to log into the NAS. A backup user can be created by running
rs-add-user hostname username [ssh-public-key-file]
sudo rs-add-user hostname username [ssh-public-key-file]
on the server where `hostname` is the name of the client host and `username` is the name of the user on that machine for whom this account is made. Of course you can use any other names for `hostname` and `username` as well, but it's generally a good idea to stick to this naming convention. The resulting UNIX username will be the combination of both.
The optional third parameter specifies the path to the SSH public key file which the user will use to log into the NAS. If you don't specify it, the user won't be able to log in at all. But you can add one later at any time by running
rs-add-ssh-key hostname username ssh-public-key-file
sudo rs-add-ssh-key hostname username ssh-public-key-file
`hostname` and `username` are the same as above and mandatory for identifying the user that should get the new key.
**TIP:** If you don't remember the parameters for all these commands, simply run them without any and you'll get simple usage instructions.
#### Making the chroot work
rs-backup-suite can chroot backup users into the backup home base directory. For this to work you need to add a few lines to your `/etc/fstab` and run `mount -a` afterwards (replace `/bkp` with your backup path):
rs-backup-suite can chroot backup users into the backup home base directory. For this to work you need to create a few bind mounts. The install script already created the respective lines in your `/etc/fstab` for you. If you don't need any special configuration on your system, all you need to do is to uncomment everything between the `BEGIN` and `END` lines (do NOT change these two lines, though):
# Chroot
/bin /bkp/bin none bind 0 0
/lib /bkp/lib none bind 0 0
/usr/bin /bkp/usr/bin none bind 0 0
/usr/lib /bkp/usr/lib none bind 0 0
/usr/share/perl5 /bkp/usr/share/perl5 none bind 0 0
/dev /bkp/dev none bind 0 0
# BEGIN: rs-backup-suite
#/bin /bkp/bin none bind 0 0
#/bin /bkp/bin none remount,ro,bind 0 0
#/lib /bkp/lib none bind 0 0
#/lib /bkp/lib none remount,ro,bind 0 0
#/dev /bkp/dev none bind 0 0
#/dev /bkp/dev none remount,ro,bind 0 0
#/usr/bin /bkp/usr/bin none bind 0 0
#/usr/bin /bkp/usr/bin none remount,ro,bind 0 0
#/usr/lib /bkp/usr/lib none bind 0 0
#/usr/lib /bkp/usr/lib none remount,ro,bind 0 0
#/usr/share/perl5 /bkp/usr/share/perl5 none bind 0 0
#/usr/share/perl5 /bkp/usr/share/perl5 none remount,ro,bind 0 0
# END: rs-backup-suite
**NOTE:** In Ubuntu the Perl modules are located at `/usr/share/perl` instead of `/usr/share/perl5`. Change that accordingly. Also note that if you are using Synology DSM, you also need to add a bind mount for /opt/bin to /bkp/opt/bin.
The necessary mounts may differ from system to system. For instance, Ubuntu needs `/usr/share/perl` instead of `/usr/share/perl5`. Synology DSM doesn't need `/usr/share/*` at all, but requires `/opt/bin`, `/opt/lib` and `/opt/libexec`. But in most cases you don't need to worry about that since the install script tries to make the correct decisions for you.
If your 64-bit system doesn't have a `/lib` folder but only `/lib64` you may need to add this to your `/etc/fstab`:
**NOTE:** If your 64-bit system doesn't have a `/lib` folder but only `/lib64` you may need to change the `/lib` line in your `/etc/fstab` as follows:
/lib64 /bkp/lib64 none bind 0 0
/lib64 /bkp/lib64 none bind 0 0
/lib64 /bkp/lib64 none remount,ro,bind 0 0
and rename `/bkp/lib` to `/bkp/lib64`. Usually `/lib` is symlinked to `/lib64` though.
Don't forget to rename `/bkp/lib` to `/bkp/lib64`. The do the same with `/usr/lib` / `/usr/lib64`.
Finally add this to the end of your `/etc/ssh/sshd_config`:
When you're done, add this to the end of your `/etc/ssh/sshd_config`:
Match Group backup
ChrootDirectory /bkp/
@ -72,22 +82,37 @@ and restart OpenSSH. Your backup users are now chrooted into `/bkp`.
**NOTE:** When using a chroot environment and you change anything in your user configuration (e.g. the username) you need to run `rs-update-passwd` or your user might not be able to log in anymore.
**NOTE about logging:** Be aware that logging of backup success or failure on the server side will not work in a chroot environment since we mounted all our binds read-only. Additionally, certain files and libraries needed by the syslog facility may not be available. So if you want server-side logging, you cannot use chroot. Client-side logging will still work, of course.
#### Changing the rotation options/backup levels
To change how many increments of which level are kept, edit the file `/bkp/etc/rsnapshot.global.conf`. This is the global configuration file for rsnapshot which will be included in each user-specific configuration. There you can tweak the names and numbers for all backup levels.
If you add or remove any backup levels, make sure you also update the cron scripts. By default three cron scripts are installed: `/etc/cron.daily/rs-backup-rotate`, `/etc/cron.weekly/rs-backup-rotate` and `/etc/cron.monthly/rs-backup-rotate`.
#### Quota support
rs-backup-suite directly supports Linux file system quota. To make use of it, you need to enable quota for your backup drive first (i.e install the necessary utility packages, mount the backup drive with needed mount options and initialize quota files). This is pretty much straight-forward and not in any way different to any other Linux system. If you need assistance with setting up quota, I recommend you read [this quota guide](http://www.linux.com/learn/tutorials/393886-enable-per-user-disk-quotas-in-linux).
Once disk quota are set up, you can change the value of `SET_QUOTA` in `/etc/rs-backup/server-config` to `true` and tweak the `QUOTA_*` directives to your liking. Any new user you create with `rs-add-user` will now be assigned these initial default quota.
Of course you can change these default quota at any time using `rs-setquota`. For instance:
sudo rs-setquota local-username 500G 505G 4M 5M
This sets soft quota for the user `local-username` to 500GiB, hard quota to 505GiB, inode soft limit to 4194304 and inode hard limit to 5242880. You can, of course, set quota like this even when `SET_QUOTA` is `false`.
Editing quota using native Linux quota tools (i.e. `setquota` or `edquota`) is also possible (in fact, `rs-setquota` only provides a more user-friendly frontend to `setquota`).
### Client
To set up the client you simply need to run
sudo ./install.sh client
on your client machine. Then copy the file `/etc/rs-backup/client-config.example` to `/etc/rs-backup/client-config`, edit it as root and replace the value of `REMOTE_HOST` with the hostname or IP address of your NAS.
on your client machine. Then open the file `/etc/rs-backup/client-config` as root and replace the value of `REMOTE_HOST` with the hostname or IP address of your NAS.
On the client machines the script `/usr/bin/rs-backup-run` is used for performing the backups. This script can either be run as root or as an unprivileged user. The behavior differs in both cases:
* If run as root, all files and folder specified in /etc/rs-backup/include-files` will be backed up. The backup user used for logging into the NAS is `hostname-root` by default (where `hostname` is the hostname of the current machine). Additionally the home directories of all users will be scanned. If a home directory contains a file called `.rs-backup-include` all files and folders specified inside that file will be backed up under this user's privileges. The username used for logging into the NAS is `hostname-username` (where `hostname` is again substituted for the hostname of the current machine and `username` for the user whose home directory is being backed up).
* If run as a normal user, only the files that are specified in your own `.rs-backup-include` will be backed up.
* If run as root, all files and folder specified in `/etc/rs-backup/include-files` will be backed up. The backup user used for logging into the NAS is `hostname-root` by default (where `hostname` is the hostname of the current machine). Additionally the home directories of all users will be scanned. If a home directory contains a file called `.rs-backup/include` all files and folders specified inside that file will be backed up under this user's privileges. The username used for logging into the NAS is `hostname-username` (where `hostname` is again substituted for the hostname of the current machine and `username` for the user whose home directory is being backed up).
* If run as a normal user, only the files that are specified in your own `.rs-backup/include` will be backed up.
#### Changing the default configuration
All the client configuration options are defined in `/etc/rs-backup/client-config`. You can edit the file as you wish. All parameters are documented clearly by comments. Most of these configuration options can also be overridden at runtime by passing command line arguments to `rs-backup-run`. For a list and a description of all possible command line arguments run
@ -95,19 +120,38 @@ All the client configuration options are defined in `/etc/rs-backup/client-confi
rs-backup-run --help
## Installing client and server on the same machine
You can of course also install server and client on the same machine. This may be useful if you want, e.g. save your data to an external USB drive instead of a real NAS. A shortcut for running both `sudo make server-install` and `sudo make client-install` is simply running
You can of course also install server and client on the same machine. This may be useful if you want, e.g. save your data to an external USB drive instead of a real NAS. A shortcut for running both `sudo ./install server` and `sudo ./install client` is simply running
sudo ./install all
## Uninstalling
For uninstalling run ./uninstall.sh [all|server|client]. These remove all the scripts but preserve the data in `/bkp` (or whatever your backup folder is).
For uninstalling run
sudo ./uninstall.sh [all|server|client]
This removes all the scripts but preserves the data in `/bkp` (or whatever your backup folder is).
## Backup strategies
The intended use case for rs-backup-suite is as follows: you set up the server part on your NAS. Then you create a backup user for each user on each client machine.
### Cron
In the next step you edit the crontab for root on each client and add a job for running `/usr/bin/rs-backup-run` at certain times. You can of course also create a shell script that calls `rs-backup-run` and put it in `/etc/cron.daily` to perform a global backup once a day.
After everything is set up that way you create the file `/etc/rs-backup/include-file` and write to it a list of files and folders you want to back up as root (e.g. you can specify `/etc/***` to backup the whole `/etc` directory and all its subdirectories). Furthermore each user creates a file called `.rs-backup-include` inside his home directory that serves the same purpose for his own home directory instead of the global system. Such a file could look like this:
### Alternative: systemd
Since version 0.2.5, rs-backup-run comes with systemd unit files which you can use for automatically running daily backups instead.
To enable the timer, run
systemctl enable rs-backup-run.timer
systemctl start rs-backup-run.timer
This will enable the timer when booting the system. By default, the timer is set to run once every day (or immediately if the system was down during the last timer tick).
You can also start a full system backup manually at any time by running
systemctl start rs-backup-run.service
### Inclusion patterns
After everything is set up that way you create the file `/etc/rs-backup/include-file` and write to it a list of files and folders you want to back up as root (e.g. you can specify `/etc/***` to backup the whole `/etc` directory and all its subdirectories). Furthermore each user creates a file called `.rs-backup/include` inside his home directory that serves the same purpose for his own home directory instead of the global system. Such a file could look like this:
- /home/johndoe/.cache/***
/home
@ -133,6 +177,50 @@ Be aware that both access methods are strictly read-only! Write access is only g
## Side note
Because rs-backup-suite uses rsync for the client-server communication you don't necessarily need both parts. As long as you have a working rsync server on your NAS you can use the client script to push files to it. On the other hand you can use the rs-backup-suite server part with any other rsync client, as well.
## Special systems
rs-backup-suite is designed to work on most generic Linux systems, but some embedded systems may require some extra love (especially those running on busybox):
### Synology DSM
To run the server component on Synology DSM, you need to install the following packages via [Entware-ng / opkg](https://github.com/Entware-ng/Entware-ng/wiki/Install-on-Synology-NAS):
* `rsnapshot`
* `openssh-sftp-server`
* `util-linux-ng`
* `logger`
In `/etc/ssh/sshd_config` make sure you replace whatever line contains the subsystem configuration for the sftp server with
Subsystem sftp /opt/libexec/sftp-server
and restart the SSH server using the configuration utility from the web interace. If you are using the `synoservicectl` utility from the command line instead, make sure you are actually starting the correct SSH server from `/usr/sbin` and not from `/opt/sbin` (although that works as well, but would have a different configuration file).
If you want to run your backups in a chroot environment please note that `/etc/fstab` will be reset to its defaults when rebooting the disk station. To avoid configuration loss, no mount directives are added to `/etc/fstab` by the install script. Instead the following entries are added to `/etc/rc` (which won't be overwritten upon reboot):
# BEGIN: rs-backup-suite
#mount -o bind /bin /var/services/homes/bin
#mount -o remount,ro,bind /var/services/homes/bin
#mount -o bind /lib /var/services/homes/lib
#mount -o remount,ro,bind /var/services/homes/lib
#mount -o bind /dev /var/services/homes/dev
#mount -o remount,ro,bind /var/services/homes/dev
#mount -o bind /usr/bin /var/services/homes/usr/bin
#mount -o remount,ro,bind /var/services/homes/usr/bin
#mount -o bind /opt/bin /var/services/homes/opt/bin
#mount -o remount,ro,bind /var/services/homes/opt/bin
#mount -o bind /opt/lib /var/services/homes/opt/lib
#mount -o remount,ro,bind /var/services/homes/opt/lib
#mount -o bind /opt/libexec /var/services/homes/opt/libexec
#mount -o remount,ro,bind /var/services/homes/opt/libexec
# END: rs-backup-suite
To enable the mounts, uncomment everything between the `BEGIN` and `END` block. Afterwards either run these commands by hand once or reboot. Of course, don't forget to also set the correct chroot path in `/etc/ssh/sshd_config` and restart the SSH daemon:
Match Group backup
ChrootDirectory /var/services/homes/
### Cygwin
The server component is incompatible with Cygwin for several reasons, but the client component works just fine. At the moment, though, there is no root mode for backing up all home directories at once. Desktop notifications are also unsupported.
## Warning to users of older versions
`rs-backup` used to reside in `/usr/local` instead of `/usr`. With the addition of a proper Makefile in version 0.2.0 this has changed. The consequence is that older setups won't work with the new version without modifications. In order to update your setup you need to update the path to `rs-run-ssh-cmd` (now at `/usr/bin/rs-run-ssh-cmd`) inside your users' `~/.ssh/authorized_keys` files as well as the path to `rs-rotate` (`/usr/bin/rs-rotate`) inside their `rsync.conf` files. Alternatively just create symlinks to the old locations.

View File

@ -0,0 +1,56 @@
##
# Global config file for rs-backup-run
# This is an example file. Copy it over without the .example extension
# and modify it to your needs.
# Most config options can also be passed directly as command line parameters.
##
# Remote host to push the files to
# The remote host must have a working rsync server running which allows
# passwordless (public key) login over SSH
REMOTE_HOST="host"
# The rsync module on the remote server
PUSH_MODULE="push"
# Username to use for logging into the remote server.
# You can use the variables %h which will be replaced with the hostname
# of this machine and %u which will be replaced with your local username.
# If a global system backup is performed, %u will be 'root' for the global
# backup and the corresponding user for the individual home directories
REMOTE_USER="%h-%u"
# Additional SSH options
#SSH_OPTIONS="-C -i .ssh/id_rsa"
# Additional options for rsync
#
# For systems that don't support it (e.g. Synology DSM), you may
# need to remove the --acls option
#
# The block size setting should avoid hangs when backing up larger
# files. It's set to the default maximum value allowed by rsync.
# If you need larger values, recompile rsync with MAX_BLOCK_SIZE
# set to an appropriate value.
RSYNC_OPTIONS="--acls --hard-links --xattrs --block-size=131072"
#RSYNC_OPTIONS="--hard-links --xattrs"
# Name of the file inside the users' home directories
# containing the patterns for matching files to include or exclude.
# The format is the same as the global 'include-files' config file
# and described in the FILTER RULES section of the rsync(1) man page.
# If no such file is found inside a home directory, it won't be backup up
INCLUSION_PATTERN_FILE=".rs-backup/include"
# Log verbosity
# (0 = quiet, 1 = errors only, 2 = errors and warnings, 3 = info, 4 = debug)
LOG_LEVEL=3
# Send error messages to STDERR
PRINT_ERRORS=true
# Send warnings to STDERR (implies PRINT_ERRORS=true)
PRINT_WARNINGS=true
# Show desktop notifications. Requires libnotify / notify-send.
DESKTOP_NOTIFICATIONS=true

View File

@ -0,0 +1,6 @@
[Unit]
Description=Start backup via rs-backup-suite
[Service]
Type=simple
ExecStart=/usr/bin/rs-backup-run

View File

@ -0,0 +1,10 @@
[Unit]
Description=Run rs-backup-run every day
[Timer]
OnCalendar=daily
Persistent=true
Unit=rs-backup-run.service
[Install]
WantedBy=multi-user.target

View File

@ -1,6 +1,6 @@
#!/bin/bash
#!/usr/bin/env bash
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Script to push backups to a remote rsync backup server.
@ -40,18 +40,32 @@
# Additional internal config
###############################################################################
_VERSION=0.2.1
_VERSION=$(rs-version version)
_GLOBAL_INCLUSION_PATTERN_FILE="/etc/rs-backup/include-files"
_FORCED_INCLUSION_PATTERN_FILE=""
_SKIP_HOME_DIRS=false
_FALLBACK_LOG_FILE="/var/log/rs-backup.log"
_FALLBACK_USER_LOG_FILE="rs-backup.user.log"
_FORCED_LOG_FILE=""
_QUIET_MODE=false
_VERBOSE_MODE=false
_SHOW_PROGRESS=false
_DRY_RUN=false
_FORCE_RUN=false
_PRE_HOOK=""
_PRE_HOOK_RUN=false
_POST_HOOK=""
_POST_HOOK_RUN=false
_FORCED_POST_HOOK=""
_FORCED_POST_HOOK_RUN=false
_ERROR_COUNT=0
if [ $(id -u) -eq 0 ]; then
_RUNFILE="/var/run/rs-backup/rs-backup-run.pid"
else
_RUNFILE="${HOME}/.rs-backup/rs-backup-run.pid"
fi
###############################################################################
# Function declarations
@ -61,10 +75,9 @@ _ERROR_COUNT=0
#
# Usage: print_help
print_help() {
rs-version headline rs-backup-run
rs-version copyright
cat << HELP
rs-backup-run version ${_VERSION}
Copyright (C) 2013-2014 by Janek Bevendorff
Web site: http://www.refining-linux.org/
Push backup to rsync backup server over SSH.
@ -78,31 +91,43 @@ user will be backed up.
Usage: $(basename $0) [OPTION]...
Options:
-r, --remote-host=HOST The remote host to connect to
--remote-user=NAME The username to use for logging into the remote server
(%h will be replaced with the host name of this
machine and %u with your username)
--push-module=NAME The remote rsync server module
--ssh-options=OPTS Additional SSH options (will be merged with the default
options set in the rs-backup client-config file)
-o, --rsync-options=OPTS Additional options for rsync
-n, --dry-run Perform a test run (same as the --dry-run option for
rsync). Enable --verbose mode for useful control output
-s, --no-home-dirs Don't back up home dirs, only perform global system
backup (root only)
-i, --include-from=FILE Specify an alternate inclusion pattern file
This will override the default setting. If the script
is run as root, only the system backup will be
performed, no additional home directories will be
backed up
-l, --log-level=NUM Set log level to NUM (between 0 and 4)
--log-file=FILE Set a different log file location
-f, --force-run Force rs-backup to run, even if a lock file exists
-q, --quiet Don't print any error messages or warnings to the
screen (only write to log file)
-v, --verbose Print all messages of the current debug level
-p, --progress Print file transfer information to the terminal
-h, --help Print this help and exit
-r, --remote-host=HOST The remote host to connect to
--remote-user=NAME The username to use for logging into the remote server
(%h will be replaced with the host name of this
machine and %u with your username)
--push-module=NAME The remote rsync server module
--ssh-options=OPTS Additional SSH options (will be merged with the default
options set in the rs-backup client-config file)
-o, --rsync-options=OPTS Additional options for rsync
-n, --dry-run Perform a test run (same as the --dry-run option for
rsync). Enable --verbose mode for useful control output
-s, --no-home-dirs Don't back up home dirs, only perform global system
backup (root only)
-i, --include-from=FILE Specify an alternate inclusion pattern file
This will override the default setting. If the script
is run as root, only the system backup will be
performed, no additional home directories will be
backed up
-l, --log-level=NUM Set log level to NUM (between 0 and 4)
--log-file=FILE Log to this file instead of syslog
-f, --force-run Force rs-backup to run, even if a lock file exists
-q, --quiet Don't print any error messages or warnings to the
screen (only write to log file)
-v, --verbose Print all messages of the current debug level
-p, --progress Print file transfer information to the terminal
--pre-hook=CMD Command to be run before the backup. Will only be run
once for multi-user / global system backup. The hook
command will be executed as the user who started the
backup command
--post-hook=CMD Similar to --pre-hook, but run after the backup
has successfully finished. If an error occurred
during the backup, the post hook will not be run.
--forced-post-hook=CMD Same as --post-hook, but will always be run, regardless
of wether an error occurred or not. Will also be run
if the backup was interrupted by SIGINT or SIGTERM.
If both --post-hook and --forced-post-hook are specified,
--post-hook is run first
-h, --help Print this help and exit
HELP
}
@ -111,43 +136,62 @@ HELP
# Usage: write_log <log level> <log message>
#
write_log() {
local log_msg
local log_date
local log_msg="${2}"
local log_date="[$(date)]"
local log_dest
local use_syslog=false
local logger="logger -t $(basename $0)"
command -v logger > /dev/null 2>&1
if [ $? -eq 0 ] && [ "${_FORCED_LOG_FILE}" == "" ]; then
use_syslog=true
fi
if [ $1 -gt 0 ] && [ $1 -le $LOG_LEVEL ]; then
if $use_syslog; then
case $1 in
1) $logger -p err "${log_msg}" ;;
2) $logger -p warning "${log_msg}" ;;
3) $logger -p info "${log_msg}" ;;
*) $logger -p debug "${log_msg}" ;;
esac
fi
# prepend priority prefixes to message for further logging output
case $1 in
1) log_msg="ERROR: ${2}" ;;
2) log_msg="WARNING: ${2}" ;;
3) log_msg="INFO: ${2}" ;;
*) log_msg="DEBUG: ${2}" ;;
1) log_msg="ERROR: ${log_msg}" ;;
2) log_msg="WARNING: ${log_msg}" ;;
3) log_msg="INFO: ${log_msg}" ;;
*) log_msg="DEBUG: ${log_msg}" ;;
esac
log_date="[$(date)]"
full_log_msg="${log_date} ${log_msg}"
if [ "${_FORCED_LOG_FILE}" != "" ]; then
log_dest=${_FORCED_LOG_FILE}
elif [ $(id -u) -eq 0 ]; then
log_dest=${LOG_FILE}
elif [ "${HOME}" != "" ] && [ "${USER_LOG_FILE}" != "" ]; then
log_dest=${HOME}/${USER_LOG_FILE}
else
echo "WARNING: Couldn't determine valid log file location, using '/var/tmp'..." >&2
log_dest="/var/tmp/${LOG_FILE}"
fi
if ! test_file_perms "w" "${log_dest}"; then
echo "ERROR: Couldn't open log file for writing, redirecting to STDOUT!" >&2
echo "${log_date} ${log_msg}" >&1
else
echo "${log_date} ${log_msg}" >> "${log_dest}"
# if no syslog facility exists, go the cumbersome way...
if ! $use_syslog || [ "${_FORCED_LOG_FILE}" != "" ]; then
if [ "${_FORCED_LOG_FILE}" != "" ]; then
log_dest=${_FORCED_LOG_FILE}
elif [ $(id -u) -eq 0 ]; then
log_dest=${_FALLBACK_LOG_FILE}
elif [ "${HOME}" != "" ] && [ "${_FALLBACK_USER_LOG_FILE}" != "" ]; then
log_dest=${HOME}/${_FALLBACK_USER_LOG_FILE}
else
echo -e "\e[1mWARNING: Couldn't determine valid log file location, using '/var/tmp'...\e[0m" >&2
log_dest="/var/tmp/$(basename ${LOG_FILE})"
fi
touch "${log_dest}" 2> /dev/null
if ! test_file_perms "w" "${log_dest}"; then
echo -e "\e[1m\e[91mERROR: Couldn't open log file for writing, redirecting to STDOUT!\e[0m" >&2
echo "${log_date} ${log_msg}" >&1
else
echo "${log_date} ${log_msg}" >> "${log_dest}"
fi
fi
# after logging stuff, print it to the screen if we're not in quiet mode
if ! $_QUIET_MODE && [ $1 -eq 1 ]; then
$_VERBOSE_MODE || $PRINT_ERRORS && echo "${log_msg}" >&2
$_VERBOSE_MODE || $PRINT_ERRORS && echo -e "\e[1m\e[91m${log_msg}\e[0m" >&2
elif ! $_QUIET_MODE && [ $1 -le 2 ]; then
$_VERBOSE_MODE || $PRINT_WARNINGS && echo "${log_msg}" >&2
$_VERBOSE_MODE || $PRINT_WARNINGS && echo -e "\e[1m${log_msg}\e[0m" >&2
elif ! $_QUIET_MODE && [ $1 -gt 2 ]; then
$_VERBOSE_MODE && echo "${log_msg}" >&1
fi
@ -161,6 +205,140 @@ write_log() {
}
# Create runfile containing current PID
#
# Usage: create_runfile
#
create_runfile() {
write_log 4 "Creating runfile (PID=${$}) at '${_RUNFILE}'..."
if [ ! -d "$(dirname $_RUNFILE)" ]; then
mkdir -p "$(dirname $_RUNFILE)"
fi
echo $$ > "$_RUNFILE"
}
# Remove created runfile
#
# Usage: remove_runfile
#
remove_runfile() {
write_log 4 "Removing runfile at '${_RUNFILE}'..."
rm -f "$_RUNFILE"
# also remove parent directory, but only if empty
# redirect error output since --ignore-fail-on-non-empty is not POSIX
rmdir "$(dirname $_RUNFILE)" > /dev/null 2>&1
}
# Run the user-specified pre hook
#
# Usage: run_pre_hook
#
run_pre_hook() {
if ! $_PRE_HOOK_RUN && [ "" != "$_PRE_HOOK" ]; then
$SHELL -c "$_PRE_HOOK"
_PRE_HOOK_RUN=true
fi
}
# Run the user-specified post hook
#
# Usage: run_post_hook
#
run_post_hook() {
if ! $_POST_HOOK_RUN && [ "" != "$_POST_HOOK" ]; then
$SHELL -c "$_POST_HOOK"
_POST_HOOK_RUN=true
fi
}
# Run the user-specified forced post hook
#
# Usage: run_forced_post_hook
#
run_forced_post_hook() {
if ! $_FORCED_POST_HOOK_RUN && [ "" != "$_FORCED_POST_HOOK" ]; then
$SHELL -c "$_FORCED_POST_HOOK"
_FORCED_POST_HOOK_RUN=true
fi
}
# Exit cleanly with given exit code.
# Removes any run files and runs the forced post hook.
#
# Usage clean_exit <exit_code>
#
clean_exit() {
remove_runfile
run_forced_post_hook
exit $@
}
# Handle script termination by external signals.
#
# Usage: handle_signals
#
handle_exit_signal() {
write_log 1 "Program terminated upon user request."
clean_exit 1
}
# Show a desktop notification using notify-send
#
# Usage: desktop_notify <type: INFO|WARNING|ERROR> <title> <message>
#
desktop_notify() {
local icon
local urgency
local user
if [[ "$(uname -o)" == "Cygwin" ]]; then
# not implemented
return
fi
command -v notify-send > /dev/null 2>&1
if [ $? -ne 0 ]; then
# notify-send not available
return
fi
if $_QUIET_MODE || $_DRY_RUN; then
return
fi
user=$(who | awk '/:0/ { print $1; exit; }')
if [ $(id -u) -ne 0 ] && [[ "$user" != "" ]] && [ $(id -u "$user") -ne $(id -u) ]; then
# we're neither root nor the current user on display :0.0,
# so don't show any notification
return
fi
case $1 in
"ERROR")
icon="dialog-error.png"
urgency="critical"
;;
"WARNING")
icon="dialog-warning.png"
urgency="normal"
;;
"SUCCESS")
icon="dialog-ok.png"
urgency="low"
;;
*)
icon="dialog-information.png"
urgency="low"
;;
esac
if [[ "$user" != "" ]]; then
dbus_pid=$(pgrep -u "$user" dbus-daemon | head -n1)
environment=$(xargs --null < /proc/$dbus_pid/environ)
sudo -u "$user" $environment notify-send -i "$icon" -u "$urgency" "rs-backup: $2" "$3"
fi
}
# Test if a file is readable and/or writeable
#
# Usage: test_file_perms <mode: r|w|rw> <filename>
@ -249,7 +427,7 @@ perform_backup() {
local msg
local backup_cmd
local ssh_cmd
local rsync_opts="${RSNC_OPTIONS}"
local rsync_opts="${RSYNC_OPTIONS}"
local exit_code
local tee_device="/dev/tty"
@ -288,11 +466,12 @@ perform_backup() {
backup_cmd="rsync \
--rsh=\"${ssh_cmd}\" \
--archive \
--acls \
--delete \
--delete-excluded \
--exclude=\"${_RUNFILE}\" \
--exclude=\"${LOG_FILE}\" \
--exclude=\"${USER_LOG_FILE}\" \
--include-from=\"${inclusion_pattern_file}\" \
--exclude=\"*\" \
${rsync_opts} \
/ \
\"${destination}\""
@ -303,18 +482,19 @@ perform_backup() {
if [ $(id -u) -eq 0 ] && [ "${username}" != "$(id -un)" ]; then
write_log 4 "Running backup with privileges of user '${username}' (UID: $(id -u ${username}))..."
msg=$(su - "${username}" -c "${backup_cmd}" 2>&1 | tee "${tee_device}")
msg=$(su - "${username}" -c "${backup_cmd}" 3>&1 1>&2 2>&3- | tee "${tee_device}")
elif [ $(id -u) -ne 0 ] && [ "${username}" != "$(id -un)" ]; then
write_log 1 "Cannot run run backup as user '${username}' (UID: $(id -u ${username}), missing root privileges!"
return 1
else
msg=$(sh -c "${backup_cmd}" 2>&1 | tee "${tee_device}")
msg=$(sh -c "${backup_cmd}" 3>&1 1>&2 2>&3- | tee "${tee_device}")
fi
exit_code=$?
if [ ${exit_code} -ne 0 ]; then
write_log 1 "Backup failed! Error message: ${msg}"
desktop_notify "ERROR" "Backup failed!" "Backup for user '${username}' failed!<br>Please refer to your log files for further information."
return ${exit_code}
else
write_log 3 "Backup finished."
@ -340,6 +520,7 @@ back_up_system() {
write_log 3 "Starting global system backup..."
fi
perform_backup "${_GLOBAL_INCLUSION_PATTERN_FILE}" "$(get_remote_username root)@${REMOTE_HOST}::${PUSH_MODULE}"
return $?
}
# Back up single home directory
@ -371,6 +552,7 @@ back_up_single_home_dir() {
write_log 3 "Starting backup of '${home_dir}'..."
fi
perform_backup "${home_dir}/${INCLUSION_PATTERN_FILE}" "$(get_remote_username ${username})@${REMOTE_HOST}::${PUSH_MODULE}" "${username}"
return $?
}
# Back up all home dirs
@ -378,14 +560,18 @@ back_up_single_home_dir() {
# Usage: back_up_home_dirs
#
back_up_home_dirs() {
local exit_code=0
write_log 3 "Starting backup of all home directories..."
get_processed_passwd_file | while read line; do
back_up_single_home_dir "$(echo -n ${line} | cut -d ':' -f 6)" "$(echo -n ${line} | cut -d ':' -f 1)"
exit_code=$(($exit_code | $?))
done
return $exit_code
}
# Prase command line args
# Parse command line args
#
# Usage: parse_cmd_args <cmd arg line>
#
@ -400,16 +586,15 @@ parse_cmd_args() {
getopt -T > /dev/null
if [ $? -ne 4 ]; then
write_log 1 "Need GNU getopt for command line parameter parsing!"
exit 1;
exit 1
fi
args=$(getopt \
-s sh \
-o "r:o:nsi:l:fqvph" \
-l "remote-host:,remote-user:,push-module:,ssh-options:,rsync-options:,dry-run,no-home-dirs,include-from:,log-level:,log-file:,force-run,quiet,verbose,progress,help" \
-n "${name}" \
-- "${@}")
short_opts="r:o:nsi:l:fqvph"
long_opts="remote-host:,remote-user:,push-module:,ssh-options:,rsync-options:,"
long_opts+="dry-run,no-home-dirs,include-from:,log-level:,log-file:,force-run"
long_opts+="quiet,verbose,progress,pre-hook:,post-hook:,forced-post-hook:,help"
args=$(getopt -s sh -o "$short_opts" -l "$long_opts" -n "${name}" -- "${@}")
if [ $? -ne 0 ]; then
exit 1
fi
@ -441,17 +626,17 @@ parse_cmd_args() {
shift ;;
"-i"|"--include-from")
# File must exist and be readable
! test_file_perms "r" "${2}" && echo "$name: '${2}' does not exist or is not readable!" >&2 && exit 1
! test_file_perms "r" "${2}" && echo "$name: '${2}' does not exist or is not readable!" >&2 && exit 1
_FORCED_INCLUSION_PATTERN_FILE=$2
_SKIP_HOME_DIRS=true
shift 2 ;;
"-l"|"--log-level")
LOG_LEVEL=$2;
LOG_LEVEL="$2"
shift 2 ;;
"--log-file")
# Test if file is writeable
! test_file_perms "w" "${2}" && echo "$name: '${2}' is not writeable!" >&2 && exit 1
_FORCED_LOG_FILE=$2
_FORCED_LOG_FILE="$2"
shift 2 ;;
"-f"|"--force-run")
_FORCE_RUN=true
@ -465,6 +650,15 @@ parse_cmd_args() {
"-p"|"--progress")
! $_QUIET_MODE && _SHOW_PROGRESS=true
shift ;;
"--pre-hook")
_PRE_HOOK="$2"
shift 2 ;;
"--post-hook")
_POST_HOOK="$2"
shift 2 ;;
"--forced-post-hook")
_FORCED_POST_HOOK="$2"
shift 2 ;;
"-h"|"--help")
print_help
exit ;;
@ -478,44 +672,68 @@ parse_cmd_args() {
###############################################################################
# Initialize the actual backup
###############################################################################
# Register exit trap to catch signals and cleanly shut down the script
trap handle_exit_signal SIGHUP SIGINT SIGTERM
parse_cmd_args "$@"
# Check if a backup is already running
if [ -f /tmp/rs-backup.lock ] && ! $_FORCE_RUN; then
write_log 1 "Backup lock file exists. Either a backup is already running or it didn't shut down properly last time."
write_log 1 "If you're sure no backup is running right now, remove the lock file '/tmp/rs-backup.lock' or use the '--force-run' parameter."
if [ -f "$_RUNFILE" ] && ! $_FORCE_RUN; then
write_log 1 "rs-backup is already running as PID $(<$_RUNFILE)."
write_log 1 "Please finish any running backups before starting a new one."
write_log 1 "If you're sure you want to run another backup, either remove the runfile at " \
"'$_RUNFILE' or use the '--force-run' parameter."
exit 1
fi
write_log 4 "No other backup running, ready to start."
if [ ! -f "$_RUNFILE" ]; then
write_log 4 "No other backup running, ready to start."
elif $_FORCE_RUN; then
write_log 4 "Backup already running as PID $(<$_RUNFILE), forcing parallel backup..."
fi
# Create lock file
write_log 4 "Creating lock file..."
touch /tmp/rs-backup.lock
run_pre_hook
create_runfile
# Backup exit code (0 if all backups have finished successfully)
_exit_code=$?
# Check if script has been invoked as root
if [ $(id -u) -eq 0 ]; then
write_log 4 "Running as root, performing global system backup..."
desktop_notify "INFO" "Full system backup" "rs-backup is starting a full system backup..."
back_up_system
_exit_code=$(($_exit_code | $?))
if ! $_SKIP_HOME_DIRS; then
back_up_home_dirs
_exit_code=$(($_exit_code | $?))
else
write_log 3 "Skipping home directory backup as requested."
fi
else
write_log 3 "Running without root privileges, only backing up user home directory..."
if [ "${HOME}" != "" ]; then
desktop_notify "INFO" "Home directory backup" "rs-backup is starting a home directory backup (unprivileged)..."
back_up_single_home_dir "$(realpath ${HOME})" "$(id -nu)"
_exit_code=$(($_exit_code | $?))
else
write_log 2 "Current user has no home directory, skipping."
fi
fi
write_log 4 "Removing lock file..."
rm /tmp/rs-backup.lock
if [ $_exit_code -eq 0 ]; then
desktop_notify "SUCCESS" "Backup finished" "Your backup has successfully finished"
fi
remove_runfile
write_log 4 "Done."
if [ $_ERROR_COUNT -gt 0 ]; then
exit 1
clean_exit 1
fi
run_post_hook
run_forced_post_hook

80
install-client.sh Executable file
View File

@ -0,0 +1,80 @@
#!/bin/sh
##
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Install script for installing server and client script files
#
# The MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
##
if [[ "$1" != "all" ]] && [[ "$1" != "client" ]] && [[ "$1" != "server" ]]; then
./server/usr/bin/rs-version headline "rs-backup-suite installer"
./server/usr/bin/rs-version copyright
echo
echo "Usage: $(basename $0) [all|server|client]"
exit
fi
if [ $(id -u) -ne 0 ] && [[ "$(uname -o)" != "Cygwin" ]]; then
echo "ERROR: This script must be run as root."
exit 1
fi
###############################################################################
# Global variables
###############################################################################
DISTRIBUTION="$(./server/usr/bin/rs-detect-distribution)"
COMPONENT="$1"
MODE="install"
if [[ "$(basename $0)" == "uninstall.sh" ]]; then
MODE="uninstall"
fi
###############################################################################
# Command aliases
###############################################################################
CP="cp -vr --preserve=mode,timestamps,links"
RM="rm -Rvf"
MKDIR="mkdir -pv"
# Client component
echo "Installing client component..."
$CP ./client/usr/bin/* /usr/bin/
$CP ./server/usr/bin/rs-version /usr/bin/
if [ -d /etc/systemd/system ]; then
echo 'Detected systemd. Run `systemctl enable rs-backup-run.timer` to enable daily backups.'
$CP ./client/etc/systemd/system/* /etc/systemd/system
fi
# Do not overwrite existing config
if [ ! -e /etc/rs-backup/client-config ]; then
$CP ./client/etc/rs-backup /etc/
elif ! $(cmp --silent ./client/etc/rs-backup/client-config /etc/rs-backup/client-config); then
$CP ./client/etc/rs-backup/client-config /etc/rs-backup/client-config.new
fi
echo "Done."

View File

@ -1,6 +1,6 @@
#!/bin/sh
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Install script for installing server and client script files
@ -27,12 +27,14 @@
##
if [[ "$1" != "all" ]] && [[ "$1" != "client" ]] && [[ "$1" != "server" ]]; then
./server/usr/bin/rs-version
./server/usr/bin/rs-version headline "rs-backup-suite installer"
./server/usr/bin/rs-version copyright
echo
echo "Usage: $(basename $0) [all|server|client]"
exit
fi
if [ $UID -ne 0 ]; then
if [ $(id -u) -ne 0 ] && [[ "$(uname -o)" != "Cygwin" ]]; then
echo "ERROR: This script must be run as root."
exit 1
fi
@ -52,7 +54,7 @@ fi
###############################################################################
# Command aliases
###############################################################################
CP="cp -vr --preserve=mode,timestamps,links,xattr"
CP="cp -vr --preserve=mode,timestamps,links"
RM="rm -Rvf"
MKDIR="mkdir -pv"
@ -73,10 +75,6 @@ if [[ $MODE == "install" ]]; then
# Do not overwrite existing config
if [ ! -e /etc/rs-backup/server-config ]; then
$CP ./server/etc/rs-backup /etc/
# Correct command paths in rsnapshot config for Synology DSM
if [[ "$DISTRIBUTION" == "Synology" ]]; then
sed -i "s#/usr/bin/\(cp\|rm\|rsync\)\$#/opt/bin/\1#" /etc/rs-backup/rsnapshot.global.conf
fi
fi
echo
@ -89,6 +87,7 @@ if [[ $MODE == "install" ]]; then
if ! grep -q "/usr/sbin/rs-rotate-cron" /etc/crontab; then
if [[ "$DISTRIBUTION" == "Synology" ]]; then
cat ./server/etc/crontab_synology >> /etc/crontab
synoservicectl --reload crond
else
cat ./server/etc/crontab >> /etc/crontab
fi
@ -97,8 +96,13 @@ if [[ $MODE == "install" ]]; then
echo "ERROR: Could not install cron scripts, please add rotation jobs manually." >&2
fi
echo
echo "Installing backup directory..."
BKP_DIR="$(grep -o '^BACKUP_ROOT=".*"$' /etc/rs-backup/server-config | sed 's#BACKUP_ROOT=\"\(.*\)\"$#\1#')"
if [ -e /etc/rs-backup/server-config ]; then
BKP_DIR="$(grep -o '^BACKUP_ROOT=".*"$' /etc/rs-backup/server-config | sed 's#BACKUP_ROOT=\"\(.*\)\"$#\1#')"
else
BKP_DIR="/bkp"
fi
if [[ "$DISTRIBUTION" == "Synology" ]] && [[ "$BKP_DIR" == "/bkp" ]]; then
if readlink -q /var/services/homes > /dev/null; then
BKP_DIR="/var/services/homes"
@ -107,6 +111,7 @@ if [[ $MODE == "install" ]]; then
BKP_DIR="/volume1/homes"
fi
fi
echo "Backup directory path will be '$BKP_DIR'."
echo -n "Do you want to use this directory? [Y/n] "
read answer
@ -126,20 +131,63 @@ if [[ $MODE == "install" ]]; then
$MKDIR "$BKP_DIR"/etc
$MKDIR "$BKP_DIR"/lib
$MKDIR "$BKP_DIR"/usr/bin
$MKDIR "$BKP_DIR"/usr/lib
$MKDIR "$BKP_DIR"/usr/share
if [[ "$DISTRIBUTION" == "Synology" ]]; then
$MKDIR "$BKP_DIR"/opt/bin
fi
[[ "$DISTRIBUTION" != "Synology" ]] && $MKDIR "$BKP_DIR"/usr/lib
if [[ "$DISTRIBUTION" == "Ubuntu" ]]; then
$MKDIR "$BKP_DIR"/usr/share/perl
elif [[ "$DISTRIBUTION" == "Synology" ]]; then
$MKDIR "$BKP_DIR"/opt/bin
$MKDIR "$BKP_DIR"/opt/lib
$MKDIR "$BKP_DIR"/opt/libexec
else
$MKDIR "$BKP_DIR"/usr/share/perl5
fi
$CP ./server/bkp/etc/* "$BKP_DIR"/etc/
# Apply distro-specific configurations
if [[ "$DISTRIBUTION" == "Synology" ]]; then
# Synology DSM restores default /etc/fstab upon reboot,
# so we better put mount commands in /etc/rc
if ! grep -q "^# BEGIN: rs-backup-suite" /etc/rc; then
tmp_name="/tmp/rs-backup_etc-rc.$RANDOM"
fstab_contents="$(cat ./server/etc/fstab_synology | sed "s#::BACKUP_ROOT::#$BKP_DIR#")"
tac /etc/rc | sed -e '1!b' -e '/^exit 0$/d' | tac > $tmp_name
echo "$fstab_contents" >> $tmp_name
echo "exit 0" >> $tmp_name
cat $tmp_name > /etc/rc
rm $tmp_name
fi
# Add our own syslog template
if ! grep -q "^# rs-backup-suite$" /usr/syno/synosdk/texts/enu/events; then
cat ./server/etc/events_synology >> /usr/syno/synosdk/texts/enu/events
fi
else
if ! grep -q "^# BEGIN: rs-backup-suite" /etc/fstab; then
if [[ "$DISTRIBUTION" == "Ubuntu" ]]; then
fstab_local_name="fstab_ubuntu"
elif [[ "$DISTRIBUTION" == "Synology" ]]; then
fstab_local_name="fstab_synology"
else
fstab_local_name="fstab"
fi
fstab_contents="$(cat ./server/etc/$fstab_local_name | sed "s#::BACKUP_ROOT::#$BKP_DIR#")"
echo "$fstab_contents" >> /etc/fstab
fi
fi
# Do not overwrite existing config
if [ ! -e "$BKP_DIR"/etc/rsnapshot.global.conf ]; then
$CP ./server/bkp/etc/* "$BKP_DIR"/etc/
# Correct command paths in rsnapshot config for Synology DSM
if [[ "$DISTRIBUTION" == "Synology" ]]; then
sed -i "s#/usr/bin/\(rsync\|logger\)\$#/opt/bin/\1#" "$BKP_DIR"/etc/rsnapshot.global.conf
fi
else
# Update command paths if upgrading from earlier version
echo "Updating cp and rm command paths..."
sed -i "s#/opt/bin/cp\$#/usr/bin/cp#" "$BKP_DIR"/etc/rsnapshot.global.conf
sed -i "s#/opt/bin/rm\$#/usr/bin/rs-rm#" "$BKP_DIR"/etc/rsnapshot.global.conf
fi
# Create symlink for chroot
dir="$(dirname ${BKP_DIR}${BKP_DIR})"
@ -167,10 +215,18 @@ if [[ $MODE == "install" ]]; then
echo "Installing client component..."
$CP ./client/usr/bin/* /usr/bin/
$CP ./server/usr/bin/rs-version /usr/bin/
if [ -d /etc/systemd/system ]; then
echo 'Detected systemd. Run `systemctl enable rs-backup-run.timer` to enable daily backups.'
$CP ./client/etc/systemd/system/* /etc/systemd/system
fi
# Do not overwrite existing config
if [ ! -e /etc/rs-backup/client-config ]; then
$CP ./client/etc/rs-backup /etc/
elif ! $(cmp --silent ./client/etc/rs-backup/client-config /etc/rs-backup/client-config); then
$CP ./client/etc/rs-backup/client-config /etc/rs-backup/client-config.new
fi
echo "Done."
@ -204,6 +260,24 @@ elif [[ "$MODE" == "uninstall" ]]; then
$RM /usr/sbin/"$(basename $i)"
done
$RM /etc/rs-skel
[ -e /etc/cron.daily/rs-backup-rotate ] && $RM /etc/cron.daily/rs-backup-rotate
[ -e /etc/cron.weekly/rs-backup-rotate ] && $RM /etc/cron.weekly/rs-backup-rotate
[ -e /etc/cron.monthly/rs-backup-rotate ] && $RM /etc/cron.monthly/rs-backup-rotate
if [ -e /etc/crontab ]; then
echo "Removing crontab entries..."
sed -i '/^@[a-z]\+ \+\/usr\/sbin\/rs-rotate-cron [a-z]\+$/d' /etc/crontab
fi
# Remove fstab entries
fstab_name="/etc/fstab"
if [[ "$DISTRIBUTION" == "Synology" ]]; then
fstab_name="/etc/rc"
fi
sed -i '/^# BEGIN: rs-backup-suite$/,/^# END: rs-backup-suite$/d' $fstab_name
echo "Done."
# Client component

View File

@ -1,7 +1,7 @@
config_version 1.2
cmd_cp /usr/bin/cp
cmd_rm /usr/bin/rm
cmd_rm /usr/bin/rs-rm
cmd_rsync /usr/bin/rsync
cmd_logger /usr/bin/logger

View File

@ -1,3 +1,3 @@
@daily /usr/sbin/rs-rotate-cron daily
@weekly /usr/sbin/rs-rotate-cron weekly
@monthly /usr/sbin/rs-rotate-cron monthly
@daily /usr/sbin/rs-rotate-cron daily
@weekly /usr/sbin/rs-rotate-cron weekly
@monthly /usr/sbin/rs-rotate-cron monthly

View File

@ -1,3 +1,3 @@
@daily root /usr/sbin/rs-rotate-cron daily
@weekly root /usr/sbin/rs-rotate-cron weekly
@monthly root /usr/sbin/rs-rotate-cron monthly
0 0 * * * root /usr/sbin/rs-rotate-cron daily
0 0 * * 0 root /usr/sbin/rs-rotate-cron weekly
0 0 1 * * root /usr/sbin/rs-rotate-cron monthly

View File

@ -0,0 +1,5 @@
[99000000]
# rs-backup-suite
99000001 = "@1"

14
server/etc/fstab Normal file
View File

@ -0,0 +1,14 @@
# BEGIN: rs-backup-suite
#/bin ::BACKUP_ROOT::/bin none bind 0 0
#/bin ::BACKUP_ROOT::/bin none remount,ro,bind 0 0
#/lib ::BACKUP_ROOT::/lib none bind 0 0
#/lib ::BACKUP_ROOT::/lib none remount,ro,bind 0 0
#/dev ::BACKUP_ROOT::/dev none bind 0 0
#/dev ::BACKUP_ROOT::/dev none remount,ro,bind 0 0
#/usr/bin ::BACKUP_ROOT::/usr/bin none bind 0 0
#/usr/bin ::BACKUP_ROOT::/usr/bin none remount,ro,bind 0 0
#/usr/lib ::BACKUP_ROOT::/usr/lib none bind 0 0
#/usr/lib ::BACKUP_ROOT::/usr/lib none remount,ro,bind 0 0
#/usr/share/perl5 ::BACKUP_ROOT::/usr/share/perl5 none bind 0 0
#/usr/share/perl5 ::BACKUP_ROOT::/usr/share/perl5 none remount,ro,bind 0 0
# END: rs-backup-suite

16
server/etc/fstab_synology Normal file
View File

@ -0,0 +1,16 @@
# BEGIN: rs-backup-suite
#mount -o bind /bin ::BACKUP_ROOT::/bin
#mount -o remount,ro,bind ::BACKUP_ROOT::/bin
#mount -o bind /lib ::BACKUP_ROOT::/lib
#mount -o remount,ro,bind ::BACKUP_ROOT::/lib
#mount -o bind /dev ::BACKUP_ROOT::/dev
#mount -o remount,ro,bind ::BACKUP_ROOT::/dev
#mount -o bind /usr/bin ::BACKUP_ROOT::/usr/bin
#mount -o remount,ro,bind ::BACKUP_ROOT::/usr/bin
#mount -o bind /opt/bin ::BACKUP_ROOT::/opt/bin
#mount -o remount,ro,bind ::BACKUP_ROOT::/opt/bin
#mount -o bind /opt/lib ::BACKUP_ROOT::/opt/lib
#mount -o remount,ro,bind ::BACKUP_ROOT::/opt/lib
#mount -o bind /opt/libexec ::BACKUP_ROOT::/opt/libexec
#mount -o remount,ro,bind ::BACKUP_ROOT::/opt/libexec
# END: rs-backup-suite

14
server/etc/fstab_ubuntu Normal file
View File

@ -0,0 +1,14 @@
# BEGIN: rs-backup-suite
#/bin ::BACKUP_ROOT::/bin none bind 0 0
#/bin ::BACKUP_ROOT::/bin none remount,ro,bind 0 0
#/lib ::BACKUP_ROOT::/lib none bind 0 0
#/lib ::BACKUP_ROOT::/lib none remount,ro,bind 0 0
#/dev ::BACKUP_ROOT::/dev none bind 0 0
#/dev ::BACKUP_ROOT::/dev none remount,ro,bind 0 0
#/usr/bin ::BACKUP_ROOT::/usr/bin none bind 0 0
#/usr/bin ::BACKUP_ROOT::/usr/bin none remount,ro,bind 0 0
#/usr/lib ::BACKUP_ROOT::/usr/lib none bind 0 0
#/usr/lib ::BACKUP_ROOT::/usr/lib none remount,ro,bind 0 0
#/usr/share/perl ::BACKUP_ROOT::/usr/share/perl none bind 0 0
#/usr/share/perl ::BACKUP_ROOT::/usr/share/perl none remount,ro,bind 0 0
# END: rs-backup-suite

View File

@ -1,4 +1,4 @@
include_conf ${BACKUP_ROOT}/etc/rs-backup/rsnapshot.global.conf
include_conf ${BACKUP_ROOT}/etc/rsnapshot.global.conf
snapshot_root ${HOME_DIR}/${FILES_DIR}
logfile ${HOME_DIR}/rsnapshot.log

View File

@ -0,0 +1,26 @@
# Base directory for all backups
BACKUP_ROOT="/bkp"
# Backup user group
USER_GROUP="backup"
# Directory containing the actual backup files (relative to BACKUP_ROOT/<user>)
FILES_DIR="files"
# Set default quota for new users
SET_QUOTA=false
# Mount point for backup device that has quota enabled
# If nothing is set, quota will be set for all available quota-enabled devices
QUOTA_MOUNT_POINT=""
# Default quota limits. Hard and soft size limit are in bytes (min. 1024)
# Numbers may also end with k, M, G or T for magnitudes of 1024
#
# These numbers are only the defaults. If you want to change them later for
# individual users, you can do that with rs-setquota or directly using
# the native Linux quota tools (i.e. setquota / edquota)
QUOTA_SOFT_LIMIT="350G"
QUOTA_HARD_LIMIT="355G"
QUOTA_INODE_SOFT_LIMIT="3900k"
QUOTA_INODE_HARD_LIMIT="4000k"

View File

@ -1,6 +1,6 @@
#!/bin/sh
#!/usr/bin/env bash
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Print out Linux distribution identification.
@ -31,7 +31,7 @@ command -v lsb_release > /dev/null 2>&1
if [ $? -eq 0 ]; then
lsb_release -is
elif [ -e /etc/synoinfo.conf ]; then
elif [ -e /etc/synoinfo.conf ] || [ -e /lib/$(ls /lib | grep libsynosdk | head -n1) ]; then
echo "Synology"
else
echo "unknown"

59
server/usr/bin/rs-logger Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env bash
##
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Log to appropriate syslog facility.
#
# The MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
##
if [ "$2" == "" ] ; then
echo "Usage: $(basename $0) <info|warn|err> <message>"
exit
fi
if [ "$1" != "info" ] && [ "$1" != "warn" ] && [ "$1" != "err" ]; then
echo "Invalid log priority '$1'. Choose from <info|warn|err>."
exit 1
fi
distribution=$(rs-detect-distribution)
if [[ "Synology" == "${distribution}" ]]; then
# Use Synology's crappy synologd if we're on DSM
/usr/syno/bin/synologset1 sys $1 0x99000001 "[rs-backup-server] $2"
else
# Any other distribution
command -v logger > /dev/null
if [ $? -eq 0 ]; then
logger -p $1 -t rs-backup-server "$2"
else
# Log to STDOUT/STDERR if we have no syslog facility
prefix="$(date) [rs-backup-server]"
if [ "$1" == "err" ]; then
echo "$prefix ERROR: $2" >&2
elif [ "$1" == "warn" ]; then
echo "$prefix WARNING: $2" >&2
else
echo "$prefix INFO: $2"
fi
fi
fi

37
server/usr/bin/rs-rm Executable file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env bash
##
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Recursively adjust permissions of given file or directory and remove it.
#
# The MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
##
if [ "$1" == "" ]; then
. rs-version
echo "Usage: $(basename $0) [flags] <file>"
exit
fi
chmod -R +w "${@: -1}"
rm ${@}

View File

@ -1,6 +1,6 @@
#!/bin/sh
#!/usr/bin/env bash
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Rotate backup revisions. Intended for use with cron.
@ -36,6 +36,50 @@ if [ "$RSYNC_EXIT_STATUS" == "" ]; then
exit 1
fi
# rsync exit code descriptions found at
# <https://lxadm.com/Rsync_exit_codes>
msg="unknown code"
case $RSYNC_EXIT_STATUS in
0) msg="Success" ;;
1) msg="Syntax or usage error" ;;
2) msg="Protocol incompatibility" ;;
3) msg="Errors selecting input/output files, dirs" ;;
4) msg="Requested action not supported: an attempt was made to manipulate 64-bit files on a platformi " \
"that cannot support them; or an option was specified that is supported by the client and not by the server" ;;
5) msg="Error starting client-server protocol" ;;
6) msg="Daemon unable to append to log-file" ;;
10) msg="Error in socket I/O" ;;
11) msg="Error in file I/O" ;;
12) msg="Error in rsync protocol data stream" ;;
13) msg="Errors with program diagnostics" ;;
14) msg="Error in IPC code" ;;
20) msg="Received SIGUSR1 or SIGINT" ;;
21) msg="Some error returned by waitpid()" ;;
22) msg="Error allocating core memory buffers" ;;
23) msg="Partial transfer due to error" ;;
24) msg="Partial transfer due to vanished source files" ;;
25) msg="The --max-delete limit stopped deletions" ;;
30) msg="Timeout in data send/receive" ;;
35) msg="Timeout waiting for daemon connection" ;;
esac
if [ $RSYNC_EXIT_STATUS -eq 0 ] || [ $RSYNC_EXIT_STATUS -eq 24 ]; then
rsnapshot -c "$1" push
rs-logger info "Backup for user '$(id -un)' finished with exit code ${RSYNC_EXIT_STATUS} (${msg})"
RSNAPSHOT="rsnapshot"
if [ -x /usr/bin/rsnapshot ]; then
RSNAPSHOT="/usr/bin/rsnapshot"
elif [ -x /opt/bin/rsnapshot ]; then
RSNAPSHOT="/opt/bin/rsnapshot"
fi
$RSNAPSHOT -c "$1" push
rsnapshot_exit_code=$?
if [ $rsnapshot_exit_code -eq 1 ]; then
rs-logger err "Backup rotation for level 'push' of user '$(id -un)' failed."
elif [ $rsnapshot_exit_code -eq 2 ]; then
rs-logger warn "Backup rotation for level 'push' of user '$(id -un)' finished with warnings."
else
rs-logger info "Backup rotation for level 'push' of user '$(id -un)' finished."
fi
else
rs-logger err "Backup for user '$(id -un)' failed with exit code ${RSYNC_EXIT_STATUS} (${msg})"
fi

View File

@ -1,6 +1,6 @@
#!/bin/sh
#!/usr/bin/env bash
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Run a restricted command in an SSH session.
@ -27,13 +27,19 @@
##
home_dir=$1
export HOME="${home_dir}/files"
cd $HOME
if [ "${SSH_ORIGINAL_COMMAND}" == "internal-sftp" ] || [ "${SSH_ORIGINAL_COMMAND}" == "/usr/lib/ssh/sftp-server" ]; then
cd "${home_dir}/files"
exec /usr/lib/ssh/sftp-server -R
if $(echo "${SSH_ORIGINAL_COMMAND}" | grep -q "^\(internal-sftp\|.*/sftp-server\)"); then
[ -x /usr/lib/openssh/sftp-server ] && exec /usr/lib/openssh/sftp-server -R
[ -x /usr/lib/ssh/sftp-server ] && exec /usr/lib/ssh/sftp-server -R
[ -x /usr/libexec/sftp-server ] && exec /usr/libexec/sftp-server -R
[ -x /opt/libexec/sftp-server ] && exec /opt/libexec/sftp-server -R
else
source /etc/profile
exec `which rsync` --server --daemon --config="${home_dir}/rsync.conf" .
rs-logger info "Starting backup for user '$(id -un)'."
RSYNC_OPTS="--server --daemon --config='$home_dir/rsync.conf' ."
[ -x /usr/bin/rsync ] && exec /usr/bin/rsync $(eval echo $RSYNC_OPTS)
[ -x /opt/bin/rsync ] && exec /opt/bin/rsync $(eval echo $RSYNC_OPTS)
fi
echo "Session failed." >&2

View File

@ -1,6 +1,6 @@
#!/bin/sh
#!/usr/bin/env bash
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Print out version and copyright information for rs-backup.
@ -26,7 +26,29 @@
# THE SOFTWARE.
##
VERSION="0.2.1"
_VERSION="0.3.0"
echo -e "\e[1mrs-backup-suite\e[0m version ${VERSION}"
echo "Copyright (C) 2013-2014 Janek Bevendorff"
headline() {
if [ "$1" != "" ]; then
component="$1"
else
component="rs-backup-suite"
fi
echo -e "\e[1m${component}\e[0m version ${_VERSION}"
}
copyright() {
echo "Copyright (C) 2013-2016 Janek Bevendorff"
echo "Website: https://github.com/Manko10/rs-backup-suite"
}
if [ "$1" == "version" ]; then
echo -n "${_VERSION}"
elif [ "$1" == "headline" ]; then
headline "$2"
elif [ "$1" == "copyright" ]; then
copyright
else
headline
copyright
fi

View File

@ -1,6 +1,6 @@
#!/bin/sh
#!/usr/bin/env bash
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Add SSH key to a backup user for passwordless login

View File

@ -1,6 +1,6 @@
#!/bin/sh
#!/usr/bin/env bash
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Set up a backup user.
@ -88,6 +88,10 @@ fi
rs-update-passwd
if $SET_QUOTA; then
rs-setquota "${local_username}"
fi
# Generate config files from templates
rsync_conf="$(cat /etc/rs-backup/rsync.conf.template)"
rsnapshot_conf="$(cat /etc/rs-backup/rsnapshot.conf.template)"

View File

@ -1,6 +1,6 @@
#!/bin/sh
#!/usr/bin/env bash
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Create daily, weekly or monthly snapshots from manual push backups
@ -43,21 +43,33 @@ for home_dir in "${BACKUP_ROOT}"/*; do
config=$(cat "${BACKUP_ROOT}/etc/rsnapshot.global.conf")
# Get number of preceding increments
config=$(echo "${config}" | grep -P '^retain\t')
config=$(echo "${config}" | grep -oPz "retain\t+(\w+)\t+(\d+)\nretain\s+${1}\t+" | sed -n 1p)
config=$(echo "${config}" | awk '$1 == "retain" && $2 == "'${1}'" { print lastline } { lastline = $0 }')
preceding_name=$(echo "${config}" | awk '{ print $2 }')
preceding_number=$(($(echo "${config}" | awk ' { print $3 }') - 1))
# Continue if no proper preceding increment could be found
if [ "${preceding_name}" == "" ] ||
[ ! -d "${home_dir}/${FILES_DIR}/${preceding_name}.${preceding_number}" ]; then
echo "Not rotating ${1}"
echo "Not enough preceding increments found, not rotating ${1}"
continue
fi
cd "${home_dir}/${FILES_DIR}"
owner=$(ls -ld "${home_dir}/${FILES_DIR}" | awk '{ print $3 }')
stat_cmd="stat"
# avoid using BusyBox stat on Synology
if [ -x /opt/bin/stat ]; then
stat_cmd="/opt/bin/stat"
fi
owner=$(${stat_cmd} -c '%U' .)
su - "${owner}" -c "rsnapshot -c '${home_dir}/rsnapshot.conf' '$1'"
rsnapshot_exit_code=$?
if [ $rsnapshot_exit_code -eq 1 ]; then
rs-logger err "Backup rotation for level '$1' of user '$(id -un)' failed."
elif [ $rsnapshot_exit_code -eq 2 ]; then
rs-logger warn "Backup rotation for level '$1' of user '$(id -un)' finished with warnings."
else
rs-logger info "Backup rotation for level '$1' of user '$(id -un)' finished."
fi
fi
done

83
server/usr/sbin/rs-setquota Executable file
View File

@ -0,0 +1,83 @@
#!/usr/bin/env bash
##
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Set quota for user
#
# The MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
##
if [[ "$1" == "" ]]; then
. rs-version
echo "Usage: $(basename $0) <username> [<soft limit> <hard limit> <inode soft limit> <inode hard limit>]"
echo " Limits are in bytes and can contain the postfixes k, M, G, T."
echo " If no limits are specified the default values from the config file will be used."
exit
fi
. /etc/rs-backup/server-config
if ! $QUOTA_MOUNT_POINT; then
echo "ERROR: No mount point specified in config files." >&2
exit 1
fi
if [[ "$2" == "" ]] && [[ "$QUOTA_HARD_LIMIT" == "" ]]; then
echo "ERROR: No limits have been specified. You need to set at least default values in your config file." >&2
exit 1
fi
# Expand postfixes (kilobytes, megabytes, gigabytes, terabytes)
# and calculate number
expand_postfixes() {
local number=$1
number=$(echo "$number" | sed 's/k$/ * 1024/I')
number=$(echo "$number" | sed 's/M$/ * 1024 * 1024/I')
number=$(echo "$number" | sed 's/G$/ * 1024 * 1024 * 1024/I')
number=$(echo "$number" | sed 's/T$/ * 1024 * 1024 * 1024 * 1024/I')
number=$(echo "$number" | sed 's/[^0-9\*]//g')
number=$(eval "echo $(($number))")
echo "$number"
}
block_size=1024
quota_file_system="$QUOTA_MOUNT_POINT"
if [[ "$quota_file_system" == "" ]]; then
quota_file_system="-a"
fi
soft_limit=$(expand_postfixes "$QUOTA_SOFT_LIMIT")
hard_limit=$(expand_postfixes "$QUOTA_HARD_LIMIT")
inode_soft_limit=$(expand_postfixes "$QUOTA_INODE_SOFT_LIMIT")
inode_hard_limit=$(expand_postfixes "$QUOTA_INODE_HARD_LIMIT")
[[ "$2" != "" ]] && soft_limit=$(expand_postfixes "$2")
[[ "$3" != "" ]] && hard_limit=$(expand_postfixes "$3")
[[ "$4" != "" ]] && inode_soft_limit=$(expand_postfixes "$4")
[[ "$5" != "" ]] && inode_hard_limit=$(expand_postfixes "$5")
soft_limit=$(($soft_limit / $block_size))
hard_limit=$(($hard_limit / $block_size))
setquota "$1" "$soft_limit" "$hard_limit" "$inode_soft_limit" "$inode_hard_limit" "$quota_file_system"

View File

@ -1,6 +1,6 @@
#!/bin/sh
#!/usr/bin/env bash
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Update passwd file in chroot folder based on the contents of /etc/passwd.
@ -34,5 +34,4 @@ if [ "${BACKUP_ROOT}" == "" ] || [ "$(realpath ${BACKUP_ROOT})" == "/" ]; then
exit 1
fi
touch "${BACKUP_ROOT}/etc/passwd"
cat /etc/passwd | grep "::${BACKUP_ROOT}/[^/:]\+:/bin/[^:]\+$" > "${BACKUP_ROOT}/etc/passwd"
grep ":rs-backup user:${BACKUP_ROOT}/[^:]\+:[^:]\+$" /etc/passwd > "${BACKUP_ROOT}/etc/passwd"

View File

@ -1,6 +1,6 @@
#!/bin/sh
#!/usr/bin/env bash
##
# Copyright (C) 2013-2014 Janek Bevendorff
# Copyright (C) 2013-2016 Janek Bevendorff
# Website: http://www.refining-linux.org/
#
# Utility program. Check if disk has been used since last check and spin it down if not.