new script vor encryption manager and add combobox fo encrypt profiles part two

This commit is contained in:
2025-09-13 18:40:19 +02:00
parent 41d63743c1
commit 9b9b0743a8
3 changed files with 101 additions and 32 deletions

View File

@@ -158,6 +158,24 @@ do_resize() {
log "Resize and remount complete."
}
do_unmount_all() {
log "Unmounting all provided pairs."
for pair in "$@"; do
# Split the pair 'mapper:mount_point'
MAPPER_NAME="${pair%%:*}"
MOUNT_POINT="${pair#*:}"
log "Unmounting $MOUNT_POINT ($MAPPER_NAME)"
if mountpoint -q "$MOUNT_POINT"; then
chown root:root "$MOUNT_POINT"
umount "$MOUNT_POINT"
fi
if [ -e "/dev/mapper/$MAPPER_NAME" ]; then
cryptsetup luksClose "$MAPPER_NAME"
fi
done
log "Bulk unmount complete."
}
# --- Main Command Dispatcher ---
case "$COMMAND" in
@@ -173,6 +191,9 @@ case "$COMMAND" in
resize)
do_resize "$@"
;;
unmount_all)
do_unmount_all "$@"
;;
*)
log "Unknown command: $COMMAND"
exit 1

View File

@@ -310,14 +310,42 @@ class EncryptionManager:
return success
def unmount_all(self) -> bool:
self.logger.log(f"Unmounting all: {self.mounted_destinations}")
all_unmounted_successfully = True
# Create a copy of the set to avoid issues with modifying it while iterating
if not self.mounted_destinations:
return True
self.logger.log(f"Bulk unmounting: {self.mounted_destinations}")
pairs_to_unmount = []
for base_path, profile_name in list(self.mounted_destinations):
if not self.unmount_and_reset_owner(
base_path, profile_name, force_unmap=True):
all_unmounted_successfully = False
return all_unmounted_successfully
username = os.path.basename(base_path.rstrip('/'))
mapper_name = f"pybackup_luks_{username}_{profile_name}"
mount_point = self.get_mount_point(base_path, profile_name)
pairs_to_unmount.append(f'{mapper_name}:{mount_point}')
helper_path = os.path.join(os.path.dirname(__file__), 'encryption_helper.sh')
# This command doesn't need a LUKS password, but pkexec might need the user's password.
password = None
# Build the command string with quoted arguments
script = f'"{helper_path}" unmount_all {" ".join(f"\"{pair}\"" for pair in pairs_to_unmount)}'
success = self._execute_as_root(script, password)
if success:
# Clear all locks and destinations on success
for base_path, profile_name in list(self.mounted_destinations):
username = os.path.basename(base_path.rstrip('/'))
mapper_name = f"pybackup_luks_{username}_{profile_name}"
self.remove_from_lock_file(mapper_name)
self.mounted_destinations.clear()
self.logger.log("Successfully unmounted all profiles.")
if self.app and hasattr(self.app, 'header_frame'):
self.app.header_frame.refresh_status()
else:
self.logger.log("Failed to unmount all profiles.")
return success
def _get_chown_ids(self, is_system: bool) -> Tuple[Optional[int], Optional[int]]:
if not is_system:

View File

