AppConsole

1. Overview

The AppConsole class implements the command-line interface of BastionGuard Backup.

It translates terminal arguments and interactive user input into operations performed by the central Main controller. The class does not implement the snapshot engines directly; instead, it coordinates repository selection, snapshot creation, restoration, deletion, device mapping, GRUB configuration, and console output.

The supported command-line workflows include:

  • Creating scheduled snapshots
  • Creating on-demand snapshots
  • Restoring an existing snapshot
  • Deleting one snapshot
  • Deleting all snapshots
  • Listing available snapshots
  • Listing Linux filesystem devices
  • Selecting restore-target devices interactively
  • Selecting the GRUB installation device
  • Running in interactive or scripted mode

2. Class Definition

The class derives from GLib.Object:

public class AppConsole : GLib.Object

The class contains a static main() entry point and instance methods for executing the selected console operation.


3. Dependencies

The module imports:

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

It also integrates several TeeJee utility modules:

  • TeeJee.Logging – console, debug, command, and error output
  • TeeJee.FileSystem – filesystem and temporary-directory helpers
  • TeeJee.JsonHelper – shared JSON utilities
  • TeeJee.ProcessHelper – process and command integration
  • TeeJee.GtkHelper – GTK event and message helpers
  • TeeJee.System – device and administrator-account checks
  • TeeJee.Misc – shared formatting and utility functions

The class directly interacts with:

  • Main – central backup and restore controller
  • SnapshotRepo – snapshot repository selection and status
  • Snapshot – snapshot selection and deletion
  • Device – block-device enumeration and resolution
  • MountEntry – restore mount-point mappings
  • TimeoutCounter – timeout handling for terminal input

4. Global Application State

The console module defines the shared application controller:

public Main App;

This global object is initialized after privilege checks and is used by all console methods.

The application identity constants are:

public const string AppName = "BastionGuard Backup";
public const string AppShortName = "bastionguard-backup";
public const string AppVersion = Constants.VERSION;
public const string AppAuthor = "Tony George";
public const string AppAuthorEmail = "teejeetech@gmail.com";

5. Localization Configuration

The gettext configuration is defined through:

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

5.1 set_locale()

private static void set_locale()

The method initializes the process locale and translation domain:

Intl.setlocale(GLib.LocaleCategory.ALL, "");
Intl.bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
Intl.bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
Intl.textdomain(GETTEXT_PACKAGE);

Console help, status messages, prompts, and errors can therefore be translated through gettext.


6. Console Pagination State

public int snapshot_list_start_index = 0;

This field stores the first snapshot index displayed by the interactive snapshot selector.

It is modified when the user requests the previous or next page.


7. Console Entry Point

7.1 main()

public static int main(string[] args)

The static entry point initializes and executes the complete command-line application.

The startup sequence is:

  1. Initialize localization
  2. Disable timestamp prefixes for initial console output
  3. Process --help and --version
  4. Display help when no arguments are supplied
  5. Copy the graphical-session environment when required
  6. Initialize temporary application paths
  7. Verify administrator privileges
  8. Create the central Main controller
  9. Parse all command-line options
  10. Initialize the snapshot repository
  11. Create the AppConsole instance
  12. Dispatch the selected operation
  13. Exit through Main.exit_app()

8. Help and Version Fast Paths

The following arguments are processed before administrator validation:

--help
-h
--version

When no operational argument is supplied, the application also displays the help message and returns successfully.

This allows ordinary users to inspect command syntax and version information without root privileges.


9. Temporary Environment Initialization

Before temporary files are initialized, logging is disabled:

LOG_ENABLE = false;
init_tmp();
LOG_ENABLE = true;

This prevents temporary initialization messages from appearing in normal command-line output.


10. Administrator Requirement

10.1 check_if_admin()

public static void check_if_admin()

Operational console commands require administrator privileges.

The method checks:

user_is_admin()

If the process is not running with sufficient privileges, the application displays:

Application needs admin access.
Please run the application as admin (using 'sudo' or 'su')

The process then terminates with exit status 1.


11. Main Controller Initialization

The central application object is created in non-GUI mode:

App = new Main(args, false);

After complete command-line parsing, repository initialization is performed through:

App.initialize();

