add functions in bash skript
This commit is contained in:
@@ -176,6 +176,21 @@ do_unmount_all() {
|
||||
log "Bulk unmount complete."
|
||||
}
|
||||
|
||||
do_mount_all() {
|
||||
log "Mounting all provided profiles."
|
||||
# Don't exit on error inside the loop
|
||||
set +e
|
||||
for profile_info in "$@"; do
|
||||
# Split the info string container_path:mapper_name:mount_point:uid:gid
|
||||
IFS=':' read -r container_path mapper_name mount_point uid gid <<< "$profile_info"
|
||||
log "Attempting to mount $container_path"
|
||||
# Call do_mount with the parsed arguments. Note: keyfile not supported in bulk mount.
|
||||
do_mount "$container_path" "$mapper_name" "$mount_point" "$uid" "$gid" ""
|
||||
done
|
||||
set -e
|
||||
log "Bulk mount process complete."
|
||||
}
|
||||
|
||||
|
||||
# --- Main Command Dispatcher ---
|
||||
case "$COMMAND" in
|
||||
@@ -194,10 +209,13 @@ case "$COMMAND" in
|
||||
unmount_all)
|
||||
do_unmount_all "$@"
|
||||
;;
|
||||
mount_all)
|
||||
do_mount_all "$@"
|
||||
;;
|
||||
*)
|
||||
log "Unknown command: $COMMAND"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
exit 0
|
||||
|
@@ -347,6 +347,45 @@ class EncryptionManager:
|
||||
|
||||
return success
|
||||
|
||||
def mount_all_profiles(self, profiles: list, password: str) -> bool:
|
||||
"""Attempts to mount a list of encrypted profiles using a single password."""
|
||||
if not profiles:
|
||||
return True
|
||||
|
||||
self.logger.log(f"Attempting to bulk mount {len(profiles)} profiles.")
|
||||
|
||||
profile_info_strings = []
|
||||
for profile in profiles:
|
||||
# Create the info string: container_path:mapper_name:mount_point:uid:gid
|
||||
username = os.path.basename(profile['base_dest_path'].rstrip('/'))
|
||||
mapper_name = f"pybackup_luks_{username}_{profile['profile_name']}"
|
||||
container_path = self.get_container_path(profile['base_dest_path'], profile['profile_name'])
|
||||
mount_point = self.get_mount_point(profile['base_dest_path'], profile['profile_name'])
|
||||
uid, gid = self._get_chown_ids(profile['is_system'])
|
||||
# Keyfiles are not supported in bulk mount, as it relies on a single shared password.
|
||||
info_str = f'{container_path}:{mapper_name}:{mount_point}:{uid or ""}:{gid or ""}'
|
||||
profile_info_strings.append(info_str)
|
||||
|
||||
helper_path = os.path.join(os.path.dirname(__file__), 'encryption_helper.sh')
|
||||
script = f'"{helper_path}" mount_all {" ".join(f"\"{info}\"" for info in profile_info_strings)}'
|
||||
|
||||
success = self._execute_as_root(script, password)
|
||||
|
||||
if success:
|
||||
# After a bulk attempt, we need to refresh the internal state for all of them
|
||||
for profile in profiles:
|
||||
if os.path.ismount(self.get_mount_point(profile['base_dest_path'], profile['profile_name'])):
|
||||
self.mounted_destinations.add((profile['base_dest_path'], profile['profile_name']))
|
||||
self.add_to_lock_file(profile['base_dest_path'], profile['profile_name'], f"pybackup_luks_{os.path.basename(profile['base_dest_path'].rstrip('/'))}_{profile['profile_name']}")
|
||||
self.logger.log("Bulk mount script executed.")
|
||||
else:
|
||||
self.logger.log("Bulk mount script failed to execute.")
|
||||
|
||||
if self.app and hasattr(self.app, 'header_frame'):
|
||||
self.app.header_frame.refresh_status()
|
||||
|
||||
return success
|
||||
|
||||
def _get_chown_ids(self, is_system: bool) -> Tuple[Optional[int], Optional[int]]:
|
||||
if not is_system:
|
||||
try:
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
import os
|
||||
import inspect
|
||||
from shared_libs.animated_icon import AnimatedIcon
|
||||
from core.pbp_app_config import Msg
|
||||
from pyimage_ui.system_backup_content_frame import SystemBackupContentFrame
|
||||
@@ -82,41 +83,84 @@ class BackupContentFrame(ttk.Frame):
|
||||
|
||||
action_button_frame = ttk.Frame(self, padding=10)
|
||||
action_button_frame.grid(row=2, column=0, sticky="ew")
|
||||
action_button_frame.columnconfigure(1, weight=1) # Make middle column expandable
|
||||
|
||||
# --- Mount Controls ---
|
||||
self.mount_labelframe = ttk.LabelFrame(action_button_frame, text="Mount Encrypt")
|
||||
|
||||
# Blue border frame inside the LabelFrame
|
||||
mount_border_frame = tk.Frame(self.mount_labelframe, background="#0078D7")
|
||||
mount_border_frame.pack(padx=5, pady=5)
|
||||
|
||||
# Content frame inside the border frame
|
||||
theme_bg_color = self.winfo_toplevel().style.lookup('TFrame', 'background')
|
||||
mount_content_frame = tk.Frame(mount_border_frame, background=theme_bg_color)
|
||||
mount_content_frame.pack(padx=2, pady=2)
|
||||
|
||||
mount_label = ttk.Label(mount_content_frame, text="Profile:")
|
||||
mount_label.grid(row=0, column=0, sticky="w", padx=5, pady=5)
|
||||
|
||||
self.mount_all_button = ttk.Button(
|
||||
mount_content_frame, text="Mount All", command=self._mount_all_profiles)
|
||||
self.mount_all_button.grid(row=0, column=1, sticky="ew", padx=5, pady=5)
|
||||
|
||||
self.profile_combobox = ttk.Combobox(
|
||||
mount_content_frame, state="readonly", width=20)
|
||||
self.profile_combobox.grid(row=1, column=0, sticky="w", padx=5, pady=5)
|
||||
|
||||
self.mount_button = ttk.Button(
|
||||
mount_content_frame, text="Mount", command=self._mount_selected_profile)
|
||||
self.mount_button.grid(row=1, column=1, sticky="ew", padx=5, pady=5)
|
||||
|
||||
# --- Action Buttons ---
|
||||
self.action_buttons_container = ttk.Frame(action_button_frame)
|
||||
|
||||
self.restore_button = ttk.Button(
|
||||
action_button_frame, text=Msg.STR["restore"], command=self._restore_selected, state="disabled")
|
||||
self.action_buttons_container, text=Msg.STR["restore"], command=self._restore_selected, state="disabled", style="Success.TButton")
|
||||
self.restore_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
self.delete_button = ttk.Button(
|
||||
action_button_frame, text=Msg.STR["delete"], command=self._delete_selected, state="disabled")
|
||||
self.action_buttons_container, text=Msg.STR["delete"], command=self._delete_selected, state="disabled", style="Danger.TButton")
|
||||
self.delete_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
self.edit_comment_button = ttk.Button(
|
||||
action_button_frame, text=Msg.STR["comment"], command=self._edit_comment, state="disabled")
|
||||
self.action_buttons_container, text=Msg.STR["comment"], command=self._edit_comment, state="disabled")
|
||||
self.edit_comment_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# Mount controls for encrypted profiles, now at the bottom
|
||||
self.mount_frame = ttk.Frame(action_button_frame)
|
||||
self.mount_frame.pack(side=tk.RIGHT, padx=10)
|
||||
mount_label = ttk.Label(self.mount_frame, text="Encrypted Profile:")
|
||||
mount_label.pack(side=tk.LEFT, padx=(0, 5))
|
||||
self.profile_combobox = ttk.Combobox(
|
||||
self.mount_frame, state="readonly", width=20)
|
||||
self.profile_combobox.pack(side=tk.LEFT)
|
||||
self.mount_button = ttk.Button(
|
||||
self.mount_frame, text="Mount", command=self._mount_selected_profile)
|
||||
self.mount_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
self._switch_view(0)
|
||||
|
||||
def _mount_selected_profile(self):
|
||||
profile_name = self.profile_combobox.get()
|
||||
if not profile_name:
|
||||
return
|
||||
self._mount_profile(profile_name, single_mount=True)
|
||||
|
||||
def _mount_all_profiles(self):
|
||||
unmounted_profiles = [{'profile_name': name, **data} for name, data in self.encrypted_profiles.items() if not data['is_mounted']]
|
||||
if not unmounted_profiles:
|
||||
MessageDialog(message_type="info", title="Info", text="All encrypted profiles are already mounted.").show()
|
||||
return
|
||||
|
||||
# Get password once. The username for the password prompt is taken from the destination path, which is common.
|
||||
username = os.path.basename(self.base_backup_path.rstrip('/'))
|
||||
password = self.backup_manager.encryption_manager.get_password(username, confirm=False)
|
||||
|
||||
if not password:
|
||||
app_logger.log("Mount All cancelled: No password provided.")
|
||||
return
|
||||
|
||||
self.app.config(cursor="watch")
|
||||
self.update()
|
||||
|
||||
self.backup_manager.encryption_manager.mount_all_profiles(unmounted_profiles, password)
|
||||
|
||||
self.app.config(cursor="")
|
||||
self.show(self.base_backup_path, self.current_view_index)
|
||||
|
||||
def _mount_profile(self, profile_name: str, single_mount: bool = False) -> bool:
|
||||
profile_data = self.encrypted_profiles.get(profile_name)
|
||||
if not profile_data:
|
||||
return
|
||||
return False
|
||||
|
||||
self.app.config(cursor="watch")
|
||||
self.update()
|
||||
@@ -129,12 +173,14 @@ class BackupContentFrame(ttk.Frame):
|
||||
|
||||
self.app.config(cursor="")
|
||||
|
||||
if success:
|
||||
# Refresh the view
|
||||
self.show(self.base_backup_path, self.current_view_index)
|
||||
else:
|
||||
if not success:
|
||||
MessageDialog(message_type="error", title="Error",
|
||||
text=f"Failed to mount profile '{profile_name}'.\nPlease check the password and try again.").show()
|
||||
|
||||
if single_mount:
|
||||
self.show(self.base_backup_path, self.current_view_index)
|
||||
|
||||
return success
|
||||
|
||||
def update_button_state(self, is_selected):
|
||||
self.restore_button.config(
|
||||
@@ -190,7 +236,8 @@ class BackupContentFrame(ttk.Frame):
|
||||
f"Backup path {pybackup_dir} does not exist or is not a directory.")
|
||||
self.system_backups_frame.show(backup_path, [])
|
||||
self.user_backups_frame.show(backup_path, [])
|
||||
self.mount_frame.pack_forget()
|
||||
self.mount_labelframe.grid_remove()
|
||||
self.action_buttons_container.grid_remove()
|
||||
return
|
||||
|
||||
backup_data = self.backup_manager.list_all_backups(backup_path)
|
||||
@@ -205,11 +252,13 @@ class BackupContentFrame(ttk.Frame):
|
||||
]
|
||||
|
||||
if unmounted_profiles:
|
||||
self.mount_labelframe.grid(row=0, column=0, sticky="w")
|
||||
self.profile_combobox['values'] = unmounted_profiles
|
||||
self.profile_combobox.set(unmounted_profiles[0])
|
||||
self.mount_frame.pack(side=tk.RIGHT, padx=10)
|
||||
else:
|
||||
self.mount_frame.pack_forget()
|
||||
self.mount_labelframe.grid_remove()
|
||||
|
||||
self.action_buttons_container.grid(row=0, column=1, sticky="") # Center the buttons
|
||||
|
||||
self.after(10, lambda: self._switch_view(initial_tab_index))
|
||||
|
||||
@@ -225,4 +274,4 @@ class BackupContentFrame(ttk.Frame):
|
||||
def hide_deletion_status(self):
|
||||
app_logger.log("Hiding deletion status text.")
|
||||
self.deletion_status_label.config(text="")
|
||||
self.deletion_animated_icon.stop("DISABLE")
|
||||
self.deletion_animated_icon.stop("DISABLE")
|
Reference in New Issue
Block a user