Support home-based mailboxes during automated upgrades

This commit is contained in:
admin committed 2026-09-10 14:31:32 +02:00
1 parent 8e6c9a058f
commit 56fbcdad74
8 files changed
+194 -9

No files matched your search

+14 -2
View File
@@ -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.
+3
View File
@@ -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
View File
@@ -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
+3
View File
@@ -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
View File
@@ -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
+70
View File
@@ -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()
+1 -1
View File
@@ -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; }}
+52 -1
View File
@@ -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#*:}