add mount/mount all button
This commit is contained in:
@@ -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,
|
||||
|
@@ -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
162
python.py
@@ -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
|
Reference in New Issue
Block a user