AppGtk

1. Overview

The AppGtk class implements the graphical entry point of BastionGuard Backup.

It initializes localization, detects recovery mode, prepares the graphical environment, verifies administrator privileges, creates the central Main controller, initializes the snapshot repository, displays the main application window, and manages the GTK application lifecycle.

The module also integrates two startup-specific subsystems:

  • BastionGuardRecovery – displays the recovery-mode warning and keeps backup and restore functions available in the recovery environment
  • ImportDialog – checks whether an existing repository or configuration should be imported

The class acts as a startup coordinator. Backup, restore, repository, scheduling, and device operations remain delegated to the central Main controller and the graphical MainWindow.


2. Class Definition

The class derives from GLib.Object:

public class AppGtk : GLib.Object

The executable entry point is implemented as a static method:

public static int main(string[] args)

3. Dependencies

The module imports the following libraries:

using GLib;
using Gtk;
using Gee;
using Json;

It also includes multiple TeeJee utility modules:

  • TeeJee.Logging – startup, debug, and error logging
  • TeeJee.FileSystem – temporary-directory initialization
  • TeeJee.JsonHelper – shared JSON utilities
  • TeeJee.ProcessHelper – process environment integration
  • TeeJee.GtkHelper – GTK message dialogs and helper functions
  • TeeJee.System – administrator-account detection
  • TeeJee.Misc – shared application utilities

The principal application-level dependencies are:

  • Main – central backup and restore controller
  • MainWindow – primary graphical interface
  • BastionGuardRecovery – recovery-mode banner
  • ImportDialog – repository import prompt

4. Global Application Controller

public Main App;

The graphical entry point exposes a global Main instance named App.

This object is created during startup and contains the shared runtime state used by the graphical interface, including:

  • Snapshot repository configuration
  • Backup and restore settings
  • Detected devices and partitions
  • Exclusion lists
  • Scheduling options
  • Application mode
  • Logging and cleanup state

5. Application Identity

The graphical executable defines the following application constants:

public const string AppName = "BastionGuard Backup-gtk";
public const string AppShortName = "bastionguard-backup";
public const string AppVersion = Constants.VERSION;
public const string AppAuthor = "BastionGuard™";
public const string AppAuthorEmail = "info@bastionguard.eu";
  • AppName – display name of the graphical executable
  • AppShortName – common application identifier
  • AppVersion – version supplied by the shared constants module
  • AppAuthor – displayed author or product identity
  • AppAuthorEmail – contact address used in the help output

6. Localization Constants

const string GETTEXT_PACKAGE = "bastionguard-backup";
const string LOCALE_DIR = "/usr/share/locale";

The gettext domain is:

bastionguard-backup

Translation catalogs are loaded from:

/usr/share/locale

Translated strings throughout the module use the standard _() gettext helper.


7. Recovery Boot Flag

const string BASTIONGUARD_RECOVERY_FLAG =
    "bastionguard.recovery=1";

This value identifies a BastionGuard recovery boot through the Linux kernel command line.

Recovery mode can therefore be activated in two ways:

  • By passing --recovery to the graphical executable
  • By booting the system with bastionguard.recovery=1

8. External Process Exit Function

extern void exit(int exit_code);

The module declares the standard process-level exit() function.

It is used when administrator privileges are unavailable, before the central Main controller has been created.


9. Graphical Entry Point

9.1 main()

public static int main(string[] args)

The main() method controls the complete graphical startup sequence.

The initialization flow is:

  1. Initialize localization
  2. Detect command-line or kernel recovery mode
  3. Handle help and version requests
  4. Copy the graphical environment from the user session
  5. Initialize GTK
  6. Set the global GTK initialization flag
  7. Initialize temporary application paths
  8. Verify administrator privileges
  9. Create the central Main controller
  10. Enable the recovery application mode when required
  11. Parse graphical command-line options
  12. Initialize the snapshot repository
  13. Create and display the main window
  14. Run the GLib main loop
  15. Perform centralized application cleanup

10. Recovery Mode Detection

