add mount/mount all button

This commit is contained in:
2025-09-14 01:30:51 +02:00
parent 507c1554ee
commit 7decaf46ef
3 changed files with 52 additions and 193 deletions

View File

@@ -231,7 +231,8 @@ class EncryptionManager:
mapper_name = f"pybackup_luks_{username}_{profile_name}"
uid, gid = self._get_chown_ids(is_system)
helper_path = os.path.join(os.path.dirname(__file__), 'encryption_helper.sh')
helper_path = os.path.join(os.path.dirname(
__file__), 'encryption_helper.sh')
script = f'"{helper_path}" create "{container_path}" "{mount_point}" "{int(size_gb)}" "{mapper_name}" "{uid or ''}" "{gid or ''}"'
@@ -261,7 +262,8 @@ class EncryptionManager:
mapper_name = f"pybackup_luks_{username}_{profile_name}"
uid, gid = self._get_chown_ids(is_system)
helper_path = os.path.join(os.path.dirname(__file__), 'encryption_helper.sh')
helper_path = os.path.join(os.path.dirname(
__file__), 'encryption_helper.sh')
script = f'"{helper_path}" mount "{container_path}" "{mapper_name}" "{mount_point}" "{uid or ''}" "{gid or ''}" "{key_file_path or ''}"'
@@ -287,7 +289,8 @@ class EncryptionManager:
self.logger.log(
f"Unmounting and resetting owner for {base_dest_path}/{profile_name}")
helper_path = os.path.join(os.path.dirname(__file__), 'encryption_helper.sh')
helper_path = os.path.join(os.path.dirname(
__file__), 'encryption_helper.sh')
# The unmount command doesn't need the LUKS password, but pkexec might need the user's password.
password = self.password_cache.get(username)
@@ -322,7 +325,8 @@ class EncryptionManager:
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')
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
@@ -359,14 +363,17 @@ class EncryptionManager:
# 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'])
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')
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)
@@ -375,8 +382,10 @@ class EncryptionManager:
# 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.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.")
@@ -399,8 +408,10 @@ class EncryptionManager:
try:
command = ['pkexec', '/bin/bash', '-c', script_content]
self.logger.log(f"Executing privileged command: {' '.join(command)}")
self.logger.log(f"Script content passed to bash -c: {script_content}")
self.logger.log(
f"Executing privileged command: {' '.join(command)}")
self.logger.log(
f"Script content passed to bash -c: {script_content}")
process = subprocess.Popen(
command,

View File

@@ -82,28 +82,33 @@ class BackupContentFrame(ttk.Frame):
self.user_backups_frame.grid(row=0, column=0, sticky=tk.NSEW)
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
action_button_frame.grid(row=2, column=0, sticky="nsew")
action_button_frame.columnconfigure(
1, weight=1) # Make middle column expandable
# --- Mount Controls ---
self.mount_labelframe = ttk.LabelFrame(action_button_frame, text="Mount Encrypt")
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)
mount_border_frame = tk.Frame(
self.mount_labelframe, background="#6bbbff")
mount_border_frame.pack(padx=10, pady=10)
# 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 = 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 = 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.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)
@@ -114,7 +119,7 @@ class BackupContentFrame(ttk.Frame):
# --- Action Buttons ---
self.action_buttons_container = ttk.Frame(action_button_frame)
self.restore_button = ttk.Button(
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)
@@ -136,14 +141,17 @@ class BackupContentFrame(ttk.Frame):
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']]
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()
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)
password = self.backup_manager.encryption_manager.get_password(
username, confirm=False)
if not password:
app_logger.log("Mount All cancelled: No password provided.")
@@ -152,8 +160,9 @@ class BackupContentFrame(ttk.Frame):
self.app.config(cursor="watch")
self.update()
self.backup_manager.encryption_manager.mount_all_profiles(unmounted_profiles, password)
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)
@@ -176,7 +185,7 @@ class BackupContentFrame(ttk.Frame):
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)
@@ -257,8 +266,9 @@ class BackupContentFrame(ttk.Frame):
self.profile_combobox.set(unmounted_profiles[0])
else:
self.mount_labelframe.grid_remove()
self.action_buttons_container.grid(row=0, column=1, sticky="") # Center the buttons
self.action_buttons_container.grid(
row=0, column=1, sticky="") # Center the buttons
self.after(10, lambda: self._switch_view(initial_tab_index))
@@ -274,4 +284,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")

162
python.py
View File

