Assistent mit Menuefuehrung, dazu zwei Haertungen
kerio_tui.py fuehrt durch Umbenennung und Uebernahme, ohne dass man Parameter kennen muss: Benutzer und Domains kommen zur Auswahl, verwaiste Mailbox-Verzeichnisse findet der Assistent selbst, und vor dem Ausfuehren zeigt er den gleichwertigen Kommandozeilenaufruf. Nur curses aus der Standardbibliothek, keine neue Abhaengigkeit. Beim Durchfahren ueber ein PTY kamen zwei Dinge heraus: keypad(True) loest die Pfeiltasten auf diesem Server nicht auf - getch() liefert 27, 91, 66 statt KEY_DOWN, wofuer ein unpassendes terminfo genuegt. Das fuehrende ESC haette als "zurueck" gegolten und den Assistenten bei jedem Pfeildruck verlassen. Er loest die Sequenzen jetzt selbst auf; ein einzelnes ESC wirkt weiterhin als Abbruch. Und: Bricht die Verbindung weg, waehrend Kerio gestoppt ist, stirbt das Script an SIGHUP und laesst den Dienst unten - genau so passiert, als der Testtreiber die Verbindung schloss. ServiceController.abgesichert() faengt SIGHUP, SIGTERM und SIGINT ab und startet den Dienst wieder, bevor der Abbruch durchgereicht wird. Gegen den Testserver geprueft: Signal mitten im Stopp-Fenster, Dienst kommt hoch, Daten unberuehrt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b9cbf65fac
commit
24070cba4e
+76
-68
@@ -373,35 +373,39 @@ def adopt_store(args: argparse.Namespace, config: UsersConfig,
|
||||
|
||||
print()
|
||||
was_running = service.is_running()
|
||||
service.stop()
|
||||
|
||||
done: list[tuple[Path, Path]] = []
|
||||
try:
|
||||
check_rename_targets(moves)
|
||||
done = apply_store_renames(moves)
|
||||
# Ab hier ist Kerio gestoppt. Der Waechter sorgt dafuer, dass der Dienst
|
||||
# auch dann wieder hochkommt, wenn das Script abgeschossen wird.
|
||||
with service.abgesichert(bool(was_running)):
|
||||
service.stop()
|
||||
try:
|
||||
check_rename_targets(moves)
|
||||
done = apply_store_renames(moves)
|
||||
|
||||
# Die uebernommene Mailbox traegt noch die Identitaet des alten
|
||||
# Benutzers. stats.usr legt Kerio selbst neu an, die Kontaktkarte
|
||||
# nicht - die muss umgeschrieben werden.
|
||||
for _, target in done:
|
||||
if ".abgeloest-" in target.name:
|
||||
continue
|
||||
if rewrite_personal_vcard(
|
||||
target,
|
||||
f"{args.old_login}@{domain}",
|
||||
f"{args.new_login}@{domain}",
|
||||
target_user["fullName"]):
|
||||
print(f" Kontaktkarte auf {target_user['fullName']} umgeschrieben")
|
||||
stats = target / "stats.usr"
|
||||
if stats.is_file():
|
||||
stats.rename(target / f"stats.usr.alt-{stamp}")
|
||||
print(" Beiseite: stats.usr (Kerio legt sie neu an)")
|
||||
except (AbortedError, OSError) as exc:
|
||||
print(f"\nFEHLER: {exc}", file=sys.stderr)
|
||||
revert_store_renames(done)
|
||||
if was_running:
|
||||
service.start()
|
||||
return 2
|
||||
# Die uebernommene Mailbox traegt noch die Identitaet des alten
|
||||
# Benutzers. stats.usr legt Kerio selbst neu an, die Kontaktkarte
|
||||
# nicht - die muss umgeschrieben werden.
|
||||
for _, target in done:
|
||||
if ".abgeloest-" in target.name:
|
||||
continue
|
||||
if rewrite_personal_vcard(
|
||||
target,
|
||||
f"{args.old_login}@{domain}",
|
||||
f"{args.new_login}@{domain}",
|
||||
target_user["fullName"]):
|
||||
print(f" Kontaktkarte auf {target_user['fullName']} "
|
||||
"umgeschrieben")
|
||||
stats = target / "stats.usr"
|
||||
if stats.is_file():
|
||||
stats.rename(target / f"stats.usr.alt-{stamp}")
|
||||
print(" Beiseite: stats.usr (Kerio legt sie neu an)")
|
||||
except (AbortedError, OSError) as exc:
|
||||
print(f"\nFEHLER: {exc}", file=sys.stderr)
|
||||
revert_store_renames(done)
|
||||
if was_running:
|
||||
service.start()
|
||||
return 2
|
||||
|
||||
if was_running:
|
||||
service.start()
|
||||
@@ -513,54 +517,58 @@ def rename_user(args: argparse.Namespace, config: UsersConfig,
|
||||
# -- Schreiben: users.cfg und Verzeichnisse in einem Stopp-Fenster ----
|
||||
print()
|
||||
was_running = service.is_running()
|
||||
service.stop()
|
||||
|
||||
done: list[tuple[Path, Path]] = []
|
||||
backup: Path | None = None
|
||||
try:
|
||||
# Nach dem Stoppen neu einlesen: Kerio hat users.cfg beim Beenden
|
||||
# aus dem Speicher zurueckgeschrieben, unsere Kopie ist veraltet.
|
||||
fresh = UsersConfig(config.path)
|
||||
backup = fresh.backup(BACKUP_SUFFIX)
|
||||
print(f" Backup: {backup}")
|
||||
# Ab hier ist Kerio gestoppt. Der Waechter sorgt dafuer, dass der Dienst
|
||||
# auch dann wieder hochkommt, wenn das Script abgeschossen wird - etwa
|
||||
# weil die SSH-Verbindung wegbricht.
|
||||
with service.abgesichert(bool(was_running)):
|
||||
service.stop()
|
||||
try:
|
||||
# Nach dem Stoppen neu einlesen: Kerio hat users.cfg beim Beenden
|
||||
# aus dem Speicher zurueckgeschrieben, unsere Kopie ist veraltet.
|
||||
fresh = UsersConfig(config.path)
|
||||
backup = fresh.backup(BACKUP_SUFFIX)
|
||||
print(f" Backup: {backup}")
|
||||
|
||||
changed = fresh.rename_user(
|
||||
old_login, new_login, domain,
|
||||
args.full_name if needs_fullname else None)
|
||||
if changed == 0:
|
||||
raise AbortedError(
|
||||
"Kein passender Eintrag in users.cfg gefunden - nichts geaendert."
|
||||
)
|
||||
fresh.save()
|
||||
print(f" users.cfg: {changed} Eintraege angepasst "
|
||||
f"(Listen: User, UserAdditionalData)")
|
||||
changed = fresh.rename_user(
|
||||
old_login, new_login, domain,
|
||||
args.full_name if needs_fullname else None)
|
||||
if changed == 0:
|
||||
raise AbortedError(
|
||||
"Kein passender Eintrag in users.cfg gefunden - nichts geaendert."
|
||||
)
|
||||
fresh.save()
|
||||
print(f" users.cfg: {changed} Eintraege angepasst "
|
||||
f"(Listen: User, UserAdditionalData)")
|
||||
|
||||
if renames:
|
||||
# Zielpruefung wiederholen: der Zustand kann sich seit der
|
||||
# Vorpruefung geaendert haben.
|
||||
check_rename_targets(renames)
|
||||
done = apply_store_renames(renames)
|
||||
if renames:
|
||||
# Zielpruefung wiederholen: der Zustand kann sich seit der
|
||||
# Vorpruefung geaendert haben.
|
||||
check_rename_targets(renames)
|
||||
done = apply_store_renames(renames)
|
||||
|
||||
# Die Kontaktkarte traegt noch die alte Adresse - Kerio zieht dort
|
||||
# nur den Namen nach, nicht die Adresse.
|
||||
display_name = args.full_name if needs_fullname else user["fullName"]
|
||||
for _, target in done:
|
||||
if rewrite_personal_vcard(target, f"{old_login}@{domain}",
|
||||
f"{new_login}@{domain}", display_name):
|
||||
print(" Kontaktkarte auf die neue Adresse umgeschrieben")
|
||||
except (AbortedError, OSError) as exc:
|
||||
print(f"\nFEHLER: {exc}", file=sys.stderr)
|
||||
revert_store_renames(done)
|
||||
if backup and backup.is_file():
|
||||
try:
|
||||
shutil.copy2(backup, config.path)
|
||||
print(f" users.cfg aus {backup} zurueckgesetzt.", file=sys.stderr)
|
||||
except OSError as restore_exc:
|
||||
print(f" WARNUNG: Zuruecksetzen von users.cfg fehlgeschlagen: "
|
||||
f"{restore_exc}", file=sys.stderr)
|
||||
if was_running:
|
||||
service.start()
|
||||
return 2
|
||||
# Die Kontaktkarte traegt noch die alte Adresse - Kerio zieht dort
|
||||
# nur den Namen nach, nicht die Adresse.
|
||||
display_name = args.full_name if needs_fullname else user["fullName"]
|
||||
for _, target in done:
|
||||
if rewrite_personal_vcard(target, f"{old_login}@{domain}",
|
||||
f"{new_login}@{domain}", display_name):
|
||||
print(" Kontaktkarte auf die neue Adresse umgeschrieben")
|
||||
except (AbortedError, OSError) as exc:
|
||||
print(f"\nFEHLER: {exc}", file=sys.stderr)
|
||||
revert_store_renames(done)
|
||||
if backup and backup.is_file():
|
||||
try:
|
||||
shutil.copy2(backup, config.path)
|
||||
print(f" users.cfg aus {backup} zurueckgesetzt.", file=sys.stderr)
|
||||
except OSError as restore_exc:
|
||||
print(f" WARNUNG: Zuruecksetzen von users.cfg fehlgeschlagen: "
|
||||
f"{restore_exc}", file=sys.stderr)
|
||||
if was_running:
|
||||
service.start()
|
||||
return 2
|
||||
|
||||
if was_running:
|
||||
service.start()
|
||||
|
||||
Reference in New Issue
Block a user