Recovery detection begins before GTK initialization.

The module first scans all command-line arguments:

bool recovery_mode = false;

foreach (var arg in args) {
    if (arg == "--recovery") {
        recovery_mode = true;
        break;
    }
}

If the command-line option is absent, the module checks the Linux kernel command line:

if (!recovery_mode && is_kernel_recovery_boot()) {
    recovery_mode = true;
}

The result is retained in the local recovery_mode variable and later passed to the graphical startup function.


11. Help and Version Fast Paths

When the first command-line option is one of the following values, the program returns before GTK initialization and privilege validation:

--help
--h
-h
--version

Help output is written through:

stdout.printf(help_message());

Version output is written through:

stdout.printf(version_message());

Both operations return exit status 0.


12. User Session Environment

Before GTK is initialized, the module calls:

Main.setup_env();

This copies required graphical-session environment variables from the active user session into the privileged process.

The environment may include:

  • DISPLAY
  • XAUTHORITY
  • DBUS_SESSION_BUS_ADDRESS
  • GTK_THEME
  • WAYLAND_DISPLAY
  • XDG_RUNTIME_DIR

This permits the elevated BastionGuard process to connect to the user’s X11 or Wayland graphical session.


13. GTK Initialization

GTK is initialized through:

Gtk.init();

After successful initialization, the shared state flag becomes:

GTK_INITIALIZED = true;

Other components can use this flag to determine whether GTK dialogs and graphical helpers are available.


14. Temporary Runtime Initialization

The module initializes application temporary paths through:

init_tmp();

This occurs after GTK initialization and before administrator validation and construction of the central controller.

Temporary runtime paths are used by later operations such as:

  • Device mounting
  • Restore staging
  • Backup repository access
  • Transient command scripts

15. Administrator Validation

15.1 check_if_admin()

public static void check_if_admin()

The graphical application requires administrator privileges because backup and restore operations access protected system files, mount block devices, and update system configuration.

The current account is checked through:

user_is_admin()

If administrator access is unavailable, the module constructs a translated error message:

Admin access is required to backup and restore system files.

Please re-run the application as admin
(using 'sudo' or 'su')

The message is:

  • Written to the application log
  • Displayed through a GTK message box
  • Followed by process termination with status 1

The dialog title is:

Admin Access Required

16. Main Controller Construction

The central controller is created in graphical mode:

App = new Main(args, true);

Passing true enables GUI-oriented behavior inside Main, including:

  • Graphical error dialogs
  • A dummy parent window during early initialization
  • GUI session logging
  • Graphical single-instance conflict handling

17. Recovery Application Mode

When recovery mode is active, the graphical entry point assigns:

App.app_mode = "recovery";

This assignment affects the central live-system detection logic.

In recovery mode, Main.live_system() returns false even when the operating system was started from a live environment.

This keeps the following functions available:

  • Snapshot creation
  • Snapshot restoration
  • Guided configuration wizard
  • Backup-device configuration

18. Graphical Argument Parsing

18.1 parse_arguments()

public static bool parse_arguments(string[] args)

The full graphical argument parser processes all arguments after the executable path.

Supported options are:

  • --debug – enables debug logging
  • --recovery – accepted because it was already processed during early startup

Debug mode sets:

LOG_DEBUG = true;

18.2 Unknown Options

An unknown option produces:

Unknown option: <argument>

The parser then:

  1. Writes the help message to the application log
  2. Calls App.exit_app(1)
  3. Returns false

The centralized exit routine performs configuration saving, mount cleanup, log cleanup, application-lock release, and temporary-directory cleanup when applicable.


19. Repository Initialization

After command-line parsing, the module calls:

App.initialize();

This delegates to the main controller’s repository initialization logic.

The repository may be selected from:

  • The configured backup-device UUID
  • The active system BTRFS device
  • A command-line override
  • A first-run default device
  • An encrypted parent device

20. Starting the Graphical Interface

The initialized recovery state is passed to:

start_application(recovery_mode);

This function creates the main window and starts the GLib event loop.