@@ -1,162 +0,0 @@
#!/usr/bin/python3
def _create_install_script(self, project_key):
"""Create installation script based on project"""
if project_key == "wirepy":
return self._create_wirepy_install_script()
elif project_key == "logviewer":
return self._create_logviewer_install_script()
else:
raise Exception(f"{LocaleStrings.MSGI['unknow_project']}{project_key}")
def _create_wirepy_install_script(self):
gpg_checker = self.check_gpg()
result_appimage = self.check_appimage()
if not gpg_checker:
print(gpg_checker)
return
if result_appimage is True or result_appimage is None:
return
detected_os = Detector.get_os()
if detected_os == "Arch Linux":
result_unzip = Detector.get_unzip()
result_wget = Detector.get_wget()
result_requests = Detector.get_requests()
else:
result_unzip = None
result_wget = None
result_requests = None
"""Create Wire-Py installation script"""
script = f"""#!/bin/bash
set -e
if [ "{result_appimage}" = "False" ]; then
cp -f /tmp/portinstaller/lxtools_installer /usr/local/bin/lxtools_installer
fi
if [ "{detected_os}" = "Arch Linux" ]; then
if [ "{result_unzip}" = "False" ]; then
pacman -S --noconfirm unzip
fi
if [ "{result_wget}" = "False" ]; then
pacman -S --noconfirm wget
fi
if [ "{result_requests}" = "False" ]; then
pacman -S --noconfirm python-requests
fi
fi
{LXToolsAppConfig.TKINTER_INSTALL_COMMANDS[detected_os]} 2>&1 | grep -v "apt does not have a stable CLI interface"
# Create necessary directories
mkdir -p {LXToolsAppConfig.SHARED_LIBS_DESTINATION[detected_os]}
mkdir -p /usr/share/icons/lx-icons
mkdir -p /usr/share/locale/de/LC_MESSAGES
mkdir -p /usr/share/applications
mkdir -p /usr/local/etc/ssl
mkdir -p /usr/share/polkit-1/actions
mkdir -p /usr/share/TK-Themes
# Download and extract Wire-Py
cd /tmp
rm -rf wirepy_install
mkdir wirepy_install
cd wirepy_install
echo "Downloading Wire-Py..."
wget -q "{LXToolsAppConfig.WIREPY_URL}" -O wirepy.zip
unzip -q wirepy.zip
WIREPY_DIR=$(find . -name "wire-py" -type d | head -1)
echo "Downloading shared libraries..."
wget -q "{LXToolsAppConfig.SHARED_LIBS_URL}" -O shared.zip
unzip -q shared.zip
SHARED_DIR=$(find . -name "shared_libs" -type d | head -1)
# Install Wire-Py files
echo "Installing Wire-Py executables..."
for file in wirepy.py start_wg.py ssl_encrypt.py ssl_decrypt.py match_found.py tunnel.py; do
if [ -f "$WIREPY_DIR/$file" ]; then
cp -f "$WIREPY_DIR/$file" /usr/local/bin/
chmod 755 /usr/local/bin/$file
echo "Installed $file"
fi
done
# Install config
if [ -f "$WIREPY_DIR/wp_app_config.py" ]; then
cp -f "$WIREPY_DIR/wp_app_config.py" {LXToolsAppConfig.SHARED_LIBS_DESTINATION[detected_os]}/
echo "Installed wp_app_config.py"
fi
# Install shared libraries
echo "Installing shared libraries..."
for file in common_tools.py message.py file_and_dir_ensure.py gitea.py __init__.py logview_app_config.py; do
if [ -f "$SHARED_DIR/$file" ]; then
cp -f "$SHARED_DIR/$file" {LXToolsAppConfig.SHARED_LIBS_DESTINATION[detected_os]}/
echo "Installed shared lib: $file"
fi
done
# Install LogViewer executable
if [ -f "$SHARED_DIR/logviewer.py" ]; then
cp -f "$SHARED_DIR/logviewer.py" {LXToolsAppConfig.SHARED_LIBS_DESTINATION[detected_os]}/
chmod 755 {LXToolsAppConfig.SHARED_LIBS_DESTINATION[detected_os]}/logviewer.py
echo "Installed logviewer.py (executable)"
fi
# Install icons
if [ -d "$WIREPY_DIR/lx-icons" ]; then
echo "Installing icons..."
cp -rf "$WIREPY_DIR/lx-icons"/* /usr/share/icons/lx-icons/
fi
# Install TK-Themes
if [ -d "$WIREPY_DIR/TK-Themes" ]; then
echo "Installing TK-Themes..."
cp -rf "$WIREPY_DIR/TK-Themes"/* /usr/share/TK-Themes/
fi
# Install desktop file
if [ -f "$WIREPY_DIR/Wire-Py.desktop" ]; then
cp -f "$WIREPY_DIR/Wire-Py.desktop" /usr/share/applications/
echo "Installed desktop file"
fi
# Install language files
if [ -d "$WIREPY_DIR/languages/de" ]; then
echo "Installing language files..."
cp -f "$WIREPY_DIR/languages/de"/*.mo /usr/share/locale/de/LC_MESSAGES/ 2>/dev/null || true
fi
# Install policy file
if [ -f "$WIREPY_DIR/org.sslcrypt.policy" ]; then
cp -f "$WIREPY_DIR/org.sslcrypt.policy" /usr/share/polkit-1/actions/
echo "Installed policy file"
fi
# Create symlink for Wirepy
ln -sf /usr/local/bin/wirepy.py /usr/local/bin/wirepy
# Create symlink for LogViewer
ln -sf {LXToolsAppConfig.SHARED_LIBS_DESTINATION[detected_os]}/logviewer.py /usr/local/bin/logviewer
echo "Created Wirepy and LogViewer symlink"
# Install language files if available
if [ -d "$SHARED_DIR/languages/de" ]; then
echo "Installing language files..."
cp "$SHARED_DIR/languages/de"/*.mo /usr/share/locale/de/LC_MESSAGES/ 2>/dev/null || true
fi
echo "Created symlink"
# Create SSL key if not exists
if [ ! -f /usr/local/etc/ssl/pwgk.pem ]; then
echo "Creating SSL key..."
openssl genrsa -out /usr/local/etc/ssl/pwgk.pem 4096
chmod 600 /usr/local/etc/ssl/pwgk.pem
fi
# Cleanup
cd /tmp
rm -rf wirepy_install portinstaller
echo "Wire-Py installation completed!"
"""
return script