The console layer then invokes:

console.start_application();

12. Command-Line Argument Parsing

12.1 parse_arguments()

private static void parse_arguments(string[] args)

The method processes all arguments beginning at index 1. Index 0 contains the executable path.

Each recognized option updates fields in the shared Main controller.


13. Snapshot Creation Options

The following options select snapshot-creation modes:

  • --check – create a snapshot only when a configured schedule is due
  • --create – always create an on-demand snapshot

The corresponding application modes are:

backup
ondemand

Both modes disable timestamped and debug output by default.


14. Restore and Clone Options

The restore options are:

  • --restore – restore an existing snapshot
  • --clone – use the restore engine to clone the current system

For ordinary restoration:

App.mirror_system = false;
App.app_mode = "restore";

For cloning:

App.mirror_system = true;
App.app_mode = "restore";

The clone option is accepted by the parser even though it is omitted from the generated help text.


15. Snapshot Deletion Options

The deletion modes are:

  • --delete – delete a selected snapshot
  • --delete-all – delete every snapshot in the repository

The corresponding application modes are:

delete
delete-all

16. Listing Options

The console can display repository and device information through:

  • --list
  • --list-snapshots
  • --list-devices

The first two options select:

App.app_mode = "list-snapshots";

The device option selects:

App.app_mode = "list-devices";

17. Snapshot Metadata Options

User comments can be supplied with:

--comment <text>
--comments <text>

The value is stored in:

App.cmd_comments

Snapshot tags are supplied through:

--tags O,B,H,D,W,M

The value is stored in App.cmd_tags and validated immediately through:

App.validate_cmd_tags();

18. Restore Device Options

The following options specify restore-related devices:

  • --target or --target-device – root restore target
  • --grub or --grub-device – GRUB installation device
  • --backup-device or --snapshot-device – snapshot repository device
  • --snapshot or --snapshot-name – snapshot to restore or delete

The supplied values can later be resolved as:

  • Device names
  • Filesystem UUIDs
  • Known device symbolic links

19. GRUB Options

The option:

--skip-grub

sets:

App.cmd_skip_grub = true;

Supplying a GRUB device enables bootloader reinstallation and stores the requested target:

App.reinstall_grub2 = true;
App.cmd_grub_device = <device>;

20. Output and Automation Options

The global output options are:

  • --verbose – display RSYNC output
  • --quiet – suppress RSYNC output
  • --debug – enable debug and command logging

Scripted execution is enabled through:

--scripted

This option also automatically confirms destructive prompts:

App.cmd_scripted = true;
App.cmd_confirm = true;

The option:

--yes

sets only the confirmation flag.


21. Snapshot Engine Overrides

The configured snapshot engine can be overridden through:

  • --btrfs
  • --rsync

The parser updates both the active mode and the nullable command-line override:

App.btrfs_mode
App.cmd_btrfs_mode

22. Deprecated Options

The following legacy arguments are rejected:

  • --backup – replaced by --check
  • --backup-now – replaced by --create

The parser displays an explanatory error and terminates through App.exit_app(1).


23. Invalid Arguments

Any unrecognized argument produces:

Invalid command line arguments: <argument>

The application mode is changed to:

abort

The process then exits with status 1.


24. Application Dispatch

24.1 start_application()

public bool start_application()

This method dispatches the operation selected by App.app_mode.

The supported dispatch targets are:

  • backup → scheduled snapshot check
  • ondemand → forced snapshot creation
  • restore → snapshot restoration or cloning
  • delete → single-snapshot deletion
  • delete-all → complete snapshot deletion
  • list-snapshots → repository status and snapshot table
  • list-devices → Linux filesystem device table

25. Live-System Restrictions

Before dispatching an operation, the class checks:

App.live_system()

In a normal live environment, the following modes are rejected:

backup
ondemand

The console reports:

Snapshots cannot be created in Live CD mode

Recovery mode is handled by the central Main.live_system() logic and can bypass this restriction.


26. Version Output

26.1 version_message()

The version output uses:

<application-name> <version>

Example:

BastionGuard Backup 1.0.0

27. Help Output

27.1 help_message()

The generated help document contains:

  • Application name and version
  • Syntax summary
  • Snapshot-listing options
  • Backup options
  • Restore options
  • Deletion options
  • Global options
  • Examples
  • Operational notes