21. Centralized Shutdown

After the graphical main loop ends, the module calls:

App.exit_app();

The central shutdown process can perform:

  • Application configuration saving
  • Cron schedule updates
  • Unmounting of application-created mounts
  • Old-log cleanup
  • Application-lock release
  • Temporary-file cleanup
  • Removal of stale runtime mount directories

The graphical entry point then returns status 0.


22. Locale Initialization

22.1 set_locale()

private static void set_locale()

The method prepares the gettext translation environment.

The initialization sequence is:

Intl.setlocale(
    GLib.LocaleCategory.MESSAGES,
    "bastionguard-backup"
);

Intl.textdomain(GETTEXT_PACKAGE);

Intl.bind_textdomain_codeset(
    GETTEXT_PACKAGE,
    "utf-8"
);

Intl.bindtextdomain(
    GETTEXT_PACKAGE,
    LOCALE_DIR
);

A debug message is written before localization is configured:

setting locale...

23. Version Message

23.1 version_message()

private static string version_message()

The method returns the graphical application name and version:

<AppName> <AppVersion>

Example:

BastionGuard Backup-gtk 1.0.0

A trailing newline is included.


24. Help Message

24.1 help_message()

public static string help_message()

The generated help output contains:

  • Application name
  • Application version
  • BastionGuard contact address
  • Command syntax
  • Available graphical options
  • Reference to the command-line executable

The syntax line is:

bastionguard-backup-gtk [options]

Available options are documented as:

--debug
--recovery
--h[elp]
--version

The final message directs the user to:

bastionguard-backup

for the command-line version of the tool.


25. Graphical Application Lifecycle

25.1 start_application()

public static void start_application(
    bool recovery_mode = false
)

The method creates:

var window = new MainWindow();
var loop = new GLib.MainLoop(null, false);

The implementation uses a manually managed GLib.MainLoop instead of a Gtk.Application object.


26. Window Close Handling

The main window’s close_request signal is connected to the GLib loop:

window.close_request.connect(() => {
    loop.quit();
    return false;
});

When the user closes the window:

  1. The main loop is asked to quit
  2. The close event continues because the handler returns false
  3. Control returns to main()
  4. Centralized cleanup is executed

27. Recovery Startup Path

When recovery_mode is enabled, the startup sequence is:

window.present();
BastionGuardRecovery.show_recovery_banner(window);

The main window is presented before the recovery dialog.

This ensures that the recovery message has a visible and fully initialized parent window.

The normal import dialog is not displayed in recovery mode.


28. Standard Startup Path

During an ordinary graphical startup, the module executes:

ImportDialog.check_and_show_if_needed(window);
window.present();

The import subsystem checks whether an existing BastionGuard or compatible snapshot repository should be imported.

After the import check, the main window is presented.


29. Main Event Loop

After startup dialogs and window presentation have been configured, the method runs:

loop.run();

The GLib loop processes:

  • GTK input events
  • Window signals
  • Timers
  • Idle callbacks
  • Background-operation UI updates
  • Dialog responses

The method returns only after the window-close callback invokes loop.quit().


30. Kernel Recovery Detection

30.1 is_kernel_recovery_boot()

private static bool is_kernel_recovery_boot()

The method reads:

/proc/cmdline

This virtual file contains the arguments passed to the Linux kernel by the bootloader.

The method returns true when the command line contains:

bastionguard.recovery=1

The implementation is:

try {
    string cmdline;

    FileUtils.get_contents(
        "/proc/cmdline",
        out cmdline
    );

    return BASTIONGUARD_RECOVERY_FLAG in cmdline;
}
catch {
    return false;
}

31. Recovery Detection Error Handling

If /proc/cmdline cannot be read, the method returns:

false

No exception is propagated and no error dialog is shown.

The graphical application therefore continues in standard mode when kernel recovery detection fails.


32. Normal Startup Flow

