From 600b2d8a61c9cb9a259d552892310cb7426739f9 Mon Sep 17 00:00:00 2001 From: Federico Kircheis Date: Mon, 8 Jun 2026 19:55:30 +0200 Subject: [PATCH] Add option for building without gui libs --- docs/development.md | 22 +- docs/man/meson.build | 8 +- meson.build | 33 +- meson_options.txt | 1 + src/AppConsole.vala | 123 +++-- src/Core/Main.vala | 721 +++++++++++++++-------------- src/Utility/Device.vala | 200 ++++---- src/Utility/FileItem.vala | 38 +- src/Utility/GtkHeadlessTypes.vala | 5 + src/Utility/GtkHelperHeadless.vala | 16 + src/Utility/TeeJee.Misc.vala | 13 +- src/meson.build | 53 ++- 12 files changed, 654 insertions(+), 579 deletions(-) create mode 100644 src/Utility/GtkHeadlessTypes.vala create mode 100644 src/Utility/GtkHelperHeadless.vala diff --git a/docs/development.md b/docs/development.md index a6649420..36af0419 100644 --- a/docs/development.md +++ b/docs/development.md @@ -8,9 +8,13 @@ This documentation provides instructions for developing Timeshift. - help2man - gettext - valac -- libvte-2.91-dev - libgee-0.8-dev - libjson-glib-dev + +Optional (for GTK frontend): + +- libgtk-3-dev +- libvte-2.91-dev - libxapp-dev If you are using a Debian-based distribution, you can install these @@ -21,9 +25,12 @@ sudo apt install meson \ help2man \ gettext \ valac \ -libvte-2.91-dev \ libgee-0.8-dev \ -libjson-glib-dev \ +libjson-glib-dev + +# Optional GUI dependencies +sudo apt install libgtk-3-dev \ +libvte-2.91-dev \ libxapp-dev ``` @@ -48,6 +55,15 @@ meson setup build meson compile -C build ``` +### Building command-line-only + +To build only the `timeshift` command-line tool (without GTK/VTE dependencies): + +```bash +meson setup build-cli -Dgtk=false +meson compile -C build-cli +``` + ### Step 4. Install Timeshift ```bash diff --git a/docs/man/meson.build b/docs/man/meson.build index f86fc1bc..79cac280 100644 --- a/docs/man/meson.build +++ b/docs/man/meson.build @@ -2,9 +2,14 @@ help2man = find_program('help2man') mans = [ ['timeshift', [help2man, '-N', '--output=@OUTPUT@', timeshift]], - ['timeshift-gtk', [help2man, '-N', '--output=@OUTPUT@', timeshift_gtk]], ] +if enable_gtk + mans += [ + ['timeshift-gtk', [help2man, '-N', '--output=@OUTPUT@', timeshift_gtk]], + ] +endif + foreach man: mans custom_target( man[0], @@ -14,4 +19,3 @@ foreach man: mans install_dir: join_paths(get_option('mandir'), 'man1'), ) endforeach - diff --git a/meson.build b/meson.build index d3530a2b..5e4e34c6 100644 --- a/meson.build +++ b/meson.build @@ -8,26 +8,37 @@ project( default_options : ['c_std=c99', 'build.c_std=c99'] ) -dependencies = [ +dependencies_common = [ dependency('glib-2.0'), dependency('gobject-2.0'), - dependency('gtk+-3.0'), dependency('gio-2.0'), dependency('gio-unix-2.0'), dependency('json-glib-1.0'), - dependency('vte-2.91'), dependency('gee-0.8'), meson.get_compiler('vala').find_library('posix'), meson.get_compiler('c').find_library('m'), ] +enable_gtk = get_option('gtk') +dependencies_gui = [] +if enable_gtk + dependencies_gui = [ + dependency('gtk+-3.0'), + dependency('vte-2.91'), + ] +endif + enable_xapp = get_option('xapp') -if enable_xapp - xapp_dep = dependency('xapp') +xapp_dep = [] +if enable_xapp and enable_gtk + xapp_dep = [dependency('xapp')] add_project_arguments('-D', 'XAPP', language: 'vala') - dependencies += xapp_dep +elif enable_xapp and not enable_gtk + warning('xapp support requested, but GTK is disabled. xapp support will be disabled.') endif +dependencies = dependencies_common + dependencies_gui + xapp_dep + # Include the translations module i18n = import('i18n') @@ -47,7 +58,9 @@ if enable_man subdir('docs/man') endif -install_data( - sources: 'debian/com.linuxmint.timeshift.metainfo.xml', - install_dir: join_paths(get_option('datadir'), 'metainfo') -) +if enable_gtk + install_data( + sources: 'debian/com.linuxmint.timeshift.metainfo.xml', + install_dir: join_paths(get_option('datadir'), 'metainfo') + ) +endif diff --git a/meson_options.txt b/meson_options.txt index 80250b34..215aeaac 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,2 +1,3 @@ option('man', type: 'boolean', value: true, description: 'build man pages using help2man') option('xapp', type: 'boolean', value: true, description: 'enable support for xapp library') +option('gtk', type: 'boolean', value: true, description: 'build GTK frontend and GUI assets') diff --git a/src/AppConsole.vala b/src/AppConsole.vala index e0983ccc..6083ea2c 100644 --- a/src/AppConsole.vala +++ b/src/AppConsole.vala @@ -22,7 +22,6 @@ */ using GLib; -using Gtk; using Gee; //using Soup; using Json; @@ -31,7 +30,6 @@ using TeeJee.Logging; using TeeJee.FileSystem; using TeeJee.JsonHelper; using TeeJee.ProcessHelper; -using TeeJee.GtkHelper; using TeeJee.System; using TeeJee.Misc; @@ -52,11 +50,11 @@ public class AppConsole : GLib.Object { public int snapshot_list_start_index = 0; public static int main (string[] args) { - + set_locale(); LOG_TIMESTAMP = false; - + if (args.length > 1) { switch (args[1].down()) { case "--help": @@ -80,13 +78,13 @@ public class AppConsole : GLib.Object { LOG_ENABLE = false; init_tmp(); LOG_ENABLE = true; - + check_if_admin(); App = new Main(args, false); parse_arguments(args); App.initialize(); - + var console = new AppConsole(); bool ok = console.start_application(); App.exit_app((ok) ? 0 : 1); @@ -95,7 +93,7 @@ public class AppConsole : GLib.Object { } private static void set_locale() { - + log_debug("setting locale..."); Intl.setlocale(GLib.LocaleCategory.MESSAGES, "timeshift"); Intl.textdomain(GETTEXT_PACKAGE); @@ -104,7 +102,7 @@ public class AppConsole : GLib.Object { } public static void check_if_admin(){ - + if (!user_is_admin()) { log_msg(_("Application needs admin access.")); log_msg(_("Please run the application as admin (using 'sudo' or 'su')")); @@ -118,7 +116,7 @@ public class AppConsole : GLib.Object { private static void parse_arguments(string[] args){ log_debug("AppConsole: parse_arguments()"); - + for (int k = 1; k < args.length; k++) // Oth arg is app path { switch (args[k].down()){ @@ -165,7 +163,7 @@ public class AppConsole : GLib.Object { case "--comment": case "--comments": App.cmd_comments = args[++k]; - break; + break; case "--skip-grub": App.cmd_skip_grub = true; @@ -253,7 +251,7 @@ public class AppConsole : GLib.Object { log_error("Run 'timeshift --help' to list all available options"); App.exit_app(1); break; - + default: LOG_TIMESTAMP = false; log_error("%s: %s".printf( @@ -283,7 +281,7 @@ public class AppConsole : GLib.Object { public bool start_application(){ log_debug("AppConsole: start_application()"); - + bool is_success = true; if (App.live_system()){ @@ -349,10 +347,10 @@ public class AppConsole : GLib.Object { } private static string help_message (){ - + string msg = "\n%s v%s by Tony George (%s)\n".printf( AppName, AppVersion, AppAuthorEmail); - + msg += "\n"; msg += "Syntax:\n"; msg += "\n"; @@ -460,7 +458,7 @@ public class AppConsole : GLib.Object { } private void list_devices(Gee.ArrayList device_list){ - + string[,] grid = new string[device_list.size+1,6]; bool[] right_align = { false, false, false, true, true, false}; @@ -538,7 +536,7 @@ public class AppConsole : GLib.Object { if (dev.type == "disk"){ grub_device_list.add(dev); } - else if (dev.type == "part"){ + else if (dev.type == "part"){ if (dev.has_linux_filesystem()){ grub_device_list.add(dev); } @@ -614,15 +612,15 @@ public class AppConsole : GLib.Object { select_snapshot_device(false); return App.create_snapshot(ondemand, null); } - + // restore - + private bool restore_snapshot(){ select_snapshot_device(true); select_snapshot_for_restore(); - + if (!App.cmd_scripted) { stdout.printf("\n\n"); log_msg(string.nfill(78, '*')); @@ -634,7 +632,7 @@ public class AppConsole : GLib.Object { } init_mounts(); - + if (!App.btrfs_mode){ map_devices(); @@ -664,13 +662,13 @@ public class AppConsole : GLib.Object { list.add(pi); } } - + if ((App.repo.device == null) || (prompt_if_empty && (App.repo.snapshots.size == 0))){ //prompt user for backup device log_msg(""); if (App.cmd_scripted){ - + if (App.repo.device == null){ if (App.backup_uuid.length == 0){ log_debug("device is null"); @@ -711,7 +709,7 @@ public class AppConsole : GLib.Object { } log_msg(""); - + if (dev == null){ log_error(_("Failed to get input from user in 3 attempts")); log_msg(_("Aborted.")); @@ -728,13 +726,13 @@ public class AppConsole : GLib.Object { private Snapshot? select_snapshot(){ Snapshot selected_snapshot = null; - + log_debug("AppConsole: select_snapshot()"); - + if (App.mirror_system){ return null; } - + if (App.cmd_snapshot.length > 0){ //check command line arguments @@ -781,7 +779,7 @@ public class AppConsole : GLib.Object { selected_snapshot = read_stdin_snapshot(); } log_msg(""); - + if (selected_snapshot == null){ log_error(_("Failed to get input from user in 3 attempts")); log_msg(_("Aborted.")); @@ -807,18 +805,18 @@ public class AppConsole : GLib.Object { App.exit_app(1); } } - + private void init_mounts(){ log_debug("AppConsole: init_mounts()"); - + App.init_mount_list(); // remove mount points which will remain on root fs for(int i = App.mount_list.size-1; i >= 0; i--){ - + var entry = App.mount_list[i]; - + if (entry.device == null){ App.mount_list.remove(entry); } @@ -828,15 +826,15 @@ public class AppConsole : GLib.Object { private void map_devices(){ log_debug("AppConsole: map_devices()"); - + if (App.cmd_target_device.length > 0){ //check command line arguments bool found = false; foreach(Device pi in App.partitions) { - + if (!pi.has_linux_filesystem()) { continue; } - + if ((pi.device == App.cmd_target_device)||((pi.uuid == App.cmd_target_device))){ App.dst_root = pi; found = true; @@ -863,7 +861,7 @@ public class AppConsole : GLib.Object { } for(int i = 0; i < App.mount_list.size; i++){ - + MountEntry mnt = App.mount_list[i]; Device dev = null; string default_device = ""; @@ -873,7 +871,7 @@ public class AppConsole : GLib.Object { // no need to ask user to map remaining devices if restoring same system if ((App.dst_root != null) && (App.sys_root != null) && (App.dst_root.uuid == App.sys_root.uuid)){ - + break; } @@ -909,14 +907,14 @@ public class AppConsole : GLib.Object { while (dev == null){ attempts++; if (attempts > 3) { break; } - + stdout.printf("" + _("[ENTER = Default (%s), r = Root device, a = Abort]").printf(default_device) + "\n\n"); - + stdout.printf( _("Enter device name or number") + ": "); - + stdout.flush(); dev = read_stdin_device_mounts(device_list, mnt); } @@ -932,26 +930,26 @@ public class AppConsole : GLib.Object { if (dev != null){ log_debug("selected: %s".printf(dev.uuid)); - + mnt.device = dev; log_msg(string.nfill(78, '*')); - + if ((mnt.mount_point != "/") && (App.dst_root != null) && (dev.device == App.dst_root.device)){ - + log_msg(_("'%s' will be on root device").printf(mnt.mount_point), true); } else{ log_msg(_("'%s' will be on '%s'").printf( mnt.mount_point, mnt.device.short_name_with_alias), true); - + //log_debug("UUID=%s".printf(dst_root.uuid)); } log_msg(string.nfill(78, '*')); } - + } } @@ -961,17 +959,17 @@ public class AppConsole : GLib.Object { bool grub_reinstall_default = App.reinstall_grub2; App.reinstall_grub2 = false; App.grub_device = ""; - + if (App.cmd_grub_device.length > 0){ log_debug("Grub device is specified as command argument"); - + //check command line arguments bool found = false; var device_list = list_grub_devices(false); - + foreach(Device dev in device_list) { - + if ((dev.device == App.cmd_grub_device) ||((dev.uuid.length > 0) && (dev.uuid == App.cmd_grub_device))){ @@ -1000,18 +998,18 @@ public class AppConsole : GLib.Object { return; } } - + if (App.mirror_system){ App.reinstall_grub2 = true; } else { if ((App.cmd_skip_grub == false) && (App.reinstall_grub2 == false)){ log_msg(""); - + if (App.cmd_scripted) { App.reinstall_grub2 = grub_reinstall_default; } - else if (App.cmd_confirm){ + else if (App.cmd_confirm){ // suppose that it would be as user would typed 'y' in read_stdin_grub_install App.reinstall_grub2 = true; } @@ -1035,7 +1033,7 @@ public class AppConsole : GLib.Object { } if ((App.reinstall_grub2) && (App.grub_device.length == 0)){ - + log_msg(""); log_msg(_("Select GRUB device") + ":\n"); var device_list = list_grub_devices(); @@ -1070,13 +1068,13 @@ public class AppConsole : GLib.Object { list.add(pi); } } - + dev = read_stdin_device(device_list, grub_device_default); if (dev != null) { break; } } if (dev == null){ - + log_error(_("Failed to get input from user in 3 attempts")); log_msg(_("Aborted.")); App.exit_app(0); @@ -1089,7 +1087,7 @@ public class AppConsole : GLib.Object { } if ((App.reinstall_grub2) && (App.grub_device.length > 0)){ - + log_msg(string.nfill(78, '*')); log_msg(_("GRUB Device") + ": %s".printf(App.grub_device)); log_msg(string.nfill(78, '*')); @@ -1102,7 +1100,7 @@ public class AppConsole : GLib.Object { } private void confirm_restore(){ - + if (App.cmd_confirm == false){ string msg_devices = ""; @@ -1129,9 +1127,9 @@ public class AppConsole : GLib.Object { } } } - + private Device? read_stdin_device(Gee.ArrayList device_list, string device_default){ - + var counter = new TimeoutCounter(); counter.exit_on_timeout(); string? line = stdin.read_line(); @@ -1322,7 +1320,7 @@ public class AppConsole : GLib.Object { private bool read_stdin_restore_confirm(){ var counter = new TimeoutCounter(); counter.exit_on_timeout(); - + string? line = stdin.read_line(); counter.stop(); @@ -1367,11 +1365,11 @@ public class AppConsole : GLib.Object { } public bool delete_all_snapshots(){ - + select_snapshot_device(true); - + //return App.repo.remove_all(); - + foreach(var snap in App.repo.snapshots){ snap.remove(true); } @@ -1380,4 +1378,3 @@ public class AppConsole : GLib.Object { } } - diff --git a/src/Core/Main.vala b/src/Core/Main.vala index 52c4650e..47164fd3 100644 --- a/src/Core/Main.vala +++ b/src/Core/Main.vala @@ -22,7 +22,6 @@ */ using GLib; -using Gtk; using Gee; using Json; @@ -37,7 +36,7 @@ using TeeJee.Misc; public bool GTK_INITIALIZED = false; public class Main : GLib.Object{ - + public string app_path = ""; public string share_folder = ""; public string rsnapshot_conf_path = ""; @@ -45,7 +44,7 @@ public class Main : GLib.Object{ public string app_conf_path_old = ""; public string app_conf_path_default = ""; public bool first_run = false; - + public string backup_uuid = ""; public string backup_parent_uuid = ""; @@ -53,9 +52,9 @@ public class Main : GLib.Object{ public bool include_btrfs_home_for_backup = false; public bool include_btrfs_home_for_restore = false; public static bool btrfs_version__can_recursive_delete = false; - + public bool stop_cron_emails = true; - + public Gee.ArrayList partitions; public Gee.ArrayList exclude_list_user; @@ -66,8 +65,8 @@ public class Main : GLib.Object{ public Gee.ArrayList exclude_list_apps; public Gee.ArrayList mount_list; public Gee.ArrayList exclude_app_names; - - public SnapshotRepo repo; + + public SnapshotRepo repo; //temp //private Gee.ArrayList grub_device_list; @@ -106,7 +105,7 @@ public class Main : GLib.Object{ //global vars for controlling threads public bool thr_success = false; - + public bool thread_restore_running = false; public bool thread_restore_success = false; @@ -115,7 +114,7 @@ public class Main : GLib.Object{ public bool thread_subvol_info_running = false; public bool thread_subvol_info_success = false; - + public int thr_retval = -1; public string thr_arg1 = ""; public bool thr_timeout_active = false; @@ -127,7 +126,7 @@ public class Main : GLib.Object{ public const uint64 MIN_FREE_SPACE = 1 * GB; public static uint64 first_snapshot_size = 0; public static int64 first_snapshot_count = 0; - + public string log_dir = ""; public string log_file = ""; public AppLock app_lock; @@ -136,7 +135,7 @@ public class Main : GLib.Object{ public const string date_format_default = "%Y-%m-%d %H:%M:%S"; public Gee.ArrayList delete_list; - + public Snapshot snapshot_to_delete; public Snapshot snapshot_to_restore; //public Device restore_target; @@ -157,11 +156,11 @@ public class Main : GLib.Object{ public string cmd_comments = ""; public string cmd_tags = ""; public bool? cmd_btrfs_mode = null; - + public string progress_text = ""; public Gtk.Window? parent_window = null; - + public RsyncTask task; public DeleteFileTask delete_file_task; public RsyncSpaceCheckTask space_check_task; @@ -189,12 +188,12 @@ public class Main : GLib.Object{ } public Main(string[] args, bool gui_mode){ - + this.mount_point_app = "/run/timeshift/%d".printf(Posix.getpid()); dir_create(this.mount_point_app); - + parse_some_arguments(args); - + if (gui_mode){ app_mode = ""; parent_window = new Gtk.Window(); // dummy @@ -214,7 +213,7 @@ public class Main : GLib.Object{ try { string suffix = gui_mode ? "gui" : app_mode; - + DateTime now = new DateTime.now_local(); log_dir = "/var/log/timeshift"; log_file = path_combine(log_dir, @@ -238,9 +237,9 @@ public class Main : GLib.Object{ catch (Error e) { log_error (e.message); } - + // get Linux distribution info ----------------------- - + this.current_distro = LinuxDistro.get_dist_info("/"); if (LOG_DEBUG || gui_mode){ @@ -264,7 +263,7 @@ public class Main : GLib.Object{ // check and create lock ---------------------------- app_lock = new AppLock(); - + if (!app_lock.create("timeshift", app_mode)){ if (gui_mode){ string msg = ""; @@ -294,7 +293,7 @@ public class Main : GLib.Object{ this.app_conf_path_old = "/etc/timeshift.json"; this.app_conf_path_default = GLib.Path.build_path (GLib.Path.DIR_SEPARATOR_S, Constants.SYSCONFDIR, "timeshift", "default.json"); //sys_root and sys_home will be initialized by update_partition_list() - + // check if running locally ------------------------ string local_exec = args[0]; @@ -332,7 +331,7 @@ public class Main : GLib.Object{ delete_file_task = new DeleteFileTask(); update_partitions(); - + detect_system_devices(); detect_encrypted_dirs(); @@ -341,19 +340,23 @@ public class Main : GLib.Object{ load_app_config(); + #if TS_NO_GTK + // No icon theme initialization is needed for command-line-only builds. + #else IconManager.init(args, AppShortName); - + #endif + log_debug("Main(): ok"); } public void initialize(){ - + initialize_repo(); } public bool check_dependencies(out string msg){ log_debug("Main: check_dependencies()"); - + string[] dependencies = { "rsync","/sbin/blkid","df","mount","umount","fuser","crontab","cp","rm","touch","ln","sync", "run-parts"}; //"shutdown","chroot", msg = ""; @@ -481,14 +484,14 @@ public class Main : GLib.Object{ } public void check_and_remove_timeshift_btrfs(){ - + if (cmd_exists("timeshift-btrfs")){ string std_out, std_err; exec_sync("timeshift-btrfs-uninstall", out std_out, out std_err); log_msg(_("** Uninstalled Timeshift BTRFS **")); } } - + public bool check_btrfs_layout_system(Gtk.Window? win = null){ log_debug("check_btrfs_layout_system()"); @@ -503,7 +506,7 @@ public class Main : GLib.Object{ msg += _("Only ubuntu-type layouts with @ and @home subvolumes are currently supported.") + "\n\n"; msg += _("Application will exit.") + "\n\n"; string title = _("Not Supported"); - + if (app_mode == ""){ gtk_set_busy(false, win); gtk_messagebox(title, msg, win, true); @@ -517,15 +520,15 @@ public class Main : GLib.Object{ } public bool check_btrfs_layout(Device? dev_root, Device? dev_home, bool unlock){ - + bool supported = true; // keep true for non-btrfs systems if ((dev_root != null) && (dev_root.fstype == "btrfs")){ - + if ((dev_home != null) && (dev_home.fstype == "btrfs")){ if (dev_home != dev_root){ - + supported = supported && check_btrfs_volume(dev_root, "@", unlock); if (include_btrfs_home_for_backup){ @@ -547,7 +550,7 @@ public class Main : GLib.Object{ } private void parse_some_arguments(string[] args){ - + for (int k = 1; k < args.length; k++) // Oth arg is app path { switch (args[k].down()){ @@ -565,7 +568,7 @@ public class Main : GLib.Object{ btrfs_mode = false; cmd_btrfs_mode = btrfs_mode; break; - + case "--check": app_mode = "backup"; break; @@ -603,23 +606,23 @@ public class Main : GLib.Object{ } private void detect_encrypted_dirs(){ - + current_system_users = SystemUser.read_users_from_file("/etc/passwd"); string txt = ""; users_with_encrypted_home = ""; encrypted_home_dirs = ""; encrypted_private_dirs = ""; - + foreach(var user in current_system_users.values){ - + if (user.is_system) { continue; } - + if (txt.length > 0) { txt += " "; } txt += "%s".printf(user.name); if (user.has_encrypted_home){ - + users_with_encrypted_home += " %s".printf(user.name); encrypted_home_dirs += "%s\n".printf(user.home_path); @@ -633,26 +636,26 @@ public class Main : GLib.Object{ } } users_with_encrypted_home = users_with_encrypted_home.strip(); - + log_debug("Users: %s".printf(txt)); log_debug("Encrypted home users: %s".printf(users_with_encrypted_home)); log_debug("Encrypted home dirs:\n%s".printf(encrypted_home_dirs)); log_debug("Encrypted private dirs:\n%s".printf(encrypted_private_dirs)); } - + // exclude lists - + public void add_default_exclude_entries(){ log_debug("Main: add_default_exclude_entries()"); - + exclude_list_user = new Gee.ArrayList(); exclude_list_default = new Gee.ArrayList(); exclude_list_default_extra = new Gee.ArrayList(); exclude_list_home = new Gee.ArrayList(); exclude_list_restore = new Gee.ArrayList(); exclude_list_apps = new Gee.ArrayList(); - + partitions = new Gee.ArrayList(); // default exclude entries ------------------- @@ -752,7 +755,7 @@ public class Main : GLib.Object{ exclude_list_default_extra.add("/var/cache/xbps/*"); exclude_list_default_extra.add("/var/cache/zypp/*"); exclude_list_default_extra.add("/var/cache/edb/*"); - + // default home ---------------- //exclude_list_home.add("+ /root/.**"); @@ -778,9 +781,9 @@ public class Main : GLib.Object{ public void add_app_exclude_entries(){ log_debug("Main: add_app_exclude_entries()"); - + AppExcludeEntry.clear(); - + if (snapshot_to_restore != null){ add_app_exclude_entries_for_prefix(path_combine(snapshot_to_restore.path, "localhost")); } @@ -795,7 +798,7 @@ public class Main : GLib.Object{ } private void add_app_exclude_entries_for_prefix(string path_prefix){ - + string path = ""; path = path_combine(path_prefix, "root"); @@ -804,19 +807,19 @@ public class Main : GLib.Object{ path = path_combine(path_prefix, "home"); AppExcludeEntry.add_app_exclude_entries_from_home(path); } - + public Gee.ArrayList create_exclude_list_for_backup(){ log_debug("Main: create_exclude_list_for_backup()"); - + var list = new Gee.ArrayList(); // add user entries from current setting // user entry is first since rsync prioritizes the first // inclusion/exclusion patterns seen // ------------------------------------------------------- - + foreach(string path in exclude_list_user){ if (!list.contains(path)){ list.add(path); @@ -824,7 +827,7 @@ public class Main : GLib.Object{ } // add default entries --------------------------- - + foreach(string path in exclude_list_default){ if (!list.contains(path)){ list.add(path); @@ -832,7 +835,7 @@ public class Main : GLib.Object{ } // add default extra entries --------------------------- - + foreach(string path in exclude_list_default_extra){ if (!list.contains(path)){ list.add(path); @@ -843,22 +846,22 @@ public class Main : GLib.Object{ // decrypted contents should never be backed-up or restored // this overrides all other user entries in exclude_list_user // ------------------------------------------------------- - + foreach(var user in current_system_users.values){ - + if (user.is_system){ continue; } - + if (user.has_encrypted_home){ - + // exclude decrypted contents in user's home ($HOME) string path = "%s/**".printf(user.home_path); list.add(path); } - + if (user.has_encrypted_private_dirs){ foreach(string enc_path in user.encrypted_private_dirs){ - + // exclude decrypted contents in private dirs ($HOME/Private) string path = "%s/**".printf(enc_path); list.add(path); @@ -880,7 +883,7 @@ public class Main : GLib.Object{ inc_pattern = "+ /home/.ecryptfs/%s/***".printf(user.name); exc_pattern = "/home/.ecryptfs/%s/***".printf(user.name); } - + bool include_hidden = exclude_list_user.contains(inc_hidden_pattern); bool include_all = exclude_list_user.contains(inc_pattern); bool exclude_all = !include_hidden && !include_all; @@ -899,7 +902,7 @@ public class Main : GLib.Object{ } // add common entries for excluding home folders for all users -------- - + foreach(string path in exclude_list_home){ if (!list.contains(path)){ list.add(path); @@ -912,16 +915,16 @@ public class Main : GLib.Object{ } log_debug("Main: create_exclude_list_for_backup(): exit"); - + return list; } public Gee.ArrayList create_exclude_list_for_restore(){ log_debug("Main: create_exclude_list_for_restore()"); - + exclude_list_restore.clear(); - + //add user entries from current settings //user entry is first since rsync prioritizes the first //inclusion/exclusion patterns seen @@ -929,7 +932,7 @@ public class Main : GLib.Object{ // skip include filters for restore if (path.strip().has_prefix("+")){ continue; } - + if (!exclude_list_restore.contains(path) && !exclude_list_home.contains(path)){ exclude_list_restore.add(path); } @@ -987,7 +990,7 @@ public class Main : GLib.Object{ } log_debug("Main: create_exclude_list_for_restore(): exit"); - + return exclude_list_restore; } @@ -996,16 +999,16 @@ public class Main : GLib.Object{ private string? save_exclude_list_for_backup(string output_path){ log_debug("Main: save_exclude_list_for_backup()"); - + var list = create_exclude_list_for_backup(); - + var txt = ""; foreach(var pattern in list){ if (pattern.strip().length > 0){ txt += "%s\n".printf(pattern); } } - + string list_file = path_combine(output_path, "exclude.list"); if (file_write(list_file, txt)) { return list_file; @@ -1016,11 +1019,11 @@ public class Main : GLib.Object{ public bool save_exclude_list_for_restore(string output_path){ log_debug("Main: save_exclude_list_for_restore()"); - + var list = create_exclude_list_for_restore(); log_debug("Exclude list -------------"); - + var txt = ""; foreach(var pattern in list){ if (pattern.strip().length > 0){ @@ -1028,14 +1031,14 @@ public class Main : GLib.Object{ log_debug(pattern); } } - + return file_write(restore_exclude_file, txt); } public void save_exclude_list_selections(){ log_debug("Main: save_exclude_list_selections()"); - + // add new selected items foreach(var entry in exclude_list_apps){ if (entry.enabled && !exclude_app_names.contains(entry.name)){ @@ -1058,7 +1061,7 @@ public class Main : GLib.Object{ } // properties - + public bool scheduled{ get{ return !live_system() @@ -1099,13 +1102,13 @@ public class Main : GLib.Object{ bool update_symlinks = false; string sys_uuid = (sys_root == null) ? "" : sys_root.uuid; - + try { if (btrfs_mode && (check_btrfs_layout_system() == false)){ return false; } - + // create a timestamp DateTime now = new DateTime.now_local(); @@ -1114,7 +1117,7 @@ public class Main : GLib.Object{ log_error(repo.status_message); log_error(repo.status_details + "\n"); - + // remove invalid snapshots if (app_mode.length != 0){ repo.auto_remove(); @@ -1138,7 +1141,7 @@ public class Main : GLib.Object{ // ondemand if (is_ondemand){ - bool ok = create_snapshot_for_tag ("ondemand",now); + bool ok = create_snapshot_for_tag ("ondemand",now); if(!ok){ return false; } @@ -1315,11 +1318,11 @@ public class Main : GLib.Object{ log_msg(_("Scheduled snapshots are disabled") + " - " + _("Nothing to do!")); cron_job_update(); } - + log_msg(string.nfill(78, '-')); repo.load_snapshots(); // reload list for new snapshot - + if (app_mode.length != 0){ repo.auto_remove(); repo.load_snapshots(); @@ -1328,7 +1331,7 @@ public class Main : GLib.Object{ if (update_symlinks){ repo.create_symlinks(); } - + //log_msg("OK"); } catch(Error e){ @@ -1342,11 +1345,11 @@ public class Main : GLib.Object{ private bool create_snapshot_for_tag(string tag, DateTime dt_created){ log_debug("Main: backup_and_rotate()"); - + // save start time var dt_begin = new DateTime.now_local(); bool status = true; - + // get system boot time DateTime now = new DateTime.now_local(); DateTime dt_sys_boot = now.add_seconds((-1) * get_system_uptime_seconds()); @@ -1381,7 +1384,7 @@ public class Main : GLib.Object{ } if (backup_to_rotate != null){ - + // tag the backup backup_to_rotate.add_tag(tag); @@ -1397,7 +1400,7 @@ public class Main : GLib.Object{ log_error(repo.status_details); exit_app(); } - + if (repo.mount_path.length == 0){ log_error("Backup location not mounted"); @@ -1446,7 +1449,7 @@ public class Main : GLib.Object{ var dt_end = new DateTime.now_local(); TimeSpan elapsed = dt_end.difference(dt_begin); long seconds = (long)(elapsed * 1.0 / TimeSpan.SECOND); - + var message = ""; if (new_snapshot != null){ message = "%s %s (%lds)".printf((btrfs_mode ? "BTRFS" : "RSYNC"), _("Snapshot saved successfully"), seconds); @@ -1471,7 +1474,7 @@ public class Main : GLib.Object{ log_msg("Checking if target drive has enough free space for a snapshot (RSYNC)"); log_msg("Target device: %s, mount path: %s".printf(repo.device.device, repo.mount_path)); - + string time_stamp = dt_created.format("%Y-%m-%d_%H-%M-%S"); string snapshot_dir = repo.snapshots_path; string snapshot_name = time_stamp; @@ -1479,7 +1482,7 @@ public class Main : GLib.Object{ dir_create(snapshot_path); string localhost_path = path_combine(snapshot_path, "localhost"); dir_create(localhost_path); - + string sys_uuid = (sys_root == null) ? "" : sys_root.uuid; Snapshot snapshot_to_link = null; @@ -1487,16 +1490,16 @@ public class Main : GLib.Object{ // check if a snapshot was restored recently and use it for linking --------- try{ - + string ctl_path = path_combine(snapshot_dir, ".sync-restore"); var f = File.new_for_path(ctl_path); - + if (f.query_exists()){ // read snapshot name from file string snap_path = file_read(ctl_path); string snap_name = file_basename(snap_path); - + // find the snapshot that was restored foreach(var bak in repo.snapshots){ if ((bak.name == snap_name) && (bak.sys_uuid == sys_uuid)){ @@ -1573,11 +1576,11 @@ public class Main : GLib.Object{ log_msg(_("Estimating system size...")); estimate_system_size(); } - + log_msg(_("Creating new snapshot...") + "(RSYNC)"); log_msg(_("Saving to device") + ": %s".printf(repo.device.device) + ", " + _("mounted at path") + ": %s".printf(repo.mount_path)); - + // take new backup --------------------------------- if (repo.mount_path.length == 0){ @@ -1592,7 +1595,7 @@ public class Main : GLib.Object{ dir_create(snapshot_path); string localhost_path = path_combine(snapshot_path, "localhost"); dir_create(localhost_path); - + string sys_uuid = (sys_root == null) ? "" : sys_root.uuid; Snapshot snapshot_to_link = null; @@ -1600,16 +1603,16 @@ public class Main : GLib.Object{ // check if a snapshot was restored recently and use it for linking --------- try{ - + string ctl_path = path_combine(snapshot_dir, ".sync-restore"); var f = File.new_for_path(ctl_path); - + if (f.query_exists()){ // read snapshot name from file string snap_path = file_read(ctl_path); string snap_name = file_basename(snap_path); - + // find the snapshot that was restored foreach(var bak in repo.snapshots){ if ((bak.name == snap_name) && (bak.sys_uuid == sys_uuid)){ @@ -1647,9 +1650,9 @@ public class Main : GLib.Object{ log_error(_("Failed to save exclude list")); return null; } - + // rsync file system ------------------- - + progress_text = _("Syncing files with rsync..."); log_msg(progress_text); @@ -1695,7 +1698,7 @@ public class Main : GLib.Object{ } string initial_tags = (tag == "ondemand") ? "" : tag; - + // write control file // this step is redundant - just in case if app crashes while parsing log file in next step //Snapshot.write_control_file( @@ -1743,24 +1746,24 @@ public class Main : GLib.Object{ string snapshot_name = time_stamp; string sys_uuid = (sys_root == null) ? "" : sys_root.uuid; string snapshot_path = ""; - + // create subvolume snapshots var subvol_names = new string[] { "@" }; - + if (include_btrfs_home_for_backup){ - + subvol_names = new string[] { "@","@home" }; } - + foreach(var subvol_name in subvol_names){ snapshot_path = path_combine(repo.mount_paths[subvol_name], "timeshift-btrfs/snapshots/%s".printf(snapshot_name)); - + dir_create(snapshot_path, true); - + string src_path = path_combine(repo.mount_paths[subvol_name], subvol_name); - + string dst_path = path_combine(snapshot_path, subvol_name); // Dirty hack to fix the nested subvilumes issue (cause of issue is unknown) @@ -1770,17 +1773,17 @@ public class Main : GLib.Object{ else if (dst_path.has_suffix("/@home/@home")){ dst_path = dst_path.replace("/@home/@home", "/@home"); } - + string cmd = "btrfs subvolume snapshot '%s' '%s' \n".printf(src_path, dst_path); - + if (LOG_COMMANDS) { log_debug(cmd); } string std_out, std_err; - + int ret_val = exec_sync(cmd, out std_out, out std_err); - + if (ret_val != 0){ - + log_error (std_err); log_error(_("btrfs returned an error") + ": %d".printf(ret_val)); log_error(_("Failed to create subvolume snapshot") + ": %s".printf(subvol_name)); @@ -1796,7 +1799,7 @@ public class Main : GLib.Object{ snapshot_path = path_combine(repo.mount_paths["@"], "timeshift-btrfs/snapshots/%s".printf(snapshot_name)); string initial_tags = (tag == "ondemand") ? "" : tag; - + // write control file var snapshot = Snapshot.write_control_file( snapshot_path, dt_created, sys_uuid, current_distro.full_name(), @@ -1809,7 +1812,7 @@ public class Main : GLib.Object{ snapshot.update_control_file(); // save subvolume info set_tags(snapshot); // set_tags() will update the control file - + // Perform any post-backup actions this.run_post_backup_hooks(snapshot_path); @@ -1835,7 +1838,7 @@ public class Main : GLib.Object{ private void set_tags(Snapshot snapshot){ // add tags passed on commandline for both --check and --create - + foreach(string tag in cmd_tags.split(",")){ switch(tag.strip().up()){ case "O": @@ -1860,7 +1863,7 @@ public class Main : GLib.Object{ } // add tag as ondemand if no other tag is specified - + if (snapshot.tags.size == 0){ snapshot.add_tag("ondemand"); } @@ -1884,14 +1887,14 @@ public class Main : GLib.Object{ } } } - + // gui delete public void delete_begin(){ log_debug("Main: delete_begin()"); progress_text = _("Preparing..."); - + try { thread_delete_running = true; thread_delete_success = false; @@ -1919,7 +1922,7 @@ public class Main : GLib.Object{ foreach(var bak in delete_list){ bak.mark_for_deletion(); } - + while (delete_list.size > 0){ var bak = delete_list[0]; @@ -1935,7 +1938,7 @@ public class Main : GLib.Object{ delete_file_task = bak.delete_file_task; delete_file_task.prg_count_total = (int64) Main.first_snapshot_count; - + status = bak.remove(true); // wait till complete if (delete_file_task.status != AppStatus.CANCELLED){ @@ -1950,7 +1953,7 @@ public class Main : GLib.Object{ thread_delete_running = false; thread_delete_success = status; } - + // restore - properties public Device? dst_root{ @@ -2028,12 +2031,12 @@ public class Main : GLib.Object{ } } } - + public bool restore_current_system{ get { if ((sys_root != null) && ((dst_root != null && dst_root.device == sys_root.device) || (dst_root != null && dst_root.uuid == sys_root.uuid))){ - + return true; } else{ @@ -2054,7 +2057,7 @@ public class Main : GLib.Object{ } } } - + public string restore_target_path{ owned get { if (restore_current_system){ @@ -2079,16 +2082,16 @@ public class Main : GLib.Object{ } // restore - + public void init_mount_list(){ log_debug("Main: init_mount_list()"); - + mount_list.clear(); Gee.ArrayList fstab_list = null; Gee.ArrayList crypttab_list = null; - + if (mirror_system){ string fstab_path = "/etc/fstab"; fstab_list = FsTabEntry.read_file(fstab_path); @@ -2104,21 +2107,21 @@ public class Main : GLib.Object{ bool boot_found = false; bool home_found = false; dst_root = null; - + foreach(var fs_entry in fstab_list){ // skip mounting for non-system devices ---------- - + if (!fs_entry.is_for_system_directory()){ continue; } // skip mounting excluded devices ----------------------- - + string p1 = "%s/*".printf(fs_entry.mount_point); string p2 = "%s/**".printf(fs_entry.mount_point); string p3 = "%s/***".printf(fs_entry.mount_point); - + if (exclude_list_default.contains(p1) || exclude_list_user.contains(p1)){ continue; } @@ -2130,7 +2133,7 @@ public class Main : GLib.Object{ } // find device by name or uuid -------------------------- - + Device dev_fstab = null; if (fs_entry.device_uuid.length > 0){ dev_fstab = Device.get_device_by_uuid(fs_entry.device_uuid); @@ -2145,30 +2148,30 @@ public class Main : GLib.Object{ Check if the device mentioned in fstab entry is a mapped device. If it is, then try finding the parent device which may be available on the current system. Prompt user to unlock it if found. - + Note: Mapped name may be different on running system, or it may be same. Since it is not reliable, we will try to identify the parent instead of the mapped device. */ - + if (fs_entry.device_string.has_prefix("/dev/mapper/")){ - + string mapped_name = fs_entry.device_string.replace("/dev/mapper/",""); - + foreach(var crypt_entry in crypttab_list){ - + if (crypt_entry.mapped_name == mapped_name){ // we found the entry for the mapped device fs_entry.device_string = crypt_entry.device_string; if (fs_entry.device_uuid.length > 0){ - + // we have the parent's uuid. get the luks device and prompt user to unlock it. var dev_luks = Device.get_device_by_uuid(fs_entry.device_uuid); - + if (dev_luks != null){ - + string msg_out, msg_err; var dev_unlocked = Device.luks_unlock( dev_luks, "", "", parent_window, out msg_out, out msg_err); @@ -2193,12 +2196,12 @@ public class Main : GLib.Object{ } if (dev_fstab != null){ - + log_debug("added: dev: %s, path: %s, options: %s".printf( dev_fstab.device, fs_entry.mount_point, fs_entry.options)); - + mount_list.add(new MountEntry(dev_fstab, fs_entry.mount_point, fs_entry.options)); - + if (fs_entry.mount_point == "/"){ dst_root = dev_fstab; } @@ -2242,12 +2245,12 @@ public class Main : GLib.Object{ All other mounts like /home will be defaulted to target device (to prevent the "cloned" system from using the original device) */ - + if (mirror_system){ dst_root = null; foreach (var entry in mount_list){ // user should select another device - entry.device = null; + entry.device = null; } } @@ -2266,7 +2269,7 @@ public class Main : GLib.Object{ }); init_boot_options(); // boot options depend on the mount list - + log_debug("Main: init_mount_list(): exit"); } @@ -2302,26 +2305,26 @@ public class Main : GLib.Object{ } } } - + public bool restore_snapshot(Gtk.Window? parent_win){ log_debug("Main: restore_snapshot()"); - + parent_window = parent_win; // remove mount points which will remain on root fs - + for(int i = mount_list.size-1; i >= 0; i--){ var entry = mount_list[i]; if (entry.device == null){ mount_list.remove(entry); } } - + // check if we have all required inputs and abort on error - + if (!mirror_system){ - + if (repo.device == null){ log_error(_("Backup device not specified!")); return false; @@ -2331,7 +2334,7 @@ public class Main : GLib.Object{ log_msg(_("Backup Device") + ": %s".printf(repo.device.device)); log_msg(string.nfill(78, '*')); } - + if (snapshot_to_restore == null){ log_error(_("Snapshot to restore not specified!")); return false; @@ -2347,7 +2350,7 @@ public class Main : GLib.Object{ log_msg(string.nfill(78, '*')); } } - + // final check - check if target root device is mounted if (btrfs_mode){ @@ -2377,7 +2380,7 @@ public class Main : GLib.Object{ try { thread_restore_running = true; thr_success = false; - + if (btrfs_mode){ new Thread.try ("restore-execute-btrfs", () => {restore_execute_btrfs(); return true;}); } @@ -2401,19 +2404,19 @@ public class Main : GLib.Object{ } log_debug("Main: restore_snapshot(): exit"); - + return thr_success; } public void get_restore_messages(bool formatted, out string msg_devices, out string msg_reboot, out string msg_disclaimer){ - + string msg = ""; log_debug("Main: get_restore_messages()"); // msg_devices ----------------------------------------- - + if (!formatted){ msg += "\n%s\n%s\n%s\n".printf( string.nfill(70,'='), @@ -2421,25 +2424,25 @@ public class Main : GLib.Object{ string.nfill(70,'=') ); } - + msg += _("Data will be modified on following devices:") + "\n\n"; int max_mount = _("Mount").length; int max_dev = _("Device").length; foreach(var entry in mount_list){ - + if (entry.device == null){ continue; } if (btrfs_mode){ - + if (entry.subvolume_name().length == 0){ continue; } - + if (!App.snapshot_to_restore.subvolumes.has_key(entry.subvolume_name())){ continue; } if ((entry.subvolume_name() == "@home") && !include_btrfs_home_for_restore){ continue; } } - + string dev_name = entry.device.full_name_with_parent; if (entry.subvolume_name().length > 0){ dev_name = dev_name + "(%s)".printf(entry.subvolume_name()); @@ -2447,7 +2450,7 @@ public class Main : GLib.Object{ else if (entry.lvm_name().length > 0){ dev_name = dev_name + "(%s)".printf(entry.lvm_name()); } - + if (dev_name.length > max_dev){ max_dev = dev_name.length; } @@ -2462,20 +2465,20 @@ public class Main : GLib.Object{ txt += string.nfill(max_dev, '-') + " " + string.nfill(max_mount, '-'); txt += "\n"; - + foreach(var entry in mount_list){ - + if (entry.device == null){ continue; } if (btrfs_mode){ if (entry.subvolume_name().length == 0){ continue; } - + if (!App.snapshot_to_restore.subvolumes.has_key(entry.subvolume_name())){ continue; } if ((entry.subvolume_name() == "@home") && !include_btrfs_home_for_restore){ continue; } } - + string dev_name = entry.device.full_name_with_parent; if (entry.subvolume_name().length > 0){ dev_name = dev_name + "(%s)".printf(entry.subvolume_name()); @@ -2483,7 +2486,7 @@ public class Main : GLib.Object{ else if (entry.lvm_name().length > 0){ dev_name = dev_name + "(%s)".printf(entry.lvm_name()); } - + txt += ("%%-%ds %%-%ds".printf(max_dev, max_mount)).printf(dev_name, entry.mount_point); txt += "\n"; @@ -2502,9 +2505,9 @@ public class Main : GLib.Object{ //msg += _("If restore fails and you are unable to boot the system, then boot from the Live CD, install Timeshift, and try to restore again.") + "\n"; // msg_reboot ----------------------- - + msg = ""; - if (restore_current_system){ + if (restore_current_system){ msg += _("Please save your work and close all applications.") + "\n"; msg += _("System will reboot after files are restored."); } @@ -2521,18 +2524,18 @@ public class Main : GLib.Object{ string.nfill(70,'=') ); } - + msg += _("This software comes without absolutely NO warranty and the author takes no responsibility for any damage arising from the use of this program."); msg += " " + _("If these terms are not acceptable to you, please do not proceed beyond this point!"); if (!formatted){ msg += "\n"; } - + msg_disclaimer = msg; // display messages in console mode - + if (app_mode.length > 0){ log_msg(msg_devices); log_msg(msg_reboot); @@ -2545,7 +2548,7 @@ public class Main : GLib.Object{ private void create_restore_scripts(out string sh_sync, out string sh_finish){ log_debug("Main: create_restore_scripts()"); - + string sh = "export LC_ALL=C.UTF-8\n"; // create scripts -------------------------------------- @@ -2554,7 +2557,7 @@ public class Main : GLib.Object{ sh += "echo ''\n"; if (restore_current_system){ log_debug("restoring current system"); - + sh += "echo '" + _("Please do not interrupt the restore process!") + "'\n"; sh += "echo '" + _("System will reboot after files are restored") + "'\n"; } @@ -2568,7 +2571,7 @@ public class Main : GLib.Object{ if (dry_run){ sh += " --dry-run"; } - + sh += " --log-file=\"%s\"".printf(restore_log_file); sh += " --exclude-from=\"%s\"".printf(restore_exclude_file); @@ -2591,14 +2594,14 @@ public class Main : GLib.Object{ log_debug(sh); sh_sync = sh; - + // chroot and re-install grub2 --------------------- log_debug("reinstall_grub2=%s".printf(reinstall_grub2.to_string())); log_debug("grub_device=%s".printf((grub_device == null) ? "null" : grub_device)); var target_distro = LinuxDistro.get_dist_info(restore_target_path); - + sh = ""; string chroot = ""; @@ -2615,14 +2618,14 @@ public class Main : GLib.Object{ } if (reinstall_grub2 && (grub_device != null) && (grub_device.length > 0)){ - + sh += "sync \n"; sh += "echo '' \n"; sh += "echo '" + _("Re-installing GRUB2 bootloader...") + "' \n"; // search for other operating systems //sh += "chroot \"%s\" os-prober \n".printf(restore_target_path); - + // re-install grub --------------- if (target_distro.dist_type == "redhat"){ @@ -2632,7 +2635,7 @@ public class Main : GLib.Object{ sh += "%s grub2-install --recheck --force %s \n".printf(chroot, grub_device); /* NOTE: - * grub2-install should NOT be run on Fedora EFI systems + * grub2-install should NOT be run on Fedora EFI systems * https://fedoraproject.org/wiki/GRUB_2 * Instead following packages should be reinstalled: * dnf reinstall grub2-efi grub2-efi-modules shim @@ -2660,7 +2663,7 @@ public class Main : GLib.Object{ if (update_initramfs){ sh += "echo '' \n"; sh += "echo '" + _("Generating initramfs...") + "' \n"; - + if (target_distro.dist_type == "redhat"){ sh += "%s dracut -f -v \n".printf(chroot); } @@ -2671,13 +2674,13 @@ public class Main : GLib.Object{ sh += "%s update-initramfs -u -k all \n".printf(chroot); } } - + // update grub menu -------------- if (update_grub){ sh += "echo '' \n"; sh += "echo '" + _("Updating GRUB menu...") + "' \n"; - + if (target_distro.dist_type == "redhat"){ sh += "%s grub2-mkconfig -o /boot/grub2/grub.cfg \n".printf(chroot); } @@ -2691,12 +2694,12 @@ public class Main : GLib.Object{ sh += "sync \n"; sh += "echo '' \n"; } - + // sync file systems sh += "echo '" + _("Syncing file systems...") + "' \n"; sh += "sync ; sleep 10s; \n"; sh += "echo '' \n"; - + if (!restore_current_system){ // unmount chrooted system sh += "echo '" + _("Cleaning up...") + "' \n"; @@ -2730,10 +2733,10 @@ public class Main : GLib.Object{ private bool restore_current_console(string sh_sync, string sh_finish){ log_debug("Main: restore_current_console()"); - + string script = sh_sync + sh_finish; int ret_val = -1; - + if (cmd_verbose){ //current/other system, console, verbose ret_val = exec_script_sync(script, null, null, false, false, false, true); @@ -2750,11 +2753,13 @@ public class Main : GLib.Object{ return (ret_val == 0); } +#if !TS_NO_GTK private bool restore_current_gui(string sh_sync, string sh_finish){ log_debug("Main: restore_current_gui()"); - + string script = sh_sync + sh_finish; + string temp_script = save_bash_script_temp(script); var dlg = new TerminalWindow.with_parent(parent_window); @@ -2762,16 +2767,17 @@ public class Main : GLib.Object{ return true; } +#endif private bool restore_other_console(string sh_sync, string sh_finish){ log_debug("Main: restore_other_console()"); - + // execute sh_sync -------------------- - + string script = sh_sync; int ret_val = -1; - + if (cmd_verbose){ ret_val = exec_script_sync(script, null, null, false, false, false, true); log_msg(""); @@ -2784,7 +2790,7 @@ public class Main : GLib.Object{ } // update files ------------------- - + fix_fstab_file(restore_target_path); fix_crypttab_file(restore_target_path); @@ -2797,7 +2803,7 @@ public class Main : GLib.Object{ log_debug("executing sh_finish: "); log_debug(sh_finish); - + script = sh_finish; if (cmd_verbose){ @@ -2814,10 +2820,11 @@ public class Main : GLib.Object{ return (ret_val == 0); } +#if !TS_NO_GTK private bool restore_other_gui(string sh_sync, string sh_finish){ log_debug("Main: restore_other_gui()"); - + progress_text = _("Building file list..."); task = new RsyncTask(); @@ -2828,7 +2835,7 @@ public class Main : GLib.Object{ task.delete_after = true; task.dry_run = dry_run; - + if (mirror_system){ task.source_path = "/"; } @@ -2837,7 +2844,7 @@ public class Main : GLib.Object{ } task.dest_path = restore_target_path; - + task.exclude_from_file = restore_exclude_file; task.rsync_log_file = restore_log_file; @@ -2866,7 +2873,7 @@ public class Main : GLib.Object{ progress_text = _("Syncing files with rsync..."); } } - + gtk_do_events(); } @@ -2892,29 +2899,30 @@ public class Main : GLib.Object{ log_debug("executing sh_finish: "); log_debug(sh_finish); - + int ret_val = exec_script_sync(sh_finish, null, null, false, false, false, true); log_debug("script exit code: %d".printf(ret_val)); return (ret_val == 0); } +#endif private void fix_fstab_file(string target_path){ log_debug("Main: fix_fstab_file()"); - + string fstab_path = path_combine(target_path, "etc/fstab"); if (!file_exists(fstab_path)){ log_debug("File not found: %s".printf(fstab_path)); return; } - + var fstab_list = FsTabEntry.read_file(fstab_path); log_debug("updating entries (1/2)..."); - + foreach(var mnt in mount_list){ // find existing var entry = FsTabEntry.find_entry_by_mount_point(fstab_list, mnt.mount_point); @@ -2945,18 +2953,18 @@ public class Main : GLib.Object{ * */ log_debug("updating entries(2/2)..."); - + for(int i = fstab_list.size - 1; i >= 0; i--){ var entry = fstab_list[i]; - + if (!entry.is_for_system_directory()){ continue; } - + var mnt = MountEntry.find_entry_by_mount_point(mount_list, entry.mount_point); if (mnt == null){ fstab_list.remove(entry); } } - + // write the updated file log_debug("writing updated file..."); @@ -2970,22 +2978,22 @@ public class Main : GLib.Object{ foreach(var entry in fstab_list){ if (entry.mount_point.length == 0){ continue; } if (!entry.mount_point.has_prefix("/")){ continue; } - + string mount_path = path_combine( target_path, entry.mount_point); - + if (entry.is_comment || entry.is_empty_line || (mount_path.length == 0)){ - + continue; } if (!dir_exists(mount_path)){ - + log_msg("Created mount point on target device: %s".printf( entry.mount_point)); - + dir_create(mount_path); } } @@ -2996,7 +3004,7 @@ public class Main : GLib.Object{ private void fix_crypttab_file(string target_path){ log_debug("Main: fix_crypttab_file()"); - + string crypttab_path = path_combine(target_path, "etc/crypttab"); if (!file_exists(crypttab_path)){ @@ -3005,11 +3013,11 @@ public class Main : GLib.Object{ } var crypttab_list = CryptTabEntry.read_file(crypttab_path); - + // add option "nofail" to existing entries log_debug("checking for 'nofail' option..."); - + foreach(var entry in crypttab_list){ entry.append_option("nofail"); } @@ -3017,7 +3025,7 @@ public class Main : GLib.Object{ log_debug("updating entries..."); // check and add entries for mapped devices which are encrypted - + foreach(var mnt in mount_list){ if ((mnt.device != null) && (mnt.device.parent != null) && ((mnt.device.is_on_encrypted_partition()) @@ -3038,7 +3046,7 @@ public class Main : GLib.Object{ entry = new CryptTabEntry(); crypttab_list.add(entry); } - + // set custom values entry.device_uuid = crypt_parent; entry.mapped_name = "luks-%s".printf(crypt_parent); @@ -3057,7 +3065,7 @@ public class Main : GLib.Object{ } private void check_and_repair_filesystems(){ - + if (!restore_current_system){ string sh_fsck = "echo '" + _("Checking file systems for errors...") + "' \n"; foreach(var mnt in mount_list){ @@ -3071,22 +3079,22 @@ public class Main : GLib.Object{ } public bool restore_execute_rsync(){ - + log_debug("Main: restore_execute_rsync()"); try{ log_debug("source_path=%s".printf(restore_source_path)); log_debug("target_path=%s".printf(restore_target_path)); - + string sh_sync, sh_finish; create_restore_scripts(out sh_sync, out sh_finish); - + save_exclude_list_for_restore(restore_source_path); file_delete(restore_log_file); file_delete(restore_log_file + "-changes"); file_delete(restore_log_file + ".gz"); - + if (restore_current_system){ string control_file_path = path_combine(snapshot_to_restore.path,".sync-restore"); @@ -3099,7 +3107,7 @@ public class Main : GLib.Object{ } // run the scripts -------------------- - + if (snapshot_to_restore != null){ if (dry_run){ log_msg(_("Comparing Files (Dry Run)...")); @@ -3116,7 +3124,8 @@ public class Main : GLib.Object{ log_msg(progress_text); bool ok = true; - + + #if !TS_NO_GTK if (app_mode == ""){ // GUI if (!restore_current_system || dry_run){ ok = restore_other_gui(sh_sync, sh_finish); @@ -3125,7 +3134,9 @@ public class Main : GLib.Object{ ok = restore_current_gui(sh_sync, sh_finish); } } - else{ + else + #endif + { if (restore_current_system){ ok = restore_current_console(sh_sync, sh_finish); } @@ -3154,27 +3165,27 @@ public class Main : GLib.Object{ thread_restore_running = false; return thr_success; } - + public bool restore_execute_btrfs(){ log_debug("Main: restore_execute_btrfs()"); - + bool ok = create_pre_restore_snapshot_btrfs(); log_msg(string.nfill(78, '-')); - + if (!ok){ thread_restore_running = false; thr_success = false; return thr_success; } - + // restore snapshot subvolumes by creating new subvolume snapshots foreach(var subvol in snapshot_to_restore.subvolumes.values){ if ((subvol.name == "@home") && !include_btrfs_home_for_restore){ continue; } - + subvol.restore(); } @@ -3185,13 +3196,13 @@ public class Main : GLib.Object{ log_debug("Running post-restore tasks..."); string sh = "test -d \"/etc/timeshift/restore-hooks.d\" &&" + - " export TS_SNAPSHOT_PATH=\"" + snapshot_to_restore.path + "\" &&" + + " export TS_SNAPSHOT_PATH=\"" + snapshot_to_restore.path + "\" &&" + " run-parts --verbose /etc/timeshift/restore-hooks.d"; exec_script_sync(sh, null, null, false, false, false, true); log_debug("Finished running post-restore tasks..."); - + if (restore_current_system){ log_msg(_("Snapshot will become active after system is rebooted.")); } @@ -3205,13 +3216,13 @@ public class Main : GLib.Object{ public bool create_pre_restore_snapshot_btrfs(){ log_debug("Main: create_pre_restore_snapshot_btrfs()"); - + string cmd, std_out, std_err; DateTime dt_created = new DateTime.now_local(); string time_stamp = dt_created.format("%Y-%m-%d_%H-%M-%S"); string snapshot_name = time_stamp; string snapshot_path = ""; - + /* Note: * The @ and @home subvolumes need to be backed-up only if they are in use by the system. * If user restores a snapshot and then tries to restore another snapshot before the next reboot @@ -3221,7 +3232,7 @@ public class Main : GLib.Object{ bool create_pre_restore_backup = false; if (restore_current_system){ - + // check for an existing pre-restore backup Snapshot snap_prev = null; @@ -3261,11 +3272,11 @@ public class Main : GLib.Object{ if (create_pre_restore_backup){ log_msg(_("Creating pre-restore snapshot from system subvolumes...")); - + dir_create(snapshot_path); // move subvolumes ---------------- - + bool no_subvolumes_found = true; var subvol_list = new Gee.ArrayList(); @@ -3274,27 +3285,27 @@ public class Main : GLib.Object{ if (include_btrfs_home_for_restore){ subvol_names = new string[] { "@","@home" }; } - + foreach(string subvol_name in subvol_names){ snapshot_path = path_combine(repo.mount_paths[subvol_name], "timeshift-btrfs/snapshots/%s".printf(snapshot_name)); dir_create(snapshot_path, true); - + string src_path = path_combine(repo.mount_paths[subvol_name], subvol_name); if (!dir_exists(src_path)){ log_error(_("Could not find system subvolume") + ": %s".printf(subvol_name)); dir_delete(snapshot_path); continue; } - + no_subvolumes_found = false; string dst_path = path_combine(snapshot_path, subvol_name); cmd = "mv '%s' '%s'".printf(src_path, dst_path); log_debug(cmd); - + int status = exec_sync(cmd, out std_out, out std_err); - + if (status != 0){ log_error (std_err); log_error(_("Failed to move system subvolume to snapshot directory") + ": %s".printf(subvol_name)); @@ -3303,7 +3314,7 @@ public class Main : GLib.Object{ else{ var subvol_dev = (subvol_name == "@") ? repo.device : repo.device_home; subvol_list.add(new Subvolume(subvol_name, dst_path, subvol_dev.uuid, repo)); - + log_msg(_("Moved system subvolume to snapshot directory") + ": %s".printf(subvol_name)); } } @@ -3316,7 +3327,7 @@ public class Main : GLib.Object{ // write control file ----------- snapshot_path = path_combine(repo.mount_paths["@"], "timeshift-btrfs/snapshots/%s".printf(snapshot_name)); - + var snap = Snapshot.write_control_file( snapshot_path, dt_created, repo.device.uuid, LinuxDistro.get_dist_info(path_combine(snapshot_path,"@")).full_name(), @@ -3324,36 +3335,36 @@ public class Main : GLib.Object{ snap.description = "Before restoring '%s'".printf(snapshot_to_restore.date_formatted); snap.live = true; - + // write subvolume info foreach(var subvol in subvol_list){ snap.subvolumes.set(subvol.name, subvol); } - + snap.update_control_file(); // save subvolume info log_msg(_("Created pre-restore snapshot") + ": %s".printf(snap.name)); - + repo.load_snapshots(); } } return true; } - + //app config public void save_app_config(){ log_debug("Main: save_app_config()"); - + var config = new Json.Object(); - + if ((repo != null) && repo.available()){ // save backup device uuid config.set_string_member("backup_device_uuid", (repo.device == null) ? "" : repo.device.uuid); - + // save parent uuid if backup device has parent config.set_string_member("parent_device_uuid", (repo.device.has_parent()) ? repo.device.parent.uuid : ""); @@ -3394,7 +3405,7 @@ public class Main : GLib.Object{ } config.set_string_member("date_format", date_format); - + Json.Array arr = new Json.Array(); foreach(string path in exclude_list_user){ arr.add_string_element(path); @@ -3436,11 +3447,11 @@ public class Main : GLib.Object{ log_debug("Main: load_app_config()"); // check if first run ----------------------- - + var f = File.new_for_path(this.app_conf_path); - + if (!f.query_exists()) { - + if (file_exists(app_conf_path_old)){ // move old file file_move(app_conf_path_old, app_conf_path); @@ -3454,9 +3465,9 @@ public class Main : GLib.Object{ file_copy(app_conf_path_default, app_conf_path); } } - + // load settings from config file -------------------------- - + var parser = new Json.Parser(); try{ parser.load_from_file(this.app_conf_path); @@ -3471,7 +3482,7 @@ public class Main : GLib.Object{ bool do_first_run = json_get_bool(config, "do_first_run", false); // false as default btrfs_mode = json_get_bool(config, "btrfs_mode", false); // false as default - + if (do_first_run){ set_first_run_flag(); } @@ -3482,14 +3493,14 @@ public class Main : GLib.Object{ else{ include_btrfs_home_for_backup = json_get_bool(config, "include_btrfs_home_for_backup", include_btrfs_home_for_backup); } - + include_btrfs_home_for_restore = json_get_bool(config, "include_btrfs_home_for_restore", include_btrfs_home_for_restore); stop_cron_emails = json_get_bool(config, "stop_cron_emails", stop_cron_emails); if (cmd_btrfs_mode != null){ btrfs_mode = cmd_btrfs_mode; //override } - + backup_uuid = json_get_string(config,"backup_device_uuid", backup_uuid); backup_parent_uuid = json_get_string(config,"parent_device_uuid", backup_parent_uuid); @@ -3508,21 +3519,21 @@ public class Main : GLib.Object{ this.date_format = json_get_string(config, "date_format", date_format_default); Main.first_snapshot_size = json_get_uint64(config,"snapshot_size", Main.first_snapshot_size); - + Main.first_snapshot_count = (int64) json_get_uint64(config,"snapshot_count", Main.first_snapshot_count); - + exclude_list_user.clear(); - + if (config.has_member ("exclude")){ - + foreach (Json.Node jnode in config.get_array_member ("exclude").get_elements()) { - + string path = jnode.get_string(); - + if (!exclude_list_user.contains(path) && !exclude_list_default.contains(path) && !exclude_list_home.contains(path)){ - + exclude_list_user.add(path); } } @@ -3531,13 +3542,13 @@ public class Main : GLib.Object{ exclude_app_names.clear(); if (config.has_member ("exclude-apps")){ - + var apps = config.get_array_member("exclude-apps"); - + foreach (Json.Node jnode in apps.get_elements()) { - + string name = jnode.get_string(); - + if (!exclude_app_names.contains(name)){ exclude_app_names.add(name); } @@ -3602,13 +3613,13 @@ public class Main : GLib.Object{ } public void set_first_run_flag(){ - + first_run = true; - + log_msg("First run mode (config file not found)"); // load some defaults for first-run based on user's system type - + bool supported = sys_subvolumes.has_key("@") && cmd_exists("btrfs"); // && sys_subvolumes.has_key("@home") if (supported || (cmd_btrfs_mode == true)){ log_msg(_("Selected default snapshot type") + ": %s".printf("BTRFS")); @@ -3619,11 +3630,11 @@ public class Main : GLib.Object{ btrfs_mode = false; } } - + public void initialize_repo(){ log_debug("Main: initialize_repo()"); - + log_debug("backup_uuid=%s".printf(backup_uuid)); log_debug("backup_parent_uuid=%s".printf(backup_parent_uuid)); @@ -3656,7 +3667,7 @@ public class Main : GLib.Object{ } // select default device for first run mode else if (first_run && (backup_uuid.length == 0)){ - + try_select_default_device_for_backup(parent_window); if ((repo != null) && (repo.device != null)){ @@ -3665,7 +3676,7 @@ public class Main : GLib.Object{ } else { log_debug("Setting snapshot device from config file"); - + // find devices from uuid Device dev = null; Device dev_parent = null; @@ -3681,7 +3692,7 @@ public class Main : GLib.Object{ log_debug("Snapshot device is on an encrypted partition"); repo = new SnapshotRepo.from_uuid(backup_parent_uuid, parent_window, btrfs_mode); } - // try device + // try device else if (dev != null){ log_debug("repo: creating from uuid"); repo = new SnapshotRepo.from_uuid(backup_uuid, parent_window, btrfs_mode); @@ -3709,15 +3720,15 @@ public class Main : GLib.Object{ log_debug("Main: initialize_repo(): exit"); } - + //core functions public void update_partitions(){ log_debug("update_partitions()"); - + partitions.clear(); - + partitions = Device.get_filesystems(); foreach(var pi in partitions){ @@ -3727,7 +3738,7 @@ public class Main : GLib.Object{ repo.device = pi; } } - + if (partitions.size == 0){ log_error("ts: " + _("Failed to get partition list.")); } @@ -3745,9 +3756,9 @@ public class Main : GLib.Object{ sys_home = null; foreach(var pi in partitions){ - + foreach(var mp in pi.mount_points){ - + // skip loop devices - Fedora Live uses loop devices containing ext4-formatted lvm volumes if ((pi.type == "loop") || (pi.has_parent() && (pi.parent.type == "loop"))){ continue; @@ -3810,11 +3821,11 @@ public class Main : GLib.Object{ * */ log_debug("mount_target_device()"); - + if (dst_root == null){ return false; } - + //check and create restore mount point for restore mount_point_restore = mount_point_app + "/restore"; dir_create(mount_point_restore); @@ -3827,13 +3838,13 @@ public class Main : GLib.Object{ foreach(var mp in dev_mounted.mount_points){ if ((mp.mount_point == mount_point_restore) && (mp.mount_options == "subvol=@")){ - + = true; return; //already_mounted } } }*/ - + // unmount unmount_target_device(); @@ -3843,7 +3854,7 @@ public class Main : GLib.Object{ //check subvolume layout bool supported = check_btrfs_layout(dst_root, dst_home, false); - + if (!supported && snapshot_to_restore.has_subvolumes()){ string msg = _("The target partition has an unsupported subvolume layout.") + "\n"; msg += _("Only ubuntu-type layouts with @ and @home subvolumes are currently supported."); @@ -3866,7 +3877,7 @@ public class Main : GLib.Object{ if (mnt.device == null){ continue; } - + // unlock encrypted device if (mnt.device.is_encrypted_partition()){ @@ -3877,7 +3888,7 @@ public class Main : GLib.Object{ else{ // prompt user string msg_out, msg_err; - + var dev_unlocked = Device.luks_unlock( mnt.device, "", "", parent_win, out msg_out, out msg_err); @@ -3892,7 +3903,7 @@ public class Main : GLib.Object{ } string mount_options = ""; - + if (mnt.device.fstype == "btrfs"){ if (mnt.mount_point == "/"){ mount_options = "subvol=@"; @@ -3913,11 +3924,11 @@ public class Main : GLib.Object{ } public void unmount_target_device(bool exit_on_error = true){ - + if (mount_point_restore == null) { return; } log_debug("unmount_target_device()"); - + //unmount the target device only if it was mounted by application if (mount_point_restore.has_prefix(mount_point_app)){ //always true unmount_device(mount_point_restore, exit_on_error); @@ -3952,23 +3963,23 @@ public class Main : GLib.Object{ public bool check_btrfs_volume(Device dev, string subvol_names, bool unlock){ log_debug("check_btrfs_volume():%s".printf(subvol_names)); - + string mnt_btrfs = mount_point_app + "/btrfs"; dir_create(mnt_btrfs); if (!dev.is_mounted_at_path("", mnt_btrfs)){ - + Device.unmount(mnt_btrfs); // unlock encrypted device if (dev.is_encrypted_partition()){ if (unlock){ - + string msg_out, msg_err; var dev_unlocked = Device.luks_unlock( dev, "", "", parent_window, out msg_out, out msg_err); - + if (dev_unlocked == null){ log_debug("device is null"); log_debug("SnapshotRepo: check_btrfs_volume(): exit"); @@ -4020,7 +4031,7 @@ public class Main : GLib.Object{ repo = new SnapshotRepo.from_null(); } } - + update_partitions(); // In BTRFS mode, select the system disk if system disk is BTRFS @@ -4029,7 +4040,7 @@ public class Main : GLib.Object{ repo = new SnapshotRepo.from_device(subvol_root.get_device(), parent_win, btrfs_mode); return; } - + foreach(var dev in partitions){ if (check_device_for_backup(dev, false)){ repo = new SnapshotRepo.from_device(dev, parent_win, btrfs_mode); @@ -4046,7 +4057,7 @@ public class Main : GLib.Object{ if (dev.type == "disk") { return false; } if (dev.has_children()) { return false; } - + if (btrfs_mode && ((dev.fstype == "btrfs")||(dev.fstype == "luks"))){ if (check_btrfs_volume(dev, "@", unlock)){ return true; @@ -4064,7 +4075,7 @@ public class Main : GLib.Object{ public uint64 estimate_system_size(progressCallback? callback = null) { log_debug("estimate_system_size()"); - + if (Main.first_snapshot_size > 0){ return Main.first_snapshot_size; } @@ -4106,7 +4117,7 @@ public class Main : GLib.Object{ save_app_config(); log_debug("estimate_system_size(): ok"); - + return Main.first_snapshot_size; } @@ -4119,13 +4130,13 @@ public class Main : GLib.Object{ repo = parent_repo; // TODO: move query_subvolume_info() and related methods to SnapshotRepo - + if ((repo == null) || !repo.btrfs_mode){ return; } - + log_debug(_("Querying subvolume info...")); - + try { thread_subvol_info_running = true; thread_subvol_info_success = false; @@ -4145,12 +4156,12 @@ public class Main : GLib.Object{ } public void query_subvolume_info_thread(){ - + thread_subvol_info_running = true; //query IDs bool ok = query_subvolume_ids(); - + if (!ok){ thread_subvol_info_success = false; thread_subvol_info_running = false; @@ -4177,11 +4188,11 @@ public class Main : GLib.Object{ } return ok; } - + public bool query_subvolume_id(string subvol_name){ log_debug("query_subvolume_id():%s".printf(subvol_name)); - + string cmd = ""; string std_out; string std_err; @@ -4217,7 +4228,7 @@ public class Main : GLib.Object{ else if ((sys_subvolumes.size > 0) && sys_subvolumes.has_key("@home") && line.has_suffix(sys_subvolumes["@home"].path.replace(repo.mount_paths["@home"] + "/"," "))){ - + subvol = sys_subvolumes["@home"]; } else { @@ -4354,18 +4365,18 @@ public class Main : GLib.Object{ // cron jobs public void cron_job_update(){ - + if (live_system()) { return; } // remove entries created by previous versions ----------- - + string entry = "timeshift --backup"; int count = 0; while (CronTab.has_job(entry, true, false)){ - + CronTab.remove_job(entry, true, true); - + if (++count == 100){ break; } @@ -4375,23 +4386,23 @@ public class Main : GLib.Object{ count = 0; while (CronTab.has_job(entry, true, false)){ - + CronTab.remove_job(entry, true, true); - + if (++count == 100){ break; } } CronTab.remove_script_file("timeshift-hourly", "hourly"); - + // start update --------------------------- - + if (scheduled){ - + //hourly CronTab.add_script_file("timeshift-hourly", "d", "0 * * * * root timeshift --check --scripted", stop_cron_emails); - + //boot if (schedule_boot){ CronTab.add_script_file("timeshift-boot", "d", "@reboot root sleep 10m && timeshift --create --scripted --tags B", stop_cron_emails); @@ -4405,13 +4416,13 @@ public class Main : GLib.Object{ CronTab.remove_script_file("timeshift-boot", "d"); } } - + // cleanup public void clean_logs(){ log_debug("clean_logs()"); - + Gee.ArrayList list = new Gee.ArrayList(); try{ @@ -4442,15 +4453,15 @@ public class Main : GLib.Object{ // delete oldest 100 files --------------- for(int k = 0; k < 100; k++){ - + var file = File.new_for_path (list[k]); - - if (file.query_exists()){ + + if (file.query_exists()){ file.delete(); log_msg("%s: %s".printf(_("Removed"), list[k])); } } - + log_msg(_("Older log files removed")); } } @@ -4462,7 +4473,7 @@ public class Main : GLib.Object{ public void exit_app (int exit_code = 0){ log_debug("exit_app()"); - + if (app_mode == ""){ //update app config only in GUI mode save_app_config(); @@ -4475,57 +4486,57 @@ public class Main : GLib.Object{ clean_logs(); app_lock.remove(); - + dir_delete(TEMP_DIR); - + cleanup_unmount_devices(); - + exit(exit_code); //Gtk.main_quit (); } - + private void cleanup_unmount_devices(){ - + log_debug("cleanup_unmount_devices()"); - + if (!dir_exists("/run/timeshift")){ return; } - + var dirlist = dir_list_names("/run/timeshift"); - + foreach(var dname in dirlist){ - + int pid = int.parse(dname); if (pid != Posix.getpid()){ // if some other process - + // check if the process is still running - + string procdir = "/proc/%d".printf(pid); - + if (dir_exists(procdir)){ continue; } } - + // ----------------------------------------------- - + string mdir = "/run/timeshift/%s".printf(dname); - + var dirlist2 = dir_list_names(mdir); - + foreach(var dname2 in dirlist2){ - + string mdir2 = "/run/timeshift/%s/%s".printf(dname, dname2); - + // check if a device is mounted here - + foreach (var dev in Device.get_filesystems()){ - + foreach (var mnt in dev.mount_points){ - + if (mnt.mount_point == mdir2){ - + log_debug("\nFound stale mount for device '%s' at path '%s'".printf(dev.device, mdir2)); - + string cmd = "umount '%s'".printf(escape_single_quote(mdir2)); int retval = exec_sync(cmd); @@ -4546,12 +4557,12 @@ public class Main : GLib.Object{ } } } - + if (dir_exists(mdir)){ string cmd3 = "rmdir '%s'".printf(escape_single_quote(mdir)); int retval3 = exec_sync(cmd3); - + if (retval3 != 0){ log_error("Failed to remove directory"); log_msg("Ret=%d".printf(retval3)); @@ -4561,7 +4572,3 @@ public class Main : GLib.Object{ } } } - - - - diff --git a/src/Utility/Device.vala b/src/Utility/Device.vala index 3fafb9d7..f6ef9f49 100644 --- a/src/Utility/Device.vala +++ b/src/Utility/Device.vala @@ -21,7 +21,7 @@ * * */ - + /* Functions and classes for handling disk partitions */ using TeeJee.Logging; @@ -40,7 +40,7 @@ public class Device : GLib.Object{ public static double KiB = 1024; public static double MiB = 1024 * KiB; public static double GiB = 1024 * MiB; - + public string device = ""; public string name = ""; public string kname = ""; @@ -51,7 +51,7 @@ public class Device : GLib.Object{ public string label = ""; public string partuuid = ""; public string partlabel = ""; - + public int major = -1; public int minor = -1; @@ -73,11 +73,11 @@ public class Device : GLib.Object{ public bool removable = false; public bool read_only = false; - + public uint64 size_bytes = 0; public uint64 used_bytes = 0; public uint64 available_bytes = 0; - + public string used_percent = ""; public string dist_info = ""; public Gee.ArrayList mount_points; @@ -88,7 +88,7 @@ public class Device : GLib.Object{ private static string lsblk_version = ""; private static bool lsblk_is_ancient = false; - + private static Gee.ArrayList device_list; public Device(){ @@ -116,7 +116,7 @@ public class Device : GLib.Object{ lsblk_is_ancient = true; } } - + public uint64 free_bytes{ get{ return (used_bytes == 0) ? 0 : (available_bytes); @@ -130,7 +130,7 @@ public class Device : GLib.Object{ } else if (size_bytes > 0){ return "%.1f GB".printf(size_bytes / GB); - } + } else{ return ""; } @@ -156,7 +156,7 @@ public class Device : GLib.Object{ } public bool is_mounted_at_path(string subvolname, string mount_path){ - + foreach (var mnt in mount_points){ if (mnt.mount_point == mount_path){ if (subvolname.length == 0){ @@ -169,7 +169,7 @@ public class Device : GLib.Object{ } } } - + return false; } @@ -230,13 +230,13 @@ public class Device : GLib.Object{ } public Device? first_linux_child(){ - + foreach(var child in children){ if (child.has_linux_filesystem()){ return child; } } - + return null; } @@ -245,7 +245,7 @@ public class Device : GLib.Object{ } // static -------------------------------- - + public static Gee.ArrayList get_filesystems(bool get_space = true, bool get_mounts = true){ /* Returns list of block devices @@ -283,7 +283,7 @@ public class Device : GLib.Object{ //print_device_mounts(list); log_debug("Device: get_filesystems(): %d".printf(list.size)); - + return list; } @@ -313,7 +313,7 @@ public class Device : GLib.Object{ if (dev.pkname.length == 0){ return; } var top_kname = dev.pkname; - + foreach (var part in list){ if (part.kname == top_kname){ if (part.pkname.length > 0){ @@ -406,21 +406,21 @@ public class Device : GLib.Object{ ++removed; } } - + // If the parent is still set then it didn't exist to remove - likely removed while handling a dupe // Clean up but nothing removed if ( dev.pkname != "" ) { dev.pkname = ""; - } + } } - + return removed; } public static Gee.ArrayList get_block_devices_using_lsblk(string dev_name = ""){ //log_debug("Device: get_block_devices_using_lsblk()"); - + /* Returns list of mounted partitions using 'lsblk' command Populates device, type, uuid, label */ @@ -485,10 +485,10 @@ public class Device : GLib.Object{ Device pi = new Device(); int pos = 0; - + pi.name = match.fetch(++pos).strip(); pi.kname = match.fetch(++pos).strip(); - + pi.label = match.fetch(++pos); // don't strip; labels can have leading or trailing spaces pi.uuid = match.fetch(++pos).strip(); @@ -516,12 +516,12 @@ public class Device : GLib.Object{ pi.major = int.parse(txt.split(":")[0]); pi.minor = int.parse(txt.split(":")[1]); } - + if (!lsblk_is_ancient){ - + pi.partlabel = match.fetch(++pos); // don't strip; labels can have leading or trailing spaces pi.partuuid = match.fetch(++pos).strip(); - + pi.pkname = match.fetch(++pos).strip(); pi.vendor = match.fetch(++pos).strip(); pi.serial = match.fetch(++pos).strip(); @@ -740,7 +740,7 @@ public class Device : GLib.Object{ if (LOG_DEBUG){ log_debug(cmd); } - + ret_val = exec_script_sync(cmd, out std_out, out std_err); if (ret_val != 0){ var msg = "blkid: " + _("Failed to get partition list"); @@ -815,7 +815,7 @@ public class Device : GLib.Object{ } log_debug("Device: get_block_devices_using_blkid(): %d".printf(list.size)); - + return list; } @@ -913,7 +913,7 @@ public class Device : GLib.Object{ // resolve device name -------------------- pi.device = resolve_device_name(pi.device); - + // get uuid --------------------------- pi.uuid = get_device_uuid(pi.device); @@ -924,7 +924,7 @@ public class Device : GLib.Object{ list.add(pi); } } - + log_debug("Device: get_disk_space_using_df(): %d".printf(list.size)); return list; @@ -1072,7 +1072,7 @@ public class Device : GLib.Object{ } log_debug("Device: get_mounted_filesystems_using_mtab(): %d".printf(list.size)); - + return list; } @@ -1089,18 +1089,18 @@ public class Device : GLib.Object{ } public static Device? get_device_by_path(string path_to_check){ - + var list = Device.get_disk_space_using_df(path_to_check); - + if (list.size > 0){ return list[0]; } - + return null; } - + public static string get_device_uuid(string device){ - + if (device_list == null){ device_list = get_block_devices_using_lsblk(); } @@ -1109,7 +1109,7 @@ public class Device : GLib.Object{ return dev.uuid; } } - + return ""; } @@ -1128,7 +1128,7 @@ public class Device : GLib.Object{ } var list_mtab = get_mounted_filesystems_using_mtab(); - + var dev = find_device_in_list(list_mtab, uuid); if (dev != null){ @@ -1177,20 +1177,20 @@ public class Device : GLib.Object{ public static Device? find_device_in_list(Gee.ArrayList list, string _dev_alias){ string dev_alias = _dev_alias; - + if (dev_alias.down().has_prefix("uuid=")){ - + dev_alias = dev_alias.split("=",2)[1].strip().down(); } else if (file_exists(dev_alias) && file_is_symlink(dev_alias)){ var link_path = file_get_symlink_target(dev_alias); - + dev_alias = link_path.replace("../../../","/dev/").replace("../../","/dev/").replace("../","/dev/"); } foreach(var dev in list){ - + if (dev.device == dev_alias){ return dev; } @@ -1228,9 +1228,9 @@ public class Device : GLib.Object{ return null; } - + public void copy_fields_from(Device dev2){ - + this.device = dev2.device; this.name = dev2.name; this.kname = dev2.kname; @@ -1238,14 +1238,14 @@ public class Device : GLib.Object{ this.label = dev2.label; this.uuid = dev2.uuid; this.mapped_name = dev2.mapped_name; - + this.type = dev2.type; this.fstype = dev2.fstype; this.size_bytes = dev2.size_bytes; this.used_bytes = dev2.used_bytes; this.available_bytes = dev2.available_bytes; - + this.mount_points = dev2.mount_points; this.symlinks = dev2.symlinks; this.parent = dev2.parent; @@ -1261,9 +1261,9 @@ public class Device : GLib.Object{ } public Device? query_changes(){ - + Device dev_new = null; - + foreach (var dev in get_block_devices_using_lsblk()){ if (uuid.length > 0){ if (dev.uuid == uuid){ @@ -1278,18 +1278,18 @@ public class Device : GLib.Object{ } } } - + return dev_new; } - + public void query_disk_space(){ /* Updates disk space info and returns the given Device object */ var list_df = get_disk_space_using_df(device); - + var dev_df = find_device_in_list(list_df, uuid); - + if (dev_df != null){ // update dev fields size_bytes = dev_df.size_bytes; @@ -1300,14 +1300,14 @@ public class Device : GLib.Object{ } // mounting --------------------------------- - + public static bool automount_udisks(string dev_name_or_uuid, Gtk.Window? parent_window){ - + if (dev_name_or_uuid.length == 0){ log_error(_("Device name is empty!")); return false; } - + var cmd = "udisksctl mount -b '%s'".printf(dev_name_or_uuid); log_debug(cmd); int status = Posix.system(cmd); @@ -1334,11 +1334,11 @@ public class Device : GLib.Object{ var cmd = "udisksctl loop-setup -r -f '%s'".printf( escape_single_quote(iso_file_path)); - + log_debug(cmd); string std_out, std_err; int exit_code = exec_sync(cmd, out std_out, out std_err); - + if (exit_code == 0){ log_msg("%s".printf(std_out)); //log_msg("%s".printf(std_err)); @@ -1350,7 +1350,7 @@ public class Device : GLib.Object{ loop_device = std_out.split(" as ")[1].replace(".","").strip(); log_msg("loop_device: %s".printf(loop_device)); - + var list = get_block_devices_using_lsblk(); foreach(var dev in list){ if ((dev.pkname == loop_device.replace("/dev/","")) && (dev.fstype == "iso9660")){ @@ -1359,7 +1359,7 @@ public class Device : GLib.Object{ } } } - + return false; } @@ -1369,7 +1369,7 @@ public class Device : GLib.Object{ log_error(_("Device name is empty!")); return false; } - + var cmd = "udisksctl unmount -b '%s'".printf(dev_name_or_uuid); log_debug(cmd); int status = Posix.system(cmd); @@ -1380,12 +1380,12 @@ public class Device : GLib.Object{ gtk_messagebox("Error", msg, parent_window, true); } } - + return (status == 0); } public static Device? luks_unlock( - Device luks_device, string mapped_name, string passphrase, Gtk.Window? parent_window, + Device luks_device, string mapped_name, string passphrase, Gtk.Window? parent_window, out string message, out string details){ /* Unlocks a LUKS device using provided passphrase. @@ -1393,10 +1393,10 @@ public class Device : GLib.Object{ * Displays a GTK prompt if parent_window is not null * Otherwise prompts user on terminal with a timeout of 20 secsonds * */ - + Device unlocked_device = null; string std_out = "", std_err = ""; - + // check if not encrypted if (!luks_device.fstype.contains("luks") && !luks_device.fstype.contains("crypt")){ message = _("This device is not encrypted"); @@ -1411,7 +1411,7 @@ public class Device : GLib.Object{ unlocked_device = part; message = _("Device is unlocked"); details = _("Unlocked device is mapped to '%s'").printf(part.mapped_name); - return part; + return part; } } @@ -1425,11 +1425,11 @@ public class Device : GLib.Object{ if (parent_window == null){ // console mode - + if ((luks_pass == null) || (luks_pass.length == 0)){ // prompt user on terminal and unlock, else timeout after 20 secs - + var counter = new TimeoutCounter(); counter.kill_process_on_timeout("cryptsetup", 20, true); string cmd = "cryptsetup luksOpen '%s' '%s'".printf(luks_device.device, luks_name); @@ -1438,7 +1438,7 @@ public class Device : GLib.Object{ Posix.system(cmd); counter.stop(); log_msg(""); - + } else{ @@ -1448,7 +1448,7 @@ public class Device : GLib.Object{ escape_single_quote(luks_pass), luks_device.device, luks_name); log_debug(cmd.replace(escape_single_quote(luks_pass), "**PASSWORD**")); - + int status = exec_script_sync(cmd, out std_out, out std_err, false, true); switch (status){ @@ -1462,6 +1462,7 @@ public class Device : GLib.Object{ } } + #if !TS_NO_GTK else{ // gui mode @@ -1471,7 +1472,7 @@ public class Device : GLib.Object{ // show input prompt log_debug("Prompting user for passphrase.."); - + luks_pass = gtk_inputbox( _("Encrypted Device"), _("Enter passphrase to unlock '%s'").printf(luks_device.name), @@ -1494,7 +1495,7 @@ public class Device : GLib.Object{ escape_single_quote(luks_pass), luks_device.device, luks_name); log_debug(cmd.replace(escape_single_quote(luks_pass), "**PASSWORD**")); - + int status = exec_script_sync(cmd, out std_out, out std_err, false, true); switch (status){ @@ -1506,15 +1507,16 @@ public class Device : GLib.Object{ break; } } - + } + #endif // find unlocked device list = get_block_devices_using_lsblk(); foreach(var part in list){ if (part.pkname == luks_device.kname){ unlocked_device = part; - break; + break; } } @@ -1537,7 +1539,7 @@ public class Device : GLib.Object{ } public static bool luks_lock(string kname, Gtk.Window? parent_window){ - + var cmd = "cryptsetup luksClose %s".printf(kname); log_debug(cmd); @@ -1546,18 +1548,18 @@ public class Device : GLib.Object{ int status = exec_script_sync(cmd, out std_out, out std_err, false, true); log_msg(std_out); log_msg(std_err); - + if (status != 0){ if (parent_window != null){ string msg = "Failed to lock device: %s".printf(kname); gtk_messagebox("Error", msg, parent_window, true); } } - + return (status == 0); - + /*log_debug(cmd); - + if (bash_admin_shell != null){ int status = bash_admin_shell.execute(cmd); return (status == 0); @@ -1573,7 +1575,7 @@ public class Device : GLib.Object{ /* * Mounts specified device at specified mount point. - * + * * */ string cmd = ""; @@ -1585,7 +1587,7 @@ public class Device : GLib.Object{ string uuid = ""; // resolve uuid and device name ---------- - + if (dev_name_or_uuid.has_prefix("/dev")){ device = dev_name_or_uuid; uuid = get_device_uuid(dev_name_or_uuid); @@ -1597,7 +1599,7 @@ public class Device : GLib.Object{ } // check if already mounted -------------- - + var mps = Device.get_device_mount_points(dev_name_or_uuid); log_debug("------------------"); @@ -1606,7 +1608,7 @@ public class Device : GLib.Object{ log_debug(mp.mount_point); } log_debug("------------------"); - + foreach(var mp in mps){ if ((mp.mount_point == mount_point) && mp.mount_options.contains(mount_options)){ if (!silent){ @@ -1652,7 +1654,7 @@ public class Device : GLib.Object{ } return true; } - + // check if mounted successfully ------------------ /*mps = Device.get_device_mount_points(dev_name_or_uuid); @@ -1805,7 +1807,7 @@ public class Device : GLib.Object{ if (has_parent() && (parent.type == "part")){ text += " (%s)".printf(pkname); } - + return text; } } @@ -1817,7 +1819,7 @@ public class Device : GLib.Object{ if (has_parent() && (parent.type == "part")){ text += " (%s)".printf(parent.kname); } - + return text; } } @@ -1852,13 +1854,13 @@ public class Device : GLib.Object{ return s.strip(); } - + public string description_simple(){ return description_simple_formatted().replace("","").replace("",""); } - + public string description_simple_formatted(){ - + string s = ""; if (type == "disk"){ @@ -1924,7 +1926,7 @@ public class Device : GLib.Object{ s += (uuid.length > 0) ? " ~ " + uuid : ""; s += (fstype.length > 0) ? " ~ " + fstype : ""; s += (used.length > 0) ? " ~ " + used + " / " + size + " GB used (" + used_percent + ")" : ""; - + return s; } @@ -1963,7 +1965,7 @@ public class Device : GLib.Object{ else{ tt += "%-15s: %s\n".printf(_("Device"), (mapped_name.length > 0) ? "%s → %s".printf(device, mapped_name) : device); - + if (has_parent()){ tt += "%-15s: %s\n".printf(_("Parent Device"), parent.device); } @@ -1971,10 +1973,10 @@ public class Device : GLib.Object{ tt += "%-15s: %s\n".printf(_("Type"),type); tt += "%-15s: %s\n".printf(_("Filesystem"),fstype); tt += "%-15s: %s\n".printf(_("Label"),label); - + tt += "%-15s: %s\n".printf(_("Size"), (size_bytes > 0) ? format_file_size(size_bytes) : "N/A"); - + tt += "%-15s: %s\n".printf(_("Used"), (used_bytes > 0) ? format_file_size(used_bytes) : "N/A"); @@ -1987,13 +1989,13 @@ public class Device : GLib.Object{ // testing ----------------------------------- public static void test_all(){ - + var list = get_block_devices_using_lsblk(); log_msg("\n> get_block_devices_using_lsblk()"); print_device_list(list); log_msg(""); - + //list = get_block_devices_using_blkid(); //log_msg("\nget_block_devices_using_blkid()\n"); //print_device_list(list); @@ -2015,14 +2017,14 @@ public class Device : GLib.Object{ print_device_list(list); print_device_mounts(list); print_device_disk_space(list); - + log_msg(""); } public static void print_device_list(Gee.ArrayList list){ log_debug(""); - + log_debug("%-12s ,%-5s ,%-5s ,%-36s ,%s".printf( "device", "pkname", @@ -2043,7 +2045,7 @@ public class Device : GLib.Object{ } log_debug(""); - + /* log_debug("%-20s %-20s %s %s %s %s".printf( "device", @@ -2068,7 +2070,7 @@ public class Device : GLib.Object{ log_debug(""); */ - + /*log_debug("%-20s %-10s %-15s %-3s %-3s %15s %15s".printf( "device", "type", @@ -2098,7 +2100,7 @@ public class Device : GLib.Object{ public static void print_device_mounts(Gee.ArrayList list){ log_debug(""); - + log_debug("%-15s %s".printf( "device", //"fstype", @@ -2122,7 +2124,7 @@ public class Device : GLib.Object{ //dev.fstype, mps )); - + } log_debug(""); @@ -2130,7 +2132,7 @@ public class Device : GLib.Object{ public static void print_device_disk_space(Gee.ArrayList list){ log_debug(""); - + log_debug("%-15s %-12s %15s %15s %15s %10s".printf( "device", "fstype", diff --git a/src/Utility/FileItem.vala b/src/Utility/FileItem.vala index dd517c0f..f9e576ee 100644 --- a/src/Utility/FileItem.vala +++ b/src/Utility/FileItem.vala @@ -22,7 +22,9 @@ */ using GLib; +#if !TS_NO_GTK using Gtk; +#endif using Gee; using Json; @@ -35,7 +37,7 @@ using TeeJee.System; using TeeJee.Misc; public class FileItem : GLib.Object,Gee.Comparable { - + public string file_name = ""; public string file_location = ""; public string file_path = ""; @@ -46,7 +48,7 @@ public class FileItem : GLib.Object,Gee.Comparable { public string owner_user = ""; public string owner_group = ""; public string content_type = ""; - public string file_status = ""; + public string file_status = ""; public bool is_selected = false; public bool is_symlink = false; @@ -59,7 +61,7 @@ public class FileItem : GLib.Object,Gee.Comparable { public GLib.Icon icon; // constructors ------------------------------- - + public FileItem(string name) { file_name = name; } @@ -79,7 +81,7 @@ public class FileItem : GLib.Object,Gee.Comparable { } // properties ------------------------------------------------- - + public int64 size { get{ return _size; @@ -106,11 +108,11 @@ public class FileItem : GLib.Object,Gee.Comparable { //} } } - + // instance methods ------------------------------------------- - + public void query_file_info() { - + try { FileInfo info; File file = File.parse_name (file_path); @@ -118,7 +120,7 @@ public class FileItem : GLib.Object,Gee.Comparable { if (file.query_exists()) { // get type without following symlinks - + info = file.query_info("%s,%s,%s".printf( FileAttribute.STANDARD_TYPE, FileAttribute.STANDARD_ICON, @@ -128,14 +130,14 @@ public class FileItem : GLib.Object,Gee.Comparable { var item_file_type = info.get_file_type(); this.icon = info.get_icon(); - + if (item_file_type == FileType.SYMBOLIC_LINK) { //this.icon = GLib.Icon.new_for_string("emblem-symbolic-link"); this.is_symlink = true; this.symlink_target = info.get_symlink_target(); } else { - + this.is_symlink = false; this.symlink_target = ""; @@ -143,7 +145,7 @@ public class FileItem : GLib.Object,Gee.Comparable { //log_msg(file_basename(file_path) + " (gicon): " + icon.to_string()); /*var themed_icon = (GLib.ThemedIcon) icon; - + string txt = "-> "; foreach(var name in themed_icon.names){ txt += ", " + name; @@ -153,7 +155,7 @@ public class FileItem : GLib.Object,Gee.Comparable { } // get file info - follow symlinks - + info = file.query_info("%s,%s,%s,%s,%s,%s,%s,%s".printf( FileAttribute.STANDARD_TYPE, FileAttribute.STANDARD_SIZE, @@ -175,7 +177,7 @@ public class FileItem : GLib.Object,Gee.Comparable { // content type this.content_type = info.get_content_type(); - + // size if (!this.is_symlink && (this.file_type == FileType.REGULAR)) { this._size = info.get_size(); @@ -189,7 +191,7 @@ public class FileItem : GLib.Object,Gee.Comparable { // owner_group this.owner_group = info.get_attribute_string(FileAttribute.OWNER_GROUP); - + } } catch (Error e) { @@ -198,7 +200,7 @@ public class FileItem : GLib.Object,Gee.Comparable { } public void query_file_info_basic() { - + try { FileInfo info; File file = File.parse_name(file_path); @@ -206,12 +208,12 @@ public class FileItem : GLib.Object,Gee.Comparable { if (file.query_exists()) { // get type and icon -- follow symlinks - + info = file.query_info("%s,%s".printf( FileAttribute.STANDARD_TYPE, FileAttribute.STANDARD_ICON ), 0); - + this.icon = info.get_icon(); this.file_type = info.get_file_type(); @@ -224,6 +226,7 @@ public class FileItem : GLib.Object,Gee.Comparable { // icons ------------------------------------------------------ + #if !TS_NO_GTK public Gdk.Pixbuf? get_icon(int icon_size, bool add_transparency, bool add_emblems){ Gdk.Pixbuf? pixbuf = null; @@ -243,4 +246,5 @@ public class FileItem : GLib.Object,Gee.Comparable { return pixbuf; } + #endif } diff --git a/src/Utility/GtkHeadlessTypes.vala b/src/Utility/GtkHeadlessTypes.vala new file mode 100644 index 00000000..5c25bbf3 --- /dev/null +++ b/src/Utility/GtkHeadlessTypes.vala @@ -0,0 +1,5 @@ +namespace Gtk { + // Headless build only needs the Window type for method signatures. + public class Window : GLib.Object { + } +} diff --git a/src/Utility/GtkHelperHeadless.vala b/src/Utility/GtkHelperHeadless.vala new file mode 100644 index 00000000..97711d84 --- /dev/null +++ b/src/Utility/GtkHelperHeadless.vala @@ -0,0 +1,16 @@ +using TeeJee.Logging; + +namespace TeeJee.GtkHelper { + + public void gtk_do_events() { + // dummy + } + + public void gtk_set_busy(bool busy, Gtk.Window? win) { + // dummy + } + + public static void gtk_messagebox(string title, string message, Gtk.Window? parent_win, bool is_error = false) { + // dummy + } +} diff --git a/src/Utility/TeeJee.Misc.vala b/src/Utility/TeeJee.Misc.vala index 58b69f81..a4411075 100644 --- a/src/Utility/TeeJee.Misc.vala +++ b/src/Utility/TeeJee.Misc.vala @@ -21,12 +21,11 @@ * * */ - + namespace TeeJee.Misc { /* Various utility functions */ - using Gtk; using TeeJee.Logging; using TeeJee.FileSystem; using TeeJee.ProcessHelper; @@ -38,9 +37,9 @@ namespace TeeJee.Misc { Intl.setlocale(GLib.LocaleCategory.COLLATE, type); Intl.setlocale(GLib.LocaleCategory.TIME, type); } - + // timestamp ---------------- - + public string timestamp (bool show_millis = false){ /* Returns a formatted timestamp string */ @@ -48,7 +47,7 @@ namespace TeeJee.Misc { // NOTE: format() does not support milliseconds DateTime now = new GLib.DateTime.now_local(); - + if (show_millis){ var msec = now.get_microsecond () / 1000; return "%s.%03d".printf(now.format("%H:%M:%S"), msec); @@ -91,12 +90,12 @@ namespace TeeJee.Misc { } public string escape_html(string html){ - + return GLib.Markup.escape_text(html); } public string random_string(int length = 8, string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"){ - + string random = ""; for(int i=0;i