@@ -15,21 +15,18 @@ class HeaderFrame(tk.Frame):
# Configure grid weights for internal layout
self.columnconfigure(1, weight=1) # Make the middle column expand
self.rowconfigure(0, weight=1) # Make the top row expand
# Left side: Icon and Main Title/Subtitle
left_frame = tk.Frame(self, bg="#455A64")
left_frame.grid(row=0, column=0, rowspan=2, sticky="nsew")
left_frame.columnconfigure(0, weight=1)
left_frame.rowconfigure(0, weight=1)
left_frame.grid(row=0, column=0, rowspan=2, sticky="ns")
icon_label = tk.Label(
left_frame,
image=self.image_manager.get_icon(
"backup_extralarge"), # Using a generic backup icon
"backup_extralarge"),
bg="#455A64",
)
icon_label.grid(row=0, column=0, sticky="e", padx=10, pady=5)
icon_label.pack(side=tk.LEFT, padx=10, pady=5)
title_label = tk.Label(
self,
@@ -48,58 +45,81 @@ class HeaderFrame(tk.Frame):
fg="#bdc3c7",
bg="#455A64",
)
subtitle_label.grid(row=1, column=1, sticky="w",
subtitle_label.grid(row=1, column=1, sticky="nw",
padx=(5, 20), pady=(0, 10))
# Right side: Keyring status
# Right side: Status labels
right_frame = tk.Frame(self, bg="#455A64")
right_frame.grid(row=0, column=2, rowspan=2, sticky="nsew")
right_frame.columnconfigure(0, weight=1)
right_frame.rowconfigure(0, weight=1)
right_frame.grid(row=0, column=2, rowspan=2, sticky="nse", padx=10, pady=5)
self.keyring_status_label = tk.Label(
self.key_status_label = tk.Label(
right_frame,
text="",
font=("Helvetica", 10, "bold"),
bg="#455A64",
)
self.keyring_status_label.grid(
row=0, column=0, sticky="ne", padx=(10, 10), pady=(10, 0))
self.key_status_label.pack(anchor="e")
self.mount_status_label = tk.Label(
right_frame,
text="",
font=("Helvetica", 10, "bold"),
bg="#455A64",
)
self.mount_status_label.pack(anchor="e")
self.refresh_status()
def refresh_status(self):
"""Checks the mount status of all encrypted profiles and updates the label."""
"""Checks key and mount status of all encrypted profiles and updates the labels."""
app_logger.log("HeaderFrame: Refreshing status...")
dest_path = self.app.destination_path
if not dest_path or not os.path.isdir(dest_path):
self.keyring_status_label.config(text="")
self.key_status_label.config(text="")
self.mount_status_label.config(text="")
return
# Get all profile info without triggering a mount
# --- Key Status Logic ---
# This checks the key for the destination's root user, as a general indicator.
username = os.path.basename(dest_path.rstrip('/'))
key_in_keyring = self.encryption_manager.is_key_in_keyring(username)
# A keyfile for a specific profile is not checked here, we check the general one.
key_file_exists = os.path.exists(
self.encryption_manager.get_key_file_path(dest_path, username))
key_status_text = ""
if key_in_keyring:
key_status_text = "Key: In Keyring"
elif key_file_exists:
key_status_text = "Key: File Available"
else:
key_status_text = "Key: Not Available"
self.key_status_label.config(text=key_status_text, fg="#bdc3c7")
# --- Mount Status Logic ---
backup_data = self.app.backup_manager.list_all_backups(dest_path)
encrypted_profiles = backup_data.get("encrypted_profiles", {})
if not encrypted_profiles:
self.keyring_status_label.config(text="")
self.mount_status_label.config(text="")
return
total_count = len(encrypted_profiles)
mounted_count = sum(
1 for profile in encrypted_profiles.values() if profile['is_mounted'])
status_text = f"Encrypted: {mounted_count}/{total_count} Mounted"
mount_status_text = f"Mounted: {mounted_count}/{total_count}"
fg_color = ""
if mounted_count == total_count:
fg_color = "#6bbbff" # LightBlue (All Mounted)
fg_color = "#6bbbff" # Bright Blue (All Mounted)
elif mounted_count > 0:
fg_color = "#eb7f11" # Orange (Partially Mounted)
fg_color = "#FFA500" # Bright Orange (Partially Mounted)
else:
fg_color = "#E8740C" # A different Orange/Red for None Mounted
fg_color = "#FF4500" # Bright OrangeRed (None Mounted)
self.keyring_status_label.config(
text=status_text,
self.mount_status_label.config(
text=mount_status_text,
fg=fg_color
)
app_logger.log("HeaderFrame: Status refresh complete.")
app_logger.log("HeaderFrame: Status refresh complete.")