39public class Main : GLib.Object{
186 public Main(
string[] args,
bool gui_mode){
188 this.mount_point_app =
"/run/bastionguard-backup/%d".printf(Posix.getpid());
202 log_debug(
_(
"Running") +
" %s v%s".printf(AppName, AppVersion));
211 string suffix = gui_mode ?
"gui" :
app_mode;
213 DateTime now =
new DateTime.now_local();
214 log_dir =
"/var/log/bastionguard-backup";
216 "%s_%s.log".printf(now.format(
"%Y-%m-%d_%H-%M-%S"), suffix));
218 var file = File.new_for_path (
log_dir);
219 if (!file.query_exists ()) {
220 file.make_directory_with_parents();
223 file = File.new_for_path (
log_file);
224 if (file.query_exists ()) {
251 string title =
_(
"Missing Dependencies");
252 gtk_messagebox(title, message,
null,
true);
279 this.app_path = (File.new_for_path (args[0])).get_parent().get_path ();
280 this.share_folder =
"/usr/share";
281 this.app_conf_path =
"/etc/bastionguard-backup/bastionguard-backup.json";
282 this.app_conf_path_old =
"/etc/bastionguard-backup.json";
283 this.app_conf_path_default = GLib.Path.build_path (GLib.Path.DIR_SEPARATOR_S, Constants.SYSCONFDIR,
"bastionguard-backup",
"default.json");
288 string local_exec = args[0];
289 string local_conf =
app_path +
"/bastionguard-backup.json";
290 string local_share =
app_path +
"/share";
292 var f_local_exec = File.new_for_path(local_exec);
293 if (f_local_exec.query_exists()){
295 var f_local_conf = File.new_for_path(local_conf);
296 if (f_local_conf.query_exists()){
297 this.app_conf_path = local_conf;
300 var f_local_share = File.new_for_path(local_share);
301 if (f_local_share.query_exists()){
302 this.share_folder = local_share;
307 this.app_path = Environment.find_program_in_path(
"bastionguard-backup");
345 string[] dependencies = {
"rsync",
"/sbin/blkid",
"df",
"mount",
"umount",
"fuser",
"crontab",
"cp",
"rm",
"touch",
"ln",
"sync",
"run-parts"};
348 foreach(
string cmd_tool
in dependencies){
349 if(!cmd_exists(cmd_tool)) {
350 msg +=
" * " + cmd_tool +
"\n";
355 msg =
_(
"Commands listed below are not available on this system") +
":\n\n" + msg +
"\n";
356 msg +=
_(
"Please install required packages and try running TimeShift again");
369 if(user_env ==
null) {
374 string[] targets = {
"GTK_THEME",
"DISPLAY",
"XAUTHORITY",
"DBUS_SESSION_BUS_ADDRESS"};
375 foreach (
string target
in targets) {
377 if(user_var !=
null) {
378 GLib.Environment.set_variable(target, user_var,
true);
385 if (wayland_display !=
null && xdg_runtime_dir !=
null) {
386 string path =
"%s/%s".printf(xdg_runtime_dir, wayland_display);
387 GLib.Environment.set_variable(
"WAYLAND_DISPLAY", path,
true);
388 GLib.Environment.set_variable(
"XDG_RUNTIME_DIR",
"/run/user/0",
true);
398 GLib.Process.spawn_command_line_sync(
404 }
catch (GLib.Error e) {
405 log_debug(
"Failed to run btrfs command. Is btrfs-progs installed?");
409 log_debug(
"Checking btrfs-progs version and determining capabilities...");
411 if (exit_status != 0) {
412 log_error(
"btrfs command failed with exit code %d: %s".printf(exit_status, stderr));
416 string[] lines = stdout.strip().split(
"\n");
417 if (lines.length == 0) {
418 log_error(
"No output from btrfs --version");
422 string version_line =
null;
423 foreach (
string line
in lines) {
424 if (line.contains(
"btrfs-progs")) {
430 if (version_line ==
null) {
431 log_error(
"Could not find btrfs-progs version line in output");
435 string version_prefix =
"btrfs-progs v";
436 int prefix_index = version_line.index_of(version_prefix);
437 if (prefix_index == -1) {
442 string version_string = version_line.substring(prefix_index + version_prefix.length);
443 string[] version_parts = version_string.split(
".");
445 if (version_parts.length < 2) {
446 log_error(
"No version components found in: %s".printf(version_string));
450 int[] version =
new int[5];
453 for (
int i = 0; i < version_parts.length; i++) {
454 version[i] =
int.parse(version_parts[i].strip());
463 if (version !=
null) {
465 log_debug(
"-- btrfs-progs version %d.%d.x".printf(version[0], version[1]));
473 if (cmd_exists(
"bastionguard-backup-btrfs")){
474 string std_out, std_err;
475 exec_sync(
"bastionguard-backup-btrfs-uninstall", out std_out, out std_err);
476 log_msg(
_(
"** Uninstalled BastionGuard Backup BTRFS **"));
482 log_debug(
"check_btrfs_layout_system()");
490 string msg =
_(
"The system partition has an unsupported subvolume layout.") +
" ";
491 msg +=
_(
"Only ubuntu-type layouts with @ and @home subvolumes are currently supported.") +
"\n\n";
492 msg +=
_(
"Application will exit.") +
"\n\n";
493 string title =
_(
"Not Supported");
497 gtk_messagebox(title, msg, win,
true);
509 bool supported =
true;
511 if ((dev_root !=
null) && (dev_root.
fstype ==
"btrfs")){
513 if ((dev_home !=
null) && (dev_home.
fstype ==
"btrfs")){
515 if (dev_home != dev_root){
539 for (
int k = 1; k < args.length; k++)
541 switch (args[k].down()){
582 case "--list-snapshots":
586 case "--list-devices":
604 if (user.is_system) {
continue; }
606 if (txt.length > 0) { txt +=
" "; }
607 txt +=
"%s".printf(user.name);
609 if (user.has_encrypted_home){
616 if (user.has_encrypted_private_dirs){
618 foreach(
string enc_path
in user.encrypted_private_dirs){
635 log_debug(
"Main: add_default_exclude_entries()");
675 foreach(var entry
in FsTabEntry.read_file(
"/etc/fstab")){
677 if (!entry.mount_point.has_prefix(
"/")){
continue; }
680 if (entry.mount_point ==
"/"){
continue; }
681 if (entry.mount_point.has_prefix(
"/bin")){
continue; }
682 if (entry.mount_point.has_prefix(
"/boot")){
continue; }
683 if (entry.mount_point.has_prefix(
"/cdrom")){
continue; }
684 if (entry.mount_point.has_prefix(
"/dev")){
continue; }
685 if (entry.mount_point.has_prefix(
"/etc")){
continue; }
686 if (entry.mount_point.has_prefix(
"/home")){
continue; }
687 if (entry.mount_point.has_prefix(
"/lib")){
continue; }
688 if (entry.mount_point.has_prefix(
"/lib64")){
continue; }
689 if (entry.mount_point.has_prefix(
"/media")){
continue; }
690 if (entry.mount_point.has_prefix(
"/mnt")){
continue; }
691 if (entry.mount_point.has_prefix(
"/opt")){
continue; }
692 if (entry.mount_point.has_prefix(
"/proc")){
continue; }
693 if (entry.mount_point.has_prefix(
"/root")){
continue; }
694 if (entry.mount_point.has_prefix(
"/run")){
continue; }
695 if (entry.mount_point.has_prefix(
"/sbin")){
continue; }
696 if (entry.mount_point.has_prefix(
"/snap")){
continue; }
697 if (entry.mount_point.has_prefix(
"/srv")){
continue; }
698 if (entry.mount_point.has_prefix(
"/sys")){
continue; }
699 if (entry.mount_point.has_prefix(
"/system")){
continue; }
700 if (entry.mount_point.has_prefix(
"/tmp")){
continue; }
701 if (entry.mount_point.has_prefix(
"/usr")){
continue; }
702 if (entry.mount_point.has_prefix(
"/var")){
continue; }
763 log_debug(
"Main: add_default_exclude_entries(): exit");
768 log_debug(
"Main: add_app_exclude_entries()");
782 log_debug(
"Main: add_app_exclude_entries(): exit");
799 log_debug(
"Main: create_exclude_list_for_backup()");
801 var list =
new Gee.ArrayList<
string>();
809 if (!list.contains(path)){
817 if (!list.contains(path)){
825 if (!list.contains(path)){
837 if (user.is_system){
continue; }
839 if (user.has_encrypted_home){
842 string path =
"%s/**".printf(user.home_path);
846 if (user.has_encrypted_private_dirs){
848 foreach(
string enc_path
in user.encrypted_private_dirs){
851 string path =
"%s/**".printf(enc_path);
861 if (user.is_system){
continue; }
863 string exc_pattern =
"%s/**".printf(user.home_path);
864 string inc_pattern =
"+ %s/**".printf(user.home_path);
865 string inc_hidden_pattern =
"+ %s/.**".printf(user.home_path);
867 if (user.has_encrypted_home){
868 inc_pattern =
"+ /home/.ecryptfs/%s/***".printf(user.name);
869 exc_pattern =
"/home/.ecryptfs/%s/***".printf(user.name);
874 bool exclude_all = !include_hidden && !include_all;
892 if (!list.contains(path)){
897 string timeshift_path =
"/bastionguard-backup/*";
898 if (!list.contains(timeshift_path)){
899 list.add(timeshift_path);
902 log_debug(
"Main: create_exclude_list_for_backup(): exit");
909 log_debug(
"Main: create_exclude_list_for_restore()");
919 if (path.strip().has_prefix(
"+")){
continue; }
930 foreach(
string path
in file_read(list_file).split(
"\n")){
957 foreach(var pattern
in entry.patterns){
972 string timeshift_path =
"/bastionguard-backup/*";
977 log_debug(
"Main: create_exclude_list_for_restore(): exit");
986 log_debug(
"Main: save_exclude_list_for_backup()");
991 foreach(var pattern
in list){
992 if (pattern.strip().length > 0){
993 txt +=
"%s\n".printf(pattern);
997 string list_file =
path_combine(output_path,
"exclude.list");
1006 log_debug(
"Main: save_exclude_list_for_restore()");
1010 log_debug(
"Exclude list -------------");
1013 foreach(var pattern
in list){
1014 if (pattern.strip().length > 0){
1015 txt +=
"%s\n".printf(pattern);
1025 log_debug(
"Main: save_exclude_list_selections()");
1031 log_debug(
"add app name: %s".printf(entry.name));
1039 log_debug(
"remove app name: %s".printf(entry.name));
1044 return Posix.strcmp(a,b);
1067 var cmdline =
file_read(
"/proc/cmdline");
1069 if (cmdline.contains(
"boot=casper") || cmdline.contains(
"boot=live")) {
1070 log_msg (
"Live Session detected, backup is disabled.");
1087 bool update_symlinks =
false;
1098 DateTime now =
new DateTime.now_local();
1122 if (!f.query_exists()){
1124 f.make_directory_with_parents();
1134 update_symlinks =
true;
1145 bool take_new =
false;
1149 log_msg(
_(
"Boot snapshots are enabled"));
1151 if (last_snapshot_boot ==
null){
1152 log_msg(
_(
"Last boot snapshot not found"));
1155 else if (last_snapshot_boot.
date.compare(dt_sys_boot) < 0){
1156 log_msg(
_(
"Last boot snapshot is older than system start time"));
1160 int hours = (int) ((
float) now.difference(last_snapshot_boot.
date) / TimeSpan.HOUR);
1161 log_msg(
_(
"Last boot snapshot is %d hours old").printf(hours));
1172 update_symlinks =
true;
1180 log_msg(
_(
"Hourly snapshots are enabled"));
1182 if (last_snapshot_hourly ==
null){
1183 log_msg(
_(
"Last hourly snapshot not found"));
1186 else if (last_snapshot_hourly.
date.compare(now.add_hours(-1).add_minutes(1)) < 0){
1187 log_msg(
_(
"Last hourly snapshot is more than 1 hour old"));
1191 int mins = (int) ((
float) now.difference(last_snapshot_hourly.
date) / TimeSpan.MINUTE);
1192 log_msg(
_(
"Last hourly snapshot is %d minutes old").printf(mins));
1203 update_symlinks =
true;
1211 log_msg(
_(
"Daily snapshots are enabled"));
1213 if (last_snapshot_daily ==
null){
1214 log_msg(
_(
"Last daily snapshot not found"));
1217 else if (last_snapshot_daily.
date.compare(now.add_days(-1).add_minutes(1)) < 0){
1218 log_msg(
_(
"Last daily snapshot is more than 1 day old"));
1222 int hours = (int) ((
float) now.difference(last_snapshot_daily.
date) / TimeSpan.HOUR);
1223 log_msg(
_(
"Last daily snapshot is %d hours old").printf(hours));
1234 update_symlinks =
true;
1242 log_msg(
_(
"Weekly snapshots are enabled"));
1244 if (last_snapshot_weekly ==
null){
1245 log_msg(
_(
"Last weekly snapshot not found"));
1248 else if (last_snapshot_weekly.
date.compare(now.add_weeks(-1).add_minutes(1)) < 0){
1249 log_msg(
_(
"Last weekly snapshot is more than 1 week old"));
1253 int days = (int) ((
float) now.difference(last_snapshot_weekly.
date) / TimeSpan.DAY);
1254 log_msg(
_(
"Last weekly snapshot is %d days old").printf(days));
1265 update_symlinks =
true;
1273 log_msg(
_(
"Monthly snapshot are enabled"));
1275 if (last_snapshot_monthly ==
null){
1276 log_msg(
_(
"Last monthly snapshot not found"));
1279 else if (last_snapshot_monthly.
date.compare(now.add_months(-1).add_minutes(1)) < 0){
1280 log_msg(
_(
"Last monthly snapshot is more than 1 month old"));
1284 int days = (int) ((
float) now.difference(last_snapshot_monthly.
date) / TimeSpan.DAY);
1285 log_msg(
_(
"Last monthly snapshot is %d days old").printf(days));
1296 update_symlinks =
true;
1303 log_msg(
_(
"Scheduled snapshots are disabled") +
" - " +
_(
"Nothing to do!"));
1307 log_msg(
string.nfill(78,
'-'));
1316 if (update_symlinks){
1335 var dt_begin =
new DateTime.now_local();
1339 DateTime now =
new DateTime.now_local();
1344 DateTime dt_filter =
null;
1346 if (tag !=
"ondemand"){
1349 dt_filter = dt_sys_boot;
1355 dt_filter = now.add_hours(-1).add_seconds(59);
1358 log_error(
_(
"Unknown snapshot type") +
": %s".printf(tag));
1365 if (bak.date.compare(dt_filter) > 0){
1366 backup_to_rotate = bak;
1371 if (backup_to_rotate !=
null){
1374 backup_to_rotate.add_tag(tag);
1376 var message =
_(
"Tagged snapshot") +
" '%s': %s".printf(backup_to_rotate.name, tag);
1391 log_error(
"Backup location not mounted");
1408 var message =
"Space required for snapshot: %lld (%s). Space available: %lu (%s)"
1434 var dt_end =
new DateTime.now_local();
1435 TimeSpan elapsed = dt_end.difference(dt_begin);
1436 long seconds = (long)(elapsed * 1.0 / TimeSpan.SECOND);
1439 if (new_snapshot !=
null){
1440 message =
"%s %s (%lds)".printf((
btrfs_mode ?
"BTRFS" :
"RSYNC"),
_(
"Snapshot saved successfully"), seconds);
1443 message =
_(
"Failed to create snapshot");
1449 if (new_snapshot !=
null){
1450 message =
_(
"Tagged snapshot") +
" '%s': %s".printf(new_snapshot.
name, tag);
1458 log_msg(
string.nfill(78,
'-'));
1460 log_msg(
"Checking if target drive has enough free space for a snapshot (RSYNC)");
1463 string time_stamp = dt_created.format(
"%Y-%m-%d_%H-%M-%S");
1465 string snapshot_name = time_stamp;
1466 string snapshot_path =
path_combine(snapshot_dir, snapshot_name);
1468 string localhost_path =
path_combine(snapshot_path,
"localhost");
1479 string ctl_path =
path_combine(snapshot_dir,
".sync-restore");
1480 var f = File.new_for_path(ctl_path);
1482 if (f.query_exists()){
1490 if ((bak.name == snap_name) && (bak.sys_uuid == sys_uuid)){
1492 snapshot_to_link = bak;
1507 if (snapshot_to_link ==
null){
1511 string link_from_path =
"";
1512 if (snapshot_to_link !=
null){
1513 log_msg(
"Linking from snapshot: %s".printf(snapshot_to_link.
name));
1514 link_from_path =
"%s/localhost/".printf(snapshot_to_link.
path);
1521 if (
null == exclude_from_file){
1522 log_error(
"Failed to save exclude list");
1531 space_check_task.source_path =
"";
1532 space_check_task.dest_path = snapshot_path +
"/localhost/";
1533 space_check_task.link_from_path = link_from_path;
1534 space_check_task.exclude_from_file = exclude_from_file;
1535 space_check_task.prg_count_total =
Main.first_snapshot_count;
1537 space_check_task.relative =
true;
1538 space_check_task.verbose =
true;
1539 space_check_task.delete_extra =
true;
1540 space_check_task.delete_excluded =
true;
1541 space_check_task.delete_after =
false;
1558 log_msg(
string.nfill(78,
'-'));
1561 log_msg(
_(
"Estimating system size..."));
1565 log_msg(
_(
"Creating new snapshot...") +
"(RSYNC)");
1572 log_error(
"Backup location not mounted");
1576 string time_stamp = dt_created.format(
"%Y-%m-%d_%H-%M-%S");
1578 string snapshot_name = time_stamp;
1579 string snapshot_path =
path_combine(snapshot_dir, snapshot_name);
1581 string localhost_path =
path_combine(snapshot_path,
"localhost");
1592 string ctl_path =
path_combine(snapshot_dir,
".sync-restore");
1593 var f = File.new_for_path(ctl_path);
1595 if (f.query_exists()){
1603 if ((bak.name == snap_name) && (bak.sys_uuid == sys_uuid)){
1605 snapshot_to_link = bak;
1620 if (snapshot_to_link ==
null){
1624 string link_from_path =
"";
1625 if (snapshot_to_link !=
null){
1626 log_msg(
"%s: %s".printf(
_(
"Linking from snapshot"), snapshot_to_link.
name));
1627 link_from_path =
"%s/localhost/".printf(snapshot_to_link.
path);
1634 if (
null == exclude_from_file){
1644 var
log_file = snapshot_path +
"/rsync-log";
1649 task.source_path =
"";
1650 task.dest_path = snapshot_path +
"/localhost/";
1651 task.link_from_path = link_from_path;
1652 task.exclude_from_file = exclude_from_file;
1654 task.prg_count_total =
Main.first_snapshot_count;
1656 task.relative =
true;
1657 task.verbose =
true;
1658 task.delete_extra =
true;
1659 task.delete_excluded =
true;
1660 task.delete_after =
false;
1664 task.io_nice =
true;
1677 stdout.printf(
string.nfill(80,
' '));
1680 stdout.printf(
"\r");
1685 log_error(
_(
"Failed to create new snapshot"));
1689 string initial_tags = (tag ==
"ondemand") ?
"" : tag;
1720 log_msg(
_(
"Creating new backup...") +
"(BTRFS)");
1730 log_error(
"Snapshot device not mounted");
1734 string time_stamp = dt_created.format(
"%Y-%m-%d_%H-%M-%S");
1735 string snapshot_name = time_stamp;
1737 string snapshot_path =
"";
1741 var subvol_names =
new string[] {
"@" };
1745 subvol_names =
new string[] {
"@",
"@home" };
1748 foreach(var subvol_name
in subvol_names){
1756 string dst_path =
path_combine(snapshot_path, subvol_name);
1759 if (dst_path.has_suffix(
"/@/@")){
1760 dst_path = dst_path.replace(
"/@/@",
"/@");
1762 else if (dst_path.has_suffix(
"/@home/@home")){
1763 dst_path = dst_path.replace(
"/@home/@home",
"/@home");
1766 string cmd =
"btrfs subvolume snapshot '%s' '%s' \n".printf(src_path, dst_path);
1770 string std_out, std_err;
1772 int ret_val =
exec_sync(cmd, out std_out, out std_err);
1777 log_error(
_(
"btrfs returned an error") +
": %d".printf(ret_val));
1778 log_error(
_(
"Failed to create subvolume snapshot") +
": %s".printf(subvol_name));
1782 log_msg(
_(
"Created subvolume snapshot") +
": %s".printf(dst_path));
1790 string initial_tags = (tag ==
"ondemand") ?
"" : tag;
1799 snapshot.subvolumes.set(subvol.name, subvol);
1812 const string backuphooksdir =
"/etc/bastionguard-backup/backup-hooks.d";
1813 FileType fileType = File.new_for_path(backuphooksdir).query_file_type(FileQueryInfoFlags.NONE);
1814 if(fileType == FileType.DIRECTORY) {
1815 log_debug(
"Running post-backup tasks...");
1817 string sh =
"export TS_SNAPSHOT_PATH=\"" + snapshot_path +
"\" &&" +
1818 " run-parts --verbose \"%s\"".printf(backuphooksdir);
1821 log_debug(
"Finished running post-backup tasks...");
1823 log_debug(
"Backup hooks skipped, because %s does not exist".printf(backuphooksdir));
1831 foreach(
string tag
in cmd_tags.split(
",")){
1832 switch(tag.strip().up()){
1834 snapshot.add_tag(
"ondemand");
1837 snapshot.add_tag(
"boot");
1840 snapshot.add_tag(
"hourly");
1843 snapshot.add_tag(
"daily");
1846 snapshot.add_tag(
"weekly");
1849 snapshot.add_tag(
"monthly");
1856 if (snapshot.tags.size == 0){
1857 snapshot.add_tag(
"ondemand");
1862 foreach(
string tag
in cmd_tags.split(
",")){
1863 switch(tag.strip().up()){
1872 log_error(
_(
"Unknown value specified for option --tags") +
" (%s).".printf(tag));
1873 log_error(
_(
"Expected values: O, B, H, D, W, M"));
1894 log_debug(
"delete_begin(): thread created");
1902 log_debug(
"Main: delete_begin(): exit");
1912 bak.mark_for_deletion();
1918 bak.mark_for_deletion();
1921 status = bak.remove(
true);
1923 var message =
"%s '%s'".printf(
_(
"Removed"), bak.name);
1929 delete_file_task.prg_count_total = (int64)
Main.first_snapshot_count;
1931 status = bak.remove(
true);
1951 if (mnt.mount_point ==
"/"){
1959 if (mnt.mount_point ==
"/"){
1970 if (mnt.mount_point ==
"/boot"){
1978 if (mnt.mount_point ==
"/boot"){
1989 if (mnt.mount_point ==
"/boot/efi"){
1997 if (mnt.mount_point ==
"/boot/efi"){
2008 if (mnt.mount_point ==
"/home"){
2016 if (mnt.mount_point ==
"/home"){
2040 string source_path =
"/tmp/bastionguard-backup";
2085 string fstab_path =
"/etc/fstab";
2086 fstab_list =
FsTabEntry.read_file(fstab_path);
2087 string cryttab_path =
"/etc/crypttab";
2095 bool root_found =
false;
2096 bool boot_found =
false;
2097 bool home_found =
false;
2100 foreach(var fs_entry
in fstab_list){
2104 if (!fs_entry.is_for_system_directory()){
2110 string p1 =
"%s/*".printf(fs_entry.mount_point);
2111 string p2 =
"%s/**".printf(fs_entry.mount_point);
2112 string p3 =
"%s/***".printf(fs_entry.mount_point);
2127 if (fs_entry.device_uuid.length > 0){
2134 if (dev_fstab ==
null){
2146 if (fs_entry.device_string.has_prefix(
"/dev/mapper/")){
2148 string mapped_name = fs_entry.device_string.replace(
"/dev/mapper/",
"");
2150 foreach(var crypt_entry
in crypttab_list){
2152 if (crypt_entry.mapped_name == mapped_name){
2155 fs_entry.device_string = crypt_entry.device_string;
2157 if (fs_entry.device_uuid.length > 0){
2162 if (dev_luks !=
null){
2164 string msg_out, msg_err;
2168 if (dev_unlocked !=
null){
2169 dev_fstab = dev_unlocked;
2173 dev_fstab = dev_luks;
2187 if (dev_fstab !=
null){
2189 log_debug(
"added: dev: %s, path: %s, options: %s".printf(
2190 dev_fstab.
device, fs_entry.mount_point, fs_entry.options));
2194 if (fs_entry.mount_point ==
"/"){
2199 log_debug(
"missing: dev: %s, path: %s, options: %s".printf(
2200 fs_entry.device_string, fs_entry.mount_point, fs_entry.options));
2205 if (fs_entry.mount_point ==
"/"){
2208 if (fs_entry.mount_point ==
"/boot"){
2211 if (fs_entry.mount_point ==
"/home"){
2242 entry.device =
null;
2247 if (mnt.device !=
null){
2248 log_debug(
"Entry: %s -> %s".printf(mnt.device.device, mnt.mount_point));
2251 log_debug(
"Entry: null -> %s".printf(mnt.mount_point));
2257 return strcmp(a.mount_point, b.mount_point);
2262 log_debug(
"Main: init_mount_list(): exit");
2268 if(grub_dev !=
null){
2272 while ((grub_dev !=
null) && grub_dev.has_parent()){
2273 grub_dev = grub_dev.parent;
2308 if (entry.device ==
null){
2318 log_error(
_(
"Backup device not specified!"));
2322 log_msg(
string.nfill(78,
'*'));
2324 log_msg(
string.nfill(78,
'*'));
2328 log_error(
_(
"Snapshot to restore not specified!"));
2333 log_error(
_(
"Selected snapshot is marked for deletion"));
2337 log_msg(
string.nfill(78,
'*'));
2339 log_msg(
string.nfill(78,
'*'));
2347 log_error(
_(
"BTRFS device is not mounted") +
": @");
2351 log_error(
_(
"BTRFS device is not mounted") +
": @home");
2357 log_error(
_(
"Target device not specified!"));
2363 log_error(
_(
"Target device is not mounted"));
2388 Thread.usleep((ulong) GLib.TimeSpan.MILLISECOND * 100);
2395 log_debug(
"Main: restore_snapshot(): exit");
2401 out
string msg_devices, out
string msg_reboot, out
string msg_disclaimer){
2405 log_debug(
"Main: get_restore_messages()");
2410 msg +=
"\n%s\n%s\n%s\n".printf(
2411 string.nfill(70,
'='),
2413 string.nfill(70,
'=')
2417 msg +=
_(
"Data will be modified on following devices:") +
"\n\n";
2419 int max_mount =
_(
"Mount").length;
2420 int max_dev =
_(
"Device").length;
2424 if (entry.device ==
null){
continue; }
2428 if (entry.subvolume_name().length == 0){
continue; }
2435 string dev_name = entry.device.full_name_with_parent;
2436 if (entry.subvolume_name().length > 0){
2437 dev_name = dev_name +
"(%s)".printf(entry.subvolume_name());
2439 else if (entry.lvm_name().length > 0){
2440 dev_name = dev_name +
"(%s)".printf(entry.lvm_name());
2443 if (dev_name.length > max_dev){
2444 max_dev = dev_name.length;
2446 if (entry.mount_point.length > max_mount){
2447 max_mount = entry.mount_point.length;
2451 var txt = (
"%%-%ds %%-%ds".printf(max_dev, max_mount))
2452 .printf(
_(
"Device"),
_(
"Mount"));
2455 txt +=
string.nfill(max_dev,
'-') +
" " +
string.nfill(max_mount,
'-');
2460 if (entry.device ==
null){
continue; }
2464 if (entry.subvolume_name().length == 0){
continue; }
2471 string dev_name = entry.device.full_name_with_parent;
2472 if (entry.subvolume_name().length > 0){
2473 dev_name = dev_name +
"(%s)".printf(entry.subvolume_name());
2475 else if (entry.lvm_name().length > 0){
2476 dev_name = dev_name +
"(%s)".printf(entry.lvm_name());
2479 txt += (
"%%-%ds %%-%ds".printf(max_dev, max_mount)).printf(dev_name, entry.mount_point);
2485 msg +=
"<span size=\"medium\"><tt>%s</tt></span>".printf(txt);
2488 msg +=
"%s\n".printf(txt);
2500 msg +=
_(
"Please save your work and close all applications.") +
"\n";
2501 msg +=
_(
"System will reboot after files are restored.");
2510 msg +=
"\n%s\n%s\n%s\n".printf(
2511 string.nfill(70,
'='),
2512 _(
"Disclaimer").up(),
2513 string.nfill(70,
'=')
2517 msg +=
_(
"This software comes without absolutely NO warranty and the author takes no responsibility for any damage arising from the use of this program.");
2518 msg +=
" " +
_(
"If these terms are not acceptable to you, please do not proceed beyond this point!");
2524 msg_disclaimer = msg;
2534 log_debug(
"Main: get_restore_messages(): exit");
2539 log_debug(
"Main: create_restore_scripts()");
2541 string sh =
"export LC_ALL=C.UTF-8\n";
2550 sh +=
"echo '" +
_(
"Please do not interrupt the restore process!") +
"'\n";
2551 sh +=
"echo '" +
_(
"System will reboot after files are restored") +
"'\n";
2558 sh +=
"rsync -avir --force --delete --delete-before";
2606 sh +=
"for i in dev dev/pts proc run sys; do mount --bind \"/$i\" \"%s$i\"; done \n".printf(
restore_target_path);
2613 sh +=
"echo '" +
_(
"Re-installing GRUB2 bootloader...") +
"' \n";
2620 if (target_distro.dist_type ==
"redhat"){
2624 sh +=
"%s grub2-install --recheck --force %s \n".printf(chroot,
grub_device);
2639 sh +=
"%s grub-install --recheck --force %s \n".printf(chroot,
grub_device);
2646 log_debug(
"skipping sh_grub: reinstall_grub2=%s, grub_device=%s".printf(
2654 sh +=
"echo '" +
_(
"Generating initramfs...") +
"' \n";
2656 if (target_distro.dist_type ==
"redhat"){
2657 sh +=
"%s dracut -f -v \n".printf(chroot);
2659 else if (target_distro.dist_type ==
"arch"){
2660 sh +=
"%s mkinitcpio -p /etc/mkinitcpio.d/*.preset\n".printf(chroot);
2663 sh +=
"%s update-initramfs -u -k all \n".printf(chroot);
2671 sh +=
"echo '" +
_(
"Updating GRUB menu...") +
"' \n";
2673 if (target_distro.dist_type ==
"redhat"){
2674 sh +=
"%s grub2-mkconfig -o /boot/grub2/grub.cfg \n".printf(chroot);
2676 if (target_distro.dist_type ==
"arch"){
2677 sh +=
"%s grub-mkconfig -o /boot/grub/grub.cfg \n".printf(chroot);
2680 sh +=
"%s update-grub \n".printf(chroot);
2688 sh +=
"echo '" +
_(
"Syncing file systems...") +
"' \n";
2689 sh +=
"sync ; sleep 10s; \n";
2694 sh +=
"echo '" +
_(
"Cleaning up...") +
"' \n";
2695 sh +=
"for i in dev/pts dev proc run sys; do umount -f \"%s$i\"; done \n".printf(
restore_target_path);
2703 log_debug(
"Running post-restore tasks...");
2705 sh +=
"if [ -d \"/etc/bastionguard-backup/restore-hooks.d\" ]; then \n";
2706 sh +=
" run-parts --verbose /etc/bastionguard-backup/restore-hooks.d \n";
2713 sh +=
"echo '" +
_(
"Rebooting system...") +
"' \n";
2714 sh +=
"sleep 5s \n";
2715 sh +=
"reboot -f \n";
2724 log_debug(
"Main: restore_current_console()");
2726 string script = sh_sync + sh_finish;
2736 string std_out, std_err;
2742 return (ret_val == 0);
2747 log_debug(
"Main: restore_current_gui()");
2749 string script = sh_sync + sh_finish;
2760 log_debug(
"Main: restore_other_console()");
2764 string script = sh_sync;
2772 string std_out, std_err;
2800 string std_out, std_err;
2806 return (ret_val == 0);
2816 task.relative =
false;
2817 task.verbose =
true;
2818 task.delete_extra =
true;
2819 task.delete_excluded =
false;
2820 task.delete_after =
true;
2825 task.source_path =
"/";
2840 else if (
Main.first_snapshot_count > 0){
2841 task.prg_count_total =
Main.first_snapshot_count;
2844 task.prg_count_total = 500000;
2888 int ret_val =
exec_script_sync(sh_finish,
null,
null,
false,
false,
false,
true);
2890 log_debug(
"script exit code: %d".printf(ret_val));
2892 return (ret_val == 0);
2899 string fstab_path =
path_combine(target_path,
"etc/fstab");
2902 log_debug(
"File not found: %s".printf(fstab_path));
2906 var fstab_list =
FsTabEntry.read_file(fstab_path);
2912 var entry =
FsTabEntry.find_entry_by_mount_point(fstab_list, mnt.mount_point);
2917 entry.mount_point = mnt.mount_point;
2918 fstab_list.add(entry);
2922 entry.device_string =
"UUID=%s".printf(mnt.device.uuid);
2923 entry.type = mnt.device.fstype;
2926 if (mnt.device.fstype !=
"btrfs"){
2928 entry.remove_option(
"subvol=%s".printf(entry.subvolume_name()));
2941 for(
int i = fstab_list.size - 1; i >= 0; i--){
2942 var entry = fstab_list[i];
2944 if (!entry.is_for_system_directory()){
continue; }
2948 fstab_list.remove(entry);
2956 FsTabEntry.write_file(fstab_list, fstab_path,
false);
2958 log_msg(
_(
"Updated /etc/fstab on target device") +
": %s".printf(fstab_path));
2962 foreach(var entry
in fstab_list){
2963 if (entry.mount_point.length == 0){
continue; }
2964 if (!entry.mount_point.has_prefix(
"/")){
continue; }
2967 target_path, entry.mount_point);
2969 if (entry.is_comment
2970 || entry.is_empty_line
2971 || (mount_path.length == 0)){
2978 log_msg(
"Created mount point on target device: %s".printf(
2979 entry.mount_point));
2985 log_debug(
"Main: fix_fstab_file(): exit");
2992 string crypttab_path =
path_combine(target_path,
"etc/crypttab");
2995 log_debug(
"File not found: %s".printf(crypttab_path));
3003 log_debug(
"checking for 'nofail' option...");
3005 foreach(var entry
in crypttab_list){
3006 entry.append_option(
"nofail");
3014 if ((mnt.device !=
null) && (mnt.device.parent !=
null)
3015 && ((mnt.device.is_on_encrypted_partition())
3016 || ((mnt.device.parent.parent !=
null) &&
3017 (mnt.device.parent.is_on_encrypted_partition())))){
3021 var crypt_parent = (mnt.device.is_on_encrypted_partition()) ?
3022 mnt.device.parent.uuid : mnt.device.parent.parent.uuid;
3026 crypttab_list, crypt_parent);
3031 crypttab_list.add(entry);
3035 entry.device_uuid = crypt_parent;
3036 entry.mapped_name =
"luks-%s".printf(crypt_parent);
3037 entry.keyfile =
"none";
3038 entry.options =
"luks,nofail";
3044 CryptTabEntry.write_file(crypttab_list, crypttab_path,
false);
3046 log_msg(
_(
"Updated /etc/crypttab on target device") +
": %s".printf(crypttab_path));
3048 log_debug(
"Main: fix_crypttab_file(): exit");
3054 string sh_fsck =
"echo '" +
_(
"Checking file systems for errors...") +
"' \n";
3056 if (mnt.device !=
null) {
3057 sh_fsck +=
"fsck -y %s \n".printf(mnt.device.device);
3060 sh_fsck +=
"echo '' \n";
3067 log_debug(
"Main: restore_execute_rsync()");
3073 string sh_sync, sh_finish;
3085 var f = File.new_for_path(control_file_path);
3086 if(f.query_exists()){
3097 log_msg(
_(
"Comparing Files (Dry Run)..."));
3100 log_msg(
_(
"Restoring snapshot..."));
3134 log_msg(
string.nfill(78,
'-'));
3152 log_debug(
"Main: restore_execute_btrfs()");
3156 log_msg(
string.nfill(78,
'-'));
3177 log_debug(
"Running post-restore tasks...");
3179 string sh =
"test -d \"/etc/bastionguard-backup/restore-hooks.d\" &&" +
3180 " export TS_SNAPSHOT_PATH=\"" + snapshot_to_restore.path +
"\" &&" +
3181 " run-parts --verbose /etc/bastionguard-backup/restore-hooks.d";
3185 log_debug(
"Finished running post-restore tasks...");
3188 log_msg(
_(
"Snapshot will become active after system is rebooted."));
3191 log_msg(
string.nfill(78,
'-'));
3199 log_debug(
"Main: create_pre_restore_snapshot_btrfs()");
3201 string cmd, std_out, std_err;
3202 DateTime dt_created =
new DateTime.now_local();
3203 string time_stamp = dt_created.format(
"%Y-%m-%d_%H-%M-%S");
3204 string snapshot_name = time_stamp;
3205 string snapshot_path =
"";
3213 bool create_pre_restore_backup =
false;
3225 log_msg(
_(
"Found existing pre-restore snapshot") +
": %s".printf(bak.name));
3234 log_msg(
_(
"Deleted subvolume") +
": @");
3238 log_msg(
_(
"Deleted subvolume") +
": @home");
3243 snap_prev.update_control_file();
3246 create_pre_restore_backup =
true;
3250 create_pre_restore_backup =
true;
3253 if (create_pre_restore_backup){
3255 log_msg(
_(
"Creating pre-restore snapshot from system subvolumes..."));
3261 bool no_subvolumes_found =
true;
3263 var subvol_list =
new Gee.ArrayList<
Subvolume>();
3265 var subvol_names =
new string[] {
"@" };
3267 subvol_names =
new string[] {
"@",
"@home" };
3270 foreach(
string subvol_name
in subvol_names){
3277 log_error(
_(
"Could not find system subvolume") +
": %s".printf(subvol_name));
3278 dir_delete(snapshot_path);
3282 no_subvolumes_found =
false;
3284 string dst_path =
path_combine(snapshot_path, subvol_name);
3285 cmd =
"mv '%s' '%s'".printf(src_path, dst_path);
3288 int status =
exec_sync(cmd, out std_out, out std_err);
3292 log_error(
_(
"Failed to move system subvolume to snapshot directory") +
": %s".printf(subvol_name));
3297 subvol_list.add(
new Subvolume(subvol_name, dst_path, subvol_dev.uuid,
repo));
3299 log_msg(
_(
"Moved system subvolume to snapshot directory") +
": %s".printf(subvol_name));
3303 if (no_subvolumes_found){
3305 log_error(
_(
"Could not find system subvolumes for creating pre-restore snapshot"));
3315 "ondemand",
"", 0,
true,
false,
repo);
3321 foreach(var subvol
in subvol_list){
3322 snap.subvolumes.set(subvol.name, subvol);
3327 log_msg(
_(
"Created pre-restore snapshot") +
": %s".printf(snap.name));
3342 var config =
new Json.Object();
3345 config.set_string_member(
"backup_device_uuid",
3348 config.set_string_member(
"parent_device_uuid",
3352 config.set_string_member(
"backup_device_uuid",
backup_uuid);
3356 config.set_string_member(
"do_first_run",
false.to_string());
3357 config.set_string_member(
"btrfs_mode",
btrfs_mode.to_string());
3360 config.set_string_member(
"stop_cron_emails",
stop_cron_emails.to_string());
3362 config.set_string_member(
"schedule_monthly",
schedule_monthly.to_string());
3363 config.set_string_member(
"schedule_weekly",
schedule_weekly.to_string());
3364 config.set_string_member(
"schedule_daily",
schedule_daily.to_string());
3365 config.set_string_member(
"schedule_hourly",
schedule_hourly.to_string());
3366 config.set_string_member(
"schedule_boot",
schedule_boot.to_string());
3368 config.set_string_member(
"count_monthly",
count_monthly.to_string());
3369 config.set_string_member(
"count_weekly",
count_weekly.to_string());
3370 config.set_string_member(
"count_daily",
count_daily.to_string());
3371 config.set_string_member(
"count_hourly",
count_hourly.to_string());
3372 config.set_string_member(
"count_boot",
count_boot.to_string());
3383 config.set_string_member(
"date_format",
date_format);
3385 Json.Array arr =
new Json.Array();
3387 arr.add_string_element(path);
3389 config.set_array_member(
"exclude", arr);
3391 arr =
new Json.Array();
3393 arr.add_string_element(name);
3395 config.set_array_member(
"exclude-apps", arr);
3397 var
json =
new Json.Generator();
3400 var node =
new Json.Node(NodeType.OBJECT);
3401 node.set_object(config);
3402 json.set_root(node);
3405 string conf_dir = File.new_for_path(this.app_conf_path).get_parent().get_path();
3407 if (!FileUtils.test(conf_dir, FileTest.IS_DIR)) {
3408 DirUtils.create_with_parents(conf_dir, 0755);
3409 log_debug(
"Directory configurazione creata: " + conf_dir);
3412 json.to_file(this.app_conf_path);
3418 log_msg(
_(
"App config saved") +
": %s".printf(this.app_conf_path));
3423 log_debug(
"Main: import_timeshift_config_if_available()");
3425 string[] candidate_paths = {
3427 "/etc/timeshift/timeshift.json",
3428 "/etc/timeshift.json",
3431 "/etc/bastionguard-backup/bastionguard-backup.json",
3432 "/etc/bastionguard-backup.json"
3435 string src_path =
"";
3436 bool importing_legacy_timeshift =
false;
3438 foreach (
string path
in candidate_paths) {
3444 if (path == this.app_conf_path) {
3450 if ((path ==
"/etc/timeshift/timeshift.json") ||
3451 (path ==
"/etc/timeshift.json")) {
3452 importing_legacy_timeshift =
true;
3457 if (src_path.length == 0) {
3458 log_debug(
"No Timeshift/BastionGuard config found for import");
3462 if (importing_legacy_timeshift) {
3463 log_msg(
_(
"Found legacy Timeshift config") +
": %s".printf(src_path));
3466 log_msg(
_(
"Found BastionGuard config") +
": %s".printf(src_path));
3469 var parser =
new Json.Parser();
3471 parser.load_from_file(src_path);
3474 log_error(
"Failed to parse config: " + e.message);
3478 Json.Node? node = parser.get_root();
3479 Json.Object config = node?.get_object() ??
new Json.Object();
3486 if (config.has_member(
"include_btrfs_home")) {
3521 if (config.has_member(
"exclude")) {
3522 foreach (Json.Node jnode in config.get_array_member(
"exclude").get_elements()) {
3523 string path = jnode.get_string();
3535 if (config.has_member(
"exclude-apps")) {
3536 foreach (Json.Node jnode in config.get_array_member(
"exclude-apps").get_elements()) {
3537 string name = jnode.get_string();
3548 string conf_dir = File.new_for_path(this.app_conf_path).get_parent().get_path();
3549 if (!FileUtils.test(conf_dir, FileTest.IS_DIR)) {
3550 DirUtils.create_with_parents(conf_dir, 0755);
3555 if (importing_legacy_timeshift) {
3556 log_msg(
_(
"Imported Timeshift settings into BastionGuard Backup"));
3559 log_msg(
_(
"Imported BastionGuard settings"));
3570 var f = File.new_for_path(this.app_conf_path);
3572 if (!f.query_exists()) {
3590 var parser =
new Json.Parser();
3592 parser.load_from_file(this.app_conf_path);
3596 Json.Node? node = parser.get_root();
3599 Json.Object config = node?.get_object() ??
new Json.Object();
3601 bool do_first_run =
json_get_bool(config,
"do_first_run",
false);
3609 if (config.has_member(
"include_btrfs_home")){
3646 if (config.has_member (
"exclude")){
3648 foreach (Json.Node jnode in config.get_array_member (
"exclude").get_elements()) {
3650 string path = jnode.get_string();
3663 if (config.has_member (
"exclude-apps")){
3665 var apps = config.get_array_member(
"exclude-apps");
3667 foreach (Json.Node jnode in apps.get_elements()) {
3669 string name = jnode.get_string();
3678 log_msg(
_(
"App config loaded") +
": %s".printf(this.app_conf_path));
3683 string[] ts_configs = {
3684 "/etc/bastionguard-backup/bastionguard-backup.json",
3685 "/etc/bastionguard-backup.json",
3686 "/etc/timeshift/timeshift.json",
3687 "/etc/timeshift.json"
3689 foreach (
string ts_path
in ts_configs) {
3692 var ts_parser =
new Json.Parser();
3693 ts_parser.load_from_file(ts_path);
3694 var ts_cfg = ts_parser.get_root()?.get_object();
3695 if (ts_cfg !=
null) {
3697 if (uuid.length > 0) {
3699 log_msg(
"BastionGuardBackup fallback: backup_uuid = " + uuid);
3712 log_msg(
"First run mode (config file not found)");
3716 bool supported =
sys_subvolumes.has_key(
"@") && cmd_exists(
"btrfs");
3718 log_msg(
_(
"Selected default snapshot type") +
": %s".printf(
"BTRFS"));
3722 log_msg(
_(
"Selected default snapshot type") +
": %s".printf(
"RSYNC"));
3737 log_msg(
"Using system disk as snapshot device for creating snapshots in BTRFS mode");
3739 log_msg(
_(
"Option --snapshot-device should not be specified for creating snapshots in BTRFS mode"));
3751 if (cmd_dev !=
null){
3771 log_debug(
"Setting snapshot device from config file");
3775 Device dev_parent =
null;
3785 log_debug(
"Snapshot device is on an encrypted partition");
3789 else if (dev !=
null){
3814 log_debug(
"Main: initialize_repo(): exit");
3836 log_error(
"ts: " +
_(
"Failed to get partition list."));
3853 foreach(var mp
in pi.mount_points){
3856 if ((pi.type ==
"loop") || (pi.has_parent() && (pi.parent.type ==
"loop"))){
3860 if (mp.mount_point ==
"/"){
3863 string txt =
_(
"/ is mapped to device") +
": %s, UUID=%s".printf(pi.device,pi.uuid);
3864 if (mp.subvolume_name().length > 0){
3865 txt +=
", subvol=%s".printf(mp.subvolume_name());
3871 if (mp.mount_point ==
"/home"){
3874 string txt =
_(
"/home is mapped to device") +
": %s, UUID=%s".printf(pi.device,pi.uuid);
3875 if (mp.subvolume_name().length > 0){
3876 txt +=
", subvol=%s".printf(mp.subvolume_name());
3882 if (mp.mount_point ==
"/boot"){
3885 string txt =
_(
"/boot is mapped to device") +
": %s, UUID=%s".printf(pi.device,pi.uuid);
3886 if (mp.subvolume_name().length > 0){
3887 txt +=
", subvol=%s".printf(mp.subvolume_name());
3893 if (mp.mount_point ==
"/boot/efi"){
3896 string txt =
_(
"/boot/efi is mapped to device") +
": %s, UUID=%s".printf(pi.device,pi.uuid);
3897 if (mp.subvolume_name().length > 0){
3898 txt +=
", subvol=%s".printf(mp.subvolume_name());
3952 string msg =
_(
"The target partition has an unsupported subvolume layout.") +
"\n";
3953 msg +=
_(
"Only ubuntu-type layouts with @ and @home subvolumes are currently supported.");
3956 string title =
_(
"Unsupported Subvolume Layout");
3957 gtk_messagebox(title, msg,
null,
true);
3970 if (mnt.device ==
null){
3975 if (mnt.device.is_encrypted_partition()){
3978 if (mnt.device.has_children()){
3979 mnt.device = mnt.device.children[0];
3983 string msg_out, msg_err;
3986 mnt.device,
"",
"", parent_win, out msg_out, out msg_err);
3989 if (dev_unlocked ==
null){
3993 mnt.device = dev_unlocked;
3998 string mount_options =
"";
4000 if (mnt.device.fstype ==
"btrfs"){
4001 if (mnt.mount_point ==
"/"){
4002 mount_options =
"subvol=@";
4005 if (mnt.mount_point ==
"/home"){
4006 mount_options =
"subvol=@home";
4039 string title =
_(
"Critical Error");
4040 string msg =
_(
"Failed to unmount device!") +
"\n" +
_(
"Application will exit");
4041 gtk_messagebox(title, msg,
null,
true);
4046 return is_unmounted;
4058 log_debug(
"check_btrfs_volume():%s".printf(subvol_names));
4072 string msg_out, msg_err;
4076 if (dev_unlocked ==
null){
4078 log_debug(
"SnapshotRepo: check_btrfs_volume(): exit");
4082 Device.
mount(dev_unlocked.uuid, mnt_btrfs,
"subvolid=0",
true);
4094 bool supported =
true;
4096 foreach(
string subvol_name
in subvol_names.split(
",")){
4102 dir_delete(mnt_btrfs);
4103 log_debug(
_(
"Removed mount directory: '%s'").printf(mnt_btrfs));
4112 log_debug(
"try_select_default_device_for_backup()");
4151 if (dev.
type ==
"disk") {
return false; }
4172 if (
Main.first_snapshot_size > 0){
4173 return Main.first_snapshot_size;
4185 task.dest_path = dir_empty;
4188 task.delete_extra =
true;
4189 task.delete_excluded =
true;
4190 task.delete_after =
false;
4194 task.io_nice =
true;
4197 task.dry_run =
true;
4202 Thread.usleep((ulong) GLib.TimeSpan.MILLISECOND * 16);
4203 if (callback !=
null) callback();
4215 log_debug(
"estimate_system_size(): ok");
4217 return Main.first_snapshot_size;
4248 Thread.usleep((ulong) GLib.TimeSpan.MILLISECOND * 100);
4290 log_debug(
"query_subvolume_id():%s".printf(subvol_name));
4297 cmd =
"btrfs subvolume list '%s'".printf(
repo.
mount_paths[subvol_name]);
4299 ret_val =
exec_sync(cmd, out std_out, out std_err);
4302 log_error(
_(
"btrfs returned an error") +
": %d".printf(ret_val));
4303 log_error(
_(
"Failed to query subvolume list"));
4313 foreach(
string line
in std_out.split(
"\n")){
4314 if (line ==
null) {
continue; }
4316 string[] parts = line.split(
" ");
4317 if (parts.length < 2) {
continue; }
4332 foreach(var sub
in bak.subvolumes.values){
4333 if (line.has_suffix(sub.path.replace(
repo.
mount_paths[sub.name] +
"/",
""))){
4341 if (subvol !=
null){
4342 subvol.id =
long.parse(parts[1]);
4358 log_debug(
"query_subvolume_quota():%s".printf(subvol_name));
4367 cmd =
"btrfs qgroup show %s '%s'".printf(options,
repo.
mount_paths[subvol_name]);
4369 ret_val =
exec_sync(cmd, out std_out, out std_err);
4378 ret_val =
exec_sync(cmd, out std_out, out std_err);
4382 if (std_err.contains(
"not enabled")) {
4383 log_msg(
"btrfs: Quotas are not enabled");
4387 log_error(
_(
"btrfs returned an error") +
": %d".printf(ret_val));
4388 log_error(
_(
"Failed to query subvolume quota"));
4402 foreach(
string line
in std_out.split(
"\n")){
4403 if (line ==
null) {
continue; }
4405 string[] parts = line.split(
" ");
4406 if (parts.length < 3) {
continue; }
4407 if (parts[0].split(
"/").length < 2) {
continue; }
4409 int subvol_id =
int.parse(parts[0].split(
"/")[1]);
4425 foreach(var sub
in bak.subvolumes.values){
4426 if (sub.id == subvol_id){
4433 if (subvol !=
null){
4435 foreach(
string part
in parts){
4436 if (part.strip().length > 0){
4440 subvol.total_bytes = int64.parse(part);
4443 subvol.unshared_bytes = int64.parse(part);
4455 bak.update_control_file();
4469 string entry =
"bastionguard-backup --backup";
4476 if (++count == 100){
4481 entry =
"bastionguard-backup-btrfs --backup";
4488 if (++count == 100){
4527 Gee.ArrayList<
string> list =
new Gee.ArrayList<
string>();
4530 var dir = File.new_for_path (
log_dir);
4531 var enumerator = dir.enumerate_children (
"*", 0);
4533 var info = enumerator.next_file ();
4536 while (info !=
null) {
4537 if (info.get_file_type() == FileType.REGULAR) {
4538 path =
log_dir +
"/" + info.get_name();
4543 info = enumerator.next_file ();
4546 CompareDataFunc<string> compare_func = (a, b) => {
4550 list.sort((owned) compare_func);
4552 if (list.size > 500){
4556 for(
int k = 0; k < 100; k++){
4558 var file = File.new_for_path (list[k]);
4560 if (file.query_exists()){
4562 log_msg(
"%s: %s".printf(
_(
"Removed"), list[k]));
4566 log_msg(
_(
"Older log files removed"));
4604 if (!
dir_exists(
"/run/bastionguard-backup")){
return; }
4608 foreach(var dname
in dirlist){
4610 int pid =
int.parse(dname);
4612 if (pid != Posix.getpid()){
4616 string procdir =
"/proc/%d".printf(pid);
4623 string mdir =
"/run/bastionguard-backup/%s".printf(dname);
4627 foreach(var dname2
in dirlist2){
4629 string mdir2 =
"/run/bastionguard-backup/%s/%s".printf(dname, dname2);
4635 foreach (var mnt
in dev.mount_points){
4637 if (mnt.mount_point == mdir2){
4639 log_debug(
"\nFound stale mount for device '%s' at path '%s'".printf(dev.device, mdir2));
4654 if(!dir_empty_delete(mdir2)) {
4655 log_debug(
"E: Failed to delete %s".printf(mdir2));
4668 log_error(
"Failed to remove directory");
4669 log_msg(
"Ret=%d".printf(retval3));
static void add_app_exclude_entries_from_home(string home)
static Gee.ArrayList< AppExcludeEntry > get_apps_list(Gee.ArrayList< string > selected_app_names)
static void add_app_exclude_entries_from_path(string user_home)
bool create(string app_name, string message)
string??? stat_time_remaining
BackupInProgressDialog(string lock_message)
static bool has_job(string entry, bool partial_match, bool use_cached_text)
static bool remove_job(string entry, bool use_regex, bool use_cached_text)
static bool add_script_file(string file_name, string cron_dir_type, string text, bool stop_cron_emails)
static bool remove_script_file(string file_name, string cron_dir_type)
bool has_linux_filesystem()
static Gee.ArrayList< Device > get_filesystems(bool get_space=true, bool get_mounts=true)
bool is_encrypted_partition()
static bool mount(string dev_name_or_uuid, string mount_point, string mount_options="", bool silent=false)
static ? Device luks_unlock(Device luks_device, string mapped_name, string passphrase, Gtk.Window? parent_window, out string message, out string details)
bool is_mounted_at_path(string subvolname, string mount_path)
static ? Device get_device_by_uuid(string uuid)
static bool unmount(string mount_point)
static ? Device get_device_by_name(string file_name)
static void init(string[] args, string app_name)
static LinuxDistro get_dist_info(string root_path)
void query_subvolume_info_thread()
Snapshot? create_snapshot_with_rsync(string tag, DateTime dt_created)
Gee.ArrayList< string > exclude_list_restore
Gee.HashMap< string, SystemUser > current_system_users
bool restore_current_system
static uint64 first_snapshot_size
int retain_snapshots_max_days
Snapshot snapshot_to_restore
string restore_exclude_file
void query_subvolume_info(SnapshotRepo parent_repo)
void check_and_remove_timeshift_btrfs()
void run_post_backup_hooks(string snapshot_path)
void parse_some_arguments(string[] args)
void cleanup_unmount_devices()
static int64 first_snapshot_count
Gee.ArrayList< string > exclude_list_default
string restore_source_path
void add_app_exclude_entries()
string app_conf_path_default
SnapshotLocationStatus check_backup_location(out string message, out string details)
bool restore_other_console(string sh_sync, string sh_finish)
bool query_subvolume_ids()
Gee.ArrayList< string > exclude_list_home
bool check_btrfs_layout(Device? dev_root, Device? dev_home, bool unlock)
Snapshot? create_snapshot_with_btrfs(string tag, DateTime dt_created)
bool restore_execute_btrfs()
string mount_point_restore
bool thread_restore_running
delegate void progressCallback()
bool encrypted_home_warning_shown
Main(string[] args, bool gui_mode)
Gee.ArrayList< Device > partitions
bool create_snapshot_for_tag(string tag, DateTime dt_created)
bool restore_current_gui(string sh_sync, string sh_finish)
void detect_encrypted_dirs()
Gee.ArrayList< MountEntry > mount_list
Gee.ArrayList< string > create_exclude_list_for_backup()
LinuxDistro current_distro
bool encrypted_private_warning_shown
int startup_delay_interval_mins
Gee.ArrayList< string > exclude_list_user
bool thread_restore_success
Gtk.? Window parent_window
bool check_btrfs_volume(Device dev, string subvol_names, bool unlock)
void add_app_exclude_entries_for_prefix(string path_prefix)
Snapshot snapshot_to_delete
bool restore_other_gui(string sh_sync, string sh_finish)
uint64 get_space_needed_for_rsync_snapshot(DateTime dt_created)
Gee.ArrayList< AppExcludeEntry > exclude_list_apps
void fix_fstab_file(string target_path)
QGroupStatus _btrfs_qgroups_enabled_internal
uint64 estimate_system_size(progressCallback? callback=null)
string users_with_encrypted_home
bool query_subvolume_quota(string subvol_name)
string encrypted_home_dirs
string? save_exclude_list_for_backup(string output_path)
string restore_target_path
bool import_timeshift_config_if_available()
bool check_btrfs_layout_system(Gtk.Window? win=null)
void unmount_target_device(bool exit_on_error=true)
void get_restore_messages(bool formatted, out string msg_devices, out string msg_reboot, out string msg_disclaimer)
Gee.ArrayList< string > exclude_app_names
void detect_system_devices()
bool restore_execute_rsync()
Gee.HashMap< string, Subvolume > sys_subvolumes
bool include_btrfs_home_for_restore
bool btrfs_qgroups_enabled
RsyncSpaceCheckTask space_check_task
Gee.ArrayList< Snapshot > delete_list
bool restore_current_console(string sh_sync, string sh_finish)
Gee.ArrayList< string > create_exclude_list_for_restore()
Gee.ArrayList< string > exclude_list_default_extra
bool unmount_device(string mount_point, bool exit_on_error=true)
bool query_subvolume_quotas()
bool query_subvolume_id(string subvol_name)
void check_btrfs_version_capabilities()
void try_select_default_device_for_backup(Gtk.Window? parent_win)
void check_and_repair_filesystems()
void save_exclude_list_selections()
int?[] get_btrfs_version_array()
void add_default_exclude_entries()
bool save_exclude_list_for_restore(string output_path)
bool include_btrfs_home_for_backup
string backup_parent_uuid
void exit_app(int exit_code=0)
void set_first_run_flag()
void fix_crypttab_file(string target_path)
const string date_format_default
string encrypted_private_dirs
bool restore_snapshot(Gtk.Window? parent_win)
bool thread_subvol_info_success
void set_tags(Snapshot snapshot)
DeleteFileTask delete_file_task
bool mount_target_devices(Gtk.Window? parent_win=null)
bool thread_subvol_info_running
bool thread_delete_success
bool create_snapshot(bool is_ondemand, Gtk.Window? parent_win)
void create_restore_scripts(out string sh_sync, out string sh_finish)
bool check_device_for_backup(Device dev, bool unlock)
bool check_dependencies(out string msg)
const uint64 MIN_FREE_SPACE
string rsnapshot_conf_path
static bool btrfs_version__can_recursive_delete
bool create_pre_restore_snapshot_btrfs()
bool thread_delete_running
static ? MountEntry find_entry_by_mount_point(Gee.ArrayList< MountEntry > entries, string mount_path)
MountEntry(Device? device, string mount_point, string mount_options)
static int notify_send(string title, string message, int durationMillis, string urgency="low", string dialog_type="info")
Gee.ArrayList< Snapshot?> snapshots
SnapshotRepo. from_device(Device dev, Gtk.Window? parent_win, bool btrfs_repo)
Gee.HashMap< string, string > mount_paths
Snapshot? get_latest_snapshot(string tag, string sys_uuid)
bool has_space(uint64 needed=0)
SnapshotRepo. from_null()
SnapshotRepo. from_uuid(string uuid, Gtk.Window? parent_win, bool btrfs_repo)
SnapshotLocationStatus status_code
Gee.ArrayList< CryptTabEntry > cryttab_list
static Snapshot write_control_file(string snapshot_path, DateTime dt_created, string root_uuid, string distro_full_name, string tag, string comments, int64 item_count, bool is_btrfs, bool is_live, SnapshotRepo repo, bool silent=false)
Gee.HashMap< string, Subvolume > subvolumes
void update_control_file()
Gee.ArrayList< FsTabEntry > fstab_list
Subvolume(string name, string path, string parent_dev_uuid, SnapshotRepo? parent_repo)
static Gee.HashMap< string, Subvolume > detect_subvolumes_for_system_by_path(string system_path, SnapshotRepo? repo, Gtk.Window? parent_window)
static Gee.HashMap< string, SystemUser > read_users_from_file(string passwd_file)
TerminalWindow. with_parent(Gtk.Window? parent)
void execute_script(string script_path, bool wait=false)
Gee.ArrayList< string > dir_list_names(string path)
bool file_exists(string file_path)
string escape_single_quote(string file_path)
string? file_read(string file_path)
string file_basename(string file_path)
string file_parent(string file_path)
bool file_write(string file_path, string contents)
string path_combine(string path1, string path2)
bool file_copy(string src_file, string dest_file)
string format_file_size(uint64 size, bool binary_units=false, string unit="", bool show_units=true, int decimals=1)
bool file_delete(string file_path)
int64? file_line_count(string file_path)
bool dir_is_empty(string dir_path)
bool dir_create(string dir_path, bool show_message=false)
bool dir_exists(string dir_path)
void file_move(string src_file, string dest_file)
void gtk_set_busy(bool busy, Gtk.Window win)
uint64 json_get_uint64(Json.Object jobj, string member, uint64 def_value)
string json_get_string(Json.Object jobj, string member, string def_value)
int json_get_int(Json.Object jobj, string member, int def_value)
bool json_get_bool(Json.Object jobj, string member, bool def_value)
void log_debug(string message)
void log_error(string message, bool highlight=false, bool is_warning=false)
void log_msg(string message, bool highlight=false)
void log_to_file(string message, bool highlight=false)
int exec_script_sync(string script, out string? std_out=null, out string? std_err=null, bool supress_errors=false, bool run_as_admin=false, bool cleanup_tmp=true, bool print_to_terminal=false)
int exec_sync(string cmd, out string? std_out=null, out string? std_err=null)
string? save_bash_script_temp(string commands, string? script_path=null, bool force_locale=true, bool supress_errors=false)
double get_system_uptime_seconds()
void sleep(int milliseconds)