Main

1. Overview

The Main class is the central runtime controller of BastionGuard Backup. It coordinates application initialization, system discovery, snapshot creation, snapshot restoration, configuration persistence, exclusion rules, scheduling, device management, BTRFS operations, RSYNC tasks, logging, and cleanup.

The class supports both graphical and command-line execution. It maintains the global operational state used by the backup engine and delegates specialized operations to internal components such as:

  • SnapshotRepo – snapshot repository discovery, mounting, loading, validation, and retention
  • Snapshot – snapshot metadata, tags, control files, restoration, and deletion
  • RsyncTask – RSYNC-based backup and restore operations
  • RsyncSpaceCheckTask – estimation of required incremental snapshot space
  • DeleteFileTask – tracked deletion of RSYNC snapshots
  • Device – block-device discovery, mounting, unmounting, and LUKS unlocking
  • Subvolume – BTRFS subvolume detection, metadata, snapshot restoration, and deletion
  • FsTabEntry – parsing and rewriting of /etc/fstab
  • CryptTabEntry – parsing and rewriting of /etc/crypttab
  • AppExcludeEntry – application-specific restore exclusions
  • CronTab – scheduled snapshot job management
  • LinuxDistro – Linux distribution detection and bootloader command selection
  • AppLock – single-instance execution protection

2. Class Definition and Global State

The class derives from GLib.Object:

public class Main : GLib.Object

A global flag tracks whether GTK has been initialized:

public bool GTK_INITIALIZED = false;

The class contains most of the mutable runtime state of the backup application. This includes:

  • Application and configuration paths
  • Backup repository state
  • Detected partitions and system devices
  • Snapshot scheduling configuration
  • Backup and restore exclusion lists
  • Command-line parameters
  • Thread execution state
  • Restore target mappings
  • BTRFS subvolume and quota information
  • Logging paths and application lock state

3. Application Paths

The class maintains several paths used during startup and runtime:

  • app_path – directory containing the running executable
  • share_folder – shared application data directory
  • app_conf_path – active JSON configuration path
  • app_conf_path_old – legacy configuration path
  • app_conf_path_default – packaged default configuration
  • mount_point_app – process-specific temporary mount root
  • mount_point_restore – root mount point used for external restoration

The normal configuration paths are:

/etc/bastionguard-backup/bastionguard-backup.json
/etc/bastionguard-backup.json

The process-specific mount directory is created using the current process ID:

/run/bastionguard-backup/<pid>

This isolates temporary mounts created by different application processes.


4. Snapshot Modes

BastionGuard Backup supports two snapshot engines:

  • BTRFS mode – creates native BTRFS subvolume snapshots
  • RSYNC mode – creates filesystem copies with hard-link reuse between snapshots

The active mode is stored in:

public bool btrfs_mode = true;

Additional BTRFS options control whether the @home subvolume is included:

  • include_btrfs_home_for_backup
  • include_btrfs_home_for_restore

The application also records whether the installed btrfs-progs version supports recursive subvolume deletion:

public static bool btrfs_version__can_recursive_delete = false;

5. Snapshot Scheduling

The class stores independent scheduling flags for:

  • Boot snapshots
  • Hourly snapshots
  • Daily snapshots
  • Weekly snapshots
  • Monthly snapshots

Default retention counts are:

Monthly: 2
Weekly:  3
Daily:   5
Hourly:  6
Boot:    5

The computed scheduled property returns true when at least one schedule is enabled and the system is not running as a live environment.


6. Thread and Task State

Long-running operations are tracked through shared state variables:

  • thread_restore_running and thread_restore_success
  • thread_delete_running and thread_delete_success
  • thread_subvol_info_running and thread_subvol_info_success
  • thr_success
  • thr_retval
  • thr_timeout_active
  • progress_text

The principal task objects are:

RsyncTask task
DeleteFileTask delete_file_task
RsyncSpaceCheckTask space_check_task

Background operations periodically process GTK events to keep the graphical interface responsive.


7. Constructor and Startup Sequence

7.1 Main Constructor

public Main(string[] args, bool gui_mode)

The constructor performs the primary application startup sequence.