The command syntax is based on:

bastionguard-backup

Example commands include:

bastionguard-backup --list
bastionguard-backup --create --comments "after update" --tags D
bastionguard-backup --restore --snapshot "2026-07-12_10-30-00" --target /dev/sda1
bastionguard-backup --delete --snapshot "2026-07-12_10-30-00"
bastionguard-backup --delete-all

28. Snapshot Table

28.1 list_snapshots()

private void list_snapshots(
    bool paginate,
    int page_size = 20
)

The method displays a table with the following columns:

Num
Name
Tags
Description

Each row displays:

  • The repository-list index
  • A selection marker
  • The snapshot directory name
  • The compact tag representation
  • The user description

When pagination is enabled, only entries beginning at snapshot_list_start_index are displayed.


29. Device Tables

29.1 list_devices()

This method displays a supplied device collection using the columns:

Num
Device
Size
Type
Label

The displayed device name includes parent-device information when available.


29.2 list_all_devices()

The method enumerates devices through:

Device.get_block_devices_using_lsblk()

Only devices satisfying:

dev.has_linux_filesystem()

are included.

The resulting device collection is printed and returned for interactive selection.


30. GRUB Device Table

30.1 list_grub_devices()

The GRUB device list includes:

  • Complete disk devices
  • Partitions containing Linux filesystems

Encrypted mappings and loop devices are excluded.

The table displays:

Num
Device
Description

Disk descriptions can include vendor, model, and an [MBR] marker.

Partition descriptions include filesystem type, size, and optional label.


31. Grid Rendering

31.1 print_grid()

private void print_grid(
    string[,] grid,
    bool[] right_align,
    bool has_header = true
)

The method provides reusable fixed-width terminal-table formatting.

Its processing steps are:

  1. Measure the maximum string length in each column
  2. Build left- or right-aligned format strings
  3. Print each row with two spaces between columns
  4. Print a 78-character separator after the header

32. Snapshot Creation

32.1 create_snapshot()

private bool create_snapshot(bool ondemand)

The console creation workflow:

  1. Selects or validates the snapshot repository device
  2. Calls the central snapshot-creation method
select_snapshot_device(false);
return App.create_snapshot(ondemand, null);

The parent GTK window is null because the operation runs from the terminal.


33. Restore Workflow

33.1 restore_snapshot()

The complete console restore workflow is:

  1. Select the snapshot repository device
  2. Select the snapshot to restore
  3. Display an interactive restore notice
  4. Initialize the target mount list
  5. Map target devices in RSYNC mode
  6. Select the GRUB device in RSYNC mode
  7. Request final confirmation
  8. Mount the target filesystems
  9. Delegate restoration to Main.restore_snapshot()

BTRFS restoration skips the RSYNC device-mapping and GRUB-selection stages.


34. Interactive Restore Notice

Unless scripted mode is active, the user is informed that pressing Enter accepts default values for subsequent prompts.

The workflow pauses until the user presses Enter.


35. Snapshot Repository Device Selection

35.1 select_snapshot_device()

private void select_snapshot_device(bool prompt_if_empty)

Device selection is skipped during system cloning because the clone source is the active system.

Candidate devices are taken from App.partitions and filtered to Linux filesystems.

The user is prompted when:

  • No repository device is currently available
  • The operation requires snapshots and the current repository contains none

35.2 Scripted Device Failure

In scripted mode, missing or unavailable repository devices do not generate an interactive prompt.

The class reports either:

Snapshot device not selected

or:

Snapshot device not available
Device not found: UUID='...'

The application then exits with status 1.


35.3 Interactive Device Selection

The user can select a repository device by:

  • Numeric list index
  • Device path
  • Pressing a to abort

Input is attempted at most three times.


35.4 BTRFS Repository Validation

When BTRFS mode is enabled, the selected device is validated through:

App.check_device_for_backup(dev, true)

The device must represent a BTRFS system disk containing the root @ subvolume.


36. Snapshot Selection

36.1 select_snapshot()

The method selects a snapshot for restoration or deletion.

When --snapshot is supplied, the repository is searched for an exact snapshot-name match.

