Support home-based mailboxes during automated upgrades
This commit is contained in:
1 parent
8e6c9a058f
commit
56fbcdad74
8 files changed
+194
-9
No files matched your search
+14
-2
@@ -83,7 +83,7 @@ Ein neuer Lauf heißt beispielsweise `appbackup_2026-09-10_02-30-00_a1b2c3d4`. Z
|
||||
| `nextcloud-data` | `NC_DATA_DIR`, sofern aktiviert |
|
||||
| `gitea`, `gitea-etc` | `GITEA_DATA_DIR`, `GITEA_ETC_DIR` |
|
||||
| `gitea-repos`, `gitea-custom` | Optionale explizite externe Repository-/Custom-Pfade |
|
||||
| `mail`, `postfix`, `dovecot` | Mailstore und beide Konfigurationsverzeichnisse |
|
||||
| `mail`, `postfix`, `dovecot` | Zentraler Mailstore (bei `ENABLE_MAIL_STORE=true`) und beide Konfigurationsverzeichnisse |
|
||||
| `mail-home-0`, `mail-home-1`, … | Reihenfolge von `MAIL_HOME_DIRS`, mit Excludes und Dateisystemgrenze |
|
||||
| `extra-0`, `extra-1`, … | Reihenfolge von `EXTRA_PATHS` |
|
||||
|
||||
@@ -156,6 +156,17 @@ Gitea: `APP_DATA_PATH`, `[repository] ROOT`, Custom-Verzeichnis und `app.ini` pr
|
||||
|
||||
`MAIL_BACKUP_MODE=live` und abgeschaltete Stopps sind bewusste Ausnahmen mit Konsistenzrisiko. Das Log weist darauf hin. Home-Verzeichnisse werden mit `--one-file-system` kopiert: Daten anderer Mounts fehlen dann absichtlich. Mailqueues, zusätzliche Spamfilter-/DKIM-Verzeichnisse oder Maildatenbanken sind nur gesichert, wenn sie explizit im Umfang stehen.
|
||||
|
||||
`ENABLE_MAIL_STORE=true` bleibt der kompatible Default: `MAIL_DIR` muss existieren.
|
||||
Bei Mailboxen ausschließlich in Benutzerverzeichnissen kann explizit
|
||||
`ENABLE_MAIL_STORE=false` gesetzt werden. Dann sind aktivierte, nicht leere
|
||||
`MAIL_HOME_DIRS` zwingend; fehlende Homes lassen die Vorprüfung scheitern.
|
||||
Postfix/Dovecot-Konfigurationen und Dienststopps bleiben erhalten. Der Restore
|
||||
verwendet dieselbe Komponentenzuordnung und stellt Home-Mailboxen über
|
||||
`mail-home-N` wieder her. Die Vollständigkeit der Benutzerliste sowie Excludes,
|
||||
Symlinks und Mountgrenzen muss der Betreiber prüfen. Der Paket-Updater bietet
|
||||
bei fehlendem zentralem Verzeichnis einen Pfaddialog oder eine ausdrückliche
|
||||
Homes-Bestätigung; Details stehen in [UPDATE_ANLEITUNG.md](UPDATE_ANLEITUNG.md).
|
||||
|
||||
### Speicher, Retention und Protokolle
|
||||
|
||||
`MIN_FREE_GB` ist eine Reserveprüfung auf den Staging- und Archivdateisystemen, keine genaue Prognose. Die Kopie braucht Platz für unkomprimierte Quelldaten und Dumps; Archive benötigen zusätzlich komprimierten Platz. Beim Restore kommen die private Kopie des Sicherungssatzes und entpackte Daten hinzu. Eine Platte kann zwischen Prüfungen volllaufen; der Lauf muss dann scheitern. Ausreichend Reserve und ein externes Kapazitätsmonitoring einplanen.
|
||||
@@ -293,10 +304,11 @@ Die Entfernung der drei DB-Dateien aus dem aktuellen Baum löscht deren Historie
|
||||
|
||||
Reproduzierbar mit `tests/Dockerfile` und `tests/run.sh`. Der Container bekommt das Repository nur lesbar und benötigt während der Tests kein Netzwerk. Die MariaDB läuft ohne TCP-Port in einem temporären Verzeichnis. systemd/occ werden simuliert; alle eigentlichen Archive, Dateikopien, Dumps/Imports und Crypt-Operationen sind real.
|
||||
|
||||
Ergebnis des lokalen Abschlusslaufs am 10.09.2026: **30 Tests erfolgreich**, Shell-Syntax und ShellCheck ohne Befund. Testumgebung: Debian 13.6, MariaDB 11.8.6, rclone 1.60.1-DEV (Distributionspaket), ShellCheck 0.10.0. Diese Versionsangaben beschreiben die Testumgebung, keine Empfehlung für die Produktivversion.
|
||||
Ergebnis des lokalen Abschlusslaufs am 10.09.2026: **34 Tests erfolgreich**, Shell-Syntax und ShellCheck ohne Befund. Testumgebung: Debian 13.6, MariaDB 11.8.6, rclone 1.60.1-DEV (Distributionspaket), ShellCheck 0.10.0. Diese Versionsangaben beschreiben die Testumgebung, keine Empfehlung für die Produktivversion.
|
||||
|
||||
Geprüfte Fälle umfassen:
|
||||
|
||||
- Fehlender Mailstore ohne Live-Änderung, explizite Pfadkorrektur, Homes-only-Update samt tatsächlichem Maildatei-Restore und Ablehnung unvollständiger Home-Konfigurationen.
|
||||
- Shell-Syntax und ShellCheck der Paketdateien.
|
||||
- MyISAM-Dump und tatsächlicher SQL-Import samt Dateiwiederherstellung.
|
||||
- gzip/zstd, korrekte Excludes, vorhandener Wartungsmodus und inaktive Dienste.
|
||||
|
||||
@@ -89,6 +89,9 @@ RCLONE_REMOTE_BASE="OneDriveCrypt:gemini" # vorhandenes Crypt-Ziel beibehalten!
|
||||
RETENTION_KEEP_LAST=3
|
||||
ENABLE_REMOTE_RETENTION="false" # während der Migration zunächst aus
|
||||
MAIL_BACKUP_MODE="stop"
|
||||
# Zentrale Mailablage muss existieren; bei ausschließlich in Homes liegenden
|
||||
# Mailboxen ENABLE_MAIL_STORE=false und MAIL_HOME_DIRS vollständig konfigurieren.
|
||||
ENABLE_MAIL_STORE="true"
|
||||
# Tatsächliche Timer-/Service-Namen einsetzen; Timer vor seinem Service nennen:
|
||||
QUIESCE_SERVICES=()
|
||||
# Gitea war in der bisherigen mitgelieferten Konfiguration NICHT aktiviert:
|
||||
|
||||
+41
-1
@@ -55,6 +55,19 @@ für Tests/Diagnose gedacht; dabei besteht keine Absicherung gegen SSH-Abbruch.
|
||||
|
||||
## Gezielte Rückfragen und Grenzen
|
||||
|
||||
- **Zentrales Mailverzeichnis fehlt, etwa `/var/vmail`:** Der Installer fragt vor
|
||||
der Installation nach dem tatsächlichen absoluten Pfad. Alternativ lässt sich
|
||||
dieser mit `--mail-dir /tatsaechliche/mailablage` übergeben. Kein leeres
|
||||
Ersatzverzeichnis anlegen: Das würde fehlende Maildaten nur verdecken.
|
||||
Liegen **alle** Mailboxen in den angezeigten `MAIL_HOME_DIRS`, im Dialog `homes`
|
||||
wählen und die vollständige Abdeckung bestätigen. `--mail-homes-only` erteilt
|
||||
dieselbe ausdrückliche Bestätigung ohne Dialog. Dabei müssen die Home-Sicherung
|
||||
aktiviert und alle konfigurierten Homes vorhanden sein. Excludes und
|
||||
Dateisystemgrenzen gelten weiterhin; ausgeschlossene Verzeichnisse, verlinkte
|
||||
externe Mailablagen und weitere Mounts sind dadurch nicht abgedeckt.
|
||||
Postfix/Dovecot-Konfigurationen und Maildienst-Stopps bleiben aktiv.
|
||||
Bei unbekannter Mailablage abbrechen und zuerst die Mailserver-Konfiguration
|
||||
prüfen. Ein fehlender Pfad wird nie automatisch übersprungen.
|
||||
- **Rclone-Datei nicht eindeutig:** Pfad im Dialog oder mit `--rclone-config FILE`
|
||||
angeben. Eine user-eigene Datei wird in eine neue root-eigene Datei kopiert;
|
||||
die vom Benutzer verwendete Originaldatei bleibt unverändert.
|
||||
@@ -94,6 +107,33 @@ bleiben erhalten; beim Aktivieren kann `Persistent=true` einen Lauf nachholen.
|
||||
|
||||
## Fehler und Rückweg
|
||||
|
||||
Ein Abbruch `Missing source: /var/vmail` bei der **Vorprüfung**, wie beim Paket
|
||||
`8e6c9a058fa9`, erfordert kein Rollback. Mit dem korrigierten Paket erneut starten
|
||||
und die Mailablage klären. Die bisherige Konfiguration und der Timer sind erhalten;
|
||||
der abgebrochene Versuch enthält noch kein erfolgreiches neues Backup.
|
||||
Bereits bewusst getroffene Entscheidungen können als Optionen mitgegeben werden:
|
||||
|
||||
```bash
|
||||
sudo bash app-backup-update-VERSION.run --enable-gitea --cron-services crond.service
|
||||
```
|
||||
|
||||
Diese Optionen passen nur, wenn Gitea aufgenommen werden soll und das Pausieren
|
||||
aller Cronjobs über `crond.service` ausdrücklich akzeptiert wurde.
|
||||
|
||||
Für Mailablagen wie `/home/johannes/Mail` unter den konfigurierten Benutzerhomes
|
||||
zusätzlich `--mail-homes-only` verwenden, sofern damit alle Postfächer erfasst sind:
|
||||
|
||||
```bash
|
||||
sudo bash app-backup-update-VERSION.run \
|
||||
--enable-gitea --cron-services crond.service --mail-homes-only
|
||||
```
|
||||
|
||||
Der Installer zeigt die übernommenen Home-Verzeichnisse an. `Mail/` einschließlich
|
||||
`cur`, `new`, `tmp` und Dovecot-Dateien wird mit den normalen Home-Excludes der
|
||||
Vorlage vollständig kopiert, sofern es auf demselben Dateisystem liegt und kein
|
||||
Verweis auf eine externe Ablage ist. Weitere Benutzer müssen in `MAIL_HOME_DIRS`
|
||||
stehen; das Vorhandensein eines einzelnen Postfachs belegt nicht deren Abdeckung.
|
||||
|
||||
Bei einem Fehler nach Beginn der Installation bleibt der Backup-Timer gestoppt.
|
||||
Es erfolgt kein automatisches Zurücksetzen über möglicherweise noch laufende oder
|
||||
teilweise wiederhergestellte Anwendungen. Logs und gegebenenfalls
|
||||
@@ -125,7 +165,7 @@ Das ausführbare Paket ausschließlich über euren vertrauenswürdigen Kanal bez
|
||||
|
||||
## Verifikation der Lieferung
|
||||
|
||||
30 Container-Tests sowie ShellCheck und Shell-Syntaxprüfung erfolgreich.
|
||||
34 Container-Tests sowie ShellCheck und Shell-Syntaxprüfung erfolgreich.
|
||||
Die Tests enthalten echte MariaDB-Dumps/Imports und lokale rclone-Crypt-Roundtrips;
|
||||
systemd/occ und die systemd-run-Übergabe sind simuliert. Die tatsächliche
|
||||
Server-/OneDrive-Prüfung erfolgt beim ersten Paketlauf. Ein fachlicher Restore
|
||||
|
||||
@@ -82,6 +82,9 @@ GITEA_DB_CNF="/etc/app-backup/db-gitea.cnf"
|
||||
|
||||
ENABLE_MAIL="true"
|
||||
MAIL_BACKUP_MODE="stop"
|
||||
# Nur false setzen, wenn alle Mailboxen durch MAIL_HOME_DIRS abgedeckt sind.
|
||||
# Postfix/Dovecot und deren Dienststopps bleiben auch dann aktiviert.
|
||||
ENABLE_MAIL_STORE="true"
|
||||
MAIL_DIR="/var/vmail"
|
||||
POSTFIX_DIR="/etc/postfix"
|
||||
DOVECOT_DIR="/etc/dovecot"
|
||||
|
||||
+10
-4
@@ -41,7 +41,7 @@ load_config() {
|
||||
: "${GITEA_DATA_DIR:=/var/lib/gitea/data}" "${GITEA_ETC_DIR:=/etc/gitea}"
|
||||
: "${ENABLE_HTTPD_STOP:=true}" "${HTTPD_SERVICE_NAME:=httpd}"
|
||||
: "${ENABLE_PHPFPM_STOP:=true}" "${PHPFPM_SERVICE_NAME:=php-fpm}"
|
||||
: "${MAIL_BACKUP_MODE:=stop}" "${ENABLE_MAIL_HOME_DIRS:=true}"
|
||||
: "${MAIL_BACKUP_MODE:=stop}" "${ENABLE_MAIL_HOME_DIRS:=true}" "${ENABLE_MAIL_STORE:=true}"
|
||||
: "${MAIL_DIR:=/var/vmail}" "${POSTFIX_DIR:=/etc/postfix}" "${DOVECOT_DIR:=/etc/dovecot}"
|
||||
: "${RESTORE_STRICT_DELETE:=false}"
|
||||
# Preserve old space-separated lists; arrays additionally support spaces in paths.
|
||||
@@ -55,7 +55,7 @@ load_config() {
|
||||
read -r -a MAIL_HOME_EXCLUDES <<< "${MAIL_HOME_EXCLUDES[0]}"
|
||||
fi
|
||||
for v in ENABLE_UPLOAD ENABLE_REMOTE_RETENTION ENABLE_MAIL_REPORT ENABLE_WORDPRESS ENABLE_NEXTCLOUD \
|
||||
ENABLE_NEXTCLOUD_DATA ENABLE_GITEA ENABLE_MAIL ENABLE_DB_DUMPS ENABLE_NEXTCLOUD_MAINTENANCE \
|
||||
ENABLE_NEXTCLOUD_DATA ENABLE_GITEA ENABLE_MAIL ENABLE_MAIL_STORE ENABLE_DB_DUMPS ENABLE_NEXTCLOUD_MAINTENANCE \
|
||||
ENABLE_GITEA_SERVICE_STOP ENABLE_HTTPD_STOP ENABLE_PHPFPM_STOP ENABLE_MAIL_HOME_DIRS RESTORE_STRICT_DELETE; do
|
||||
[[ ${!v} == true || ${!v} == false ]] || die "$v must be true or false"
|
||||
done
|
||||
@@ -67,6 +67,10 @@ load_config() {
|
||||
[[ $ARCHIVE_PREFIX =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]] || die "Unsafe ARCHIVE_PREFIX"
|
||||
[[ $COMPRESSOR == zstd || $COMPRESSOR == gzip ]] || die "COMPRESSOR must be zstd or gzip"
|
||||
[[ $MAIL_BACKUP_MODE == stop || $MAIL_BACKUP_MODE == live ]] || die "MAIL_BACKUP_MODE must be stop or live"
|
||||
# A homes-only setup must still contain mailbox data, not just server config.
|
||||
if [[ $ENABLE_MAIL == true && $ENABLE_MAIL_STORE == false ]]; then
|
||||
[[ $ENABLE_MAIL_HOME_DIRS == true && ${#MAIL_HOME_DIRS[@]} -gt 0 ]] || die 'Mail without a central store requires enabled, nonempty MAIL_HOME_DIRS'
|
||||
fi
|
||||
if [[ -z ${DB_DUMP_BIN:-} ]]; then DB_DUMP_BIN=$(command -v mariadb-dump || command -v mysqldump || true); fi
|
||||
if [[ -z ${DB_CLIENT_BIN:-} ]]; then DB_CLIENT_BIN=$(command -v mariadb || command -v mysql || true); fi
|
||||
}
|
||||
@@ -78,7 +82,8 @@ guard_workspaces() {
|
||||
if [[ $ENABLE_NEXTCLOUD == true ]]; then sources+=("${NC_DIR:-}" "${NC_DATA_DIR:-}"); fi
|
||||
if [[ $ENABLE_GITEA == true ]]; then sources+=("$GITEA_DATA_DIR" "$GITEA_ETC_DIR" "${GITEA_REPOS_DIR:-}" "${GITEA_CUSTOM_DIR:-}"); fi
|
||||
if [[ $ENABLE_MAIL == true ]]; then
|
||||
sources+=("$MAIL_DIR" "$POSTFIX_DIR" "$DOVECOT_DIR")
|
||||
if [[ $ENABLE_MAIL_STORE == true ]]; then sources+=("$MAIL_DIR"); fi
|
||||
sources+=("$POSTFIX_DIR" "$DOVECOT_DIR")
|
||||
if [[ $ENABLE_MAIL_HOME_DIRS == true ]]; then sources+=("${MAIL_HOME_DIRS[@]}"); fi
|
||||
fi
|
||||
# Validate the WHOLE plan before mkdir/chmod touches any configured directory.
|
||||
@@ -149,7 +154,8 @@ build_plan() {
|
||||
add_db gitea GITEA
|
||||
fi
|
||||
if [[ $ENABLE_MAIL == true ]]; then
|
||||
add_source mail "$MAIL_DIR"; add_source postfix "$POSTFIX_DIR"; add_source dovecot "$DOVECOT_DIR"
|
||||
if [[ $ENABLE_MAIL_STORE == true ]]; then add_source mail "$MAIL_DIR"; fi
|
||||
add_source postfix "$POSTFIX_DIR"; add_source dovecot "$DOVECOT_DIR"
|
||||
if [[ $ENABLE_MAIL_HOME_DIRS == true ]]; then
|
||||
local i=0
|
||||
for p in "${MAIL_HOME_DIRS[@]}"; do add_source "mail-home-$i" "$p"; i=$((i+1)); done
|
||||
|
||||
@@ -440,6 +440,76 @@ QUIESCE_SERVICES=(nc-cron)
|
||||
self.assertEqual(json.loads(self.state.read_text())['services']['app-backup.timer'],'active')
|
||||
self.assertNotIn('systemctl stop',self.events.read_text())
|
||||
|
||||
def test_missing_mail_store_requires_explicit_resolution(self):
|
||||
canonical=self.configure_upgrade()
|
||||
with canonical.open('a') as f: f.write(f'MAIL_DIR="{self.src}/missing-mail"\n')
|
||||
original=canonical.read_bytes()
|
||||
package=self.build_package()
|
||||
result=subprocess.run(['bash',str(package),'--foreground'],env=self.env,capture_output=True,text=True,timeout=60)
|
||||
self.assertNotEqual(result.returncode,0)
|
||||
self.assertIn('--mail-dir DIR',result.stdout+result.stderr)
|
||||
self.assertEqual(canonical.read_bytes(),original)
|
||||
self.assertFalse((self.src/'missing-mail').exists())
|
||||
self.assertEqual(json.loads(self.state.read_text())['services']['app-backup.timer'],'active')
|
||||
self.assertNotIn('systemctl stop',self.events.read_text())
|
||||
|
||||
def test_package_mail_directory_override(self):
|
||||
canonical=self.configure_upgrade()
|
||||
with canonical.open('a') as f: f.write(f'MAIL_DIR="{self.src}/missing-mail"\n')
|
||||
package=self.build_package()
|
||||
result=subprocess.run(['bash',str(package),'--foreground','--mail-dir',str(self.src/'mail')],env=self.env,capture_output=True,text=True,timeout=120)
|
||||
self.assertEqual(result.returncode,0,result.stdout+result.stderr)
|
||||
self.assertIn('ENABLE_MAIL_STORE=true',canonical.read_text())
|
||||
run=next((self.base/'work/archives').glob('appbackup_*'))
|
||||
self.assertTrue((run/'mail.tar.zst').is_file())
|
||||
self.assertTrue((run/'UPLOADED').is_file())
|
||||
|
||||
def test_package_homes_only_keeps_mail_configs_and_service_stops(self):
|
||||
canonical=self.configure_upgrade()
|
||||
home=self.src/'johannes'
|
||||
for folder in ['cur','new','tmp']:
|
||||
(home/'Mail'/folder).mkdir(parents=True)
|
||||
mailbox=home/'Mail/cur/test-message:2,S'
|
||||
mailbox.write_bytes(b'Subject: retained mail\n\nMailbox content\n')
|
||||
mailbox.chmod(0o600)
|
||||
original=mailbox.read_bytes()
|
||||
with canonical.open('a') as f:
|
||||
f.write(f'MAIL_DIR="{self.src}/missing-mail"\nENABLE_MAIL_HOME_DIRS=true\nMAIL_HOME_DIRS=("{home}")\n')
|
||||
package=self.build_package()
|
||||
result=subprocess.run(['bash',str(package),'--foreground','--mail-homes-only'],env=self.env,capture_output=True,text=True,timeout=120)
|
||||
self.assertEqual(result.returncode,0,result.stdout+result.stderr)
|
||||
self.assertIn('ENABLE_MAIL_STORE=false',canonical.read_text())
|
||||
run=next((self.base/'work/archives').glob('appbackup_*'))
|
||||
for name in ['mail-home-0','postfix','dovecot']:
|
||||
self.assertTrue((run/f'{name}.tar.zst').is_file())
|
||||
self.assertFalse((run/'mail.tar.zst').exists())
|
||||
self.assertTrue((run/'UPLOADED').is_file())
|
||||
for unit in ['postfix','dovecot']:
|
||||
self.assertIn(f'systemctl stop {unit}',self.events.read_text())
|
||||
self.assertEqual(json.loads(self.state.read_text())['services'][unit],'active')
|
||||
mailbox.write_text('changed after backup')
|
||||
restored=subprocess.run(['bash',str(REPO/'usr-local-sbin/app-restore.sh'),'--config',str(canonical),
|
||||
'--local-run',str(run),'--components','mail-home-0','--apply','--yes'],
|
||||
env=self.env,capture_output=True,text=True,timeout=60)
|
||||
self.assertEqual(restored.returncode,0,restored.stdout+restored.stderr)
|
||||
self.assertEqual(mailbox.read_bytes(),original)
|
||||
self.assertEqual(mailbox.stat().st_mode & 0o777,0o600)
|
||||
|
||||
def test_homes_only_rejects_disabled_empty_and_missing_homes(self):
|
||||
canonical=self.configure_upgrade()
|
||||
original=canonical.read_text()
|
||||
package=self.build_package()
|
||||
for settings in ['ENABLE_MAIL_HOME_DIRS=false', 'ENABLE_MAIL_HOME_DIRS=true\nMAIL_HOME_DIRS=()',
|
||||
f'ENABLE_MAIL_HOME_DIRS=true\nMAIL_HOME_DIRS=("{self.src}/missing-home")']:
|
||||
with self.subTest(settings=settings):
|
||||
canonical.write_text(original+'\n'+settings+'\n')
|
||||
before=canonical.read_bytes()
|
||||
result=subprocess.run(['bash',str(package),'--foreground','--mail-homes-only'],env=self.env,capture_output=True,text=True,timeout=60)
|
||||
self.assertNotEqual(result.returncode,0,result.stdout+result.stderr)
|
||||
self.assertEqual(canonical.read_bytes(),before)
|
||||
self.assertEqual(json.loads(self.state.read_text())['services']['app-backup.timer'],'active')
|
||||
self.assertNotIn('systemctl stop',self.events.read_text())
|
||||
|
||||
def test_package_failed_backup_keeps_timer_stopped_and_rollback_available(self):
|
||||
canonical=self.configure_upgrade()
|
||||
original=canonical.read_bytes()
|
||||
|
||||
@@ -46,7 +46,7 @@ def build(output, version):
|
||||
set -Eeuo pipefail
|
||||
umask 077
|
||||
if [[ ${{1:-}} == --help ]]; then
|
||||
echo 'Usage: sudo bash app-backup-update-{version}.run [--foreground] [--config FILE] [--rclone-config FILE] [--enable-gitea | --no-gitea] [--cron-services UNIT,...] [--install-deps]'
|
||||
echo 'Usage: sudo bash app-backup-update-{version}.run [--foreground] [--config FILE] [--rclone-config FILE] [--enable-gitea | --no-gitea] [--cron-services UNIT,...] [--mail-dir DIR | --mail-homes-only] [--install-deps]'
|
||||
exit 0
|
||||
fi
|
||||
[[ $EUID == 0 ]] || {{ echo 'Bitte mit sudo bash starten.' >&2; exit 1; }}
|
||||
|
||||
@@ -15,6 +15,8 @@ UP_INSTALL_DEPS=false
|
||||
UP_GITEA=''
|
||||
UP_RCLONE=''
|
||||
UP_CRON=''
|
||||
UP_MAIL_DIR=''
|
||||
UP_MAIL_HOMES=false
|
||||
UP_STATE=''
|
||||
UP_WORKER=false
|
||||
while (( $# )); do
|
||||
@@ -26,11 +28,14 @@ while (( $# )); do
|
||||
--no-gitea) UP_GITEA=false; shift ;;
|
||||
--rclone-config) UP_RCLONE=${2:?Missing rclone configuration}; shift 2 ;;
|
||||
--cron-services) UP_CRON=${2:?Missing comma-separated systemd units}; shift 2 ;;
|
||||
--mail-dir) UP_MAIL_DIR=${2:?Missing mailbox directory}; shift 2 ;;
|
||||
--mail-homes-only) UP_MAIL_HOMES=true; shift ;;
|
||||
--worker) UP_WORKER=true; UP_STATE=${2:?Missing state}; shift 2 ;;
|
||||
--help) echo 'Usage: sudo bash update.sh [--foreground] [--config FILE] [--rclone-config FILE] [--enable-gitea | --no-gitea] [--cron-services UNIT,...] [--install-deps]'; exit 0 ;;
|
||||
--help) echo 'Usage: sudo bash update.sh [--foreground] [--config FILE] [--rclone-config FILE] [--enable-gitea | --no-gitea] [--cron-services UNIT,...] [--mail-dir DIR | --mail-homes-only] [--install-deps]'; exit 0 ;;
|
||||
*) die "Unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
[[ -z $UP_MAIL_DIR || $UP_MAIL_HOMES == false ]] || die 'Choose --mail-dir OR --mail-homes-only'
|
||||
[[ $EUID == 0 ]] || die 'Run with sudo bash'
|
||||
need systemctl; need flock
|
||||
# This lock excludes two coordinators, independently from ordinary backup jobs.
|
||||
@@ -46,6 +51,51 @@ prompt() {
|
||||
append_setting() { printf '%s=%q\n' "$1" "$2" >> "$UP_STATE/candidate.conf"; }
|
||||
discover() { python3 "$LIB_DIR/update-discover.py" "$@"; }
|
||||
|
||||
# Do not infer mailbox coverage from an existing directory. In particular,
|
||||
# creating an empty /var/vmail would make preflight pass without saving mail.
|
||||
prepare_mail() {
|
||||
local choice home
|
||||
if [[ $ENABLE_MAIL != true ]]; then
|
||||
[[ -z $UP_MAIL_DIR && $UP_MAIL_HOMES == false ]] || die 'Mail options require ENABLE_MAIL=true'
|
||||
return 0
|
||||
fi
|
||||
if [[ -n $UP_MAIL_DIR ]]; then
|
||||
MAIL_DIR=$UP_MAIL_DIR
|
||||
ENABLE_MAIL_STORE=true
|
||||
elif [[ $UP_MAIL_HOMES == true ]]; then
|
||||
ENABLE_MAIL_STORE=false
|
||||
elif [[ $ENABLE_MAIL_STORE == true && ! -d $MAIL_DIR ]]; then
|
||||
echo "Konfiguriertes zentrales Mailverzeichnis fehlt: $MAIL_DIR"
|
||||
printf 'Konfigurierte Mail-Homes: '; printf '%q ' "${MAIL_HOME_DIRS[@]}"; echo
|
||||
echo 'Absoluten Pfad zur tatsaechlichen zentralen Mailablage angeben.'
|
||||
echo 'Nur wenn ALLE Mailboxen in den konfigurierten Homes liegen: homes eingeben.'
|
||||
echo 'Bei unbekannter Ablage abbrechen; kein leeres Ersatzverzeichnis anlegen.'
|
||||
[[ -t 0 ]] || die 'Missing mail source: supply --mail-dir DIR or explicitly confirm coverage with --mail-homes-only'
|
||||
read -r -p 'Mailverzeichnis / homes / Enter zum Abbrechen: ' choice
|
||||
[[ -n $choice ]] || die 'Mail storage unresolved; installed package/config/timer unchanged'
|
||||
if [[ $choice == homes ]]; then
|
||||
printf 'Home-Excludes: '; printf '%q ' "${MAIL_HOME_EXCLUDES[@]}"; echo
|
||||
prompt 'Alle Mailboxen liegen in diesen Homes, ohne ausgeschlossene Pfade oder weitere Mounts? [j/N]' || die 'Mail coverage not confirmed'
|
||||
ENABLE_MAIL_STORE=false
|
||||
else MAIL_DIR=$choice; fi
|
||||
fi
|
||||
if [[ $ENABLE_MAIL_STORE == true ]]; then
|
||||
[[ $MAIL_DIR == /* ]] || die 'MAIL_DIR must be absolute'
|
||||
check_source_path "$MAIL_DIR"
|
||||
append_setting MAIL_DIR "$MAIL_DIR"
|
||||
echo "Zentrales Mailverzeichnis: $MAIL_DIR"
|
||||
else
|
||||
[[ $ENABLE_MAIL_HOME_DIRS == true && ${#MAIL_HOME_DIRS[@]} -gt 0 ]] || die 'Homes-only mail requires enabled, nonempty MAIL_HOME_DIRS'
|
||||
for home in "${MAIL_HOME_DIRS[@]}"; do
|
||||
[[ $home == /* ]] || die 'MAIL_HOME_DIRS must be absolute'
|
||||
check_source_path "$home"
|
||||
done
|
||||
printf 'Mail ausschliesslich in Homes (Excludes und Mountgrenzen gelten): '
|
||||
printf '%q ' "${MAIL_HOME_DIRS[@]}"; echo
|
||||
fi
|
||||
append_setting ENABLE_MAIL_STORE "$ENABLE_MAIL_STORE"
|
||||
}
|
||||
|
||||
if [[ $UP_WORKER == false ]]; then
|
||||
systemctl is-active --quiet app-backup-upgrade.service && die 'Upgrade service is already running'
|
||||
# Only install missing, small prerequisites when requested. Never upgrade PHP,
|
||||
@@ -87,6 +137,7 @@ if [[ $UP_WORKER == false ]]; then
|
||||
append_setting ENABLE_UPLOAD true
|
||||
append_setting ENABLE_NEXTCLOUD_MAINTENANCE true
|
||||
append_setting MAIL_BACKUP_MODE stop
|
||||
prepare_mail
|
||||
if [[ $ENABLE_WORDPRESS == true || $ENABLE_NEXTCLOUD == true ]]; then
|
||||
for pair in "ENABLE_HTTPD_STOP:$HTTPD_SERVICE_NAME" "ENABLE_PHPFPM_STOP:$PHPFPM_SERVICE_NAME"; do
|
||||
name=${pair%%:*}; unit=${pair#*:}
|
||||
|
||||
Reference in new issue
Block a user