diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/build-installer.sh timeshift-btrfs-1.1~13~ubuntu12.04.1/build-installer.sh --- timeshift-btrfs-1.0~7~ubuntu12.04.1/build-installer.sh 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/build-installer.sh 2015-01-21 17:16:39.000000000 +0000 @@ -1,5 +1,11 @@ #!/bin/bash +app_name='timeshift-btrfs' +app_fullname='Timeshift BTRFS' +tgz="../../pbuilder/" +dsc="../../builds/${app_name}*.dsc" +libs="../../libs" + backup=`pwd` DIR="$( cd "$( dirname "$0" )" && pwd )" cd $DIR @@ -9,9 +15,6 @@ echo "Building installer..." -tgz="../../pbuilder/" -dsc="../../builds/timeshift-btrfs*.dsc" - chmod u+x ./install.sh # build installer ------------------------------------- @@ -31,11 +34,15 @@ exit 1 fi -dpkg-deb -x ${arch}/timeshift-btrfs*.deb ${arch}/extracted +dpkg-deb -x ${arch}/${app_name}*.deb ${arch}/extracted cp -p --no-preserve=ownership -t ${arch}/extracted ./install.sh +cp -p --no-preserve=ownership -t ${arch}/extracted/usr/share/${app_name}/libs ${libs}/${arch}/libgee.so.2 +cp -p --no-preserve=ownership -t ${arch}/extracted/usr/share/${app_name}/libs ${libs}/${arch}/libgudev-1.0.so.0 +cp -p --no-preserve=ownership -t ${arch}/extracted/usr/share/${app_name}/libs ${libs}/${arch}/libjson-glib-1.0.so.0 +chmod --recursive 0755 ${arch}/extracted/usr/share/${app_name} -makeself ${arch}/extracted ./timeshift-btrfs-latest-${arch}.run "TimeShift BTRFS (${arch})" ./install.sh +makeself ${arch}/extracted ./${app_name}-latest-${arch}.run "${app_fullname} (${arch})" ./install.sh #check for errors if [ $? -ne 0 ]; then @@ -44,7 +51,7 @@ exit 1 fi -cp -p --no-preserve=ownership ./${arch}/timeshift-btrfs*.deb ./timeshift-btrfs-latest-${arch}.deb +cp -p --no-preserve=ownership ./${arch}/${app_name}*.deb ./${app_name}-latest-${arch}.deb done diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/debian/bzr-builder.manifest timeshift-btrfs-1.1~13~ubuntu12.04.1/debian/bzr-builder.manifest --- timeshift-btrfs-1.0~7~ubuntu12.04.1/debian/bzr-builder.manifest 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/debian/bzr-builder.manifest 2015-01-21 17:16:40.000000000 +0000 @@ -1,2 +1,2 @@ -# bzr-builder format 0.3 deb-version {debupstream}~7 -lp:timeshift-btrfs-tool revid:tony.george.kol@gmail.com-20141001142633-xc0nu61xhfkuoais +# bzr-builder format 0.3 deb-version {debupstream}~13 +lp:timeshift-btrfs-tool revid:tony.george.kol@gmail.com-20150121171109-a72ro95tsljo6egt diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/debian/changelog timeshift-btrfs-1.1~13~ubuntu12.04.1/debian/changelog --- timeshift-btrfs-1.0~7~ubuntu12.04.1/debian/changelog 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/debian/changelog 2015-01-21 17:16:40.000000000 +0000 @@ -1,11 +1,17 @@ -timeshift-btrfs (1.0~7~ubuntu12.04.1) precise; urgency=low +timeshift-btrfs (1.1~13~ubuntu12.04.1) precise; urgency=low * Auto build. - -- Tony George Wed, 01 Oct 2014 14:36:45 +0000 + -- Tony George Wed, 21 Jan 2015 17:16:40 +0000 -timeshift-btrfs (1.0) trusty; urgency=medium +timeshift-btrfs (1.1) trusty; urgency=low - * Initial release + * Use libgudev for querying device information - -- Tony George Wed, 01 Oct 2014 11:00:00 +0530 + * Merged changes from Timeshift v1.7.x + + * Improved handling of encrypted devices + + * Updated command line options + + -- Tony George Wed, 21 Jan 2014 10:00:00 +0530 diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/debian/control timeshift-btrfs-1.1~13~ubuntu12.04.1/debian/control --- timeshift-btrfs-1.0~7~ubuntu12.04.1/debian/control 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/debian/control 2015-01-21 17:16:39.000000000 +0000 @@ -2,7 +2,7 @@ Section: utils Priority: extra Maintainer: Tony George -Build-Depends: debhelper (>= 8.0.0), autotools-dev, valac, libgtk-3-dev, libgee-dev, libsoup2.4-dev, libjson-glib-dev +Build-Depends: debhelper (>= 8.0.0), autotools-dev, valac, libgtk-3-dev, libgee-dev, libgudev-1.0-dev, libjson-glib-dev Standards-Version: 3.9.3 Homepage: http://teejeetech.blogspot.in/ #Vcs-Git: git://git.debian.org/collab-maint/hello.git @@ -10,7 +10,7 @@ Package: timeshift-btrfs Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libgtk-3-0, libgee2, libblkid1 +Depends: ${shlibs:Depends}, ${misc:Depends}, libblkid1, btrfs-tools #Recommends: Description: System restore utility for Linux systems installed on BTRFS partitions TimeShift BTRFS is a system restore utility which takes BTRFS snapshots of system subvolumes diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/installer/install.sh timeshift-btrfs-1.1~13~ubuntu12.04.1/installer/install.sh --- timeshift-btrfs-1.0~7~ubuntu12.04.1/installer/install.sh 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/installer/install.sh 2015-01-21 17:16:39.000000000 +0000 @@ -3,10 +3,10 @@ app_name='timeshift-btrfs' app_fullname='Timeshift' -generic_depends=(libgee json-glib) -debian_depends=(libgee2 libjson-glib-1.0-0 libblkid1) -redhat_depends=(libgee json-glib) -arch_depends=(libgee06 json-glib) +generic_depends=() +debian_depends=() +redhat_depends=() +arch_depends=() generic_recommends=() debian_recommends=() @@ -150,87 +150,11 @@ RESET_IFS -install_dependencies=y - -if command -v apt-get >/dev/null 2>&1; then - - if [ -f /etc/debian_version ]; then - install_dependencies=y - else - MSG_INFO "Found 'apt-get' package manager" - MSG_INFO "Install dependencies with 'apt-get'? (y/n):" "0" - read install_dependencies - if [ "$install_dependencies" == "" ]; then - install_dependencies=y - fi - fi - - if [ "$install_dependencies" == "y" ]; then - MSG_INFO "Installing Debian packages..." - echo "" - for i in "${debian_depends[@]}"; do - MSG_INFO "Installing: $i" - apt-get -y install $i - echo "" - done - fi - -elif command -v yum >/dev/null 2>&1; then - - if [ -f /etc/redhat-release ]; then - install_dependencies=y - else - MSG_INFO "Found 'yum' package manager" - MSG_INFO "Install dependencies with 'yum'? (y/n):" "0" - read install_dependencies - if [ "$install_dependencies" == "" ]; then - install_dependencies=y - fi - fi - - if [ "$install_dependencies" == "y" ]; then - MSG_INFO "Installing RedHat packages..." - echo "" - for i in "${redhat_depends[@]}"; do - MSG_INFO "Installing: $i" - yum -y install $i - echo "" - done - fi - -elif command -v pacman >/dev/null 2>&1; then - - if [ -f /etc/arch-release ] || [ -f /etc/manjaro-release ]; then - install_dependencies=y - else - MSG_INFO "Found 'pacman' package manager" - MSG_INFO "Install dependencies with 'pacman'? (y/n):" "0" - read install_dependencies - if [ "$install_dependencies" == "" ]; then - install_dependencies=y - fi - fi - - if [ "$install_dependencies" == "y" ]; then - MSG_INFO "Installing ArchLinux packages..." - echo "" - for i in "${arch_depends[@]}"; do - MSG_INFO "Installing: $i" - pacman -S --noconfirm $i - echo "" - done - fi -fi -echo "" - MSG_INFO "Install completed." echo "" echo "******************************************************************" echo "Start ${app_fullname} using the shortcut in the Applications Menu" echo "or by running the command: sudo ${app_name}" -echo "If it fails to start, check and install the following packages:" -echo "Required: ${generic_depends[@]}" -echo "Optional: (none)" echo "******************************************************************" WAIT_FOR_INPUT EXIT 0 diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/src/AboutWindow.vala timeshift-btrfs-1.1~13~ubuntu12.04.1/src/AboutWindow.vala --- timeshift-btrfs-1.0~7~ubuntu12.04.1/src/AboutWindow.vala 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/src/AboutWindow.vala 2015-01-21 17:16:39.000000000 +0000 @@ -56,7 +56,7 @@ _artists = value; } } - + private string[] _authors; public string[] authors{ get{ @@ -116,7 +116,7 @@ _license = value; } } - + private Gdk.Pixbuf _logo; public Gdk.Pixbuf logo{ get{ @@ -147,6 +147,16 @@ } } + private string[] _third_party; + public string[] third_party{ + get{ + return _third_party; + } + set{ + _third_party = value; + } + } + private string _version = ""; public string version{ get{ @@ -307,7 +317,15 @@ } add_line("\n"); } - + + if (third_party.length > 0){ + add_line("%s\n".printf(_("Third Party Tools"))); + foreach(string name in third_party){ + add_line("%s\n".printf(name)); + } + add_line("\n"); + } + if (artists.length > 0){ add_line("%s\n".printf(_("Artists"))); foreach(string name in artists){ @@ -346,15 +364,15 @@ } public void add_line(string text){ - if (text.split(":").length == 2){ + if (text.split(":").length >= 2){ var link = new LinkButton(text.split(":")[0]); vbox_lines.add(link); - string val = text.split(":")[1]; + string val = text[text.index_of(":") + 1:text.length]; if (val.contains("@")){ link.uri = "mailto:" + val; } - else if(val.has_suffix("http://")){ + else if(val.has_prefix("http://")){ link.uri = val; } else{ @@ -374,6 +392,8 @@ var lbl = new Label(text); lbl.set_use_markup(true); lbl.valign = Align.START; + lbl.wrap = true; + lbl.wrap_mode = Pango.WrapMode.WORD; vbox_lines.add(lbl); } } diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/src/Main.vala timeshift-btrfs-1.1~13~ubuntu12.04.1/src/Main.vala --- timeshift-btrfs-1.0~7~ubuntu12.04.1/src/Main.vala 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/src/Main.vala 2015-01-21 17:16:39.000000000 +0000 @@ -24,12 +24,11 @@ using GLib; using Gtk; using Gee; -using Soup; using Json; using TeeJee.Logging; using TeeJee.FileSystem; -using TeeJee.DiskPartition; +using TeeJee.Devices; using TeeJee.JSON; using TeeJee.ProcessManagement; using TeeJee.GtkHelper; @@ -40,7 +39,7 @@ public Main App; public const string AppName = "Timeshift BTRFS"; public const string AppShortName = "timeshift-btrfs"; -public const string AppVersion = "1.0"; +public const string AppVersion = "1.1"; public const string AppAuthor = "Tony George"; public const string AppAuthorEmail = "teejee2008@gmail.com"; @@ -55,20 +54,18 @@ public string app_conf_path = ""; public Gee.ArrayList snapshot_list; - public Gee.HashMap partition_map; - + public Gee.HashMap partition_map; public Gee.ArrayList mount_list; - public PartitionInfo root_device; - public PartitionInfo snapshot_device; + public Device root_device; + public Device snapshot_device; public Subvolume subvol_root; public Subvolume subvol_home; public string mount_point_app = "/mnt/timeshift-btrfs"; public string mount_point_btrfs = "/mnt/timeshift-btrfs/btrfs"; - public string mount_point_system = "/mnt/timeshift-btrfs/system"; - + public DistInfo current_distro; public bool _is_scheduled = false; @@ -94,6 +91,11 @@ public int retain_snapshots_max_days = 200; public int minimum_free_disk_space_mb = 2048; + public string cmd_backup_device = ""; + public string cmd_snapshot = ""; + public bool cmd_confirm = false; + public bool cmd_verbose = true; + public string log_dir; public string log_file; public string lock_dir; @@ -102,8 +104,19 @@ public TimeShiftBackup snapshot_to_delete; public TimeShiftBackup snapshot_to_restore; - public string progress_text; + public string progress_text = ""; + public int snapshot_list_start_index = 0; + //global vars for controlling threads + public bool thr_success = false; + public bool thr_running = false; + public int thr_retval = -1; + public string thr_arg1 = ""; + public bool thr_timeout_active = false; + public string thr_timeout_cmd = ""; + + //initialization + public static int main (string[] args) { set_locale(); @@ -195,17 +208,21 @@ } dos_log = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION)); - log_msg(_("Session log file") + ": %s".printf(log_file)); + if ((app_mode == "")||(LOG_DEBUG)){ + log_msg(_("Session log file") + ": %s".printf(log_file)); + } } catch (Error e) { is_success = false; log_error (e.message); } - //log dist info ----------------------- + //get Linux distribution info ----------------------- - DistInfo info = DistInfo.get_dist_info("/"); - log_msg(_("Distribution") + ": " + info.full_name(),true); + this.current_distro = DistInfo.get_dist_info("/"); + if ((app_mode == "")||(LOG_DEBUG)){ + log_msg(_("Distribution") + ": " + current_distro.full_name(),true); + } //check dependencies --------------------- @@ -279,13 +296,9 @@ //initialize lists ------------------------- snapshot_list = new Gee.ArrayList(); - partition_map = new Gee.HashMap(); + partition_map = new Gee.HashMap(); mount_list = new Gee.ArrayList(); - //check current linux distribution ----------------- - - this.current_distro = DistInfo.get_dist_info("/"); - //initialize app -------------------- update_partition_list(); @@ -316,71 +329,160 @@ load_app_config(); update_snapshot_list(); } + + //console functions private void parse_arguments(string[] args){ for (int k = 1; k < args.length; k++) // Oth arg is app path { - switch (args[k].down ()){ + switch (args[k].down()){ case "--backup": + LOG_TIMESTAMP = false; + LOG_DEBUG = false; app_mode = "backup"; break; - + case "--backup-now": + LOG_TIMESTAMP = false; + LOG_DEBUG = false; app_mode = "ondemand"; break; - + + case "--delete": + LOG_TIMESTAMP = false; + LOG_DEBUG = false; + app_mode = "delete"; + break; + + case "--delete-all": + LOG_TIMESTAMP = false; + LOG_DEBUG = false; + app_mode = "delete-all"; + break; + + case "--restore": + LOG_TIMESTAMP = false; + LOG_DEBUG = false; + app_mode = "restore"; + break; + + case "--verbose": + cmd_verbose = true; + break; + + case "--quiet": + cmd_verbose = false; + break; + + case "--yes": + cmd_confirm = true; + break; + + case "--device": + cmd_backup_device = args[++k]; + break; + + case "--snapshot": + case "--snapshot-name": + cmd_snapshot = args[++k]; + break; + case "--debug": LOG_COMMANDS = true; LOG_DEBUG = true; break; case "--list": - app_mode = "list"; - LOG_ENABLE = false; + case "--list-snapshots": + app_mode = "list-snapshots"; + LOG_TIMESTAMP = false; + LOG_DEBUG = false; + break; + + case "--list-devices": + app_mode = "list-devices"; LOG_TIMESTAMP = false; LOG_DEBUG = false; break; default: - //nothing + LOG_TIMESTAMP = false; + log_error(_("Invalid command line arguments") + ": %s".printf(args[k]), true); + log_msg(Main.help_message()); + exit(1); break; } } + /* LOG_ENABLE = false; disables all console output + * LOG_TIMESTAMP = false; disables the timestamp prepended to every line in terminal output + * LOG_DEBUG = false; disables additional console messages + * LOG_COMMANDS = true; enables printing of all commands on terminal + * */ + if (app_mode == ""){ //Initialize GTK + LOG_TIMESTAMP = true; + //LOG_ENABLE = true; Gtk.init(ref args); } + else{ + //LOG_ENABLE = false; + } } public bool start_application(string[] args){ bool is_success = true; + + switch(app_mode){ + case "backup": + case "ondemand": + case "restore": + case "delete": + case "delete-all": + case "list-snapshots": + //set backup device from commandline argument (if available) or prompt user (if device is null) + get_backup_device_from_cmd(false, null); + break; + } switch(app_mode){ case "backup": - is_success = take_snapshot(); - if(!is_success){ - log_error(_("Failed to take snapshot.")); - } + is_success = take_snapshot(false, "", null); return is_success; - + + case "restore": + is_success = restore_snapshot(null); + return is_success; + + case "delete": + is_success = delete_snapshot(); + return is_success; + + case "delete-all": + is_success = delete_all_snapshots(); + return is_success; + case "ondemand": - is_success = take_snapshot(true); - if(!is_success){ - log_error(_("Failed to take snapshot.")); - } + is_success = take_snapshot(true,"",null); return is_success; - case "list": - + case "list-snapshots": LOG_ENABLE = true; - LOG_TIMESTAMP = false; - - log_msg(_("Snapshots") + ":"); - foreach (TimeShiftBackup bak in snapshot_list){ - log_msg("%s%s%s".printf(bak.name, " ~ " + bak.taglist, (bak.description.length > 0) ? " ~ " + bak.description : "")); + if (snapshot_list.size > 0){ + log_msg(_("Snapshots on device %s").printf(snapshot_device.full_name_with_alias) + ":\n"); + list_snapshots(false); + return true; + } + else{ + log_msg(_("No snapshots found on device") + " '%s'".printf(snapshot_device.device)); + return false; } - LOG_ENABLE = false; + + case "list-devices": + LOG_ENABLE = true; + log_msg(_("Devices with BTRFS and encrypted file systems") + ":\n"); + list_devices(); return true; default: @@ -396,6 +498,337 @@ } } + public static string help_message (){ + string msg = "\n" + AppName + " v" + AppVersion + " by Tony George (teejee2008@gmail.com)" + "\n"; + msg += "\n"; + msg += "Syntax:\n"; + msg += "\n"; + msg += " timeshift-btrfs --list-{snapshots|devices} [OPTIONS]\n"; + msg += " timeshift-btrfs --backup[-now] [OPTIONS]\n"; + msg += " timeshift-btrfs --restore [OPTIONS]\n"; + msg += " timeshift-btrfs --delete-[all] [OPTIONS]\n"; + msg += "\n"; + msg += _("Options") + ":\n"; + msg += "\n"; + msg += _("List") + ":\n"; + msg += " --list[-snapshots] " + _("List snapshots") + "\n"; + msg += " --list-devices " + _("List devices") + "\n"; + msg += "\n"; + msg += _("Backup") + ":\n"; + msg += " --backup " + _("Take scheduled backup") + "\n"; + msg += " --backup-now " + _("Take on-demand backup") + "\n"; + msg += "\n"; + msg += _("Restore") + ":\n"; + msg += " --restore " + _("Restore snapshot") + "\n"; + msg += " --snapshot " + _("Specify snapshot to restore") + "\n"; + msg += "\n"; + msg += _("Delete") + ":\n"; + msg += " --delete " + _("Delete snapshot") + "\n"; + msg += " --delete-all " + _("Delete all snapshots") + "\n"; + msg += "\n"; + msg += _("Global") + ":\n"; + msg += " --device " + _("Specify BTRFS device") + "\n"; + msg += " --debug " + _("Show additional debug messages") + "\n"; + msg += " --help " + _("Show all options") + "\n"; + msg += "\n"; + return msg; + } + + + public void list_snapshots(bool paginate){ + int count = 0; + for(int index = 0; index < snapshot_list.size; index++){ + if (!paginate || ((index >= snapshot_list_start_index) && (index < snapshot_list_start_index + 10))){ + count++; + } + } + + string[,] grid = new string[count+1,5]; + bool[] right_align = { false, false, false, false, false}; + + int row = 0; + int col = -1; + grid[row, ++col] = _("Num"); + grid[row, ++col] = ""; + grid[row, ++col] = _("Name"); + grid[row, ++col] = _("Tags"); + grid[row, ++col] = _("Description"); + row++; + + for(int index = 0; index < snapshot_list.size; index++){ + TimeShiftBackup bak = snapshot_list[index]; + if (!paginate || ((index >= snapshot_list_start_index) && (index < snapshot_list_start_index + 10))){ + col = -1; + grid[row, ++col] = "%d".printf(index); + grid[row, ++col] = ">"; + grid[row, ++col] = "%s".printf(bak.name); + grid[row, ++col] = "%s".printf(bak.taglist_short); + grid[row, ++col] = "%s".printf(bak.description); + row++; + } + } + + print_grid(grid, right_align); + } + + public void list_devices(){ + int count = 0; + foreach(Device pi in partition_list) { + if (!pi.has_linux_filesystem()) { continue; } + count++; + } + + string[,] grid = new string[count+1,6]; + bool[] right_align = { false, false, false, true, true, false}; + + int row = 0; + int col = -1; + grid[row, ++col] = _("Num"); + grid[row, ++col] = ""; + grid[row, ++col] = _("Device"); + //grid[row, ++col] = _("UUID"); + grid[row, ++col] = _("Size"); + grid[row, ++col] = _("Type"); + grid[row, ++col] = _("Label"); + row++; + + foreach(Device pi in partition_list) { + if (!pi.has_linux_filesystem()) { continue; } + + col = -1; + grid[row, ++col] = "%d".printf(row - 1); + grid[row, ++col] = ">"; + grid[row, ++col] = "%s".printf(pi.full_name_with_alias); + //grid[row, ++col] = "%s".printf(pi.uuid); + grid[row, ++col] = "%s".printf((pi.size_mb > 0) ? "%s GB".printf(pi.size) : "?? GB"); + grid[row, ++col] = "%s".printf(pi.type); + grid[row, ++col] = "%s".printf(pi.label); + row++; + } + + print_grid(grid, right_align); + } + + public void print_grid(string[,] grid, bool[] right_align, bool has_header = true){ + int[] col_width = new int[grid.length[1]]; + + for(int col=0; col col_width[col]){ + col_width[col] = grid[row,col].length; + } + } + } + + for(int row=0; row (start_timeout_counter_thread, true); + } + catch (Error e) { + log_error (e.message); + } + } + + public void start_timeout_counter_thread(){ + int count = 20 * 1000; + while (thr_timeout_active && (count > 0)){ + Thread.usleep((ulong) GLib.TimeSpan.MILLISECOND * 500); + count -= 500; + } + + if (thr_timeout_active){ + thr_timeout_active = false; + stdout.printf("\n"); + + if (thr_timeout_cmd.length > 0){ + Posix.system("killall " + thr_timeout_cmd); + } + + log_error(_("No response from user")); + log_msg(_("Aborted.")); + exit(0); + } + } + + public void stop_timeout_counter(){ + thr_timeout_active = false; + } + + //prompt for input + + public Device? read_stdin_device(Gee.ArrayList device_list){ + start_timeout_counter(); + string? line = stdin.read_line(); + stop_timeout_counter(); + + line = (line != null) ? line.strip() : line; + + Device selected_device = null; + + if (line.down() == "a"){ + log_msg(_("Aborted.")); + exit_app(); + exit(0); + } + else if ((line == null)||(line.length == 0)){ + log_error("Invalid input"); + } + else if (line.contains("/")){ + selected_device = get_device_from_name(device_list, line); + if (selected_device == null){ + log_error("Invalid input"); + } + } + else{ + selected_device = get_device_from_index(device_list, line); + if (selected_device == null){ + log_error("Invalid input"); + } + } + + return selected_device; + } + + public Device? get_device_from_index(Gee.ArrayList device_list, string index_string){ + int64 index; + if (int64.try_parse(index_string, out index)){ + int i = -1; + foreach(Device pi in device_list) { + if ((pi.devtype == "partition") && !pi.has_linux_filesystem()) { continue; } + if (++i == index){ + return pi; + } + } + } + + return null; + } + + public Device? get_device_from_name(Gee.ArrayList device_list, string device_name){ + foreach(Device pi in device_list) { + if (!pi.has_linux_filesystem()) { continue; } + if ((pi.device == device_name)||(pi.device.replace("/dev/","") == device_name)){ + return pi; + } + else { + foreach(string symlink in pi.symlinks){ + if ((symlink == device_name)||(symlink.replace("/dev/","").replace("/dev/mapper/","") == device_name)){ + return pi; + } + } + } + } + + return null; + } + + public TimeShiftBackup read_stdin_snapshot(){ + start_timeout_counter(); + string? line = stdin.read_line(); + stop_timeout_counter(); + + line = (line != null) ? line.strip() : line; + + TimeShiftBackup selected_snapshot = null; + + if (line.down() == "a"){ + log_msg("Aborted."); + exit_app(); + exit(0); + } + else if (line.down() == "p"){ + snapshot_list_start_index -= 10; + if (snapshot_list_start_index < 0){ + snapshot_list_start_index = 0; + } + log_msg(""); + list_snapshots(true); + log_msg(""); + } + else if (line.down() == "n"){ + if ((snapshot_list_start_index + 10) < snapshot_list.size){ + snapshot_list_start_index += 10; + } + log_msg(""); + list_snapshots(true); + log_msg(""); + } + else if (line.contains("_")||line.contains("-")){ + //TODO + log_error("Invalid input"); + } + else if ((line == null)||(line.length == 0)){ + log_error("Invalid input"); + } + else{ + int64 index; + if (int64.try_parse(line, out index)){ + if (index < snapshot_list.size){ + selected_snapshot = snapshot_list[(int) index]; + } + else{ + log_error("Invalid input"); + } + } + else{ + log_error("Invalid input"); + } + } + + return selected_snapshot; + } + + public bool read_stdin_restore_confirm(){ + start_timeout_counter(); + string? line = stdin.read_line(); + stop_timeout_counter(); + + line = (line != null) ? line.strip() : line; + + if ((line.down() == "a")||(line.down() == "n")){ + log_msg("Aborted."); + exit_app(); + exit(0); + return true; + } + else if ((line == null)||(line.length == 0)){ + log_error("Invalid input"); + return false; + } + else if (line.down() == "y"){ + cmd_confirm = true; + return true; + } + else if ((line == null)||(line.length == 0)){ + log_error("Invalid input"); + return false; + } + else{ + log_error("Invalid input"); + return false; + } + } + + //utility methods + public bool check_dependencies(out string msg){ msg = ""; @@ -420,50 +853,6 @@ } } - public bool check_btrfs_layout_on_snapshot_device(){ - //check if snapshot device is a BTRFS volume - if ((snapshot_device != null) && (snapshot_device.type == "btrfs")){ - //check subvolume layout - if (check_btrfs_volume(mount_point_btrfs) == false){ - string msg = _("BTRFS partition '%s' has an unsupported subvolume layout.").printf(snapshot_device.device) + " "; - msg += _("Only ubuntu-type layouts with @ and @home subvolumes are currently supported."); - string title = _("Not Supported"); - - if (app_mode == ""){ - gtk_messagebox(title, msg, null, true); - } - else{ - log_error(msg); - } - return false; - } - } - - return true; - } - - public static string help_message (){ - string msg = "\n" + AppName + " v" + AppVersion + " by Tony George (teejee2008@gmail.com)" + "\n"; - msg += "\n"; - msg += "Syntax: timeshift-btrfs [options]\n"; - msg += "\n"; - msg += _("Options") + ":\n"; - msg += "\n"; - msg += " --backup " + _("Take scheduled backup") + "\n"; - msg += " --backup-now " + _("Take on-demand backup") + "\n"; - msg += " --list " + _("List all snapshots") + "\n"; - msg += " --debug " + _("Show additional debug messages") + "\n"; - msg += " --help " + _("Show all options") + "\n"; - msg += "\n"; - msg += _("Notes") + ":\n"; - msg += "\n"; - msg += " 1) '--backup' will take a snapshot only if a scheduled snapshot is due\n"; - msg += " 2) '--backup-now' will take an immediate (manual) snapshot\n"; - msg += " 3) '--backup' will not take snapshots till the first snapshot has been taken with --backup-now\n"; - msg += "\n"; - return msg; - } - public string snapshot_dir { owned get{ return "%s/timeshift-btrfs/snapshots".printf(mount_point_btrfs); @@ -521,8 +910,7 @@ log_error (e.message); } } - - + public bool is_scheduled{ get{ return _is_scheduled; @@ -532,275 +920,225 @@ } } - public bool take_snapshot(bool ondemand = false, string comments = ""){ - - in_progress = true; + //backup + + public bool take_snapshot(bool ondemand, string comments, Gtk.Window? parent_win){ + bool status; + bool update_symlinks = false; is_ondemand = ondemand; snapshot_comments = comments; - try { - is_success = false; - Thread.create (take_snapshot_thread, true); + //create a timestamp + DateTime now = new DateTime.now_local(); - while (in_progress){ - gtk_do_events (); - Thread.usleep((ulong) GLib.TimeSpan.MILLISECOND * 100); - } - } catch (Error e) { - is_success = false; - log_error (e.message); + //mount device + if (!mount_backup_device(parent_win)){ + return false; + } + + //check device + string msg; + int status_code = check_backup_device(out msg); + if (status_code != 0){ + log_error(msg); + return false; } - in_progress = false; - - return is_success; - } - - public void take_snapshot_thread(){ - bool status; - bool update_symlinks = false; - - try - { - //create a timestamp - DateTime now = new DateTime.now_local(); + //check subvolume layout + bool ok = check_btrfs_layout_on_snapshot_device(); + if (!ok){ + return false; + } + + //create snapshot root if missing + create_dir(snapshot_dir); - //mount_btrfs_device - if (!mount_backup_device()){ - is_success = false; - in_progress = false; - return; + //ondemand + if (is_ondemand){ + is_success = backup_and_rotate ("ondemand",now); + if(!is_success){ + log_error(_("On-demand snapshot failed!")); + return false; } - - //check backup device - string msg; - int status_code = check_backup_device(out msg); - if (status_code != 0){ - log_error(msg); - return; + else{ + update_symlinks = true; } + } + else if (is_scheduled){ + TimeShiftBackup last_snapshot_boot = get_latest_snapshot("boot"); + TimeShiftBackup last_snapshot_hourly = get_latest_snapshot("hourly"); + TimeShiftBackup last_snapshot_daily = get_latest_snapshot("daily"); + TimeShiftBackup last_snapshot_weekly = get_latest_snapshot("weekly"); + TimeShiftBackup last_snapshot_monthly = get_latest_snapshot("monthly"); - //check subvolume layout - bool ok = App.check_btrfs_layout_on_snapshot_device(); - if (!ok){ - is_success = false; - in_progress = false; - return; - } - - //create snapshot root if missing - var f = File.new_for_path(snapshot_dir); - if (!f.query_exists()){ - f.make_directory_with_parents(); - } + DateTime dt_sys_boot = now.add_seconds((-1) * get_system_uptime_seconds()); + bool take_new = false; - //ondemand - if (is_ondemand){ - is_success = backup_and_rotate ("ondemand",now); - if(!is_success){ - log_error(_("On-demand snapshot failed!")); - is_success = false; - in_progress = false; - return; + if (schedule_boot){ + + log_msg(_("Boot snapshots are enabled")); + + if (last_snapshot_boot == null){ + log_msg(_("Last boot snapshot not found")); + take_new = true; + } + else if (last_snapshot_boot.date.compare(dt_sys_boot) < 0){ + log_msg(_("Last boot snapshot is older than system start time")); + take_new = true; } else{ - update_symlinks = true; + int hours = (int) ((float) now.difference(last_snapshot_boot.date) / TimeSpan.HOUR); + log_msg(_("Last boot snapshot is %d hours old").printf(hours)); + take_new = false; } - } - else if (is_scheduled){ - TimeShiftBackup last_snapshot_boot = get_latest_snapshot("boot"); - TimeShiftBackup last_snapshot_hourly = get_latest_snapshot("hourly"); - TimeShiftBackup last_snapshot_daily = get_latest_snapshot("daily"); - TimeShiftBackup last_snapshot_weekly = get_latest_snapshot("weekly"); - TimeShiftBackup last_snapshot_monthly = get_latest_snapshot("monthly"); - DateTime dt_sys_boot = now.add_seconds((-1) * get_system_uptime_seconds()); - bool take_new = false; - - if (schedule_boot){ - - log_msg(_("Boot snapshots are enabled")); - - if (last_snapshot_boot == null){ - log_msg(_("Last boot snapshot not found")); - take_new = true; - } - else if (last_snapshot_boot.date.compare(dt_sys_boot) < 0){ - log_msg(_("Last boot snapshot is older than system start time")); - take_new = true; + if (take_new){ + status = backup_and_rotate ("boot",now); + if(!status){ + log_error(_("Boot snapshot failed!")); + return false; } else{ - int hours = (int) ((float) now.difference(last_snapshot_boot.date) / TimeSpan.HOUR); - log_msg(_("Last boot snapshot is %d hours old").printf(hours)); - take_new = false; - } - - if (take_new){ - status = backup_and_rotate ("boot",now); - if(!status){ - log_error(_("Boot snapshot failed!")); - is_success = false; - in_progress = false; - return; - } - else{ - update_symlinks = true; - } + update_symlinks = true; } } + } + + if (schedule_hourly){ - if (schedule_hourly){ - - log_msg(_("Hourly snapshots are enabled")); + log_msg(_("Hourly snapshots are enabled")); - if (last_snapshot_hourly == null){ - log_msg(_("Last hourly snapshot not found")); - take_new = true; - } - else if (last_snapshot_hourly.date.compare(now.add_hours(-1).add_minutes(1)) < 0){ - log_msg(_("Last hourly snapshot is more than 1 hour old")); - take_new = true; + if (last_snapshot_hourly == null){ + log_msg(_("Last hourly snapshot not found")); + take_new = true; + } + else if (last_snapshot_hourly.date.compare(now.add_hours(-1).add_minutes(1)) < 0){ + log_msg(_("Last hourly snapshot is more than 1 hour old")); + take_new = true; + } + else{ + int mins = (int) ((float) now.difference(last_snapshot_hourly.date) / TimeSpan.MINUTE); + log_msg(_("Last hourly snapshot is %d minutes old").printf(mins)); + take_new = false; + } + + if (take_new){ + status = backup_and_rotate ("hourly",now); + if(!status){ + log_error(_("Hourly snapshot failed!")); + return false; } else{ - int mins = (int) ((float) now.difference(last_snapshot_hourly.date) / TimeSpan.MINUTE); - log_msg(_("Last hourly snapshot is %d minutes old").printf(mins)); - take_new = false; - } - - if (take_new){ - status = backup_and_rotate ("hourly",now); - if(!status){ - log_error(_("Hourly snapshot failed!")); - is_success = false; - in_progress = false; - return; - } - else{ - update_symlinks = true; - } + update_symlinks = true; } } + } + + if (schedule_daily){ - if (schedule_daily){ - - log_msg(_("Daily snapshots are enabled")); + log_msg(_("Daily snapshots are enabled")); - if (last_snapshot_daily == null){ - log_msg(_("Last daily snapshot not found")); - take_new = true; - } - else if (last_snapshot_daily.date.compare(now.add_days(-1).add_minutes(1)) < 0){ - log_msg(_("Last daily snapshot is more than 1 day old")); - take_new = true; + if (last_snapshot_daily == null){ + log_msg(_("Last daily snapshot not found")); + take_new = true; + } + else if (last_snapshot_daily.date.compare(now.add_days(-1).add_minutes(1)) < 0){ + log_msg(_("Last daily snapshot is more than 1 day old")); + take_new = true; + } + else{ + int hours = (int) ((float) now.difference(last_snapshot_daily.date) / TimeSpan.HOUR); + log_msg(_("Last daily snapshot is %d hours old").printf(hours)); + take_new = false; + } + + if (take_new){ + status = backup_and_rotate ("daily",now); + if(!status){ + log_error(_("Daily snapshot failed!")); + return false; } else{ - int hours = (int) ((float) now.difference(last_snapshot_daily.date) / TimeSpan.HOUR); - log_msg(_("Last daily snapshot is %d hours old").printf(hours)); - take_new = false; - } - - if (take_new){ - status = backup_and_rotate ("daily",now); - if(!status){ - log_error(_("Daily snapshot failed!")); - is_success = false; - in_progress = false; - return; - } - else{ - update_symlinks = true; - } + update_symlinks = true; } } + } + + if (schedule_weekly){ - if (schedule_weekly){ - - log_msg(_("Weekly snapshots are enabled")); + log_msg(_("Weekly snapshots are enabled")); - if (last_snapshot_weekly == null){ - log_msg(_("Last weekly snapshot not found")); - take_new = true; - } - else if (last_snapshot_weekly.date.compare(now.add_weeks(-1).add_minutes(1)) < 0){ - log_msg(_("Last weekly snapshot is more than 1 week old")); - take_new = true; + if (last_snapshot_weekly == null){ + log_msg(_("Last weekly snapshot not found")); + take_new = true; + } + else if (last_snapshot_weekly.date.compare(now.add_weeks(-1).add_minutes(1)) < 0){ + log_msg(_("Last weekly snapshot is more than 1 week old")); + take_new = true; + } + else{ + int days = (int) ((float) now.difference(last_snapshot_weekly.date) / TimeSpan.DAY); + log_msg(_("Last weekly snapshot is %d days old").printf(days)); + take_new = false; + } + + if (take_new){ + status = backup_and_rotate ("weekly",now); + if(!status){ + log_error(_("Weekly snapshot failed!")); + return false; } else{ - int days = (int) ((float) now.difference(last_snapshot_weekly.date) / TimeSpan.DAY); - log_msg(_("Last weekly snapshot is %d days old").printf(days)); - take_new = false; - } - - if (take_new){ - status = backup_and_rotate ("weekly",now); - if(!status){ - log_error(_("Weekly snapshot failed!")); - is_success = false; - in_progress = false; - return; - } - else{ - update_symlinks = true; - } + update_symlinks = true; } } + } + + if (schedule_monthly){ - if (schedule_monthly){ - - log_msg(_("Monthly snapshot are enabled")); + log_msg(_("Monthly snapshot are enabled")); - if (last_snapshot_monthly == null){ - log_msg(_("Last monthly snapshot not found")); - take_new = true; - } - else if (last_snapshot_monthly.date.compare(now.add_months(-1).add_minutes(1)) < 0){ - log_msg(_("Last monthly snapshot is more than 1 month old")); - take_new = true; + if (last_snapshot_monthly == null){ + log_msg(_("Last monthly snapshot not found")); + take_new = true; + } + else if (last_snapshot_monthly.date.compare(now.add_months(-1).add_minutes(1)) < 0){ + log_msg(_("Last monthly snapshot is more than 1 month old")); + take_new = true; + } + else{ + int days = (int) ((float) now.difference(last_snapshot_monthly.date) / TimeSpan.DAY); + log_msg(_("Last monthly snapshot is %d days old").printf(days)); + take_new = false; + } + + if (take_new){ + status = backup_and_rotate ("monthly",now); + if(!status){ + log_error(_("Monthly snapshot failed!")); + return false; } else{ - int days = (int) ((float) now.difference(last_snapshot_monthly.date) / TimeSpan.DAY); - log_msg(_("Last monthly snapshot is %d days old").printf(days)); - take_new = false; - } - - if (take_new){ - status = backup_and_rotate ("monthly",now); - if(!status){ - log_error(_("Monthly snapshot failed!")); - is_success = false; - in_progress = false; - return; - } - else{ - update_symlinks = true; - } + update_symlinks = true; } } } - else{ - log_msg(_("Scheduled snapshots are disabled")); - log_msg(_("Nothing to do!")); - cron_job_update(); - } - - auto_delete_backups(); - - if (update_symlinks){ - update_snapshot_list(); - create_symlinks(); - } } - catch(Error e){ - log_error (e.message); - is_success = false; - in_progress = false; - return; + else{ + log_msg(_("Scheduled snapshots are disabled")); + log_msg(_("Nothing to do!")); + cron_job_update(); } + + auto_delete_backups(); - is_success = true; - in_progress = false; - return; + if (update_symlinks){ + update_snapshot_list(); + create_symlinks(); + } + + return true; } public bool backup_and_rotate(string tag, DateTime dt_created){ @@ -1061,7 +1399,7 @@ } } - log_msg (_("Symlinks updated")); + log_debug (_("Symlinks updated")); } catch (Error e) { log_error (e.message); @@ -1128,6 +1466,52 @@ return (new TimeShiftBackup(snapshot_path)); } + + //restore + + public void get_backup_device_from_cmd(bool prompt_if_empty, Gtk.Window? parent_win){ + if (cmd_backup_device.length > 0){ + //set backup device from command line argument + Device cmd_dev = get_device_from_name(partition_list, cmd_backup_device); + if (cmd_dev != null){ + snapshot_device = cmd_dev; + if (!mount_backup_device(parent_win)){ + exit_app(); + exit(1); + } + update_snapshot_list(); + } + else{ + log_error(_("Could not find device") + ": '%s'".printf(cmd_backup_device)); + exit_app(); + exit(1); + } + } + else{ + if ((snapshot_device == null) || (prompt_if_empty && (snapshot_list.size == 0))){ + //prompt user for backup device + log_msg(""); + + log_msg(TERM_COLOR_YELLOW + _("Select backup device") + ":\n" + TERM_COLOR_RESET); + list_devices(); + log_msg(""); + + snapshot_device = null; + while (snapshot_device == null){ + stdout.printf(TERM_COLOR_YELLOW + _("Enter device name or number (a=Abort)") + ": " + TERM_COLOR_RESET); + stdout.flush(); + snapshot_device = read_stdin_device(partition_list); + } + + log_msg(""); + + if (snapshot_device != null){ + mount_backup_device(parent_win); + update_snapshot_list(); + } + } + } + } public bool is_current_system_restore{ get { @@ -1140,42 +1524,88 @@ } } - public bool restore_snapshot(){ - if (snapshot_to_restore == null){ - log_error(_("Snapshot to restore not specified!")); - return false; - } + public bool restore_snapshot(Gtk.Window? parent_win){ + string cmd = ""; + string std_out; + string std_err; + int ret_val; + bool found = false; - query_subvolume_info(); + //set snapshot device ----------------------------------------------- - try { - in_progress = true; - is_success = false; - Thread.create (restore_snapshot_thread, true); - } catch (ThreadError e) { - in_progress = false; - is_success = false; - log_error (e.message); + if (snapshot_device != null){ + //print snapshot_device name + log_msg(TERM_COLOR_YELLOW + string.nfill(78, '*') + TERM_COLOR_RESET); + log_msg(_("Backup Device") + ": %s".printf(snapshot_device.device), true); + log_msg(TERM_COLOR_YELLOW + string.nfill(78, '*') + TERM_COLOR_RESET); + mount_backup_device(parent_win); + update_snapshot_list(); } - - while (in_progress){ - gtk_do_events (); - Thread.usleep((ulong) GLib.TimeSpan.MILLISECOND * 100); + else{ + //print error + log_error(_("Backup device not specified!")); + return false; } - - snapshot_to_restore = null; - - return is_success; - } + //set snapshot ----------------------------------------------- + + if (app_mode != ""){ //command-line mode + + if (cmd_snapshot.length > 0){ + + //check command line arguments + found = false; + foreach(TimeShiftBackup bak in snapshot_list) { + if (bak.name == cmd_snapshot){ + snapshot_to_restore = bak; + found = true; + break; + } + } + + //check if found + if (!found){ + log_error(_("Could not find snapshot") + ": '%s'".printf(cmd_snapshot)); + return false; + } + } + + //prompt user for snapshot + if (snapshot_to_restore == null){ + + if (snapshot_list.size == 0){ + log_error(_("No snapshots found on device") + ": '%s'".printf(snapshot_device.device)); + return false; + } + + log_msg(""); + log_msg(TERM_COLOR_YELLOW + _("Select snapshot to restore") + ":\n" + TERM_COLOR_RESET); + list_snapshots(true); + log_msg(""); - public void restore_snapshot_thread(){ - string cmd = ""; - string std_out; - string std_err; - int ret_val; - in_progress = true; + while (snapshot_to_restore == null){ + stdout.printf(TERM_COLOR_YELLOW + _("Enter snapshot number (a=Abort, p=Previous, n=Next)") + ": " + TERM_COLOR_RESET); + stdout.flush(); + snapshot_to_restore = read_stdin_snapshot(); + } + log_msg(""); + } + } + + if (snapshot_to_restore != null){ + //print snapshot name + log_msg(TERM_COLOR_YELLOW + string.nfill(78, '*') + TERM_COLOR_RESET); + log_msg(_("Snapshot") + ": %s ~ %s".printf(snapshot_to_restore.name, snapshot_to_restore.description), true); + log_msg(TERM_COLOR_YELLOW + string.nfill(78, '*') + TERM_COLOR_RESET); + } + else{ + //print error + log_error(_("Snapshot to restore not specified!")); + return false; + } + query_subvolume_info(); + DateTime now = new DateTime.now_local(); string time_stamp = now.format("%Y-%m-%d_%H-%M-%S"); string snapshot_name = time_stamp; @@ -1206,7 +1636,7 @@ //search TimeShiftBackup bak_pre_restore = null; - bool found = false; + found = false; foreach(TimeShiftBackup bak in snapshot_list){ if (bak.is_live){ found = true; @@ -1254,9 +1684,7 @@ if (ret_val != 0){ log_error (std_err); log_error(_("Failed to move subvolume") + ": %s".printf(subvol_name)); - is_success = false; - in_progress = false; - return; + return false; } } } @@ -1289,28 +1717,83 @@ log_error (std_err); log_error(_("btrfs returned an error") + ": %d".printf(ret_val)); log_error(_("Failed to restore subvolume") + ": %s".printf(subvol_name)); - is_success = false; - in_progress = false; - return; + return false; } } } - + log_msg(_("Restore completed without errors")); - is_success = true; - in_progress = false; - return; + if (restore_current_system){ + log_msg(_("Snapshot will become active after system is rebooted.")); + } + return true; } catch(Error e){ log_error (e.message); - is_success = false; - in_progress = false; + return false; + } + finally{ + snapshot_to_restore = null; } } + + //delete + + public bool delete_snapshot(TimeShiftBackup? snapshot = null){ + bool found = false; + snapshot_to_delete = snapshot; + + //set snapshot ----------------------------------------------- + + if (app_mode != ""){ //command-line mode + + if (cmd_snapshot.length > 0){ + + //check command line arguments + found = false; + foreach(TimeShiftBackup bak in snapshot_list) { + if (bak.name == cmd_snapshot){ + snapshot_to_delete = bak; + found = true; + break; + } + } + + //check if found + if (!found){ + log_error(_("Could not find snapshot") + ": '%s'".printf(cmd_snapshot)); + return false; + } + } + + //prompt user for snapshot + if (snapshot_to_delete == null){ + + if (snapshot_list.size == 0){ + log_msg(_("No snapshots found on device") + " '%s'".printf(snapshot_device.device)); + return false; + } + + log_msg(""); + log_msg(TERM_COLOR_YELLOW + _("Select snapshot to delete") + ":\n" + TERM_COLOR_RESET); + list_snapshots(true); + log_msg(""); - public bool delete_snapshot(TimeShiftBackup bak){ - snapshot_to_delete = bak; + while (snapshot_to_delete == null){ + stdout.printf(TERM_COLOR_YELLOW + _("Enter snapshot number (a=Abort, p=Previous, n=Next)") + ": " + TERM_COLOR_RESET); + stdout.flush(); + snapshot_to_delete = read_stdin_snapshot(); + } + log_msg(""); + } + } + if (snapshot_to_delete == null){ + //print error + log_error(_("Snapshot to delete not specified!")); + return false; + } + try { in_progress = true; is_success = false; @@ -1338,43 +1821,91 @@ } public bool delete_all_snapshots(){ - string cmd = ""; - string std_out; - string std_err; - int ret_val; bool success; + string timeshift_dir = mount_point_btrfs + "/timeshift-btrfs"; - //delete snapshots - foreach(TimeShiftBackup bak in snapshot_list){ - success = delete_snapshot(bak); - if (!success){ return success; } + if (dir_exists(timeshift_dir)){ + //delete snapshots + foreach(TimeShiftBackup bak in snapshot_list){ + success = delete_snapshot(bak); + if (!success){ return success; } + } + + //delete /timeshift-btrfs directory + return delete_directory(timeshift_dir); + } + else{ + log_msg(_("No snapshots found on device") + " '%s'".printf(snapshot_device.device)); + return true; + } + } + + public bool delete_directory(string dir_path){ + thr_arg1 = dir_path; + + try { + thr_running = true; + thr_success = false; + Thread.create (delete_directory_thread, true); + } catch (ThreadError e) { + thr_running = false; + thr_success = false; + log_error (e.message); + } + + while (thr_running){ + gtk_do_events (); + Thread.usleep((ulong) GLib.TimeSpan.MILLISECOND * 100); } - //delete /timeshift-btrfs directory ------------ + thr_arg1 = null; + return thr_success; + } + + public void delete_directory_thread(){ + string cmd = ""; + string std_out; + string std_err; + int ret_val; + try{ - string timeshift_dir = mount_point_btrfs + "/timeshift-btrfs"; - - cmd = "rm -rf \"%s\"".printf(timeshift_dir); - - if (LOG_COMMANDS) { log_debug(cmd); } - - Process.spawn_command_line_sync(cmd, out std_out, out std_err, out ret_val); - - if (ret_val != 0){ - log_error(_("Unable to delete") + ": '%s'".printf(timeshift_dir)); - return false; + var f = File.new_for_path(thr_arg1); + if(f.query_exists()){ + cmd = "rm -rf \"%s\"".printf(thr_arg1); + + if (LOG_COMMANDS) { log_debug(cmd); } + + Process.spawn_command_line_sync(cmd, out std_out, out std_err, out ret_val); + + if (ret_val != 0){ + log_error(_("Failed to remove") + ": '%s'".printf(thr_arg1)); + thr_success = false; + thr_running = false; + return; + } + else{ + log_msg(_("Removed") + ": '%s'".printf(thr_arg1)); + thr_success = true; + thr_running = false; + return; + } } else{ - log_msg(_("Deleted") + ": '%s'".printf(timeshift_dir)); - return true; + log_error(_("Directory not found") + ": '%s'".printf(thr_arg1)); + thr_success = true; + thr_running = false; } } catch(Error e){ log_error (e.message); - return false; + thr_success = false; + thr_running = false; + return; } } + + //config public void save_app_config(){ var config = new Json.Object(); @@ -1412,12 +1943,12 @@ log_error (e.message); } - log_msg(_("App config saved") + ": '%s'".printf(this.app_conf_path)); + if ((app_mode == "")||(LOG_DEBUG)){ + log_msg(_("App config saved") + ": '%s'".printf(this.app_conf_path)); + } } public void load_app_config(){ - debug(_("Loading app config..")); - var f = File.new_for_path(this.app_conf_path); if (!f.query_exists()) { return; } @@ -1432,25 +1963,46 @@ string uuid = json_get_string(config,"backup_device_uuid",""); - foreach(PartitionInfo pi in partition_list){ + foreach(Device pi in partition_list){ if (pi.uuid == uuid){ snapshot_device = pi; break; } } + if (cmd_backup_device.length > 0){ + Device cmd_dev = get_device_from_name(partition_list, cmd_backup_device); + if (cmd_dev != null){ + snapshot_device = cmd_dev; + } + else{ + log_error(_("Could not find device") + ": '%s'".printf(cmd_backup_device)); + exit_app(); + exit(1); + } + } + if ((uuid.length == 0) || (snapshot_device == null)){ - if (root_device != null){ + if ((root_device != null) && (app_mode == "")){ log_msg (_("Warning: Backup device not set! Defaulting to system device")); snapshot_device = root_device; } } - - if (mount_backup_device(snapshot_device)){ - update_partition_list(); - } - else{ - snapshot_device = null; + + /* The backup device specified in config file will be mounted at this point if: + * 1) app is running in GUI mode, OR + * 2) app is running command mode without backup device argument + * */ + + if (snapshot_device != null){ + if ((app_mode == "") || (cmd_backup_device.length == 0)){ + if (mount_backup_device(null)){ + update_partition_list(); + } + else{ + snapshot_device = null; + } + } } this.is_scheduled = json_get_bool(config,"is_scheduled", is_scheduled); @@ -1469,11 +2021,14 @@ this.retain_snapshots_max_days = json_get_int(config,"max_days",retain_snapshots_max_days); this.minimum_free_disk_space_mb = json_get_int(config,"min_space",minimum_free_disk_space_mb); - - log_msg(_("App config loaded") + ": '%s'".printf(this.app_conf_path)); + + if ((app_mode == "")||(LOG_DEBUG)){ + log_msg(_("App config loaded") + ": '%s'".printf(this.app_conf_path)); + } } - + //core + public bool update_snapshot_list(){ snapshot_list.clear(); @@ -1534,19 +2089,18 @@ return t1.date.compare(t2.date); }); - //log_debug(_("Updated snapshot list")); return true; } - public Gee.ArrayList partition_list { + public Gee.ArrayList partition_list { owned get{ - var list = new Gee.ArrayList(); - foreach(PartitionInfo pi in partition_map.values) { + var list = new Gee.ArrayList(); + foreach(Device pi in partition_map.values) { list.add(pi); } list.sort((a,b) => { - PartitionInfo p1 = (PartitionInfo) a; - PartitionInfo p2 = (PartitionInfo) b; + Device p1 = (Device) a; + Device p2 = (Device) b; return strcmp(p1.device,p2.device); }); @@ -1556,16 +2110,16 @@ public void update_partition_list(){ partition_map.clear(); - partition_map = new Gee.HashMap(); + partition_map = new Gee.HashMap(); - var map_all = PartitionInfo.get_filesystems(); + var map_all = Device.get_filesystems(); if (map_all.size == 0){ log_error(_("Failed to get partition list.")); return; } - foreach(PartitionInfo pi in map_all.values){ - if (pi.type == "btrfs"){ + foreach(Device pi in map_all.values){ + if ((pi.type == "btrfs")||(pi.type == "luks")){ if (pi.mount_points.contains("/")){ root_device = pi; } @@ -1612,74 +2166,296 @@ if (bak.is_live){ return bak; } - } + } + + return null; + } + + public TimeShiftBackup? get_oldest_snapshot(string tag = ""){ + var list = get_snapshot_list(tag); + + if (list.size > 0) + return list[0]; + else + return null; + } + + public bool mount_backup_device(Gtk.Window? parent_win){ + if (snapshot_device != null){ + + //unlock if required + if (snapshot_device.type == "luks"){ + snapshot_device = unlock_and_find_device(snapshot_device, parent_win); + if (snapshot_device == null){ + log_error(_("Backup device not found")); + unmount_backup_device(); //unmount any previous device + return false; + } + } + + if (snapshot_device.type == "btrfs"){ + if (!Device.get_mount_points(snapshot_device.uuid).contains(mount_point_btrfs)){ //if device is not mounted already + //unmount any previous device + unmount_backup_device(); + + //mount + check_and_create_dir_with_parents(mount_point_btrfs); + if (mount(snapshot_device.uuid, mount_point_btrfs, "")){ + if ((app_mode == "")||(LOG_DEBUG)){ + log_msg(_("Mounted backup device '%s' at '%s'").printf(snapshot_device.device, mount_point_btrfs)); + } + update_partition_list(); //update free space info + return true; + } + else{ + return false; + } + } + else{ + return true; + } + } + } + + unmount_backup_device(); //unmount any previous device + return false; + } + + public void unmount_backup_device(bool exit_on_error = true){ + if (unmount_device(mount_point_btrfs, false)){ + if (dir_exists(mount_point_btrfs) && (get_file_count(mount_point_btrfs) == 0)){ + file_delete(mount_point_btrfs); + log_debug(_("Removed mount directory: '%s'").printf(mount_point_btrfs)); + } + } + } + + public bool unmount_device(string mount_point, bool exit_on_error = true){ + bool is_unmounted = unmount(mount_point); + if (!is_unmounted){ + if (exit_on_error){ + if (app_mode == ""){ + string title = _("Critical Error"); + string msg = _("Failed to unmount device!") + "\n" + _("Application will exit"); + gtk_messagebox(title, msg, null, true); + } + exit_app(); + exit(0); + } + } + return is_unmounted; + } + + public string unlock_encrypted_device(Device dev, Gtk.Window? parent_win){ + string mapped_name = "%s_unlocked".printf(dev.name); + string[] name_list = { "%s_unlocked".printf(dev.name), "%s_crypt".printf(dev.name), "luks-%s".printf(dev.uuid)}; + + if ((parent_win == null)&&(app_mode != "")){ + + //check if unlocked + foreach(string name in name_list){ + if (device_exists("/dev/mapper/%s".printf(name))){ + //already unlocked + log_msg(_("Unlocked device is mapped to '%s'").printf(name)); + log_msg(""); + return name; + } + } + + start_timeout_counter("cryptsetup"); + + //prompt user to unlock + string cmd = "cryptsetup luksOpen '%s' '%s'".printf(dev.device, mapped_name); + int retval = Posix.system(cmd); + stop_timeout_counter(); + log_msg(""); + + switch (retval){ + case 512: //invalid passphrase + log_error(_("Wrong Passphrase") + ": " + _("Failed to unlock device")); + return ""; //return + case 1280: //already unlocked and mapped to unknown name + log_error(_("Encrypted device '%s' is already unlocked and mapped to another device name. Select the unlocked device instead of selecting the encrypted device.").printf(dev.device)); + break; + case 0: //success + log_msg(_("Unlocked device is mapped to '%s'").printf(mapped_name)); + break; + default: //unknown error + log_error(_("Failed to unlock device")); + return ""; //return + } + + update_partition_list(); + return mapped_name; + } + else{ + //check if unlocked + foreach(string name in name_list){ + if (device_exists("/dev/mapper/%s".printf(name))){ + //already unlocked + //gtk_messagebox(_("Encrypted Device"),_("Unlocked device is mapped to '%s'.").printf(name), parent_win); + return name; + } + } + + //prompt user to unlock + string passphrase = gtk_inputbox(_("Encrypted Device"), _("Enter passphrase to unlock '%s'").printf(dev.name), parent_win, true); + string cmd = "echo '%s' | cryptsetup luksOpen '%s' '%s'".printf(passphrase, dev.device, mapped_name); + int retval = execute_script_sync(cmd, false); + log_debug("cryptsetup:" + retval.to_string()); + + switch(retval){ + case 512: //invalid passphrase + gtk_messagebox(_("Wrong Passphrase"),_("Wrong Passphrase") + ": " + _("Failed to unlock device"), parent_win); + return ""; //return + case 1280: //already unlocked and mapped to unknown name + gtk_messagebox(_("Unlocked Device"),_("Encrypted device '%s' is already unlocked and mapped to another device name. Select the unlocked device instead of selecting the encrypted device.").printf(dev.device), parent_win); + break; + case 0: //success + gtk_messagebox(_("Unlocked Successfully"),_("Unlocked device is mapped to '%s'.").printf(mapped_name), parent_win); + break; + default: //unknown error + gtk_messagebox(_("Error"),_("Failed to unlock device"), parent_win, true); + return ""; //return + } - return null; + update_partition_list(); + return mapped_name; + } } - public TimeShiftBackup? get_oldest_snapshot(string tag = ""){ - var list = get_snapshot_list(tag); - - if (list.size > 0) - return list[0]; - else + public Device? unlock_and_find_device(Device enc_dev, Gtk.Window? parent_win){ + if (enc_dev.type == "luks"){ + string mapped_name = unlock_encrypted_device(enc_dev, parent_win); + string mapped_device = "/dev/mapper/%s".printf(mapped_name); + if (mapped_name.length == 0) { return null; } + enc_dev = null; + + //find unlocked device + if (mapped_name.length > 0){ + foreach(Device dev in partition_list){ + foreach(string sym in dev.symlinks){ + if (sym == mapped_device){ + return dev; + } + } + } + } + return null; + } + else{ + return enc_dev; + } } + + public int check_backup_device(out string message){ + + /* + -1 - device un-available + 0 - device available + */ - public bool mount_backup_device(PartitionInfo? dev = null){ - PartitionInfo backup_device = dev; - if (backup_device == null){ - backup_device = snapshot_device; + int status_code = 0; + + //free space message + if ((snapshot_device != null) && (snapshot_device.free.length > 0)){ + message = "%s ".printf(snapshot_device.free) + _("free"); + message = message.strip(); + } + else{ + message = ""; } - if ((backup_device != null) && (backup_device.type == "btrfs")){ - if (!PartitionInfo.get_mount_points(backup_device.uuid).contains(mount_point_btrfs)){ - //unmount - unmount_backup_device(); - - //mount - check_and_create_dir_with_parents(mount_point_btrfs); - if (mount(backup_device.uuid, mount_point_btrfs, "")){ - log_msg(_("Mounted BTRFS device '%s' at '%s'").printf(backup_device.device, mount_point_btrfs)); - return true; - } - else{ - return false; - } + if (!backup_device_online()){ + if (snapshot_device == null){ + message = _("Backup device not selected"); } else{ - return true; + message = _("Backup device not available"); + } + + status_code = -1; + } + else{ + if (snapshot_device.size_mb == 0){ + message = _("Backup device not available"); + status_code = -1; } } - return false; + log_debug("Checked backup device (status=%d)".printf(status_code)); + + return status_code; } - public void unmount_backup_device(bool exit_on_error = true){ - if (unmount_device(mount_point_btrfs, false)){ - if (dir_exists(mount_point_btrfs)){ - file_delete(mount_point_btrfs); - log_debug(_("Removed mount directory: '%s'").printf(mount_point_btrfs)); + public bool backup_device_online(){ + if (snapshot_device != null){ + if (Device.get_mount_points(snapshot_device.uuid).size > 0){ + return true; } } + return false; } - public bool unmount_device(string mount_point, bool exit_on_error = true){ - bool is_unmounted = unmount(mount_point); - if (!is_unmounted){ - if (exit_on_error){ - if (app_mode == ""){ - string title = _("Critical Error"); - string msg = _("Failed to unmount device!") + "\n" + _("Application will exit"); - gtk_messagebox(title, msg, null, true); + public void clean_logs(){ + + Gee.ArrayList list = new Gee.ArrayList(); + + try{ + var dir = File.new_for_path (log_dir); + var enumerator = dir.enumerate_children ("*", 0); + + var info = enumerator.next_file (); + string path; + + while (info != null) { + if (info.get_file_type() == FileType.REGULAR) { + path = log_dir + "/" + info.get_name(); + if (path != log_file) { + list.add(path); + } } - exit_app(); - exit(0); + info = enumerator.next_file (); + } + + list.sort(strcmp); + + if (list.size > 500){ + for(int k=0; k<100; k++){ + var file = File.new_for_path (list[k]); + if (file.query_exists()){ + file.delete(); + } + } + log_msg(_("Older log files removed")); } } - return is_unmounted; + catch(Error e){ + log_error (e.message); + } } + + public void exit_app (){ + + foreach(TimeShiftBackup bak in snapshot_list){ + bak.update_control_file(); + } + + save_app_config(); + + cron_job_update(); + + unmount_backup_device(false); + + clean_logs(); + remove_lock(); + //Gtk.main_quit (); + } + + //cron jobs + public void cron_job_update(){ string current_entry = ""; string new_entry = ""; @@ -1804,117 +2580,35 @@ return false; } } - - public int check_backup_device(out string message){ - - /* - -1 - device un-available - 0 - device available - */ - - int status_code = 0; - - //free space message - if ((snapshot_device != null) && (snapshot_device.free.length > 0)){ - message = "%s ".printf(snapshot_device.free) + _("free"); - message = message.strip(); - } - else{ - message = ""; - } - - if (!backup_device_online()){ - if (snapshot_device == null){ - message = _("Backup device not selected"); - } - else{ - message = _("Backup device not available"); - } - - status_code = -1; - } - else{ - if (snapshot_device.size_mb == 0){ - message = _("Backup device not available"); - status_code = -1; - } - } - - log_debug("Checked backup device (status=%d)".printf(status_code)); - - return status_code; - } - public bool check_btrfs_volume(string mount_path){ - return dir_exists(mount_point_btrfs + "/@") && dir_exists(mount_point_btrfs + "/@home"); - } + //btrfs - public bool backup_device_online(){ - if (snapshot_device != null){ - mount_backup_device(); - if (PartitionInfo.get_mount_points(snapshot_device.uuid).size > 0){ - return true; - } - } - return false; - } - - public void clean_logs(){ - - Gee.ArrayList list = new Gee.ArrayList(); - - try{ - var dir = File.new_for_path (log_dir); - var enumerator = dir.enumerate_children ("*", 0); - - var info = enumerator.next_file (); - string path; - - while (info != null) { - if (info.get_file_type() == FileType.REGULAR) { - path = log_dir + "/" + info.get_name(); - if (path != log_file) { - list.add(path); - } + public bool check_btrfs_layout_on_snapshot_device(){ + //check if snapshot device is a BTRFS volume + if ((snapshot_device != null) && (snapshot_device.type == "btrfs")){ + //check subvolume layout + if (check_btrfs_volume(mount_point_btrfs) == false){ + string msg = _("BTRFS partition '%s' has an unsupported subvolume layout.").printf(snapshot_device.device) + "\n"; + msg += _("Only ubuntu-type layouts with @ and @home subvolumes are currently supported."); + string title = _("Not Supported"); + + if (app_mode == ""){ + gtk_messagebox(title, msg, null, true); } - info = enumerator.next_file (); - } - - list.sort(strcmp); - - if (list.size > 500){ - for(int k=0; k<100; k++){ - var file = File.new_for_path (list[k]); - if (file.query_exists()){ - file.delete(); - } + else{ + log_error(msg); } - log_msg(_("Older log files removed")); + return false; } } - catch(Error e){ - log_error (e.message); - } - } - - public void exit_app (){ - - foreach(TimeShiftBackup bak in snapshot_list){ - bak.update_control_file(); - } - save_app_config(); - - cron_job_update(); - - unmount_backup_device(false); - - clean_logs(); - remove_lock(); - - //Gtk.main_quit (); + return true; } + public bool check_btrfs_volume(string mount_path){ + return dir_exists(mount_point_btrfs + "/@") && dir_exists(mount_point_btrfs + "/@home"); + } + public void query_subvolume_info(){ try { in_progress = true; @@ -2235,7 +2929,17 @@ } } } - + + public string taglist_short{ + owned get{ + string str = ""; + foreach(string tag in tags){ + str += " " + tag.replace("ondemand","O").replace("boot","B").replace("hourly","H").replace("daily","D").replace("weekly","W").replace("monthly","M"); + } + return str.strip(); + } + } + public string date_formatted{ owned get{ return date.format("%Y-%m-%d %I:%M %p"); diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/src/MainWindow.vala timeshift-btrfs-1.1~13~ubuntu12.04.1/src/MainWindow.vala --- timeshift-btrfs-1.0~7~ubuntu12.04.1/src/MainWindow.vala 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/src/MainWindow.vala 2015-01-21 17:16:39.000000000 +0000 @@ -26,7 +26,7 @@ using TeeJee.Logging; using TeeJee.FileSystem; -using TeeJee.DiskPartition; +using TeeJee.Devices; using TeeJee.JSON; using TeeJee.ProcessManagement; using TeeJee.GtkHelper; @@ -87,8 +87,7 @@ private uint timer_backup_device_init; //other - private PartitionInfo snapshot_device_original; - private int cmb_backup_device_index_default = -1; + private Device snapshot_device_original; public MainWindow () { this.title = AppName + " v" + AppVersion; // + " by " + AppAuthor + " (" + "teejeetech.blogspot.in" + ")"; @@ -214,8 +213,9 @@ cmb_backup_device.pack_start (cell_backup_dev_margin, false); CellRendererPixbuf cell_backup_dev_icon = new CellRendererPixbuf (); - cell_backup_dev_icon.stock_id = "gtk-harddisk"; + cell_backup_dev_icon.xpad = 1; cmb_backup_device.pack_start (cell_backup_dev_icon, false); + cmb_backup_device.set_attributes(cell_backup_dev_icon, "pixbuf", 1); CellRendererText cell_backup_device = new CellRendererText(); cmb_backup_device.pack_start( cell_backup_device, false ); @@ -250,6 +250,7 @@ tv_backups = new TreeView(); tv_backups.get_selection().mode = SelectionMode.MULTIPLE; tv_backups.headers_clickable = true; + tv_backups.has_tooltip = true; tv_backups.set_rules_hint (true); //sw_backups @@ -346,7 +347,7 @@ col_tags = new TreeViewColumn(); col_tags.title = _("Tags"); col_tags.resizable = true; - col_tags.min_width = 80; + //col_tags.min_width = 80; col_tags.clickable = true; CellRendererText cell_tags = new CellRendererText (); cell_tags.ellipsize = Pango.EllipsizeMode.END; @@ -378,6 +379,47 @@ cell_desc.editable = true; cell_desc.edited.connect (cell_desc_edited); + + //tooltips + tv_backups.query_tooltip.connect ((x, y, keyboard_tooltip, tooltip) => { + TreeModel model; + TreePath path; + TreeIter iter; + TreeViewColumn col; + if (tv_backups.get_tooltip_context (ref x, ref y, keyboard_tooltip, out model, out path, out iter)){ + int bx, by; + tv_backups.convert_widget_to_bin_window_coords(x, y, out bx, out by); + if (tv_backups.get_path_at_pos (bx, by, null, out col, null, null)){ + if (col == col_date){ + tooltip.set_markup(_("Snapshot Date: Date on which snapshot was created")); + return true; + } + else if (col == col_total){ + tooltip.set_markup(_("Total Size: Space occupied by all files")); + return true; + } + else if (col == col_unshared){ + tooltip.set_markup(_("Unshared Size: Space occupied by unshared files.\nDeleting the snapshot will free up this space.")); + return true; + } + else if (col == col_desc){ + tooltip.set_markup(_("Comments (double-click to edit)")); + return true; + } + else if (col == col_system){ + tooltip.set_markup(_("System: Installed Linux distribution")); + return true; + } + else if (col == col_tags){ + tooltip.set_markup(_("Backup Levels\n\nO On demand (manual)\nB Boot\nH Hourly\nD Daily\nW Weekly\nM Monthly")); + return true; + } + } + } + + return false; + }); + //hbox_statusbar hbox_statusbar = new Box (Orientation.HORIZONTAL, 6); @@ -449,10 +491,13 @@ snapshot_device_original = App.snapshot_device; refresh_cmb_backup_device(); - timer_backup_device_init = Timeout.add(100, initialize_backup_device); + timer_backup_device_init = Timeout.add(100, init_backup_device); } - private bool initialize_backup_device(){ + private bool init_backup_device(){ + + /* updates statusbar messages and snapshot list after backup device is changed */ + if (timer_backup_device_init > 0){ Source.remove(timer_backup_device_init); timer_backup_device_init = -1; @@ -468,7 +513,7 @@ //refresh_cmb_backup_device(); refresh_tv_backups(); - check_status(); + update_statusbar(); update_ui(true); return false; @@ -492,7 +537,7 @@ string msg = message + "\n"; msg += _("Scheduled snapshots will be disabled."); - var dialog = new Gtk.MessageDialog.with_markup(null, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, msg); + var dialog = new Gtk.MessageDialog.with_markup(this, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, msg); dialog.set_title(_("Disable Scheduled Snapshots")); dialog.set_default_size (300, -1); dialog.set_transient_for(this); @@ -516,7 +561,7 @@ string msg = _("Scheduled snapshots will be saved to ") + "%s\n".printf(App.snapshot_device.device); msg += _("Click 'OK' to confirm") + "\n"; - var dialog = new Gtk.MessageDialog.with_markup(null, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK_CANCEL, msg); + var dialog = new Gtk.MessageDialog.with_markup(this, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK_CANCEL, msg); dialog.set_title(_("Backup Device Changed")); dialog.set_default_size (300, -1); dialog.set_transient_for(this); @@ -536,7 +581,7 @@ msg += _("Scheduled snapshots will be disabled.") + "\n"; msg += _("Do you want to select another device?"); - var dialog = new Gtk.MessageDialog.with_markup(null, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK_CANCEL, msg); + var dialog = new Gtk.MessageDialog.with_markup(this, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK_CANCEL, msg); dialog.set_title(_("Backup Device Changed")); dialog.set_default_size (300, -1); dialog.set_transient_for(this); @@ -552,6 +597,7 @@ App.is_scheduled = false; return false; //close window } + } return false; @@ -576,7 +622,7 @@ model.get (iter, 0, out bak, -1); Gtk.CellRendererText ctxt = (cell as Gtk.CellRendererText); - ctxt.text = bak.taglist; + ctxt.text = bak.taglist_short; if (bak.is_live){ ctxt.background = "#F5A9A9"; } @@ -654,15 +700,15 @@ } private void cell_backup_device_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){ - PartitionInfo info; + Device info; model.get (iter, 0, out info, -1); - (cell as Gtk.CellRendererText).text = info.description(); + + (cell as Gtk.CellRendererText).markup = info.description_formatted(); } private void refresh_tv_backups(){ - App.update_snapshot_list(); - + ListStore model = new ListStore(1, typeof(TimeShiftBackup)); var list = App.snapshot_list; @@ -718,42 +764,58 @@ } private void refresh_cmb_backup_device(){ - ListStore store = new ListStore(1, typeof(PartitionInfo)); + ListStore store = new ListStore(2, typeof(Device), typeof(Gdk.Pixbuf)); TreeIter iter; int index = -1; - int index_selected = -1; - cmb_backup_device_index_default = -1; + int index_snapshot_device = -1; + int index_root_device = -1; - foreach(PartitionInfo pi in App.partition_list) { + foreach(Device pi in App.partition_list) { if (!pi.has_linux_filesystem()) { continue; } store.append(out iter); store.set (iter, 0, pi); - + + //set icon ---------------- + + Gdk.Pixbuf pix_selected = null; + Gdk.Pixbuf pix_device = get_shared_icon("disk","disk.png",16).pixbuf; + Gdk.Pixbuf pix_locked = get_shared_icon("locked","locked.svg",16).pixbuf; + + if (pi.type == "luks"){ + pix_selected = pix_locked; + } + else{ + pix_selected = pix_device; + } + + store.set (iter, 1, pix_selected, -1); + + //get device index ---------- + index++; if ((App.root_device != null) && (pi.uuid == App.root_device.uuid)){ - cmb_backup_device_index_default = index; + index_root_device = index; } if ((App.snapshot_device != null) && (pi.uuid == App.snapshot_device.uuid)){ - index_selected = index; + index_snapshot_device = index; } } - - if (index_selected > -1){ - //ok + + cmb_backup_device.set_model (store); + + if (index_snapshot_device > -1){ + cmb_backup_device.active = index_snapshot_device; } - else if (cmb_backup_device_index_default > -1){ - index_selected = cmb_backup_device_index_default; + else if (index_root_device > -1){ + cmb_backup_device.active = index_root_device; + } + else { + cmb_backup_device.active = -1; } - //else if (index > -1){ - //index_selected = 0; - //} - - cmb_backup_device.set_model (store); - cmb_backup_device.active = index_selected; } private void cmb_backup_device_changed(){ @@ -771,49 +833,34 @@ //get new device reference TreeIter iter; - PartitionInfo pi; + Device pi; combo.get_active_iter (out iter); TreeModel model = (TreeModel) combo.model; model.get(iter, 0, out pi); - - //check if device has changed + + change_backup_device(pi); + } + + private void change_backup_device(Device pi){ + //return if device has not changed if ((App.snapshot_device != null) && (pi.uuid == App.snapshot_device.uuid)){ return; } gtk_set_busy(true, this); - //try changing backup device ------------------ - + Device previous_device = App.snapshot_device; App.snapshot_device = pi; - long size_before = App.snapshot_device.size_mb; - - bool status = App.mount_backup_device(); - if (status == false){ - string msg = _("Failed to mount device") + ": %s".printf(App.snapshot_device.device); - var dlg = new Gtk.MessageDialog.with_markup(null, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, msg); - dlg.set_title(_("Error")); - dlg.set_default_size (200, -1); - dlg.set_transient_for(this); - dlg.set_modal(true); - dlg.run(); - dlg.destroy(); - - cmb_backup_device.active = -1; + //try mounting the device + if (App.mount_backup_device(this)){ + App.update_partition_list(); gtk_set_busy(false, this); - return; - } - - //get disk space after mounting - App.update_partition_list(); - long size_after = App.snapshot_device.size_mb; - - gtk_set_busy(false, this); - - if (size_after > size_before){ - refresh_cmb_backup_device(); + timer_backup_device_init = Timeout.add(100, init_backup_device); } else{ - timer_backup_device_init = Timeout.add(100, initialize_backup_device); + gtk_set_busy(false, this); + App.snapshot_device = previous_device; + refresh_cmb_backup_device(); + return; } } @@ -831,7 +878,7 @@ case 1: case 2: gtk_messagebox(_("Low Disk Space"),_("Backup device does not have enough space"),null, true); - check_status(); + update_statusbar(); return; } @@ -850,7 +897,7 @@ //take snapshot ---------------- - bool is_success = App.take_snapshot(true); + bool is_success = App.take_snapshot(true,"",this); update_progress_stop(); @@ -866,7 +913,7 @@ App.update_partition_list(); refresh_cmb_backup_device(); refresh_tv_backups(); - check_status(); + update_statusbar(); update_ui(true); } @@ -886,7 +933,7 @@ sel = tv_backups.get_selection (); if (sel.count_selected_rows() == 0){ - gtk_messagebox(_("No Snapshots Selected"),_("Please select the snapshots to delete"),this,false); + gtk_messagebox(_("No Snapshots Selected"),_("Please select the snapshots to delete"),null,false); return; } @@ -940,12 +987,12 @@ //select the iter being deleted tv_backups.get_selection().select_iter(iter_delete); - statusbar_message(_("Deleting snapshot") + ": '%s'...".printf(bak.date_formatted)); + statusbar_message(_("Deleting snapshot") + ": '%s'...".printf(bak.name)); is_success = App.delete_snapshot(bak); if (!is_success){ - statusbar_message_with_timeout(_("Error: Unable to delete snapshot") + ": '%s'".printf(bak.date_formatted), false); + statusbar_message_with_timeout(_("Error: Unable to delete snapshot") + ": '%s'".printf(bak.name), false); break; } @@ -974,7 +1021,7 @@ App.update_partition_list(); refresh_cmb_backup_device(); refresh_tv_backups(); - check_status(); + update_statusbar(); update_ui(true); } @@ -1032,7 +1079,7 @@ statusbar_message(_("Restoring snapshot...")); } - bool is_success = App.restore_snapshot(); + bool is_success = App.restore_snapshot(this); string msg; if (is_success){ @@ -1040,7 +1087,7 @@ statusbar_message_with_timeout(msg, true); if (App.is_current_system_restore){ - msg += "\n\n" + _("Snapshot will become active after next reboot. You can continue working. Any changes made till next reboot will be saved in snapshot '%s'".printf(App.get_live_snapshot().date_formatted)); + msg += "\n\n" + _("Snapshot will become active after system is rebooted.") + "\n" + _("Changes made to the running system till the next reboot will be saved in snapshot '%s'".printf(App.get_live_snapshot().date_formatted)); } var dlg = new Gtk.MessageDialog.with_markup(this,Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, msg); @@ -1078,7 +1125,7 @@ msg = _("Restore Failed!"); statusbar_message_with_timeout(msg, true); - var dlg = new Gtk.MessageDialog.with_markup(null,Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, msg); + var dlg = new Gtk.MessageDialog.with_markup(this,Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, msg); dlg.set_title(_("Error")); dlg.set_modal(true); dlg.set_transient_for(this); @@ -1098,10 +1145,7 @@ msg += "" + _("WARNING") + ":\n\n"; msg += _("System will be reset to a previous date. All files in the home directory (including documents) will be reset to previous versions.") + "\n\n"; msg += _("After restore the current system will appear as a new snapshot. This snapshot can be used for 'undoing' the restore, and for retrieving the latest versions of documents (and other files) that you were working on before the restore.") + "\n\n"; - - msg += "" + _("NOTE") + ":\n\n"; - msg += _("Restore process is very fast and will complete in less than 2 seconds. This does not mean that the restore has failed. There is no need to panic! :P") + "\n\n"; - + msg += "" + _("DISCLAIMER") + ":\n\n"; 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!") + "\n"; @@ -1129,7 +1173,7 @@ dialog.show_all(); dialog.run(); - check_status(); + update_statusbar(); } private void btn_browse_snapshot_clicked(){ @@ -1309,7 +1353,7 @@ int status = App.check_backup_device(out message); if (status != 0){ gtk_messagebox(_("Error"), message, null, true); - check_status(); + update_statusbar(); return false; } else{ @@ -1317,7 +1361,7 @@ } } - private bool check_status(){ + private bool update_statusbar(){ string img_dot_red = App.share_folder + "/timeshift-btrfs/images/item-red.png"; string img_dot_green = App.share_folder + "/timeshift-btrfs/images/item-green.png"; diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/src/makefile timeshift-btrfs-1.1~13~ubuntu12.04.1/src/makefile --- timeshift-btrfs-1.0~7~ubuntu12.04.1/src/makefile 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/src/makefile 2015-01-21 17:16:39.000000000 +0000 @@ -6,16 +6,18 @@ mandir=$(sharedir)/man man1dir=$(mandir)/man1 CFLAGS=--std=c99 +app_name=timeshift-btrfs +app_fullname="Timeshift BTRFS" all: #build binaries - valac -X -D'GETTEXT_PACKAGE="timeshift-btrfs"' --Xcc="-lm" --thread "Main.vala" "Utility.vala" "MainWindow.vala" "SettingsWindow.vala" "AboutWindow.vala" "DonationWindow.vala" -o timeshift-btrfs --pkg glib-2.0 --pkg gio-unix-2.0 --pkg posix --pkg gtk+-3.0 --pkg gee-1.0 --pkg libsoup-2.4 --pkg json-glib-1.0 + valac -X -D'GETTEXT_PACKAGE="${app_name}"' --Xcc="-lm" -X -Wl,-rpath,/usr/share/${app_name}/libs --thread "Main.vala" "Utility.vala" "MainWindow.vala" "SettingsWindow.vala" "AboutWindow.vala" "DonationWindow.vala" -o ${app_name} --pkg glib-2.0 --pkg gio-unix-2.0 --pkg posix --pkg gtk+-3.0 --pkg gee-1.0 --pkg gudev-1.0 --pkg json-glib-1.0 #update translation template - xgettext --language=C --keyword=_ --copyright-holder='Tony George (teejee2008@gmail.com)' --package-name='timeshift-btrfs' --package-version='1.6' --msgid-bugs-address='teejee2008@gmail.com' --escape --sort-output -o ../timeshift-btrfs.pot *.vala + xgettext --language=C --keyword=_ --copyright-holder='Tony George (teejee2008@gmail.com)' --package-name='${app_name}' --package-version='1.6' --msgid-bugs-address='teejee2008@gmail.com' --escape --sort-output -o ../${app_name}.pot *.vala clean: - rm -rf *.o timeshift-btrfs + rm -rf *.o ${app_name} install: mkdir -p "$(DESTDIR)$(bindir)" @@ -23,47 +25,47 @@ mkdir -p "$(DESTDIR)$(mandir)" mkdir -p "$(DESTDIR)$(man1dir)" mkdir -p "$(DESTDIR)$(launcherdir)" - mkdir -p "$(DESTDIR)$(sharedir)/timeshift-btrfs" + mkdir -p "$(DESTDIR)$(sharedir)/${app_name}" mkdir -p "$(DESTDIR)$(sharedir)/pixmaps" mkdir -p "$(DESTDIR)$(localedir)/it_IT/LC_MESSAGES" mkdir -p "$(DESTDIR)$(localedir)/ko_KR/LC_MESSAGES" mkdir -p "$(DESTDIR)$(localedir)/fr_FR/LC_MESSAGES" #binary - install -m 0755 timeshift-btrfs "$(DESTDIR)$(bindir)" - install -m 0755 timeshift-btrfs-uninstall "$(DESTDIR)$(bindir)" - install -m 0755 timeshift-btrfs-launcher "$(DESTDIR)$(bindir)" + install -m 0755 ${app_name} "$(DESTDIR)$(bindir)" + install -m 0755 ${app_name}-uninstall "$(DESTDIR)$(bindir)" + install -m 0755 ${app_name}-launcher "$(DESTDIR)$(bindir)" #shared files - cp -dpr --no-preserve=ownership -t "$(DESTDIR)$(sharedir)/timeshift-btrfs" ./share/timeshift-btrfs/* - chmod --recursive 0755 $(DESTDIR)$(sharedir)/timeshift-btrfs/* + cp -dpr --no-preserve=ownership -t "$(DESTDIR)$(sharedir)/${app_name}" ./share/${app_name}/* + chmod --recursive 0755 $(DESTDIR)$(sharedir)/${app_name}/* #launcher - install -m 0755 timeshift-btrfs.desktop "$(DESTDIR)$(launcherdir)" + install -m 0755 ${app_name}.desktop "$(DESTDIR)$(launcherdir)" #app icon - install -m 0755 ./share/pixmaps/timeshift-btrfs.png "$(DESTDIR)$(sharedir)/pixmaps/" + install -m 0755 ./share/pixmaps/${app_name}.png "$(DESTDIR)$(sharedir)/pixmaps/" #translations - #msgfmt --check --verbose -o "$(DESTDIR)$(localedir)/it_IT/LC_MESSAGES/timeshift-btrfs.mo" ../po/timeshift-btrfs-it.po - #msgfmt --check --verbose -o "$(DESTDIR)$(localedir)/ko_KR/LC_MESSAGES/timeshift-btrfs.mo" ../po/timeshift-btrfs-ko.po - #msgfmt --check --verbose -o "$(DESTDIR)$(localedir)/fr_FR/LC_MESSAGES/timeshift-btrfs.mo" ../po/timeshift-btrfs-fr.po + #msgfmt --check --verbose -o "$(DESTDIR)$(localedir)/it_IT/LC_MESSAGES/${app_name}.mo" ../po/${app_name}-it.po + #msgfmt --check --verbose -o "$(DESTDIR)$(localedir)/ko_KR/LC_MESSAGES/${app_name}.mo" ../po/${app_name}-ko.po + #msgfmt --check --verbose -o "$(DESTDIR)$(localedir)/fr_FR/LC_MESSAGES/${app_name}.mo" ../po/${app_name}-fr.po uninstall: #binary - rm -f "$(DESTDIR)$(bindir)/timeshift-btrfs" - rm -f "$(DESTDIR)$(bindir)/timeshift-btrfs-uninstall" - rm -f "$(DESTDIR)$(bindir)/timeshift-btrfs-launcher" + rm -f "$(DESTDIR)$(bindir)/${app_name}" + rm -f "$(DESTDIR)$(bindir)/${app_name}-uninstall" + rm -f "$(DESTDIR)$(bindir)/${app_name}-launcher" #shared files - rm -rf "$(DESTDIR)$(sharedir)/timeshift-btrfs" + rm -rf "$(DESTDIR)$(sharedir)/${app_name}" #launcher - rm -f "$(DESTDIR)$(launcherdir)/timeshift-btrfs.desktop" + rm -f "$(DESTDIR)$(launcherdir)/${app_name}.desktop" #app icon - rm -f "$(DESTDIR)$(sharedir)/pixmaps/timeshift-btrfs.png" + rm -f "$(DESTDIR)$(sharedir)/pixmaps/${app_name}.png" #translations - #rm -f $(DESTDIR)$(localedir)/*/LC_MESSAGES/timeshift-btrfs.mo + #rm -f $(DESTDIR)$(localedir)/*/LC_MESSAGES/${app_name}.mo diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/src/SettingsWindow.vala timeshift-btrfs-1.1~13~ubuntu12.04.1/src/SettingsWindow.vala --- timeshift-btrfs-1.0~7~ubuntu12.04.1/src/SettingsWindow.vala 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/src/SettingsWindow.vala 2015-01-21 17:16:39.000000000 +0000 @@ -26,7 +26,7 @@ using TeeJee.Logging; using TeeJee.FileSystem; -using TeeJee.DiskPartition; +using TeeJee.Devices; using TeeJee.JSON; using TeeJee.ProcessManagement; using TeeJee.GtkHelper; Binary files /tmp/Tcw31W76RO/timeshift-btrfs-1.0~7~ubuntu12.04.1/src/share/timeshift-btrfs/images/disk.png and /tmp/sReC7soMHi/timeshift-btrfs-1.1~13~ubuntu12.04.1/src/share/timeshift-btrfs/images/disk.png differ diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/src/share/timeshift-btrfs/images/locked.svg timeshift-btrfs-1.1~13~ubuntu12.04.1/src/share/timeshift-btrfs/images/locked.svg --- timeshift-btrfs-1.0~7~ubuntu12.04.1/src/share/timeshift-btrfs/images/locked.svg 1970-01-01 00:00:00.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/src/share/timeshift-btrfs/images/locked.svg 2015-01-21 17:16:39.000000000 +0000 @@ -0,0 +1,506 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/src/timeshift-btrfs.desktop timeshift-btrfs-1.1~13~ubuntu12.04.1/src/timeshift-btrfs.desktop --- timeshift-btrfs-1.0~7~ubuntu12.04.1/src/timeshift-btrfs.desktop 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/src/timeshift-btrfs.desktop 2015-01-21 17:16:39.000000000 +0000 @@ -1,5 +1,5 @@ [Desktop Entry] -Name=TimeShift BTRFS +Name=Timeshift BTRFS MimeType= Exec=timeshift-btrfs-launcher Type=Application @@ -9,4 +9,4 @@ Caption=System Restore Utility X-KDE-StartupNotify=false Categories=System; -Name[en_IN]=TimeShift BTRFS +Name[en_IN]=Timeshift BTRFS diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/src/Utility.vala timeshift-btrfs-1.1~13~ubuntu12.04.1/src/Utility.vala --- timeshift-btrfs-1.0~7~ubuntu12.04.1/src/Utility.vala 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/src/Utility.vala 2015-01-21 17:16:39.000000000 +0000 @@ -25,7 +25,7 @@ using Json; using TeeJee.Logging; using TeeJee.FileSystem; -using TeeJee.DiskPartition; +using TeeJee.Devices; using TeeJee.JSON; using TeeJee.ProcessManagement; using TeeJee.GtkHelper; @@ -58,6 +58,11 @@ public bool LOG_COLORS = true; public bool LOG_DEBUG = false; public bool LOG_COMMANDS = false; + + public const string TERM_COLOR_YELLOW = "\033[" + "1;33" + "m"; + public const string TERM_COLOR_GREEN = "\033[" + "1;32" + "m"; + public const string TERM_COLOR_RED = "\033[" + "1;31" + "m"; + public const string TERM_COLOR_RESET = "\033[" + "0" + "m"; public void log_msg (string message, bool highlight = false){ @@ -82,7 +87,7 @@ msg += "\n"; stdout.printf (msg); - + try { if (dos_log != null){ dos_log.put_string ("[%s] %s\n".printf(timestamp(), message)); @@ -92,7 +97,18 @@ stdout.printf (e.message); } } - + + public void log_msg_to_file (string message, bool highlight = false){ + try { + if (dos_log != null){ + dos_log.put_string ("[%s] %s\n".printf(timestamp(), message)); + } + } + catch (Error e) { + stdout.printf (e.message); + } + } + public void log_error (string message, bool highlight = true, bool is_warning = false){ if (!LOG_ENABLE) { return; } @@ -147,6 +163,22 @@ } } } + + public void log_empty_line(){ + if (!LOG_ENABLE) { return; } + + stdout.printf ("\n"); + stdout.flush(); + + try { + if (dos_log != null){ + dos_log.put_string ("\n"); + } + } + catch (Error e) { + stdout.printf (e.message); + } + } } namespace TeeJee.FileSystem{ @@ -387,7 +419,7 @@ } } -namespace TeeJee.DiskPartition{ +namespace TeeJee.Devices{ /* Functions and classes for handling disk partitions */ @@ -395,32 +427,146 @@ using TeeJee.FileSystem; using TeeJee.ProcessManagement; - public class PartitionInfo : GLib.Object{ + public class Device : GLib.Object{ - /* Class for storing partition information */ + /* Class for storing disk information */ + GUdev.Device udev_device; public string device = ""; public string type = ""; - public long size_mb = 0; - public long used_mb = 0; public string label = ""; public string uuid = ""; + + public string vendor = ""; + public string model = ""; + public bool removable = false; + public string devtype = ""; //disk or partition + + public long size_mb = 0; + public long used_mb = 0; + public string available = ""; public string used_percent = ""; public string dist_info = ""; public Gee.ArrayList mount_points; + public Gee.ArrayList symlinks; public string mount_options = ""; - public PartitionInfo(){ + public static Gee.HashMap device_list_master; + + public Device(){ mount_points = new Gee.ArrayList(); + symlinks = new Gee.ArrayList(); + } + + public Device.from_udev(GUdev.Device d){ + mount_points = new Gee.ArrayList(); + symlinks = new Gee.ArrayList(); + + udev_device = d; + + device = d.get_device_file(); + + devtype = d.get_devtype(); + //change devtype to 'partition' for device mapper disks + if (device.has_prefix("/dev/dm-")){ + devtype = "partition"; + } + + label = d.get_property("ID_FS_LABEL"); + label = (label == null) ? "" : label; + + uuid = d.get_property("ID_FS_UUID"); + uuid = (uuid == null) ? "" : uuid.down(); + + type = d.get_property("ID_FS_TYPE"); + type = (type == null) ? "" : type.down(); + type = type.contains("luks") ? "luks" : type; + + foreach (string symlink in d.get_device_file_symlinks()){ + symlinks.add(symlink); + } + } + + /* Returns: + * 'sda3' for '/dev/sda3' + * 'luks' for '/dev/mapper/luks' + * */ + + public string name{ + owned get{ + if (devtype == "partition"){ + return udev_device.get_name(); + } + else{ + return device.replace("/dev/mapper/","").replace("/dev/",""); + } + } + } + + public string full_name_with_alias{ + owned get{ + string text = ""; + string symlink = ""; + foreach(string sym in symlinks){ + if (sym.has_prefix("/dev/mapper/")){ + symlink = sym; + } + } + text = device + ((symlink.length > 0) ? " (" + symlink + ")" : ""); //→ + if (devtype == "partition"){ + return text; + } + else{ + return name; + } + } + } + + public string short_name_with_alias{ + owned get{ + string text = ""; + string symlink = ""; + foreach(string sym in symlinks){ + if (sym.has_prefix("/dev/mapper/")){ + symlink = sym.replace("/dev/mapper/","").replace("/dev/",""); + } + } + + if (symlink.length > 15){ + symlink = symlink[0:14] + "..."; + } + text = device.replace("/dev/mapper/","") + ((symlink.length > 0) ? " (" + symlink + ")" : ""); //→ + return text; + } + } + + public void print_properties(){ + if (udev_device != null){ + foreach(string key in udev_device.get_property_keys()){ + stdout.printf("%-50s %s\n".printf(key, udev_device.get_property(key))); + } + } } public string description(){ + return description_formatted().replace("","").replace("",""); + } + + public string description_formatted(){ string s = ""; - s += device; - s += (label.length > 0) ? " (" + label + ")": ""; - s += (type.length > 0) ? " ~ " + type : ""; - s += (used.length > 0) ? " ~ " + used + " / " + size + " GB used (" + used_percent + ")" : ""; + + if (devtype == "disk"){ + s += "" + short_name_with_alias + ""; + s += ((vendor.length > 0)||(model.length > 0)) ? (" ~ " + vendor + " " + model) : ""; + } + else{ + s += "" + short_name_with_alias + "" ; + s += (label.length > 0) ? " (" + label + ")": ""; + s += (type.length > 0) ? " ~ " + type : ""; + s += (used.length > 0) ? " ~ " + used + " / " + size + " GB used (" + used_percent + ")" : ""; + } + return s; } @@ -480,12 +626,6 @@ } } - public string partition_name{ - owned get{ - return device.replace("/dev/mapper/","").replace("/dev/",""); - } - } - public bool has_linux_filesystem(){ switch(type){ case "ext2": @@ -496,18 +636,36 @@ case "xfs": case "jfs": case "btrfs": + case "luks": return true; default: return false; } } - public static Gee.HashMap get_block_devices_using_blkid(string device_alias = "", bool exclude_unknown = false){ - - /* Returns list of mounted/unmounted, physical/logical filesystems - * Populates device, uuid, type, label and mount points */ + public static Gee.HashMap get_block_devices_using_udev(){ + var map = new Gee.HashMap(); + var uc = new GUdev.Client(null); + GLib.List devs = uc.query_by_subsystem("block"); + + foreach (GUdev.Device d in devs){ + Device dev = new Device.from_udev(d); + if ((dev.uuid.length > 0) && !map.has_key(dev.uuid)){ + map.set(dev.uuid, dev); + } + } - var map = new Gee.HashMap(); + device_list_master = map; + + return map; + } + + public static Gee.HashMap get_block_devices_using_blkid(string device_file){ + + /* Returns list of mounted partitions using 'blkid' command + Populates device, type, uuid, label */ + + var map = new Gee.HashMap(); string std_out; string std_err; @@ -516,10 +674,10 @@ Regex rex; MatchInfo match; - cmd = "/sbin/blkid" + ((device_alias.length > 0) ? " " + device_alias: ""); + cmd = "/sbin/blkid" + ((device_file.length > 0) ? " " + device_file: ""); ret_val = execute_command_script_sync(cmd, out std_out, out std_err); if (ret_val != 0){ - log_error ("blkid: " + _("Failed to get partition list") + ((device_alias.length > 0) ? ": " + device_alias : "")); + log_error ("blkid: " + _("Failed to get partition list") + ((device_file.length > 0) ? ": " + device_file : "")); return map; //return empty map } @@ -536,7 +694,7 @@ foreach(string line in std_out.split("\n")){ if (line.strip().length == 0) { continue; } - PartitionInfo pi = new PartitionInfo(); + Device pi = new Device(); pi.device = line.split(":")[0].strip(); @@ -548,19 +706,17 @@ continue; } - if (exclude_unknown){ - if (pi.device.has_prefix("/dev/sd") || pi.device.has_prefix("/dev/hd") || pi.device.has_prefix("/dev/mapper/") || pi.device.has_prefix("/dev/dm")) { - //ok - } - else if (pi.device.has_prefix("/dev/disk/by-uuid/")){ - //ok, get uuid - pi.uuid = pi.device.replace("/dev/disk/by-uuid/",""); - } - else{ - continue; //skip - } + if (pi.device.has_prefix("/dev/sd") || pi.device.has_prefix("/dev/hd") || pi.device.has_prefix("/dev/mapper/") || pi.device.has_prefix("/dev/dm")) { + //ok } - + else if (pi.device.has_prefix("/dev/disk/by-uuid/")){ + //ok, get uuid + pi.uuid = pi.device.replace("/dev/disk/by-uuid/",""); + } + else{ + continue; //skip + } + //parse & populate fields ------------------ try{ @@ -593,12 +749,12 @@ return map; } - public static Gee.HashMap get_disk_space_using_df(string device_or_mount_point = "", bool exclude_unknown = false){ + public static Gee.HashMap get_disk_space_using_df(string device_or_mount_point = ""){ /* Returns list of mounted partitions using 'df' command Populates device, type, size, used and mount_point_list */ - var map = new Gee.HashMap(); + var map = new Gee.HashMap(); string std_out; string std_err; @@ -607,10 +763,7 @@ cmd = "df -T -BM" + ((device_or_mount_point.length > 0) ? " \"%s\"".printf(device_or_mount_point): ""); ret_val = execute_command_script_sync(cmd, out std_out, out std_err); - if (ret_val != 0){ - log_error ("df: " + _("Failed to get partition list")); - return map; //return empty map - } + //ret_val is not reliable, no need to check /* sample output @@ -621,7 +774,7 @@ udev devtmpfs 3903M 1M 3903M 1% /dev tmpfs tmpfs 789M 1M 788M 1% /run none tmpfs 5M 0M 5M 0% /run/lock - /dev/sda3 ext4 25070M 19508M 4282M 83% /mnt/timeshift-btrfs + /dev/sda3 ext4 25070M 19508M 4282M 83% /mnt/timeshift */ string[] lines = std_out.split("\n"); @@ -632,7 +785,7 @@ if (++line_num == 1) { continue; } if (line.strip().length == 0) { continue; } - PartitionInfo pi = new PartitionInfo(); + Device pi = new Device(); //parse & populate fields ------------------ @@ -672,7 +825,7 @@ /* Note: * The mount points displayed by 'df' are not reliable. * For example, if same device is mounted at 2 locations, 'df' displays only the first location. - * Hence, we will not populate the 'mount_points' field in PartitionInfo object + * Hence, we will not populate the 'mount_points' field in Device object * Use get_mounted_filesystems_using_mtab() if mount info is required * */ @@ -682,17 +835,15 @@ continue; } - if (exclude_unknown){ - if (pi.device.has_prefix("/dev/sd") || pi.device.has_prefix("/dev/hd") || pi.device.has_prefix("/dev/mapper/") || pi.device.has_prefix("/dev/dm")) { - //ok - } - else if (pi.device.has_prefix("/dev/disk/by-uuid/")){ - //ok, get uuid - pi.uuid = pi.device.replace("/dev/disk/by-uuid/",""); - } - else{ - continue; //skip - } + if (pi.device.has_prefix("/dev/sd") || pi.device.has_prefix("/dev/hd") || pi.device.has_prefix("/dev/mapper/") || pi.device.has_prefix("/dev/dm")) { + //ok + } + else if (pi.device.has_prefix("/dev/disk/by-uuid/")){ + //ok, get uuid + pi.uuid = pi.device.replace("/dev/disk/by-uuid/",""); + } + else{ + continue; //skip } //get uuid --------------------------- @@ -709,12 +860,12 @@ return map; } - public static Gee.HashMap get_mounted_filesystems_using_mtab(bool exclude_unknown = false){ + public static Gee.HashMap get_mounted_filesystems_using_mtab(){ - /* Returns list of mounted partitions using 'mount' command + /* Returns list of mounted partitions by reading /proc/mounts Populates device, type and mount_point_list */ - var map = new Gee.HashMap(); + var map = new Gee.HashMap(); string mtab_path = "/etc/mtab"; string mtab_lines = ""; @@ -782,7 +933,7 @@ string line = lines[i].strip(); if (line.length == 0) { continue; } - PartitionInfo pi = new PartitionInfo(); + Device pi = new Device(); //parse & populate fields ------------------ @@ -820,17 +971,15 @@ continue; } - if (exclude_unknown){ - if (pi.device.has_prefix("/dev/sd") || pi.device.has_prefix("/dev/hd") || pi.device.has_prefix("/dev/mapper/") || pi.device.has_prefix("/dev/dm")) { - //ok - } - else if (pi.device.has_prefix("/dev/disk/by-uuid/")){ - //ok, get uuid - pi.uuid = pi.device.replace("/dev/disk/by-uuid/",""); - } - else{ - continue; //skip - } + if (pi.device.has_prefix("/dev/sd") || pi.device.has_prefix("/dev/hd") || pi.device.has_prefix("/dev/mapper/") || pi.device.has_prefix("/dev/dm")) { + //ok + } + else if (pi.device.has_prefix("/dev/disk/by-uuid/")){ + //ok, get uuid + pi.uuid = pi.device.replace("/dev/disk/by-uuid/",""); + } + else{ + continue; //skip } //get uuid --------------------------- @@ -856,14 +1005,16 @@ return map; } - public static Gee.HashMap get_filesystems(bool get_space = true, bool get_mounts = true){ + public static Gee.HashMap get_filesystems(bool get_space = true, bool get_mounts = true){ - //get block devices - var map = get_block_devices_using_blkid(); + /* Returns list of block devices + Populates all fields in Device class */ + + var map = get_block_devices_using_udev(); if (get_space){ //get used space for mounted filesystems - var map_df = get_disk_space_using_df("",false); + var map_df = get_disk_space_using_df(); foreach(string key in map_df.keys){ if (map.has_key(key)){ var pi = map.get(key); @@ -899,9 +1050,9 @@ return map; } - public static PartitionInfo refresh_partition_usage_info(PartitionInfo pi){ + public static Device refresh_partition_usage_info(Device pi){ - /* Updates disk space info and returns the given PartitionInfo object */ + /* Updates disk space info and returns the given Device object */ var map_df = get_disk_space_using_df(pi.device); if (map_df.has_key(pi.uuid)){ @@ -916,14 +1067,24 @@ } public static string get_device_uuid(string device){ - var map = get_block_devices_using_blkid(device); - if (map.size == 1){ - var pi = map.values.to_array()[0]; - return pi.uuid; + if (device_list_master == null){ + get_block_devices_using_udev(); } - else{ - return ""; + + foreach(Device dev in device_list_master.values){ + if (dev.device == device){ + return dev.uuid; + } + else{ + foreach(string symlink in dev.symlinks){ + if (symlink == device){ + return dev.uuid; + } + } + } } + + return ""; } public static Gee.ArrayList get_mount_points(string device_or_uuid){ @@ -947,28 +1108,7 @@ return (new Gee.ArrayList()); } - } - - public class DeviceInfo : GLib.Object{ - - /* Class for storing device information */ - - public string device = ""; - public bool removable = false; - public string vendor = ""; - public string model = ""; - public string name{ - owned get{ - return vendor + " " + model; - } - } - - public string description{ - owned get{ - return device + " ~ " + vendor + " " + model; - } - } } public class FsTabEntry : GLib.Object{ @@ -982,9 +1122,11 @@ public string pass = "0"; public string line = ""; - public static Gee.List read_fstab_file(string fstab_file_path){ + public static Gee.ArrayList read_fstab_file(string fstab_file_path){ Gee.ArrayList list = new Gee.ArrayList(); - + + if (!file_exists(fstab_file_path)){ return list; } + string text = read_file(fstab_file_path); string[] lines = text.split("\n"); foreach(string line in lines){ @@ -1033,7 +1175,7 @@ return list; } - + public static string create_fstab_file(FsTabEntry[] fstab_entries, bool keep_comments_and_empty_lines = true){ string text = ""; foreach(FsTabEntry entry in fstab_entries){ @@ -1051,10 +1193,10 @@ } public class MountEntry : GLib.Object{ - public PartitionInfo device = null; + public Device device = null; public string mount_point = ""; - public MountEntry(PartitionInfo device, string mount_point){ + public MountEntry(Device device, string mount_point){ this.device = device; this.mount_point = mount_point; } @@ -1076,23 +1218,23 @@ if (device_or_uuid.has_prefix("/dev")){ device = device_or_uuid; - uuid = PartitionInfo.get_device_uuid(device_or_uuid); + uuid = Device.get_device_uuid(device_or_uuid); } else{ uuid = device_or_uuid; device = "/dev/disk/by-uuid/%s".printf(uuid); } - + //check if already mounted ------------- - var map = PartitionInfo.get_mounted_filesystems_using_mtab(); + var map = Device.get_mounted_filesystems_using_mtab(); if (map.has_key(uuid)){ var pi = map.get(uuid); if (pi.mount_points.contains(mount_point)){ return true; } } - + try{ //check and create mount point ------------------- @@ -1111,6 +1253,7 @@ } Process.spawn_command_line_sync(cmd, out std_out, out std_err, out ret_val); + if (ret_val != 0){ log_error ("Failed to mount device '%s' at mount point '%s'".printf(device, mount_point)); log_error (std_err); @@ -1127,7 +1270,7 @@ //check if mounted successfully ------------- - map = PartitionInfo.get_mounted_filesystems_using_mtab(); + map = Device.get_mounted_filesystems_using_mtab(); if (map.has_key(uuid)){ var pi = map.get(uuid); if (pi.mount_points.contains(mount_point)){ @@ -1150,7 +1293,7 @@ if (device_or_uuid.has_prefix("/dev")){ device = device_or_uuid; - uuid = PartitionInfo.get_device_uuid(device_or_uuid); + uuid = Device.get_device_uuid(device_or_uuid); } else{ uuid = device_or_uuid; @@ -1159,7 +1302,7 @@ //check if already mounted and return mount point ------------- - var map = PartitionInfo.get_filesystems(); + var map = Device.get_filesystems(); if (map.has_key(uuid)){ var pi = map.get(uuid); if ((pi.mount_points.size > 0) && (pi.size_mb > 0)){ @@ -1205,8 +1348,8 @@ //check if mounted ------------- bool mounted = false; - var map = PartitionInfo.get_mounted_filesystems_using_mtab(); - foreach (PartitionInfo pi in map.values){ + var map = Device.get_mounted_filesystems_using_mtab(); + foreach (Device pi in map.values){ foreach (string mp in pi.mount_points){ if (mp.has_prefix(mount_point)){ //check for any mount_point at or under the given mount_point mounted = true; @@ -1244,8 +1387,8 @@ //check if unmounted -------------------------- mounted = false; - map = PartitionInfo.get_mounted_filesystems_using_mtab(); - foreach (PartitionInfo pi in map.values){ + map = Device.get_mounted_filesystems_using_mtab(); + foreach (Device pi in map.values){ foreach (string mp in pi.mount_points){ if (mp.has_prefix(mount_point)){ //check for any mount_point at or under the given mount_point mounted = true; @@ -1268,7 +1411,7 @@ if (device_or_uuid.has_prefix("/dev")){ device = device_or_uuid; - uuid = PartitionInfo.get_device_uuid(device_or_uuid); + uuid = Device.get_device_uuid(device_or_uuid); } else{ uuid = device_or_uuid; @@ -1277,7 +1420,7 @@ //check if already mounted and return mount point ------------- - var map = PartitionInfo.get_mounted_filesystems_using_mtab(); + var map = Device.get_mounted_filesystems_using_mtab(); if (map.has_key(uuid)){ var pi = map.get(uuid); if (pi.mount_points.size > 0){ @@ -1287,11 +1430,11 @@ return ""; } - public Gee.ArrayList get_block_devices(){ + public Gee.ArrayList get_block_devices(){ /* Returns a list of all storage devices including vendor and model number */ - var device_list = new Gee.ArrayList(); + var device_list = new Gee.ArrayList(); string letters = "abcdefghijklmnopqrstuvwxyz"; string letter = ""; @@ -1329,11 +1472,12 @@ } if ((vendor.length > 0) || (model.length > 0)){ - var dev = new DeviceInfo(); + var dev = new Device(); dev.device = "/dev/sd%s".printf(letter); dev.vendor = vendor.strip(); dev.model = model.strip(); dev.removable = (removable == "0") ? false : true; + dev.devtype = "disk"; device_list.add(dev); } } @@ -1526,6 +1670,91 @@ return -1; } } + + public int execute_script_sync_get_output (string script, out string std_out, out string std_err){ + + /* Executes commands synchronously + * Returns exit code, output messages and error messages. + * Commands are written to a temporary bash script and executed. */ + + string path = create_temp_bash_script(script); + + try { + + string[] argv = new string[1]; + argv[0] = path; + + int exit_code; + + Process.spawn_sync ( + TEMP_DIR, //working dir + argv, //argv + null, //environment + SpawnFlags.SEARCH_PATH, + null, // child_setup + out std_out, + out std_err, + out exit_code + ); + + return exit_code; + } + catch (Error e){ + log_error (e.message); + return -1; + } + } + + public int execute_script_sync(string script, bool suppress_output){ + + /* Executes commands synchronously + * Returns exit code, output messages and error messages. + * Commands are written to a temporary bash script and executed. */ + + string path = create_temp_bash_script(script); + + try { + + string[] argv = new string[1]; + argv[0] = path; + + int exit_code; + string std_out, std_err; + + if (suppress_output){ + //output will be suppressed + Process.spawn_sync ( + TEMP_DIR, //working dir + argv, //argv + null, //environment + SpawnFlags.SEARCH_PATH, + null, //child_setup + out std_out, //stdout + out std_err, //stderr + out exit_code + ); + } + else{ + //output will be displayed on terminal window if visible + Process.spawn_sync ( + TEMP_DIR, //working dir + argv, //argv + null, //environment + SpawnFlags.SEARCH_PATH, + null, //child_setup + null, //stdout + null, //stderr + out exit_code + ); + } + + return exit_code; + } + catch (Error e){ + log_error (e.message); + return -1; + } + } public bool execute_command_script_in_terminal_sync (string script){ @@ -1635,6 +1864,37 @@ return -1; } + public int[] get_pid_by_command (string proc_name, string command){ + + /* Get the process IDs for given process name and command string */ + + int[] proc_list = {}; + + //'ps' output strips double and single quotes so we will remove it too for matching with output + string cmd = command.replace("\"","").replace("'",""); + + try{ + Regex rex = new Regex("""^[ \t]*([0-9]*)[ \t]*"""); + MatchInfo match; + + string txt = execute_command_sync_get_output ("ps ew -C " + proc_name); //ew = all users + + log_msg(txt); + foreach(string line in txt.split("\n")){ + if (line.index_of(cmd) != -1){ + if (rex.match (line, 0, out match)){ + proc_list += int.parse(match.fetch(1).strip()); + } + } + } + } + catch (Error e) { + log_error (e.message); + } + + return proc_list; + } + public bool process_is_running(long pid){ /* Checks if given process is running */ @@ -1906,7 +2166,7 @@ var dlg = new Gtk.MessageDialog.with_markup(null, Gtk.DialogFlags.MODAL, type, Gtk.ButtonsType.OK, message); dlg.title = title; - dlg.set_default_size (200, -1); + dlg.set_default_size (300, -1); if (parent_win != null){ dlg.set_transient_for(parent_win); dlg.set_modal(true); @@ -1914,6 +2174,65 @@ dlg.run(); dlg.destroy(); } + + public string gtk_inputbox(string title, string message, Gtk.Window? parent_win, bool mask_password = false){ + + /* Shows a simple input prompt */ + + //vbox_main + Gtk.Box vbox_main = new Box (Orientation.VERTICAL, 0); + vbox_main.margin = 6; + + //lbl_input + Gtk.Label lbl_input = new Gtk.Label(title); + lbl_input.xalign = (float) 0.0; + lbl_input.label = message; + + //txt_input + Gtk.Entry txt_input = new Gtk.Entry(); + txt_input.margin_top = 3; + txt_input.set_visibility(false); + + //create dialog + var dlg = new Gtk.Dialog.with_buttons(title, parent_win, DialogFlags.MODAL); + dlg.title = title; + dlg.set_default_size (300, -1); + if (parent_win != null){ + dlg.set_transient_for(parent_win); + dlg.set_modal(true); + } + + //add widgets + Gtk.Box content = (Box) dlg.get_content_area (); + vbox_main.pack_start (lbl_input, false, true, 0); + vbox_main.pack_start (txt_input, false, true, 0); + content.add(vbox_main); + + //add buttons + dlg.add_button(_("OK"),Gtk.ResponseType.OK); + dlg.add_button(_("Cancel"),Gtk.ResponseType.CANCEL); + + //keyboard shortcuts + txt_input.key_press_event.connect ((w, event) => { + if (event.keyval == 65293) { + dlg.response(Gtk.ResponseType.OK); + return true; + } + return false; + }); + + dlg.show_all(); + int response = dlg.run(); + string input_text = txt_input.text; + dlg.destroy(); + + if (response == Gtk.ResponseType.CANCEL){ + return ""; + } + else{ + return input_text; + } + } public bool gtk_combobox_set_value (ComboBox combo, int index, string val){ @@ -2201,10 +2520,10 @@ return (exit_code == 0); } - public bool shutdown(){ + public bool shutdown (){ /* Shutdown the system immediately */ - + try{ string[] argv = { "shutdown", "-h", "now" }; Pid procId; diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/timeshift-btrfs.geany timeshift-btrfs-1.1~13~ubuntu12.04.1/timeshift-btrfs.geany --- timeshift-btrfs-1.0~7~ubuntu12.04.1/timeshift-btrfs.geany 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/timeshift-btrfs.geany 2015-01-21 17:16:39.000000000 +0000 @@ -18,15 +18,15 @@ [files] current_page=1 -FILE_NAME_0=15393;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FMain.vala;0;4 -FILE_NAME_1=22370;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FMainWindow.vala;0;4 -FILE_NAME_2=14767;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FSettingsWindow.vala;0;4 -FILE_NAME_3=53335;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FUtility.vala;0;4 -FILE_NAME_4=2316;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Finstaller%2Finstall.sh;0;4 -FILE_NAME_5=0;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FAboutWindow.vala;0;4 +FILE_NAME_0=59073;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FMain.vala;0;4 +FILE_NAME_1=26526;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FMainWindow.vala;0;4 +FILE_NAME_2=965;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FSettingsWindow.vala;0;4 +FILE_NAME_3=26997;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FUtility.vala;0;4 +FILE_NAME_4=1932;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Finstaller%2Finstall.sh;0;4 +FILE_NAME_5=8107;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FAboutWindow.vala;0;4 FILE_NAME_6=2333;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2FDonationWindow.vala;0;4 -FILE_NAME_7=346;Make;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2Fmakefile;0;4 -FILE_NAME_8=20;None;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fdebian%2Fchangelog;0;4 +FILE_NAME_7=571;Make;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fsrc%2Fmakefile;0;4 +FILE_NAME_8=146;None;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift-btrfs%2Fdebian%2Fchangelog;0;4 [VTE] last_dir=/home/teejee @@ -43,7 +43,7 @@ NF_03_CM=make clean NF_03_WD= EX_00_LB=_Execute Debug -EX_00_CM=gksu './timeshift-btrfs --show-commands --debug' +EX_00_CM=gksu './timeshift-btrfs --debug' EX_00_WD= EX_01_LB=Execute EX_01_CM=gksu './timeshift-btrfs' diff -Nru timeshift-btrfs-1.0~7~ubuntu12.04.1/timeshift-btrfs.pot timeshift-btrfs-1.1~13~ubuntu12.04.1/timeshift-btrfs.pot --- timeshift-btrfs-1.0~7~ubuntu12.04.1/timeshift-btrfs.pot 2014-10-01 14:36:45.000000000 +0000 +++ timeshift-btrfs-1.1~13~ubuntu12.04.1/timeshift-btrfs.pot 2015-01-21 17:16:39.000000000 +0000 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: timeshift-btrfs 1.6\n" "Report-Msgid-Bugs-To: teejee2008@gmail.com\n" -"POT-Creation-Date: 2014-10-01 19:33+0530\n" +"POT-Creation-Date: 2015-01-21 21:26+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,28 +17,66 @@ "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: Main.vala:294 +#: Main.vala:307 #, c-format msgid "/ is mapped to device: %s, UUID=%s" msgstr "" -#: Main.vala:232 +#: MainWindow.vala:414 +msgid "" +"Backup Levels\n" +"\n" +"O\tOn demand (manual)\n" +"B\tBoot\n" +"H\tHourly\n" +"D\tDaily\n" +"W\tWeekly\n" +"M\tMonthly" +msgstr "" + +#: MainWindow.vala:406 +msgid "Comments (double-click to edit)" +msgstr "" + +#: MainWindow.vala:394 +msgid "Snapshot Date: Date on which snapshot was created" +msgstr "" + +#: MainWindow.vala:410 +msgid "System: Installed Linux distribution" +msgstr "" + +#: MainWindow.vala:398 +msgid "Total Size: Space occupied by all files" +msgstr "" + +#: MainWindow.vala:402 +msgid "" +"Unshared Size: Space occupied by unshared files.\n" +"Deleting the snapshot will free up this space." +msgstr "" + +#: Main.vala:249 msgid "A scheduled job is currently taking a system snapshot." msgstr "" -#: MainWindow.vala:185 +#: Main.vala:666 Main.vala:687 +msgid "Aborted." +msgstr "" + +#: MainWindow.vala:184 msgid "About" msgstr "" -#: MainWindow.vala:637 +#: MainWindow.vala:683 msgid "Active System" msgstr "" -#: Main.vala:173 +#: Main.vala:186 msgid "Admin Access Required" msgstr "" -#: MainWindow.vala:1100 +#: MainWindow.vala:1147 msgid "" "After restore the current system will appear as a new snapshot. This " "snapshot can be used for 'undoing' the restore, and for retrieving the " @@ -54,36 +92,36 @@ msgid "All snapshots older than" msgstr "" -#: Main.vala:236 +#: Main.vala:253 msgid "Another instance of Timeshift BTRFS is currently running!" msgstr "" -#: Main.vala:492 +#: Main.vala:881 msgid "Another instance of Timeshift is currently running" msgstr "" -#: Main.vala:1473 +#: Main.vala:2026 msgid "App config loaded" msgstr "" -#: Main.vala:1415 +#: Main.vala:1947 msgid "App config saved" msgstr "" -#: MainWindow.vala:186 +#: MainWindow.vala:185 msgid "Application Info" msgstr "" -#: Main.vala:1673 +#: Main.vala:2239 msgid "Application will exit" msgstr "" -#: AboutWindow.vala:312 +#: AboutWindow.vala:330 #, c-format msgid "Artists" msgstr "" -#: AboutWindow.vala:304 +#: AboutWindow.vala:314 #, c-format msgid "Authors" msgstr "" @@ -92,28 +130,28 @@ msgid "Auto-Remove" msgstr "" -#: Main.vala:304 +#: Main.vala:317 msgid "BTRFS Partition Not Found" msgstr "" -#: Main.vala:428 +#: Main.vala:2591 #, c-format msgid "BTRFS partition '%s' has an unsupported subvolume layout." msgstr "" -#: AboutWindow.vala:275 +#: AboutWindow.vala:285 msgid "Back" msgstr "" -#: MainWindow.vala:118 +#: Main.vala:517 MainWindow.vala:117 msgid "Backup" msgstr "" -#: MainWindow.vala:202 +#: Main.vala:1539 MainWindow.vala:201 msgid "Backup Device" msgstr "" -#: MainWindow.vala:520 MainWindow.vala:540 +#: MainWindow.vala:565 MainWindow.vala:585 msgid "Backup Device Changed" msgstr "" @@ -121,7 +159,7 @@ msgid "Backup Level" msgstr "" -#: MainWindow.vala:833 +#: MainWindow.vala:880 msgid "Backup device does not have enough space" msgstr "" @@ -129,99 +167,122 @@ msgid "Backup device is not available" msgstr "" -#: MainWindow.vala:1336 +#: MainWindow.vala:1380 msgid "Backup device is not mounted!" msgstr "" -#: Main.vala:1831 Main.vala:1838 +#: Main.vala:2375 Main.vala:2382 msgid "Backup device not available" msgstr "" -#: Main.vala:1828 MainWindow.vala:1333 +#: Main.vala:2190 +msgid "Backup device not found" +msgstr "" + +#: Main.vala:2372 MainWindow.vala:1377 msgid "Backup device not selected" msgstr "" +#: Main.vala:1546 +msgid "Backup device not specified!" +msgstr "" + #: SettingsWindow.vala:336 SettingsWindow.vala:377 msgid "Boot" msgstr "" -#: Main.vala:642 +#: Main.vala:999 msgid "Boot snapshot failed!" msgstr "" -#: Main.vala:623 +#: Main.vala:980 msgid "Boot snapshots are enabled" msgstr "" -#: MainWindow.vala:136 +#: MainWindow.vala:135 msgid "Browse" msgstr "" -#: MainWindow.vala:137 +#: MainWindow.vala:136 msgid "Browse Snapshot" msgstr "" -#: RestoreWindow.vala:96 RestoreWindow.vala:97 +#: RestoreWindow.vala:96 RestoreWindow.vala:97 Utility.vala:2213 msgid "Cancel" msgstr "" -#: MainWindow.vala:967 +#: MainWindow.vala:1014 msgid "Cannot Delete" msgstr "" -#: Main.vala:2111 +#: MainWindow.vala:1090 +#, c-format +msgid "" +"Changes made to the running system till the next reboot will be saved in " +"snapshot '%s'" +msgstr "" + +#: Main.vala:2805 msgid "Checked subvolume quota" msgstr "" -#: MainWindow.vala:463 +#: MainWindow.vala:508 msgid "Checking backup device..." msgstr "" -#: MainWindow.vala:517 +#: MainWindow.vala:562 msgid "Click 'OK' to confirm" msgstr "" -#: AboutWindow.vala:285 +#: AboutWindow.vala:295 msgid "Close" msgstr "" -#: Main.vala:413 +#: Main.vala:846 msgid "Commands listed below are not available on this system" msgstr "" -#: MainWindow.vala:370 +#: MainWindow.vala:371 msgid "Comments" msgstr "" -#: MainWindow.vala:1109 +#: MainWindow.vala:1153 msgid "Continue with restore?" msgstr "" -#: Main.vala:866 +#: Main.vala:1485 Main.vala:1979 +msgid "Could not find device" +msgstr "" + +#: Main.vala:1568 Main.vala:1764 +msgid "Could not find snapshot" +msgstr "" + +#: Main.vala:1204 msgid "Creating snapshot..." msgstr "" -#: AboutWindow.vala:262 AboutWindow.vala:279 +#: AboutWindow.vala:272 AboutWindow.vala:289 msgid "Credits" msgstr "" -#: Main.vala:1672 +#: Main.vala:2238 msgid "Critical Error" msgstr "" -#: Main.vala:1788 +#: Main.vala:2564 msgid "Cron job added" msgstr "" -#: Main.vala:1799 +#: Main.vala:2575 msgid "Cron job removed" msgstr "" -#: Utility.vala:2465 Utility.vala:2494 +#: Utility.vala:2784 Utility.vala:2813 msgid "Crontab is empty" msgstr "" -#: MainWindow.vala:1105 MainWindow.vala:1112 +#: MainWindow.vala:1149 MainWindow.vala:1156 msgid "DISCLAIMER" msgstr "" @@ -229,38 +290,50 @@ msgid "Daily" msgstr "" -#: Main.vala:706 +#: Main.vala:1059 msgid "Daily snapshot failed!" msgstr "" -#: Main.vala:687 +#: Main.vala:1040 msgid "Daily snapshots are enabled" msgstr "" -#: MainWindow.vala:145 +#: Main.vala:525 MainWindow.vala:144 msgid "Delete" msgstr "" -#: MainWindow.vala:146 +#: MainWindow.vala:145 msgid "Delete Snapshot" msgstr "" -#: Main.vala:1369 -msgid "Deleted" +#: Main.vala:527 +msgid "Delete all snapshots" +msgstr "" + +#: Main.vala:526 +msgid "Delete snapshot" msgstr "" -#: MainWindow.vala:943 MainWindow.vala:958 +#: MainWindow.vala:990 MainWindow.vala:1005 msgid "Deleting snapshot" msgstr "" -#: SettingsWindow.vala:152 +#: Main.vala:555 SettingsWindow.vala:152 msgid "Description" msgstr "" +#: Main.vala:588 +msgid "Device" +msgstr "" + #: RestoreWindow.vala:119 msgid "Device Offline" msgstr "" +#: Main.vala:484 +msgid "Devices with BTRFS and encrypted file systems" +msgstr "" + #: DonationWindow.vala:53 msgid "" "Did you find this software useful?\n" @@ -280,28 +353,32 @@ "teejeetech@gmail.com" msgstr "" -#: MainWindow.vala:496 +#: Main.vala:1895 +msgid "Directory not found" +msgstr "" + +#: MainWindow.vala:541 msgid "Disable Scheduled Snapshots" msgstr "" -#: MainWindow.vala:1371 +#: MainWindow.vala:1415 msgid "Disabled" msgstr "" -#: Main.vala:208 +#: Main.vala:224 msgid "Distribution" msgstr "" -#: MainWindow.vala:537 +#: MainWindow.vala:582 msgid "Do you want to select another device?" msgstr "" -#: AboutWindow.vala:328 +#: AboutWindow.vala:346 #, c-format msgid "Documenters" msgstr "" -#: DonationWindow.vala:36 MainWindow.vala:176 MainWindow.vala:177 +#: DonationWindow.vala:36 MainWindow.vala:175 MainWindow.vala:176 msgid "Donate" msgstr "" @@ -309,7 +386,7 @@ msgid "Donate via PayPal" msgstr "" -#: AboutWindow.vala:336 +#: AboutWindow.vala:354 #, c-format msgid "Donations" msgstr "" @@ -318,24 +395,50 @@ msgid "Enable" msgstr "" -#: MainWindow.vala:1366 +#: MainWindow.vala:1410 msgid "Enabled" msgstr "" -#: Main.vala:2139 +#: Main.vala:2833 msgid "Enabled subvolume quota support" msgstr "" -#: Main.vala:240 MainWindow.vala:794 MainWindow.vala:1082 MainWindow.vala:1311 -#: Utility.vala:109 +#: Main.vala:2302 +msgid "Encrypted Device" +msgstr "" + +#: Main.vala:2278 Main.vala:2312 +#, c-format +msgid "" +"Encrypted device '%s' is already unlocked and mapped to another device name. " +"Select the unlocked device instead of selecting the encrypted device." +msgstr "" + +#: Main.vala:1501 +#, c-format +msgid "Enter device name or number (a=Abort)" +msgstr "" + +#: Main.vala:2302 +#, c-format +msgid "Enter passphrase to unlock '%s'" +msgstr "" + +#: Main.vala:1587 Main.vala:1783 +#, c-format +msgid "Enter snapshot number (a=Abort, p=Previous, n=Next)" +msgstr "" + +#: Main.vala:257 Main.vala:2318 MainWindow.vala:1129 MainWindow.vala:1355 +#: Utility.vala:125 msgid "Error" msgstr "" -#: MainWindow.vala:948 +#: MainWindow.vala:995 msgid "Error: Unable to delete snapshot" msgstr "" -#: MainWindow.vala:861 +#: MainWindow.vala:908 msgid "Error: Unable to save snapshot" msgstr "" @@ -347,87 +450,87 @@ msgid "Excluded Directories" msgstr "" -#: Main.vala:1792 +#: Main.vala:2568 msgid "Failed to add cron job" msgstr "" -#: Main.vala:882 +#: Main.vala:1220 msgid "Failed to create subvolume snapshot" msgstr "" -#: Main.vala:1058 +#: Main.vala:1396 msgid "Failed to create symlinks" msgstr "" -#: Main.vala:2407 +#: Main.vala:3111 msgid "Failed to delete snapshot directory" msgstr "" -#: Main.vala:2494 +#: Main.vala:3198 msgid "Failed to delete snapshot subvolume" msgstr "" -#: Main.vala:1088 +#: Main.vala:1426 msgid "Failed to delete symlinks" msgstr "" -#: Main.vala:2503 +#: Main.vala:3207 msgid "Failed to destroy qgroup" msgstr "" -#: Main.vala:2135 +#: Main.vala:2829 msgid "Failed to enable subvolume quota" msgstr "" -#: Utility.vala:522 Utility.vala:611 +#: Utility.vala:680 msgid "Failed to get partition list" msgstr "" -#: Main.vala:1563 +#: Main.vala:2117 msgid "Failed to get partition list." msgstr "" -#: MainWindow.vala:792 -msgid "Failed to mount device" -msgstr "" - -#: Main.vala:1256 +#: Main.vala:1686 msgid "Failed to move subvolume" msgstr "" -#: Main.vala:1984 +#: Main.vala:2678 msgid "Failed to query subvolume list" msgstr "" -#: Main.vala:2046 +#: Main.vala:2740 msgid "Failed to query subvolume quota" msgstr "" -#: Main.vala:1803 +#: Main.vala:1882 +msgid "Failed to remove" +msgstr "" + +#: Main.vala:2579 msgid "Failed to remove cron job" msgstr "" -#: Main.vala:2162 +#: Main.vala:2856 msgid "Failed to rescan subvolume quota" msgstr "" -#: Main.vala:1291 +#: Main.vala:1719 msgid "Failed to restore subvolume" msgstr "" -#: Utility.vala:2392 +#: Utility.vala:2711 msgid "Failed to set ownership" msgstr "" -#: Main.vala:363 Main.vala:370 -msgid "Failed to take snapshot." +#: Main.vala:2275 Main.vala:2284 Main.vala:2309 Main.vala:2318 +msgid "Failed to unlock device" msgstr "" -#: Utility.vala:1235 +#: Utility.vala:1378 msgid "Failed to unmount" msgstr "" -#: Main.vala:1673 +#: Main.vala:2239 msgid "Failed to unmount device!" msgstr "" @@ -439,14 +542,18 @@ msgid "Files matching the following patterns will be excluded" msgstr "" -#: MainWindow.vala:1047 +#: MainWindow.vala:1094 msgid "Finished" msgstr "" -#: Main.vala:1004 +#: Main.vala:1342 msgid "Free space is less than" msgstr "" +#: Main.vala:529 +msgid "Global" +msgstr "" + #: ExcludeMessageWindow.vala:135 msgid "" "Hidden files and folders are included by default since they contain user-" @@ -461,20 +568,24 @@ msgid "Hourly" msgstr "" -#: Main.vala:674 +#: Main.vala:1029 msgid "Hourly snapshot failed!" msgstr "" -#: Main.vala:655 +#: Main.vala:1010 msgid "Hourly snapshots are enabled" msgstr "" -#: MainWindow.vala:1107 +#: MainWindow.vala:1151 msgid "" "If these terms are not acceptable to you, please do not proceed beyond this " "point!" msgstr "" +#: Main.vala:410 +msgid "Invalid command line arguments" +msgstr "" + #: SettingsWindow.vala:327 msgid "Keep one snapshot per day" msgstr "" @@ -495,76 +606,80 @@ msgid "Keep one snapshot per week" msgstr "" -#: Main.vala:635 +#: Main.vala:592 +msgid "Label" +msgstr "" + +#: Main.vala:992 #, c-format msgid "Last boot snapshot is %d hours old" msgstr "" -#: Main.vala:630 +#: Main.vala:987 msgid "Last boot snapshot is older than system start time" msgstr "" -#: Main.vala:626 +#: Main.vala:983 msgid "Last boot snapshot not found" msgstr "" -#: Main.vala:699 +#: Main.vala:1052 #, c-format msgid "Last daily snapshot is %d hours old" msgstr "" -#: Main.vala:694 +#: Main.vala:1047 msgid "Last daily snapshot is more than 1 day old" msgstr "" -#: Main.vala:690 +#: Main.vala:1043 msgid "Last daily snapshot not found" msgstr "" -#: Main.vala:667 +#: Main.vala:1022 #, c-format msgid "Last hourly snapshot is %d minutes old" msgstr "" -#: Main.vala:662 +#: Main.vala:1017 msgid "Last hourly snapshot is more than 1 hour old" msgstr "" -#: Main.vala:658 +#: Main.vala:1013 msgid "Last hourly snapshot not found" msgstr "" -#: Main.vala:763 +#: Main.vala:1112 #, c-format msgid "Last monthly snapshot is %d days old" msgstr "" -#: Main.vala:758 +#: Main.vala:1107 msgid "Last monthly snapshot is more than 1 month old" msgstr "" -#: Main.vala:754 +#: Main.vala:1103 msgid "Last monthly snapshot not found" msgstr "" -#: MainWindow.vala:1395 MainWindow.vala:1399 +#: MainWindow.vala:1439 MainWindow.vala:1443 msgid "Last snapshot is" msgstr "" -#: MainWindow.vala:1403 +#: MainWindow.vala:1447 msgid "Last snapshot is less than 1 hour old" msgstr "" -#: Main.vala:731 +#: Main.vala:1082 #, c-format msgid "Last weekly snapshot is %d days old" msgstr "" -#: Main.vala:726 +#: Main.vala:1077 msgid "Last weekly snapshot is more than 1 week old" msgstr "" -#: Main.vala:722 +#: Main.vala:1073 msgid "Last weekly snapshot not found" msgstr "" @@ -572,27 +687,31 @@ msgid "Limit" msgstr "" -#: Main.vala:454 -msgid "List all snapshots" +#: Main.vala:513 +msgid "List" msgstr "" -#: Main.vala:1419 -msgid "Loading app config.." +#: Main.vala:515 +msgid "List devices" msgstr "" -#: MainWindow.vala:833 +#: Main.vala:514 +msgid "List snapshots" +msgstr "" + +#: MainWindow.vala:880 msgid "Low Disk Space" msgstr "" -#: Main.vala:923 Main.vala:960 +#: Main.vala:1261 Main.vala:1298 msgid "Maximum backups exceeded for backup level" msgstr "" -#: Main.vala:215 +#: Main.vala:232 msgid "Missing Dependencies" msgstr "" -#: Utility.vala:2005 +#: Utility.vala:2324 msgid "Missing Icon" msgstr "" @@ -600,81 +719,93 @@ msgid "Monthly" msgstr "" -#: Main.vala:751 +#: Main.vala:1100 msgid "Monthly snapshot are enabled" msgstr "" -#: Main.vala:770 +#: Main.vala:1119 msgid "Monthly snapshot failed!" msgstr "" -#: Main.vala:1643 +#: Main.vala:2205 #, c-format -msgid "Mounted BTRFS device '%s' at '%s'" +msgid "Mounted backup device '%s' at '%s'" msgstr "" -#: MainWindow.vala:996 +#: MainWindow.vala:1043 msgid "Multiple Snapshots Selected" msgstr "" -#: MainWindow.vala:1102 -msgid "NOTE" +#: Main.vala:553 +msgid "Name" msgstr "" -#: MainWindow.vala:889 MainWindow.vala:992 +#: MainWindow.vala:936 MainWindow.vala:1039 msgid "No Snapshots Selected" msgstr "" -#: MainWindow.vala:1387 +#: Main.vala:665 +msgid "No response from user" +msgstr "" + +#: Main.vala:478 Main.vala:1577 Main.vala:1773 Main.vala:1838 +msgid "No snapshots found on device" +msgstr "" + +#: MainWindow.vala:1431 msgid "No snapshots on device" msgstr "" -#: Main.vala:430 +#: Main.vala:2593 msgid "Not Supported" msgstr "" -#: Main.vala:458 -msgid "Notes" +#: Main.vala:1130 +msgid "Nothing to do!" msgstr "" -#: Main.vala:783 -msgid "Nothing to do!" +#: Main.vala:551 Main.vala:586 +msgid "Num" +msgstr "" + +#: Utility.vala:2212 +msgid "OK" msgstr "" -#: Main.vala:1892 +#: Main.vala:2431 msgid "Older log files removed" msgstr "" -#: Main.vala:602 +#: Main.vala:961 msgid "On-demand snapshot failed!" msgstr "" -#: Main.vala:429 +#: Main.vala:2592 msgid "" "Only ubuntu-type layouts with @ and @home subvolumes are currently supported." msgstr "" -#: Main.vala:450 +#: Main.vala:511 msgid "Options" msgstr "" -#: Main.vala:237 +#: Main.vala:254 msgid "Please check if you have multiple windows open." msgstr "" -#: Main.vala:414 +#: Main.vala:847 msgid "Please install required packages and try running Timeshift again" msgstr "" -#: Main.vala:167 +#: Main.vala:180 msgid "Please run the application as admin (using 'sudo' or 'su')" msgstr "" -#: MainWindow.vala:889 +#: MainWindow.vala:936 msgid "Please select the snapshots to delete" msgstr "" -#: Main.vala:233 +#: Main.vala:250 msgid "Please wait for a few minutes and try again." msgstr "" @@ -686,51 +817,53 @@ msgid "Refresh Devices" msgstr "" -#: Main.vala:1662 +#: Main.vala:1888 +msgid "Removed" +msgstr "" + +#: Main.vala:2228 #, c-format msgid "Removed mount directory: '%s'" msgstr "" -#: Main.vala:979 +#: Main.vala:1317 msgid "Removing backups older than" msgstr "" -#: Main.vala:1005 +#: Main.vala:1343 msgid "Removing older backups to free disk space" msgstr "" -#: MainWindow.vala:897 +#: MainWindow.vala:944 msgid "Removing selected snapshots..." msgstr "" -#: Main.vala:1023 +#: Main.vala:1361 msgid "Removing un-tagged snapshots..." msgstr "" -#: MainWindow.vala:127 RestoreWindow.vala:47 RestoreWindow.vala:86 -#: RestoreWindow.vala:87 +#: Main.vala:521 MainWindow.vala:126 RestoreWindow.vala:47 +#: RestoreWindow.vala:86 RestoreWindow.vala:87 msgid "Restore" msgstr "" -#: MainWindow.vala:1078 +#: MainWindow.vala:1125 msgid "Restore Failed!" msgstr "" -#: MainWindow.vala:128 +#: MainWindow.vala:127 msgid "Restore Snapshot" msgstr "" -#: Main.vala:1299 +#: Main.vala:1725 msgid "Restore completed without errors" msgstr "" -#: MainWindow.vala:1103 -msgid "" -"Restore process is very fast and will complete in less than 2 seconds. This " -"does not mean that the restore has failed. There is no need to panic! :P" +#: Main.vala:522 +msgid "Restore snapshot" msgstr "" -#: MainWindow.vala:1032 +#: MainWindow.vala:1079 msgid "Restoring snapshot..." msgstr "" @@ -746,27 +879,39 @@ msgid "Scheduled Snapshots" msgstr "" -#: MainWindow.vala:1366 MainWindow.vala:1371 +#: MainWindow.vala:1410 MainWindow.vala:1415 msgid "Scheduled snapshots" msgstr "" -#: Main.vala:782 +#: Main.vala:1129 msgid "Scheduled snapshots are disabled" msgstr "" -#: MainWindow.vala:493 MainWindow.vala:536 +#: MainWindow.vala:538 MainWindow.vala:581 msgid "Scheduled snapshots will be disabled." msgstr "" -#: MainWindow.vala:516 +#: MainWindow.vala:561 msgid "Scheduled snapshots will be saved to " msgstr "" -#: MainWindow.vala:996 +#: MainWindow.vala:1043 msgid "Select a single snapshot to restore" msgstr "" -#: MainWindow.vala:992 +#: Main.vala:1495 +msgid "Select backup device" +msgstr "" + +#: Main.vala:1778 +msgid "Select snapshot to delete" +msgstr "" + +#: Main.vala:1582 +msgid "Select snapshot to restore" +msgstr "" + +#: MainWindow.vala:1039 msgid "Select the snapshot to restore" msgstr "" @@ -774,62 +919,63 @@ msgid "Send Email" msgstr "" -#: Main.vala:198 +#: Main.vala:212 msgid "Session log file" msgstr "" -#: MainWindow.vala:154 MainWindow.vala:155 SettingsWindow.vala:68 +#: MainWindow.vala:153 MainWindow.vala:154 SettingsWindow.vala:68 msgid "Settings" msgstr "" -#: Main.vala:455 +#: Main.vala:531 msgid "Show additional debug messages" msgstr "" -#: Main.vala:456 +#: Main.vala:532 msgid "Show all options" msgstr "" -#: Main.vala:855 Main.vala:899 Main.vala:926 Main.vala:964 Main.vala:983 -#: MainWindow.vala:268 +#: Main.vala:590 +msgid "Size" +msgstr "" + +#: Main.vala:1193 Main.vala:1237 Main.vala:1264 Main.vala:1302 Main.vala:1321 +#: Main.vala:1598 MainWindow.vala:269 msgid "Snapshot" msgstr "" -#: MainWindow.vala:968 +#: MainWindow.vala:1015 #, c-format msgid "" "Snapshot '%s' is in use by the system.\n" "It can be deleted only after next reboot." msgstr "" -#: Main.vala:2411 +#: Main.vala:3115 msgid "Snapshot deleted" msgstr "" -#: MainWindow.vala:1039 +#: MainWindow.vala:1086 msgid "Snapshot restored successfully." msgstr "" -#: Main.vala:895 MainWindow.vala:858 +#: Main.vala:1233 MainWindow.vala:905 msgid "Snapshot saved successfully" msgstr "" -#: Main.vala:1145 -msgid "Snapshot to restore not specified!" +#: Main.vala:1793 +msgid "Snapshot to delete not specified!" msgstr "" -#: MainWindow.vala:1043 -#, c-format -msgid "" -"Snapshot will become active after next reboot. You can continue working. Any " -"changes made till next reboot will be saved in snapshot '%s'" +#: Main.vala:1603 +msgid "Snapshot to restore not specified!" msgstr "" -#: Main.vala:379 -msgid "Snapshots" +#: Main.vala:1727 MainWindow.vala:1090 +msgid "Snapshot will become active after system is rebooted." msgstr "" -#: MainWindow.vala:963 +#: MainWindow.vala:1010 msgid "Snapshots deleted successfully" msgstr "" @@ -837,54 +983,67 @@ msgid "Snapshots matching following rules will be removed automatically" msgstr "" -#: MainWindow.vala:209 +#: Main.vala:473 +#, c-format +msgid "Snapshots on device %s" +msgstr "" + +#: MainWindow.vala:208 msgid "" "Snapshots will be saved in path /timeshift-btrfs on selected device" msgstr "" -#: Utility.vala:1733 +#: Main.vala:530 +msgid "Specify BTRFS device" +msgstr "" + +#: Main.vala:523 +msgid "Specify snapshot to restore" +msgstr "" + +#: Utility.vala:1993 msgid "Stopped" msgstr "" -#: Main.vala:1064 +#: Main.vala:1402 msgid "Symlinks updated" msgstr "" -#: MainWindow.vala:323 +#: MainWindow.vala:324 msgid "System" msgstr "" -#: MainWindow.vala:1201 +#: MainWindow.vala:1245 msgid "System Restore Utility for BTRFS file system" msgstr "" -#: MainWindow.vala:1367 +#: MainWindow.vala:1411 msgid "System snapshots will be taken at regular intervals" msgstr "" -#: MainWindow.vala:1099 +#: MainWindow.vala:1146 msgid "" "System will be reset to a previous date. All files in the home directory " "(including documents) will be reset to previous versions." msgstr "" -#: MainWindow.vala:347 +#: Main.vala:554 MainWindow.vala:348 msgid "Tags" msgstr "" -#: MainWindow.vala:119 +#: MainWindow.vala:118 msgid "Take a manual (ondemand) snapshot" msgstr "" -#: Main.vala:453 +#: Main.vala:519 msgid "Take on-demand backup" msgstr "" -#: Main.vala:452 +#: Main.vala:518 msgid "Take scheduled backup" msgstr "" -#: MainWindow.vala:847 +#: MainWindow.vala:894 msgid "Taking snapshot..." msgstr "" @@ -892,56 +1051,79 @@ msgid "Target" msgstr "" -#: MainWindow.vala:535 +#: MainWindow.vala:580 msgid "The backup device is not set or unavailable." msgstr "" -#: MainWindow.vala:1106 +#: AboutWindow.vala:322 +#, c-format +msgid "Third Party Tools" +msgstr "" + +#: MainWindow.vala:1150 msgid "" "This software comes without absolutely NO warranty and the author takes no " "responsibility for any damage arising from the use of this program." msgstr "" -#: MainWindow.vala:168 +#: MainWindow.vala:167 msgid "TimeShift Logs" msgstr "" -#: Main.vala:166 +#: Main.vala:179 msgid "TimeShift needs admin access to backup and restore system files." msgstr "" -#: Main.vala:298 +#: Main.vala:311 msgid "" "Timeshift could not find any BTRFS partitions on your system.\n" "Timeshift BTRFS can be used only with BTRFS file system." msgstr "" -#: MainWindow.vala:297 +#: MainWindow.vala:298 msgid "Total" msgstr "" -#: AboutWindow.vala:320 +#: AboutWindow.vala:338 #, c-format msgid "Translators" msgstr "" -#: Main.vala:1365 -msgid "Unable to delete" +#: Main.vala:591 +msgid "Type" msgstr "" -#: Main.vala:840 +#: Main.vala:1178 msgid "Unknown snapshot type" msgstr "" -#: Utility.vala:1224 +#: Main.vala:2312 +msgid "Unlocked Device" +msgstr "" + +#: Main.vala:2315 +msgid "Unlocked Successfully" +msgstr "" + +#: Main.vala:2259 Main.vala:2281 +#, c-format +msgid "Unlocked device is mapped to '%s'" +msgstr "" + +#: Main.vala:2315 +#, c-format +msgid "Unlocked device is mapped to '%s'." +msgstr "" + +#: Utility.vala:1367 msgid "Unmounting from" msgstr "" -#: MainWindow.vala:310 +#: MainWindow.vala:311 msgid "Unshared" msgstr "" -#: MainWindow.vala:169 +#: MainWindow.vala:168 msgid "View TimeShift Logs" msgstr "" @@ -949,19 +1131,19 @@ msgid "Visit Website" msgstr "" -#: MainWindow.vala:1098 +#: MainWindow.vala:1145 msgid "WARNING" msgstr "" -#: Utility.vala:109 +#: Utility.vala:125 msgid "Warning" msgstr "" -#: Main.vala:1444 +#: Main.vala:1987 msgid "Warning: Backup device not set! Defaulting to system device" msgstr "" -#: Main.vala:496 +#: Main.vala:885 msgid "Warning: Deleted invalid lock" msgstr "" @@ -969,11 +1151,11 @@ msgid "Weekly" msgstr "" -#: Main.vala:738 +#: Main.vala:1089 msgid "Weekly snapshot failed!" msgstr "" -#: Main.vala:719 +#: Main.vala:1070 msgid "Weekly snapshots are enabled" msgstr "" @@ -981,28 +1163,32 @@ msgid "When free space less than" msgstr "" -#: Main.vala:881 Main.vala:1290 Main.vala:1983 Main.vala:2045 Main.vala:2134 -#: Main.vala:2161 +#: Main.vala:2275 Main.vala:2309 +msgid "Wrong Passphrase" +msgstr "" + +#: Main.vala:1219 Main.vala:1718 Main.vala:2677 Main.vala:2739 Main.vala:2828 +#: Main.vala:2855 msgid "btrfs returned an error" msgstr "" -#: MainWindow.vala:1395 +#: MainWindow.vala:1439 msgid "days old" msgstr "" -#: Main.vala:979 +#: Main.vala:1317 msgid "days..." msgstr "" -#: Main.vala:1819 +#: Main.vala:2363 msgid "free" msgstr "" -#: MainWindow.vala:1399 +#: MainWindow.vala:1443 msgid "hours old" msgstr "" -#: MainWindow.vala:514 +#: MainWindow.vala:559 msgid "snapshot device changed" msgstr "" @@ -1011,10 +1197,10 @@ msgid "snapshots older than" msgstr "" -#: Main.vala:855 Main.vala:899 +#: Main.vala:1193 Main.vala:1237 msgid "tagged" msgstr "" -#: Main.vala:926 Main.vala:964 Main.vala:983 +#: Main.vala:1264 Main.vala:1302 Main.vala:1321 msgid "un-tagged" msgstr ""