The initialization flow is:

  1. Create the process-specific runtime directory
  2. Parse early command-line arguments
  3. Initialize GUI state when running graphically
  4. Remove obsolete BTRFS helper installations
  5. Create a session log
  6. Detect the Linux distribution
  7. Verify required external commands
  8. Detect BTRFS command capabilities
  9. Create the application lock
  10. Resolve application and shared-data paths
  11. Initialize repositories, collections, and task objects
  12. Detect partitions and system devices
  13. Detect encrypted user directories
  14. Load the application configuration
  15. Initialize application icons

7.2 Graphical Mode

When gui_mode is enabled, the class creates a temporary parent window:

parent_window = new Gtk.Window();

In graphical mode, app_mode remains empty. A non-empty app_mode identifies command-line operations.


7.3 Single-Instance Lock

The application creates an AppLock named:

bastionguard-backup

If another incompatible operation already owns the lock:

  • GUI mode displays a dedicated BackupInProgressDialog
  • Command-line mode exits after the condition has been logged

This prevents simultaneous backup, delete, or restore operations from modifying the repository concurrently.


8. Session Logging

Session logs are stored in:

/var/log/bastionguard-backup

The log filename contains the local date, time, and execution mode:

YYYY-MM-DD_HH-MM-SS_gui.log
YYYY-MM-DD_HH-MM-SS_backup.log
YYYY-MM-DD_HH-MM-SS_restore.log

The directory is created automatically when missing. Existing files with the same generated name are removed before the new DataOutputStream is created.


9. Dependency Validation

9.1 check_dependencies()

public bool check_dependencies(out string msg)

The application verifies that the following commands are available:

rsync
/sbin/blkid
df
mount
umount
fuser
crontab
cp
rm
touch
ln
sync
run-parts

Missing commands are collected into a formatted diagnostic message.

In GUI mode, missing dependencies are displayed through a GTK message box. In command-line mode, the error is logged before the application exits.


10. User Session Environment

10.1 setup_env()

public static void setup_env()

This static method copies graphical-session environment variables from the spawning user process into the current process.

The copied variables include:

  • GTK_THEME
  • DISPLAY
  • XAUTHORITY
  • DBUS_SESSION_BUS_ADDRESS

For Wayland sessions, the method also processes:

  • WAYLAND_DISPLAY
  • XDG_RUNTIME_DIR

This is primarily required when the application runs with elevated privileges but must still access the active user’s graphical session.


11. BTRFS Version Detection

11.1 get_btrfs_version_array()

The private version parser executes:

btrfs --version

It searches the output for a line containing:

btrfs-progs v

The numeric version components are split and converted into an integer array.


11.2 Recursive Delete Capability

public void check_btrfs_version_capabilities()

Recursive BTRFS subvolume deletion is considered supported for:

btrfs-progs > 6.x
btrfs-progs >= 6.12

The resulting capability is stored in:

btrfs_version__can_recursive_delete

12. Legacy BTRFS Helper Removal

12.1 check_and_remove_timeshift_btrfs()

The application checks for an obsolete executable named:

bastionguard-backup-btrfs

If found, it executes:

bastionguard-backup-btrfs-uninstall

This removes the legacy helper before the current snapshot implementation is used.


13. BTRFS Layout Validation

13.1 Current System Layout

public bool check_btrfs_layout_system(Gtk.Window? win = null)

The supported system layout requires an @ root subvolume.

When home snapshots are enabled, an @home subvolume is also required.

Unsupported layouts generate a message explaining that Ubuntu-style @ and @home layouts are expected.


13.2 Target Device Layout

public bool check_btrfs_layout(
    Device? dev_root,
    Device? dev_home,
    bool unlock
)

The method supports:

  • @ and @home on the same BTRFS filesystem
  • @ and @home on separate BTRFS filesystems
  • Root-only restoration when home restoration is disabled

Validation is delegated to check_btrfs_volume().


14. Command-Line Mode Detection

14.1 parse_some_arguments()

The constructor performs an early command-line parsing pass to identify the execution mode.

Recognized options include:

  • --debug – enables command and debug logging
  • --btrfs – forces BTRFS mode
  • --rsync – forces RSYNC mode
  • --check – scheduled backup check
  • --delete – snapshot deletion
  • --delete-all – delete all snapshots
  • --restore – restore a snapshot
  • --clone – clone the current system
  • --create – create an on-demand snapshot
  • --list or --list-snapshots – list snapshots
  • --list-devices – list available devices

15. Live-System Detection

15.1 live_system()

The method reads:

/proc/cmdline

A live environment is detected when the kernel command line contains:

boot=casper
boot=live

The result is cached in _live_system after the first check.

Recovery mode explicitly bypasses live-system restrictions:

if (app_mode == "recovery") {
    return false;
}

This permits backup and restore features to remain available in the BastionGuard recovery environment.


16. Encrypted User Directory Detection

16.1 detect_encrypted_dirs()

User accounts are loaded from:

/etc/passwd

System accounts are ignored. For each normal user, the class records:

  • Whether the complete home directory is encrypted
  • The encrypted home path
  • Any encrypted private directories

The results are stored in:

  • users_with_encrypted_home
  • encrypted_home_dirs
  • encrypted_private_dirs

These values are later used to prevent decrypted content from being copied into snapshots.


17. Exclusion List Architecture

The class maintains several independent exclusion collections:

  • exclude_list_user – rules explicitly configured by the user
  • exclude_list_default – mandatory system exclusions
  • exclude_list_default_extra – optional cache and package exclusions
  • exclude_list_home – default user-home exclusions
  • exclude_list_restore – generated restore exclusion list
  • exclude_list_apps – application-specific restore exclusions
  • exclude_app_names – persisted names of selected applications

18. Default Exclusion Rules

18.1 add_default_exclude_entries()

The method initializes all exclusion lists and adds default paths that must not be copied into a snapshot.

Major excluded filesystem trees include:

/dev/*
/proc/*
/sys/*
/media/*
/mnt/*
/tmp/*
/run/*
/var/run/*
/var/lock/*
/var/lib/docker/*
/lost+found
/swapfile
/snap/*

BastionGuard’s own repository and log paths are excluded to prevent recursive backups:

/bastionguard-backup/*
/bastionguard-backup-btrfs/*
/var/log/bastionguard-backup/*
/var/log/bastionguard-backup-btrfs/*

18.2 Additional Mounted Filesystems

The method parses:

/etc/fstab

Filesystems mounted outside recognized system directories are automatically added to exclude_list_default_extra.

This prevents unrelated data volumes from being included in a system snapshot by default.


18.3 User Cache Exclusions

Common cache and temporary directories are excluded for both root and normal users:

/root/.cache
/root/.thumbnails
/root/.dbus
/root/.gvfs
/root/.local/share/[Tt]rash

/home/*/.cache
/home/*/.thumbnails
/home/*/.dbus
/home/*/.gvfs
/home/*/.local/share/[Tt]rash

18.4 Package Manager Caches

Optional exclusions cover several Linux package managers:

/var/cache/apt/archives/*
/var/cache/pacman/pkg/*
/var/cache/yum/*
/var/cache/dnf/*
/var/cache/eopkg/*
/var/cache/xbps/*
/var/cache/zypp/*
/var/cache/edb/*

18.5 Default Home Exclusions

User home directory contents are excluded by default through:

/root/**
/home/*/**

User-supplied include rules can override these exclusions because user rules are placed earlier in the final RSYNC filter list.


19. Application-Specific Exclusions

19.1 add_app_exclude_entries()

The method clears the global AppExcludeEntry registry and scans the snapshot selected for restoration.

Application data is discovered below:

<snapshot>/localhost/root
<snapshot>/localhost/home

The resulting entries are matched against the persisted exclude_app_names collection.


19.2 Saving Application Selections

public void save_exclude_list_selections()

Enabled application entries are added to exclude_app_names, while disabled entries are removed.

The final application-name list is sorted before being persisted.


20. Backup Exclusion List Generation

20.1 create_exclude_list_for_backup()

The final backup list is assembled in an order designed for RSYNC filter precedence:

  1. User rules
  2. Mandatory default rules
  3. Additional default rules
  4. Encrypted-directory protections
  5. Per-user home exclusions
  6. Common home exclusions
  7. BastionGuard repository exclusion

User rules are inserted first because RSYNC prioritizes the first matching include or exclude pattern.


20.2 Encrypted Home Protection

Decrypted encrypted-home contents are always excluded:

<user-home>/**

Encrypted private directories receive equivalent protection.

For legacy eCryptfs users, rules target:

/home/.ecryptfs/<username>/***

20.3 Backup Exclusion File

The generated list is saved as:

<snapshot-path>/exclude.list

The file becomes the --exclude-from source used by RSYNC.


21. Restore Exclusion List Generation

21.1 create_exclude_list_for_restore()

The restore exclusion list is rebuilt before each restoration.

It combines:

  • User exclusion rules
  • Exclusions stored in the snapshot’s exclude.list
  • Mandatory default exclusions
  • Optional default exclusions when system mirroring is disabled
  • Enabled application-specific exclusions
  • Home-directory exclusions

User include rules beginning with + are skipped during restoration.

The generated restore list is written to:

<restore-source>/exclude-restore.list

22. Repository Initialization

22.1 initialize()

public void initialize()

The public initialization method delegates repository setup to:

initialize_repo()

22.2 initialize_repo()

The repository-selection strategy depends on the current mode.

Selection priority:

  1. Use the system disk for BTRFS snapshot creation
  2. Use the device supplied through a command-line option
  3. Select a default device during the first run
  4. Restore the configured repository from stored UUID values
  5. Unlock the configured encrypted parent device when necessary

In BTRFS backup mode, specifying a separate snapshot device is rejected because snapshots must reside on the relevant BTRFS filesystem.


23. First-Run Behavior

23.1 set_first_run_flag()

During the first run, the application automatically selects a default snapshot engine.

BTRFS mode is selected when:

  • The system contains an @ subvolume
  • The btrfs command is available
  • BTRFS mode was explicitly requested on the command line

Otherwise, RSYNC mode becomes the default.


24. Snapshot Creation Entry Point

24.1 create_snapshot()

public bool create_snapshot(
    bool is_ondemand,
    Gtk.Window? parent_win
)

This method controls on-demand and scheduled snapshot creation.

The main flow is:

  1. Validate the BTRFS layout when using BTRFS mode
  2. Check repository free space
  3. Remove invalid snapshots when running in command mode
  4. Create the snapshot directory if missing
  5. Create an on-demand snapshot or evaluate enabled schedules
  6. Reload repository metadata
  7. Apply automatic retention
  8. Rebuild snapshot symlinks when required

25. Scheduled Snapshot Evaluation

For each enabled schedule, the class obtains the most recent snapshot with the corresponding tag.

A new snapshot is created when:

  • No snapshot with that tag exists
  • The latest boot snapshot predates the current boot
  • The latest hourly snapshot is older than approximately one hour
  • The latest daily snapshot is older than approximately one day
  • The latest weekly snapshot is older than approximately one week
  • The latest monthly snapshot is older than approximately one month

Repository metadata is reloaded after each newly created scheduled snapshot.


26. Snapshot Reuse and Tag Rotation

26.1 create_snapshot_for_tag()

Before creating a new snapshot, the application searches for a sufficiently recent existing snapshot.

If a suitable snapshot already exists, it is reused by adding the requested schedule tag.

This avoids creating duplicate snapshots when several schedule periods become due at approximately the same time.

Supported tags are:

ondemand
boot
hourly
daily
weekly
monthly

27. RSYNC Free-Space Estimation

27.1 get_space_needed_for_rsync_snapshot()

When the stored first-snapshot size is larger than the currently available space, the application performs a dry-run calculation before starting the snapshot.

The method creates a temporary snapshot structure and configures RsyncSpaceCheckTask with:

  • The destination snapshot path
  • The previous snapshot used for hard-link comparison
  • The generated exclusion file
  • Relative-path behavior
  • Delete and exclusion semantics matching the real snapshot

The resulting total_size represents the estimated additional storage required.


28. RSYNC Snapshot Creation

28.1 create_snapshot_with_rsync()

The RSYNC implementation creates snapshots under:

<repository>/snapshots/YYYY-MM-DD_HH-MM-SS

System files are stored below:

<snapshot>/localhost

28.2 Previous Snapshot Linking

The application selects a previous snapshot as an RSYNC link source.

Selection priority:

  1. The snapshot named in the .sync-restore control file
  2. The latest snapshot matching the current system UUID

The source is passed to RSYNC through the task’s link_from_path, enabling unchanged files to be represented by hard links.


28.3 RSYNC Task Configuration

The backup task uses:

  • Relative paths
  • Verbose progress information
  • Deletion of extra destination files
  • Deletion of excluded files
  • Generated exclusion rules
  • An RSYNC operation log

Command-line operations may enable I/O priority reduction through io_nice.


28.4 Snapshot Metadata

After RSYNC completes, the application counts the lines in the RSYNC log and writes the snapshot control file through:

Snapshot.write_control_file()

Stored metadata includes:

  • Creation date
  • System UUID
  • Linux distribution
  • Initial tags
  • User comments
  • File count
  • Snapshot engine

29. BTRFS Snapshot Creation

29.1 create_snapshot_with_btrfs()

BTRFS snapshots are created with:

btrfs subvolume snapshot '<source>' '<destination>'

The root subvolume @ is always processed. The @home subvolume is added when home backup is enabled.

Snapshot paths use:

bastionguard-backup-btrfs/snapshots/YYYY-MM-DD_HH-MM-SS

The method contains path corrections for duplicated nested names such as:

/@/@
/@home/@home

Detected system-subvolume metadata is copied into the snapshot control file after creation.


30. Post-Backup Hooks

30.1 run_post_backup_hooks()

After a successful snapshot, executable hooks are loaded from:

/etc/bastionguard-backup/backup-hooks.d

The hook environment contains:

TS_SNAPSHOT_PATH=<snapshot-path>

Hooks are executed with:

run-parts --verbose

If the hook directory does not exist, hook execution is skipped.


31. Snapshot Tags

31.1 set_tags()

Command-line tags are translated as follows:

O = ondemand
B = boot
H = hourly
D = daily
W = weekly
M = monthly

If no valid tags are supplied, the snapshot receives the ondemand tag.


31.2 validate_cmd_tags()

Unknown tag values cause an error and terminate command-line execution.


32. Snapshot Deletion

32.1 delete_begin()

Deletion starts in a dedicated thread named:

delete

The method updates the shared deletion state before launching delete_thread().


32.2 delete_thread()

Every snapshot in delete_list is first marked for deletion.

Deletion behavior depends on the snapshot engine:

  • BTRFS – removes the snapshot and its subvolumes
  • RSYNC – uses the snapshot’s DeleteFileTask and reports elapsed time

Desktop notifications report completed deletions.


33. Restore Device Properties

The following properties expose devices mapped to standard mount points:

  • dst_root – device mapped to /
  • dst_boot – device mapped to /boot
  • dst_efi – device mapped to /boot/efi
  • dst_home – device mapped to /home

The property setters update the corresponding MountEntry inside mount_list.


34. Restore Path Properties

34.1 restore_current_system

A restoration targets the current system when the selected root device matches the active system root by device name or UUID.


34.2 restore_source_path

For a normal restore, the source is the selected snapshot path.

During system cloning, the source is:

/tmp/bastionguard-backup

34.3 restore_target_path

The target is:

  • / when restoring the active system
  • The temporary restore mount when restoring another device

34.4 Restore Support Files

The restore process uses:

<restore-source>/rsync-log-restore
<restore-source>/exclude-restore.list

35. Restore Mount-List Initialization

35.1 init_mount_list()

The method builds the target-device mapping from:

  • The active system’s /etc/fstab and /etc/crypttab during cloning
  • The selected snapshot’s stored filesystem tables during restoration

Non-system mount points and explicitly excluded paths are ignored.


35.2 Device Resolution

Devices are resolved by:

  1. Filesystem UUID
  2. Device path
  3. Encrypted-parent lookup through crypttab
  4. LUKS unlocking when a matching encrypted parent is available

Missing devices are represented by mount entries whose device is null.


35.3 Required Mount Entries

The method guarantees that entries exist for:

/
/boot
/home

For cloning, all devices are reset to null so the user must explicitly select clone targets.

Entries are sorted by mount-point path so parent mounts appear before child mounts.


36. Bootloader Options

36.1 init_boot_options()

The initial GRUB device is derived from the target root device and then promoted to its top-level parent disk.

During cloning, the defaults are:

reinstall_grub2 = true
update_initramfs = true
update_grub = true

For Fedora snapshot restoration:

reinstall_grub2 = false
update_initramfs = false
update_grub = true

This avoids unsupported grub2-install operations on Fedora EFI systems.


37. Restore Validation and Execution

37.1 restore_snapshot()

The method validates all required inputs before starting the restore thread.

Checks include:

  • The backup device is available
  • A snapshot has been selected
  • The snapshot is not marked for deletion
  • Required BTRFS subvolumes are mounted
  • An RSYNC target root device has been selected
  • An external target has been mounted when required

Execution is delegated to:

  • restore_execute_btrfs()
  • restore_execute_rsync()

The caller waits while GTK events continue to be processed.


38. Restore Warning Messages

38.1 get_restore_messages()

The method generates three separate messages:

  • A table of devices and mount points that will be modified
  • A reboot warning for current-system restoration
  • A no-warranty disclaimer

The output can be generated as plain text or GTK markup.

In command-line mode, all messages are written directly to the log.


39. RSYNC Restore Script Generation

39.1 create_restore_scripts()

The method builds two shell scripts:

  • sh_sync – performs the filesystem synchronization
  • sh_finish – updates boot configuration, runs hooks, and performs cleanup

The main RSYNC command uses:

rsync -avir --force --delete --delete-before \
  --log-file="<restore-log>" \
  --exclude-from="<restore-exclude-list>" \
  "<source>" "<target>"

Dry-run mode adds:

--dry-run

40. Bootloader and Initramfs Restoration

When restoring to another system, the application bind-mounts:

/dev
/dev/pts
/proc
/run
/sys

These paths are mounted inside the target before executing commands through chroot.

Bootloader commands depend on the target distribution:

  • Red Hat family – grub2-install, grub2-mkconfig, and dracut
  • Arch family – grub-install, grub-mkconfig, and mkinitcpio
  • Debian family – grub-install, update-grub, and update-initramfs

41. Post-Restore Hooks

Restore hooks are loaded from:

/etc/bastionguard-backup/restore-hooks.d

They are executed through:

run-parts --verbose

BTRFS restoration exports:

TS_SNAPSHOT_PATH=<snapshot-path>

Current-system RSYNC restoration forces a reboot after completion.


42. Restore Execution Modes

The class provides four RSYNC execution paths:

  • restore_current_console() – restore active system from the command line
  • restore_current_gui() – restore active system through an embedded terminal window
  • restore_other_console() – restore or clone another system from the command line
  • restore_other_gui() – restore or clone another system with tracked graphical progress

Console execution supports verbose and quiet modes. Quiet mode captures stdout and stderr and writes them to the application log.


43. fstab Repair

43.1 fix_fstab_file()

After restoring another system, the target file is loaded from:

<target>/etc/fstab

For each selected mount mapping, the method:

  • Adds an entry when missing
  • Updates the filesystem UUID
  • Updates the filesystem type
  • Removes BTRFS subvol options from non-BTRFS devices

System-directory entries that were not explicitly mapped are removed.

Missing mount-point directories are created on the restored filesystem.


44. crypttab Repair

44.1 fix_crypttab_file()

The target encrypted-volume table is loaded from:

<target>/etc/crypttab

Every existing entry receives the nofail option.

Missing entries are created for selected devices located on:

  • Direct LUKS containers
  • LVM volumes hosted on LUKS containers

Generated entries use:

mapped_name = luks-<UUID>
keyfile     = none
options     = luks,nofail

45. Filesystem Repair

45.1 check_and_repair_filesystems()

After restoring another system, each mapped target device is checked with:

fsck -y <device>

This operation is skipped when restoring the active system.


46. RSYNC Restore Engine

46.1 restore_execute_rsync()

The RSYNC restore engine:

  1. Generates synchronization and finishing scripts
  2. Creates the restore exclusion file
  3. Deletes old restore logs
  4. Writes a .sync-restore control file when restoring the active system
  5. Selects the correct GUI or console execution path
  6. Unmounts external targets after completion
  7. Runs filesystem checks when applicable

The .sync-restore file allows a subsequent RSYNC snapshot to link against the recently restored snapshot.


47. BTRFS Restore Engine

47.1 restore_execute_btrfs()

Before restoring the selected snapshot, the method creates or updates a pre-restore snapshot.

Each selected snapshot subvolume is then restored through:

subvol.restore()

The @home subvolume is skipped when home restoration is disabled.

The restored BTRFS snapshot becomes active after the next system reboot.


48. BTRFS Pre-Restore Snapshot

48.1 create_pre_restore_snapshot_btrfs()

The method preserves the current system subvolumes before they are replaced.

If an existing live pre-restore snapshot is found:

  • The currently restored @ and optionally @home subvolumes are deleted
  • The existing pre-restore snapshot description is updated

Otherwise, active system subvolumes are moved into a new timestamped snapshot directory.

The generated snapshot:

  • Receives the ondemand tag
  • Is marked as live
  • Contains the description Before restoring ...
  • Stores subvolume metadata in its control file

49. Configuration Persistence

49.1 save_app_config()

Application settings are serialized as formatted JSON.

Stored values include:

  • Backup device UUID
  • Encrypted parent-device UUID
  • Snapshot engine
  • BTRFS home options
  • Cron email behavior
  • Schedule flags
  • Retention counts
  • Estimated snapshot size and file count
  • Date format
  • User exclusion rules
  • Selected application exclusions

The JSON generator uses two-space indentation.


49.2 Configuration Keys

backup_device_uuid
parent_device_uuid
do_first_run
btrfs_mode
include_btrfs_home_for_backup
include_btrfs_home_for_restore
stop_cron_emails

schedule_monthly
schedule_weekly
schedule_daily
schedule_hourly
schedule_boot

count_monthly
count_weekly
count_daily
count_hourly
count_boot

snapshot_size
snapshot_count
date_format
exclude
exclude-apps

50. Legacy Configuration Import

50.1 import_timeshift_config_if_available()

The application can import settings from legacy Timeshift or BastionGuard configuration files.

Candidate paths are checked in this order:

/etc/timeshift/timeshift.json
/etc/timeshift.json
/etc/bastionguard-backup/bastionguard-backup.json
/etc/bastionguard-backup.json

The active target configuration is excluded from the import candidates.

Legacy keys such as:

include_btrfs_home

are converted to the newer backup-specific setting.

After import, the normalized configuration is saved to the active BastionGuard path.


51. Configuration Loading

51.1 load_app_config()

If the active configuration does not exist, the method:

  1. Moves the old BastionGuard configuration when available
  2. Otherwise copies the packaged default configuration

JSON values are loaded with fallback defaults through the TeeJee JSON helper functions.

A command-line --btrfs or --rsync option overrides the stored snapshot mode.

If no backup UUID is found, additional legacy configuration files are inspected as a fallback.


52. Partition and System-Device Detection

52.1 update_partitions()

The partition list is refreshed through:

Device.get_filesystems()

If the active repository device is rediscovered, its object reference is replaced with the current device instance.


52.2 detect_system_devices()

The class identifies devices mounted at:

/
/home
/boot
/boot/efi

Loop devices are ignored to avoid incorrectly selecting live-system backing volumes.

System BTRFS subvolumes are detected through:

Subvolume.detect_subvolumes_for_system_by_path()

53. Target Device Mounting

53.1 mount_target_devices()

Restore devices are mounted below:

/run/bastionguard-backup/<pid>/restore

The application does not reuse existing mount points because nested target filesystems must be assembled into a single restore tree.

Encrypted devices are unlocked through Device.luks_unlock().

BTRFS mount options include:

subvol=@
subvol=@home

53.2 Unmounting

unmount_target_device() and unmount_device() remove application-created mounts.

A failure can produce a critical GTK dialog and terminate the application when exit_on_error is enabled.


54. Backup Device Validation

54.1 check_btrfs_volume()

The method temporarily mounts the BTRFS top-level filesystem using:

subvolid=0

It verifies that all requested subvolume names exist and then removes the temporary mount.

Encrypted devices may be unlocked when the caller permits it.


54.2 check_device_for_backup()

Rejected devices include:

  • Whole-disk objects
  • Devices that contain child devices

In BTRFS mode, the device must contain an @ subvolume.

In RSYNC mode, any supported Linux filesystem can be selected.


54.3 try_select_default_device_for_backup()

The method first checks the current repository device.

When BTRFS mode is active and the system contains an @ subvolume, the root BTRFS device is selected automatically.

Otherwise, available partitions are inspected until a valid backup device is found.


55. System-Size Estimation

55.1 estimate_system_size()

The method runs an RSYNC dry run against an empty temporary directory.

The generated statistics are stored in:

Main.first_snapshot_size
Main.first_snapshot_count

If RSYNC reports a zero size, the used-space value of the system root device is used as a fallback.

The result is persisted in the application configuration to avoid repeating the calculation unnecessarily.


56. BTRFS Subvolume Information

56.1 query_subvolume_info()

BTRFS metadata queries run in a dedicated thread.

The operation retrieves:

  • Subvolume IDs
  • Total referenced bytes
  • Unshared bytes

The caller waits while GTK events continue to be processed.


56.2 Subvolume IDs

IDs are obtained with:

btrfs subvolume list '<mount-path>'

Parsed IDs are associated with:

  • The active system @ subvolume
  • The active system @home subvolume
  • Subvolumes belonging to stored snapshots

56.3 BTRFS Quotas

Quota information is queried with:

btrfs qgroup show --raw '<mount-path>'

If --raw is unsupported, the command is retried without it.

The internal quota status can be:

UNKNOWN
DISABLED
ENABLED

When quotas are disabled, repeated quota queries are avoided.

Updated size values are written back to each snapshot’s control file.


57. Cron Scheduling

57.1 cron_job_update()

Cron configuration is skipped on live systems.

Legacy jobs and script files are removed before the current schedule is applied.

The hourly dispatcher is installed as:

0 * * * * root bastionguard-backup --check --scripted

Boot snapshots use:

@reboot root sleep 10m && bastionguard-backup --create --scripted --tags B

The stop_cron_emails option controls whether command output is suppressed by the CronTab helper.


58. Log Retention

58.1 clean_logs()

Session logs are enumerated and sorted alphabetically, which corresponds to chronological order because filenames begin with ISO-style timestamps.

When more than 500 old logs exist, the oldest 100 are deleted.

The active session log is never removed.


59. Application Shutdown

59.1 exit_app()

The controlled shutdown sequence is:

  1. Save the configuration in GUI mode
  2. Update Cron jobs
  3. Unmount restore devices
  4. Clean old logs
  5. Release the application lock
  6. Remove temporary files
  7. Clean stale process mount points
  8. Exit with the requested status code

59.2 Stale Mount Cleanup

cleanup_unmount_devices() inspects:

/run/bastionguard-backup

For each process directory, it verifies whether the owning process still exists below /proc.

Stale device mounts are unmounted and their empty directories are removed.


60. Runtime and Security Considerations

  • Single-instance protection: the application lock prevents conflicting backup and restore operations.
  • Privilege requirements: the class modifies system files, mounts devices, executes bootloader commands, and writes below /etc and /var/log.
  • Encrypted-volume support: LUKS devices are detected and unlocked before repository or restore operations.
  • Decrypted-data protection: decrypted encrypted-home contents are forcibly excluded from snapshots.
  • Restore isolation: external targets are mounted below a process-specific runtime directory.
  • Repository recursion prevention: BastionGuard snapshot directories are always excluded from backups.
  • Boot safety: restored fstab, crypttab, initramfs, and GRUB configuration are updated when required.
  • Pre-restore protection: BTRFS restoration preserves the previous active subvolumes as a recoverable snapshot.
  • Hook execution: backup and restore hook directories execute scripts as the application’s privileged user.
  • Live-system restrictions: scheduled backups are disabled in ordinary live sessions.
  • Filesystem checks: restored external filesystems are checked with fsck -y.
  • Log retention: automatic cleanup prevents unbounded growth of session logs.

61. Implementation Considerations

  • Centralized mutable state: the class contains a large amount of shared runtime state and acts as both controller and service coordinator.
  • Thread synchronization: worker completion is tracked through boolean fields rather than synchronization primitives.
  • GTK polling: several operations wait synchronously while repeatedly processing GTK events.
  • Shell command construction: restore, BTRFS, GRUB, hook, mount, and cleanup operations rely heavily on dynamically generated shell commands.
  • Snapshot creation result: create_snapshot_for_tag() initializes its return status to true; the final value may remain true even when the engine returns no snapshot.
  • Restore exclusion output: save_exclude_list_for_restore() accepts an output path but writes through the computed restore_exclude_file property.
  • BTRFS quota device access: quota queries assume that the home-device reference is available when comparing root and home UUIDs.
  • Configuration compatibility: several legacy Timeshift and BastionGuard paths remain supported for migration.
  • Legacy naming: some notifications and translated messages still reference TimeShift because the implementation derives from the original project.
  • Cron cleanup: the hourly legacy script removal is invoked more than once, which is redundant but normally harmless.
  • Version parsing: BTRFS version components are parsed into a fixed-size integer array and assume numeric version segments.
  • Hook trust boundary: scripts stored in the backup and restore hook directories execute with the permissions of the application process and must be administrator-controlled.

62. Licensing

The source file is distributed under the GNU General Public License, version 2 or, at the user’s option, any later version.

The original copyright notice identifies:

Copyright 2012-2018 Tony George
<teejeetech@gmail.com>

The software is provided without warranty, including implied warranties of merchantability or fitness for a particular purpose.