The complete standard graphical startup flow is:

  1. Start bastionguard-backup-gtk
  2. Initialize gettext
  3. Confirm that recovery mode is inactive
  4. Copy the user graphical environment
  5. Initialize GTK
  6. Verify administrator access
  7. Create the central controller
  8. Parse graphical options
  9. Initialize the snapshot repository
  10. Create MainWindow
  11. Check whether repository import is required
  12. Present the main window
  13. Run the main event loop
  14. Clean up after window closure

33. Recovery Startup Flow

The complete recovery startup flow is:

  1. Receive --recovery or detect bastionguard.recovery=1
  2. Initialize GTK and administrator context
  3. Create the central controller
  4. Set App.app_mode to recovery
  5. Initialize the snapshot repository
  6. Create and present MainWindow
  7. Display the recovery warning banner
  8. Skip the standard import prompt
  9. Run the normal graphical event loop
  10. Keep backup and restore features enabled

34. Separation of Responsibilities

The graphical entry point is deliberately limited to startup and lifecycle coordination.

Responsibilities are divided as follows:

  • AppGtk – environment, GTK, recovery detection, arguments, privilege validation, main loop, and shutdown
  • Main – repositories, devices, snapshots, scheduling, backup, restore, deletion, and configuration
  • MainWindow – graphical controls and user interaction
  • BastionGuardRecovery – recovery-mode notification
  • ImportDialog – graphical import detection and migration prompt

35. Runtime and Security Considerations

  • Administrator requirement: the graphical interface refuses to start operationally without elevated privileges.
  • User-session environment: graphical variables are copied before GTK initialization so the elevated process can access the desktop session.
  • Recovery override: recovery mode bypasses normal live-session backup restrictions.
  • Kernel-controlled activation: the recovery environment can be identified directly from the bootloader-supplied kernel parameter.
  • Centralized cleanup: normal window closure reaches Main.exit_app().
  • Single-instance protection: construction of Main creates the application lock before the main window is displayed.
  • Unknown-option rejection: unsupported graphical arguments terminate the application with an error.
  • Import separation: repository import checks are skipped during recovery startup.
  • Fail-safe recovery detection: errors reading the kernel command line produce standard startup behavior.
  • Translated messages: graphical errors and help strings use the BastionGuard gettext domain.

36. Implementation Considerations

  • Locale argument: Intl.setlocale() receives bastionguard-backup as the requested message locale, although this value is also the gettext domain and may not represent a valid system locale.
  • Locale category: only LocaleCategory.MESSAGES is configured rather than all locale categories.
  • Help position: help and version options are recognized only when they appear at args[1]; placing them after another option causes the full parser to reject them.
  • Case-sensitive early recovery: the initial recovery scan compares arguments directly with --recovery, while the later parser converts options to lowercase.
  • Recovery precedence: recovery detection is performed before help and version processing, although these fast paths return before recovery mode is applied.
  • Direct process exit: administrator failure uses exit(1) instead of the centralized cleanup routine because Main has not yet been constructed.
  • Manual main loop: the module uses GLib.MainLoop instead of the higher-level Gtk.Application lifecycle.
  • Close-request return value: the handler quits the custom loop and returns false, allowing normal window closing to continue.
  • Import presentation order: the import check occurs before window.present(); dialog behavior therefore depends on how ImportDialog presents and parents its own windows.
  • Recovery presentation order: recovery mode explicitly presents the main window before displaying the warning dialog.
  • Parser return value: parse_arguments() returns a Boolean, but main() does not inspect it because invalid options terminate through App.exit_app().
  • Debug scope: --debug is applied after the Main constructor, while the constructor already performs substantial logging and initialization.
  • Duplicate recovery detection: recovery mode is detected by this file even though a separate recovery module also exposes recovery-boot detection.
  • Global mutable controller: the graphical interface depends on the globally accessible App object.
  • Unused imports: Gee and Json are imported but are not directly used in this source file.

37. Licensing

The source file contains the following copyright notices:

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

Copyright 2025-2026 Calogero Scarnà
BastionGuard Backup

The module is distributed under:

GPL-2.0-or-later

This permits redistribution and modification under version 2 of the GNU General Public License or any later version.