If no matching snapshot is found, the method reports an error.


36.2 Scripted Snapshot Selection

Scripted mode requires an explicit snapshot name:

--snapshot <name>

If it is omitted, the application exits with an error instead of prompting.


36.3 Interactive Snapshot Selection

Interactive selection supports:

  • A numeric snapshot index
  • p – display the previous page
  • n – display the next page
  • a – abort

The user receives three attempts to provide a valid selection.


37. Restore and Delete Snapshot Assignment

The selected restore snapshot is assigned to:

App.snapshot_to_restore

The selected deletion snapshot is assigned to:

App.snapshot_to_delete

A null selection causes the application to terminate with an error.


38. Restore Mount Initialization

38.1 init_mounts()

The method delegates initial mount construction to:

App.init_mount_list();

Mount entries whose device remains null are then removed from the console restore list.

The remaining entries represent mount points that require an explicit target-device association.


39. Restore Device Mapping

39.1 map_devices()

The method maps restore mount points to target devices.

If --target-device is supplied, the requested root target is resolved by:

  • Device path
  • Filesystem UUID
  • Known device symbolic link

A missing target device causes immediate termination.


39.2 Default Device Mapping

For every mount point, the default target depends on the operation:

  • Clone mode: the selected root destination
  • Normal restore: the original mapped device when available
  • Fallback: the selected root destination

39.3 Current-System Restore

If the selected target root matches the active system root UUID, the device-mapping loop stops.

The existing system mount structure can then be reused by the restore engine.


39.4 Scripted Mapping

In scripted mode, each default device name must resolve successfully through:

Device.get_device_by_name(default_device)

A failed lookup terminates the operation.


39.5 Interactive Mapping

For each mount point, the user can:

  • Press Enter to accept the default
  • Enter r to use the root device
  • Enter a device path
  • Enter a numeric device index
  • Enter a to abort

The selected mapping is written to the corresponding MountEntry.


40. GRUB Device Selection

40.1 select_grub_device()

The method controls whether GRUB is reinstalled and selects its target device.

It begins by preserving the defaults generated by the central restore controller and resetting the active selection.


40.2 Command-Line GRUB Device

A supplied GRUB target is resolved against the GRUB-capable device list using:

  • Device path
  • Filesystem UUID
  • Partition symbolic links

A missing device terminates the restore operation.


40.3 Clone Behavior

System cloning always enables GRUB reinstallation:

App.reinstall_grub2 = true;

40.4 Interactive Reinstallation Prompt

For an ordinary restore, the user may be asked:

Re-install GRUB2 bootloader? (y/n)

The default behavior is derived from the central controller’s distribution and restore-target analysis.

The --skip-grub option suppresses reinstallation.


40.5 GRUB Target Prompt

When GRUB reinstallation is enabled but no device has been selected, the class displays the GRUB device table.

The user can select:

  • A numeric index
  • A device path
  • The default target by pressing Enter
  • a to abort

41. Restore Confirmation

41.1 confirm_restore()

If automatic confirmation is disabled, the class requests restore messages from:

App.get_restore_messages()

The messages describe:

  • Devices and mount points that will be modified
  • Reboot implications
  • The restore disclaimer

The user must answer y to continue.

Entering n or a aborts the operation.


42. Terminal Input Timeout

All interactive input helpers create a TimeoutCounter:

var counter = new TimeoutCounter();
counter.exit_on_timeout();

After stdin.read_line() returns, the timeout counter is stopped.

This prevents unattended interactive operations from waiting indefinitely.


43. Generic Device Input

43.1 read_stdin_device()

The method accepts:

  • a – abort
  • An empty line – select the default device when available
  • A path containing / – resolve by device name
  • A number – resolve by list index

The values c and d are also treated as default-device requests.


44. Mount-Device Input

44.1 read_stdin_device_mounts()

This method handles target-device selection for a specific mount point.

Accepted values include:

  • Empty input – keep the current mapping or use the clone root device
  • r or n – use the selected root target
  • a – abort
  • A device path
  • A numeric index

45. Numeric Device Resolution

45.1 get_device_from_index()

The method parses the supplied text as an int64 and returns the device stored at the corresponding list position.

An invalid or out-of-range index returns null.


46. Snapshot Input and Pagination

