SHA256
Compare commits
13
Commits
ee86603dcd
...
v1.0.0
| Author | SHA256 | Date | |
|---|---|---|---|
|
|
bc8287a28c
|
||
|
|
04d351a859
|
||
|
|
d5d4ea2430
|
||
|
|
e423b97c8a
|
||
|
|
01c299e242
|
||
|
|
56b619372d
|
||
|
|
73202f5e3b
|
||
|
|
d243ea1c89
|
||
|
|
0623c6a985
|
||
|
|
4fb949c26e
|
||
|
|
e065ab2cd7
|
||
|
|
7421c8f43e
|
||
|
|
e184d1c92c
|
@@ -0,0 +1,272 @@
|
|||||||
|
# pbc
|
||||||
|
|
||||||
|
A Bash wrapper around Proxmox's `proxmox-backup-client` for Proxmox Backup Server (PBS).
|
||||||
|
|
||||||
|
It keeps the repository, credentials and the list of paths to back up in a single config
|
||||||
|
file, injects the configured namespace (`--ns`) into every command that needs it, and lets
|
||||||
|
you pick a snapshot group, snapshot and archive from an interactive menu instead of typing
|
||||||
|
them out. Any command it does not implement itself is passed straight through to
|
||||||
|
`proxmox-backup-client`.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- A Debian-based host with `bash` and `curl`
|
||||||
|
- `jq` and `proxmox-backup-client` — both installed by `pbc install`
|
||||||
|
- A reachable PBS with a datastore and an API token
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://git.ccc-rheintal.ch/spacefreak/pbc/raw/branch/master/install.sh | sudo bash
|
||||||
|
```
|
||||||
|
|
||||||
|
This installs the newest tagged version to `/usr/local/bin/pbc`, installs the
|
||||||
|
dependencies, and puts an example config at `/etc/pbc/config.example`.
|
||||||
|
|
||||||
|
### Updating
|
||||||
|
|
||||||
|
Run the same command again to update `pbc` itself:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://git.ccc-rheintal.ch/spacefreak/pbc/raw/branch/master/install.sh | sudo bash
|
||||||
|
```
|
||||||
|
|
||||||
|
It fetches the newest tag and overwrites `/usr/local/bin/pbc`. Your `/etc/pbc/config` is
|
||||||
|
left untouched.
|
||||||
|
|
||||||
|
To update only the `proxmox-backup-client` binary:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo pbc install --update
|
||||||
|
```
|
||||||
|
|
||||||
|
This is skipped when the client is managed by the package manager — use `apt` in that case.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp /etc/pbc/config.example /etc/pbc/config
|
||||||
|
sudo chmod 640 /etc/pbc/config
|
||||||
|
sudo editor /etc/pbc/config
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PBS_SERVER='pbs.domain.tld:8007'
|
||||||
|
PBS_USER='backup@pam:token-name'
|
||||||
|
PBS_PASSWORD='XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
|
||||||
|
PBS_DATASTORE='backup'
|
||||||
|
PBS_NAMESPACE='MyBackups'
|
||||||
|
|
||||||
|
BACKUP=('root.pxar:/' 'data.pxar:/mnt/data')
|
||||||
|
```
|
||||||
|
|
||||||
|
| Key | Description |
|
||||||
|
|---|---|
|
||||||
|
| `PBS_SERVER` | PBS host and port |
|
||||||
|
| `PBS_USER` | User and API token name, `user@realm:token-name` |
|
||||||
|
| `PBS_PASSWORD` | The API token secret |
|
||||||
|
| `PBS_DATASTORE` | Datastore to back up to |
|
||||||
|
| `PBS_NAMESPACE` | Namespace inside the datastore |
|
||||||
|
| `BACKUP` | Array of `archive-name.pxar:/path` entries to back up |
|
||||||
|
| `PBC` | Optional, path to `proxmox-backup-client` (default `/usr/local/bin/proxmox-backup-client`) |
|
||||||
|
|
||||||
|
`pbc` never creates the config for you — copy the example and edit it yourself. The file
|
||||||
|
has to be readable for the user that runs `pbc`, so if that is not root, hand
|
||||||
|
it over:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chown youruser /etc/pbc/config
|
||||||
|
```
|
||||||
|
|
||||||
|
The config is sourced as Bash and contains your token secret. `pbc` refuses to run if the
|
||||||
|
file is writable by group or others, and warns if it is readable by others.
|
||||||
|
|
||||||
|
Check that it works:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pbc list
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a different config with `-c`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pbc -c ./myconfig list
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Run `pbc --help` for the synopsis and the list of options, or `pbc -H` to additionally
|
||||||
|
list every command of `proxmox-backup-client`. The commands `pbc` implements itself are
|
||||||
|
described below.
|
||||||
|
|
||||||
|
### backup
|
||||||
|
|
||||||
|
Back up everything listed in `BACKUP` and print the client output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pbc backup
|
||||||
|
```
|
||||||
|
|
||||||
|
Extra arguments are forwarded to the client:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pbc backup --exclude '/mnt/data/cache'
|
||||||
|
```
|
||||||
|
|
||||||
|
### backup-cron
|
||||||
|
|
||||||
|
Same backup, but output is suppressed unless it fails — so cron only mails you on errors:
|
||||||
|
|
||||||
|
```cron
|
||||||
|
0 3 * * * /usr/local/bin/pbc backup-cron
|
||||||
|
```
|
||||||
|
|
||||||
|
### list
|
||||||
|
|
||||||
|
List backup groups with their latest snapshot and archives:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pbc list
|
||||||
|
```
|
||||||
|
|
||||||
|
### snapshot-list
|
||||||
|
|
||||||
|
List snapshots of a group with sizes and archives. Without an argument you get a menu of
|
||||||
|
the available groups:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pbc snapshot-list
|
||||||
|
pbc snapshot-list host/myhost
|
||||||
|
```
|
||||||
|
|
||||||
|
### mount
|
||||||
|
|
||||||
|
Mount a single archive of a snapshot on a local directory. Anything you leave out is
|
||||||
|
asked for interactively:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pbc mount
|
||||||
|
pbc mount host/myhost/2026-08-24T01:00:00Z root.pxar /mnt/restore
|
||||||
|
```
|
||||||
|
|
||||||
|
The target directory is not created for you — create it beforehand and make sure it is
|
||||||
|
writable for the user that runs `pbc`. Unmount when done:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
umount /mnt/restore
|
||||||
|
```
|
||||||
|
|
||||||
|
### catalog-shell
|
||||||
|
|
||||||
|
Open an interactive shell to browse an archive and restore selected files — see below.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pbc catalog-shell
|
||||||
|
pbc catalog-shell host/myhost/2026-08-24T01:00:00Z root.pxar
|
||||||
|
```
|
||||||
|
|
||||||
|
### install
|
||||||
|
|
||||||
|
Install or update the dependencies. Requires root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo pbc install
|
||||||
|
sudo pbc install --update
|
||||||
|
```
|
||||||
|
|
||||||
|
## Restoring files
|
||||||
|
|
||||||
|
### Selectively, with `catalog-shell`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pbc catalog-shell
|
||||||
|
```
|
||||||
|
|
||||||
|
Pick a snapshot and an archive from the menu, then browse the archive as if it were a
|
||||||
|
filesystem and mark what you want back:
|
||||||
|
|
||||||
|
```
|
||||||
|
pxar:/ > cd etc
|
||||||
|
pxar:/etc > ls
|
||||||
|
pxar:/etc > select hosts
|
||||||
|
pxar:/etc > find etc/nginx/** --select
|
||||||
|
pxar:/etc > list-selected
|
||||||
|
pxar:/etc > restore-selected /mnt/restore
|
||||||
|
pxar:/etc > exit
|
||||||
|
```
|
||||||
|
|
||||||
|
- `ls`, `cd`, `pwd`, `stat` — browse the archive
|
||||||
|
- `select <path>` — mark a single file, relative to the current directory
|
||||||
|
- `find <pattern> --select` — mark everything matching a glob
|
||||||
|
- `deselect <path>` / `clear-selected` — drop one entry or all of them
|
||||||
|
- `list-selected` — show what is currently marked
|
||||||
|
- `restore-selected <target>` — restore only the marked entries
|
||||||
|
- `restore <target> [pattern]` — restore the current directory and everything below it
|
||||||
|
- `help` — list all available shell commands
|
||||||
|
- `exit` — leave the shell
|
||||||
|
|
||||||
|
#### Selecting a directory and everything beneath it
|
||||||
|
|
||||||
|
`select` marks exactly the entry you give it and nothing else. Marking a directory
|
||||||
|
therefore restores an empty directory — its contents are *not* included. Use a glob with
|
||||||
|
`find --select` instead:
|
||||||
|
|
||||||
|
```
|
||||||
|
pxar:/ > find etc/nginx/** --select
|
||||||
|
pxar:/ > select etc/nginx
|
||||||
|
pxar:/ > restore-selected /mnt/restore
|
||||||
|
```
|
||||||
|
|
||||||
|
The `find` line picks up everything below `etc/nginx`; the `select` line adds the
|
||||||
|
directory itself, so its own permissions and ownership are restored as well.
|
||||||
|
|
||||||
|
Note that `find` patterns are always matched against paths relative to the archive root,
|
||||||
|
no matter which directory you are in, and that `find` scans the whole archive — expect it
|
||||||
|
to take a while on large backups.
|
||||||
|
|
||||||
|
The target path must not exist yet; the restore creates it. Its parent directory has to be
|
||||||
|
writable for the user that runs `pbc`.
|
||||||
|
|
||||||
|
### Everything, or with normal tools
|
||||||
|
|
||||||
|
To copy files out with `cp`, `rsync` or a file manager, mount the archive instead:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /mnt/restore # must exist and be writable for the user running pbc
|
||||||
|
pbc mount
|
||||||
|
cp -a /mnt/restore/etc/nginx /etc/nginx
|
||||||
|
umount /mnt/restore
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running in WSL
|
||||||
|
|
||||||
|
`pbc` also works inside a Debian-based WSL instance, which makes it a way to back up
|
||||||
|
Windows directories: the Windows drives show up under `/mnt`, so they can be listed in
|
||||||
|
`BACKUP` like any other path.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
BACKUP=('users.pxar:/mnt/c/Users' 'projects.pxar:/mnt/d/projects')
|
||||||
|
```
|
||||||
|
|
||||||
|
A few things to keep in mind:
|
||||||
|
|
||||||
|
- Access to files under `/mnt` is governed by Windows, not by the user you are inside the
|
||||||
|
WSL instance — `sudo` does not help there. To back up paths your Windows user cannot
|
||||||
|
read, start the WSL instance itself as administrator (run the terminal or `wsl.exe` via
|
||||||
|
*Run as administrator*), then run `pbc` in it.
|
||||||
|
- The owner and permission metadata stored in the archive is the one WSL synthesizes for
|
||||||
|
Windows files, not the original Windows ACLs.
|
||||||
|
- Cron is not running in a WSL instance by default, so `backup-cron` only fires if you
|
||||||
|
enable it — either by starting `cron` yourself, or by triggering `pbc backup-cron` from
|
||||||
|
the Windows Task Scheduler with `wsl.exe`:
|
||||||
|
|
||||||
|
```
|
||||||
|
wsl.exe -d Debian -u root /usr/local/bin/pbc backup-cron
|
||||||
|
```
|
||||||
|
|
||||||
|
For the reason above, such a task has to run with highest privileges to reach files that
|
||||||
|
are not accessible to your Windows user.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
GPLv3 — see [LICENSE](LICENSE).
|
||||||
+3
-3
@@ -20,13 +20,13 @@ function download_files() {
|
|||||||
shift
|
shift
|
||||||
curl -fsSL --output-dir "$WORK_DIR/" --remote-name "$API_ENDPOINT/raw/$path$query_params"
|
curl -fsSL --output-dir "$WORK_DIR/" --remote-name "$API_ENDPOINT/raw/$path$query_params"
|
||||||
rc=$?
|
rc=$?
|
||||||
if (( $rc != 0 )); then
|
if (( rc != 0 )); then
|
||||||
return $rc
|
return $rc
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_TAG=$(curl -fsSL "$API_ENDPOINT/tags" | grep -o '{"name":"[^"]*"' | sed 's/^{"name":"//;s/"$//' | sort --version-sort | tail -n 1)
|
GIT_TAG=$(curl -fsSL "$API_ENDPOINT/tags" | grep -o '{"name":"[^"]*"' | sed 's/^{"name":"//;s/"$//' | grep -E '^v[0-9]+' | sort --version-sort | tail -n 1)
|
||||||
if [ -n "$GIT_TAG" ]; then
|
if [ -n "$GIT_TAG" ]; then
|
||||||
echo "Info: installing pbc version $GIT_TAG"
|
echo "Info: installing pbc version $GIT_TAG"
|
||||||
else
|
else
|
||||||
@@ -42,7 +42,7 @@ chmod o= "$WORK_DIR/config.example"
|
|||||||
|
|
||||||
"$WORK_DIR/pbc" install
|
"$WORK_DIR/pbc" install
|
||||||
rc=$?
|
rc=$?
|
||||||
if (( $rc != 0 )); then
|
if (( rc != 0 )); then
|
||||||
echo "Error installing pbc requirements!" >&2
|
echo "Error installing pbc requirements!" >&2
|
||||||
exit $rc
|
exit $rc
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
function usage() {
|
function usage() {
|
||||||
full=${1:-false}
|
local full=${1:-false}
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: $SCRIPT [-h] [-H] [-c config] command [OPTIONS]
|
Usage: $SCRIPT [-h] [-H] [-c config] command [OPTIONS]
|
||||||
|
|
||||||
@@ -80,7 +82,7 @@ function apt_get_install() {
|
|||||||
echo "Installing $package using package manager ..."
|
echo "Installing $package using package manager ..."
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$package" >/dev/null
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$package" >/dev/null
|
||||||
local rc=$?
|
local rc=$?
|
||||||
(( $rc != 0 )) && echo "Error installing package!" >&2
|
(( rc != 0 )) && echo "Error installing package!" >&2
|
||||||
return $rc
|
return $rc
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,16 +182,44 @@ function may_select() {
|
|||||||
echo "$value"
|
echo "$value"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function select_group() {
|
||||||
|
local groups raw
|
||||||
|
raw=$(get_group_names) || { echo "Error: failed to query PBS!" >&2; return 1; }
|
||||||
|
[ -z "$raw" ] && return 2
|
||||||
|
readarray -t groups <<< "$raw"
|
||||||
|
|
||||||
|
PS3="Snapshot group [1-${#groups[@]}]: "
|
||||||
|
GROUP=$(may_select "$1" "${groups[@]}") || { echo "Error: invalid group" >&2; return 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function select_snapshot_and_archive() {
|
||||||
|
local snapshots archives raw
|
||||||
|
raw=$(get_snapshot_names) || { echo "Error: failed to query PBS!" >&2; return 1; }
|
||||||
|
[ -z "$raw" ] && return 2
|
||||||
|
readarray -t snapshots <<< "$raw"
|
||||||
|
|
||||||
|
PS3="Snapshot [1-${#snapshots[@]}]: "
|
||||||
|
SNAPSHOT=$(may_select "$1" "${snapshots[@]}") || { echo "Error: invalid snapshot" >&2; return 1; }
|
||||||
|
|
||||||
|
raw=$(get_snapshot_files "$SNAPSHOT") || { echo "Error: failed to query PBS!" >&2; return 1; }
|
||||||
|
[ -z "$raw" ] && return 3
|
||||||
|
readarray -t archives <<< "$raw"
|
||||||
|
|
||||||
|
PS3="Archive [1-${#archives[@]}]: "
|
||||||
|
ARCHIVE=$(may_select "$2" "${archives[@]}") || { echo "Error: invalid archive" >&2; return 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SCRIPT_PATH=$(realpath -s "$0")
|
SCRIPT_PATH=$(realpath -s "$0")
|
||||||
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
|
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||||
SCRIPT=$(basename "$SCRIPT_PATH")
|
SCRIPT=$(basename "$SCRIPT_PATH")
|
||||||
|
|
||||||
PBC=$(command -v proxmox-backup-client)
|
PBC=$(command -v proxmox-backup-client)
|
||||||
PBC_DOWNLOAD_URL='http://download.proxmox.com/debian/pbs-client/dists/trixie/main/binary-amd64'
|
PBC_DOWNLOAD_URL='http://download.proxmox.com/debian/pbs-client/dists/trixie/main/binary-amd64'
|
||||||
ETC_DIR="/etc/$SCRIPT"
|
ETC_DIR="/etc/$SCRIPT"
|
||||||
|
|
||||||
FULL_HELP=false
|
full_help=false
|
||||||
|
update_pbc=false
|
||||||
|
|
||||||
action=''
|
action=''
|
||||||
cfgfile="$ETC_DIR/config"
|
cfgfile="$ETC_DIR/config"
|
||||||
@@ -210,7 +240,7 @@ while (( $# > 0 )); do
|
|||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
-H|--full-help)
|
-H|--full-help)
|
||||||
FULL_HELP=true
|
full_help=true
|
||||||
;;
|
;;
|
||||||
-*)
|
-*)
|
||||||
echo "Error: invalid option: $opt" >&2
|
echo "Error: invalid option: $opt" >&2
|
||||||
@@ -225,7 +255,7 @@ while (( $# > 0 )); do
|
|||||||
elif [ "$action" == "install" ]; then
|
elif [ "$action" == "install" ]; then
|
||||||
case "$opt" in
|
case "$opt" in
|
||||||
-u|--update)
|
-u|--update)
|
||||||
args+=('true')
|
update_pbc=true
|
||||||
;;
|
;;
|
||||||
-*)
|
-*)
|
||||||
echo "Error: invalid option: $opt" >&2
|
echo "Error: invalid option: $opt" >&2
|
||||||
@@ -252,7 +282,7 @@ if [ -f "$cfgfile" ]; then
|
|||||||
(( 8#$perms & 0007 )) && echo "Warning: $cfgfile is readable by others!" >&2
|
(( 8#$perms & 0007 )) && echo "Warning: $cfgfile is readable by others!" >&2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if $FULL_HELP; then
|
if $full_help; then
|
||||||
[ -f "$cfgfile" ] && source "$cfgfile"
|
[ -f "$cfgfile" ] && source "$cfgfile"
|
||||||
PBC=${PBC:-/usr/local/bin/proxmox-backup-client}
|
PBC=${PBC:-/usr/local/bin/proxmox-backup-client}
|
||||||
full_usage
|
full_usage
|
||||||
@@ -296,71 +326,44 @@ case "$action" in
|
|||||||
pbc-list "${args[@]}" || exit $?
|
pbc-list "${args[@]}" || exit $?
|
||||||
;;
|
;;
|
||||||
snapshot-list)
|
snapshot-list)
|
||||||
readarray -t groups < <(get_group_names)
|
select_group "${args[0]}"
|
||||||
(( ${#groups[@]} == 0 )) && echo "no snapshot groups found" && exit 0
|
case $? in
|
||||||
|
0) ;;
|
||||||
group=${args[0]}
|
2) echo "no snapshot groups found"; exit 0 ;;
|
||||||
args=("${args[@]:1}")
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
PS3="Snapshot group [1-${#groups[@]}]: "
|
|
||||||
group=$(may_select "$group" "${groups[@]}")
|
|
||||||
(( $? != 0 )) && echo "Error: invalid group" >&2 && exit 1
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
pbc-snapshot-list "$group" "${args[@]}" || exit $?
|
pbc-snapshot-list "$GROUP" "${args[@]:1}" || exit $?
|
||||||
;;
|
;;
|
||||||
catalog-shell)
|
catalog-shell)
|
||||||
readarray -t snapshots < <(get_snapshot_names)
|
select_snapshot_and_archive "${args[0]}" "${args[1]}"
|
||||||
(( ${#snapshots[@]} == 0 )) && echo "no snapshots found" && exit 0
|
case $? in
|
||||||
|
0) ;;
|
||||||
snapshot=${args[0]}
|
2) echo "no snapshots found"; exit 0 ;;
|
||||||
args=("${args[@]:1}")
|
3) echo "no archives found"; exit 0 ;;
|
||||||
|
*) exit 1 ;;
|
||||||
PS3="Snapshot [1-${#snapshots[@]}]: "
|
esac
|
||||||
snapshot=$(may_select "$snapshot" "${snapshots[@]}")
|
|
||||||
(( $? != 0 )) && echo "Error: invalid snapshot" >&2 && exit 1
|
|
||||||
|
|
||||||
readarray -t archives < <(get_snapshot_files "$snapshot")
|
|
||||||
(( ${#archives[@]} == 0 )) && echo "no archives found" && exit 0
|
|
||||||
|
|
||||||
archive=${args[0]}
|
|
||||||
args=("${args[@]:1}")
|
|
||||||
|
|
||||||
PS3="Archive [1-${#archives[@]}]: "
|
|
||||||
archive=$(may_select "$archive" "${archives[@]}")
|
|
||||||
(( $? != 0 )) && echo "Error: invalid archive" >&2 && exit 1
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
pbc-catalog-shell "$snapshot" "$archive" || exit $?
|
pbc-catalog-shell "$SNAPSHOT" "$ARCHIVE" || exit $?
|
||||||
;;
|
;;
|
||||||
mount)
|
mount)
|
||||||
readarray -t snapshots < <(get_snapshot_names)
|
select_snapshot_and_archive "${args[0]}" "${args[1]}"
|
||||||
(( ${#snapshots[@]} == 0 )) && echo "no snapshots found" && exit 0
|
case $? in
|
||||||
|
0) ;;
|
||||||
|
2) echo "no snapshots found"; exit 0 ;;
|
||||||
|
3) echo "no archives found"; exit 0 ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
snapshot=${args[0]}
|
target=${args[2]}
|
||||||
args=("${args[@]:1}")
|
|
||||||
|
|
||||||
PS3="Snapshot [1-${#snapshots[@]}]: "
|
|
||||||
snapshot=$(may_select "$snapshot" "${snapshots[@]}")
|
|
||||||
(( $? != 0 )) && echo "Error: invalid snapshot" >&2 && exit 1
|
|
||||||
|
|
||||||
readarray -t archives < <(get_snapshot_files "$snapshot")
|
|
||||||
(( ${#archives[@]} == 0 )) && echo "no archives found" && exit 0
|
|
||||||
|
|
||||||
archive=${args[0]}
|
|
||||||
args=("${args[@]:1}")
|
|
||||||
|
|
||||||
PS3="Archive [1-${#archives[@]}]: "
|
|
||||||
archive=$(may_select "$archive" "${archives[@]}")
|
|
||||||
(( $? != 0 )) && echo "Error: invalid archive" >&2 && exit 1
|
|
||||||
|
|
||||||
target=${args[0]}
|
|
||||||
args=("${args[@]:1}")
|
|
||||||
|
|
||||||
if [ -z "$target" ]; then
|
if [ -z "$target" ]; then
|
||||||
|
[ ! -t 0 ] && echo "Error: no target path given and stdin is not a terminal" >&2 && exit 1
|
||||||
while [ -z "$target" ]; do
|
while [ -z "$target" ]; do
|
||||||
echo -n "Target path: "
|
echo -n "Target path: "
|
||||||
read -r target
|
! read -r target && [ -z "$target" ] && echo "Error: no target path given" >&2 && exit 1
|
||||||
[ -d "$target" ] && break
|
[ -d "$target" ] && break
|
||||||
echo -e "\nError: $target: no such directory\n" >&2
|
echo -e "\nError: $target: no such directory\n" >&2
|
||||||
target=''
|
target=''
|
||||||
@@ -371,30 +374,26 @@ case "$action" in
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
pbc-mount "$snapshot" "$archive" "$target" || exit $?
|
pbc-mount "$SNAPSHOT" "$ARCHIVE" "$target" || exit $?
|
||||||
;;
|
;;
|
||||||
install)
|
install)
|
||||||
update=${args[0]:-false}
|
|
||||||
|
|
||||||
(( EUID != 0 )) && echo "Error: installation only works as root!" >&2 && exit 1
|
(( EUID != 0 )) && echo "Error: installation only works as root!" >&2 && exit 1
|
||||||
|
|
||||||
install_proxmox_backup_client "$update" || exit $?
|
install_proxmox_backup_client "$update_pbc" || exit $?
|
||||||
|
|
||||||
if dpkg -s jq &>/dev/null; then
|
if dpkg -s jq &>/dev/null; then
|
||||||
$update && echo "Info: skip update of jq, package is managed by package manager" || echo "Info: jq is already installed"
|
$update_pbc && echo "Info: skip update of jq, package is managed by package manager" || echo "Info: jq is already installed"
|
||||||
else
|
else
|
||||||
apt_get_install jq || exit $?
|
apt_get_install jq || exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ETC_DIR="/etc/$SCRIPT"
|
|
||||||
|
|
||||||
if [ ! -d "$ETC_DIR" ]; then
|
if [ ! -d "$ETC_DIR" ]; then
|
||||||
echo "Creating config directory $ETC_DIR ..."
|
echo "Creating config directory $ETC_DIR ..."
|
||||||
! mkdir -p "$ETC_DIR" && echo "Error creating directory!" >&2 && exit 3
|
! mkdir -p "$ETC_DIR" && echo "Error creating directory!" >&2 && exit 3
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f "$SCRIPT_DIR/config.example" ]; then
|
if [ -f "$SCRIPT_DIR/config.example" ]; then
|
||||||
if $update || [ ! -f "$ETC_DIR/config.example" ]; then
|
if $update_pbc || [ ! -f "$ETC_DIR/config.example" ]; then
|
||||||
echo "Install example config to $ETC_DIR/config.example ..."
|
echo "Install example config to $ETC_DIR/config.example ..."
|
||||||
! install -m 0640 "$SCRIPT_DIR/config.example" "$ETC_DIR/config.example" && echo "Error installing file!" >&2 && exit 4
|
! install -m 0640 "$SCRIPT_DIR/config.example" "$ETC_DIR/config.example" && echo "Error installing file!" >&2 && exit 4
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user