46.1 read_stdin_snapshot()

The method handles interactive snapshot selection.

The pagination controls change snapshot_list_start_index in increments of ten:

p → -10
n → +10

The index is prevented from becoming negative or moving beyond the available snapshot count.

Snapshot names containing underscores or hyphens are currently rejected in the interactive reader. Selection is therefore performed by numeric index.


47. GRUB Confirmation Input

47.1 read_stdin_grub_install()

The accepted values are:

  • Empty input – apply the recommended default
  • y – reinstall GRUB
  • n – skip GRUB reinstallation
  • a – abort

The method updates both:

App.reinstall_grub2
App.cmd_skip_grub

48. Restore Confirmation Input

48.1 read_stdin_restore_confirm()

The accepted values are:

  • y – enable App.cmd_confirm
  • n – abort
  • a – abort

Empty or unknown input is rejected.


49. Single-Snapshot Deletion

49.1 delete_snapshot()

The deletion workflow is:

  1. Select the repository device
  2. Select the snapshot
  3. Invoke synchronous snapshot removal
App.snapshot_to_delete.remove(true);

The underlying Snapshot object selects the appropriate RSYNC or BTRFS deletion backend.


50. Complete Snapshot Deletion

50.1 delete_all_snapshots()

The method selects the repository device and iterates through every loaded snapshot:

foreach(var snap in App.repo.snapshots){
    snap.remove(true);
}

Each deletion is performed synchronously.


51. Console Operation Flow

The complete runtime flow is:

  1. Parse the requested operation
  2. Initialize the main controller and repository
  3. Validate live-system restrictions
  4. Select required repository, snapshot, and target devices
  5. Collect interactive confirmation when necessary
  6. Delegate the operation to the main backup engine
  7. Perform centralized cleanup through App.exit_app()
  8. Return exit status 0 on success or 1 on failure

52. Runtime and Security Considerations

  • Administrator enforcement: operational commands require root or equivalent privileges.
  • Non-interactive support: scripted mode prevents terminal prompts and enables confirmation automatically.
  • Input timeouts: interactive reads are protected by TimeoutCounter.
  • Device validation: target and repository devices are resolved against detected block-device information.
  • BTRFS validation: BTRFS repositories must contain a supported system subvolume layout.
  • Restore confirmation: destructive restore operations require explicit confirmation unless automation options are used.
  • GRUB control: bootloader reinstallation can be accepted, skipped, or directed to a specific device.
  • Live-environment protection: ordinary live systems cannot create snapshots.
  • Centralized cleanup: application exit is delegated to Main.exit_app().
  • Translated interface: console messages use the configured gettext domain.

53. Implementation Considerations

  • Argument bounds: options requiring a value use args[++k] without checking whether another argument exists.
  • Global controller: the console relies on a mutable global App object.
  • Hidden clone option: --clone is parsed but intentionally omitted from the help output.
  • Pagination mismatch: list_snapshots() defaults to a page size of 20, while the previous and next controls move by 10 entries.
  • Unused parameter: list_grub_devices() accepts print_to_console but does not use it.
  • Snapshot-name input: interactive selection does not currently support entering a snapshot name directly.
  • Redundant variable: the command-line snapshot lookup declares a found flag that is never set because successful lookup returns immediately.
  • Normalized null input: several input methods replace a null line with an empty string, making later null checks redundant.
  • Undocumented defaults: generic device input treats c and d as default selections even though the prompts do not describe these values.
  • Duplicate condition: read_stdin_grub_install() contains two branches checking for null or empty input.
  • Deletion status: delete_snapshot() ignores the Boolean result returned by Snapshot.remove() and always returns true.
  • Delete-all status: delete_all_snapshots() ignores individual deletion failures and always returns true.
  • Repository refresh: deletion methods do not explicitly reload the repository after completing their loops.
  • Empty-repository exit code: interactive snapshot selection exits with status 0 when no snapshots are found.
  • Direct process exit: the administrator check calls the external exit() function instead of centralized application cleanup.
  • Legacy author identity: help output continues to identify the original Timeshift author.
  • GTK dependency: the command-line module imports GTK and processes GTK-compatible runtime behavior even though it does not create a normal GUI.

54. 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.