39public const string AppName =
"BastionGuard Backup";
48extern void exit(
int exit_code);
54 public static int main (
string[] args) {
60 if (args.length > 1) {
61 switch (args[1].down()) {
73 else if (args.length == 1){
91 bool ok = console.start_application();
99 Intl.setlocale(GLib.LocaleCategory.ALL,
"");
108 log_msg(
_(
"Application needs admin access."));
109 log_msg(
_(
"Please run the application as admin (using 'sudo' or 'su')"));
119 log_debug(
"AppConsole: parse_arguments()");
121 for (
int k = 1; k < args.length; k++)
123 switch (args[k].down()){
128 App.app_mode =
"backup";
134 App.app_mode =
"delete";
140 App.app_mode =
"delete-all";
146 App.mirror_system =
false;
147 App.app_mode =
"restore";
153 App.mirror_system =
true;
154 App.app_mode =
"restore";
161 App.app_mode =
"ondemand";
166 App.cmd_comments = args[++k];
170 App.cmd_skip_grub =
true;
174 App.cmd_verbose =
true;
178 App.cmd_verbose =
false;
182 App.cmd_scripted =
true;
183 App.cmd_confirm =
true;
187 App.cmd_confirm =
true;
191 case "--grub-device":
192 App.reinstall_grub2 =
true;
193 App.cmd_grub_device = args[++k];
197 case "--target-device":
198 App.cmd_target_device = args[++k];
201 case "--backup-device":
202 case "--snapshot-device":
203 App.cmd_backup_device = args[++k];
207 case "--snapshot-name":
208 App.cmd_snapshot = args[++k];
212 App.cmd_tags = args[++k];
222 case "--list-snapshots":
223 App.app_mode =
"list-snapshots";
228 case "--list-devices":
229 App.app_mode =
"list-devices";
235 App.btrfs_mode =
true;
236 App.cmd_btrfs_mode =
true;
240 App.btrfs_mode =
false;
241 App.cmd_btrfs_mode =
false;
245 log_error(
"Option --backup has been replaced by option --check");
246 log_error(
"Run 'bastionguard-backup --help' to list all available options");
251 log_error(
"Option --backup-now has been replaced by option --create");
252 log_error(
"Run 'bastionguard-backup --help' to list all available options");
259 _(
"Invalid command line arguments"), args[k]),
true);
260 log_error(
"Run 'bastionguard-backup --help' to list all available options");
261 App.app_mode =
"abort";
284 log_debug(
"AppConsole: start_application()");
286 bool is_success =
true;
292 log_error(
_(
"Snapshots cannot be created in Live CD mode"));
318 case "list-snapshots":
335 log_msg(
"\n" +
_(
"Devices with Linux file systems") +
":\n");
352 string msg =
"\n%s v%s by Tony George (%s)\n".printf(
358 msg +=
" bastionguard-backup --check\n";
359 msg +=
" bastionguard-backup --create [OPTIONS]\n";
360 msg +=
" bastionguard-backup --restore [OPTIONS]\n";
361 msg +=
" bastionguard-backup --delete-[all] [OPTIONS]\n";
362 msg +=
" bastionguard-backup --list-{snapshots|devices} [OPTIONS]\n";
364 msg +=
_(
"Options") +
":\n";
366 msg +=
_(
"List") +
":\n";
367 msg +=
" --list[-snapshots] " +
_(
"List snapshots") +
"\n";
368 msg +=
" --list-devices " +
_(
"List devices") +
"\n";
370 msg +=
_(
"Backup") +
":\n";
371 msg +=
" --check " +
_(
"Create snapshot if scheduled") +
"\n";
372 msg +=
" --create " +
_(
"Create snapshot (even if not scheduled)") +
"\n";
373 msg +=
" --comments <string> " +
_(
"Set snapshot description") +
"\n";
374 msg +=
" --tags {O,B,H,D,W,M} " +
_(
"Add tags to snapshot (default: O)") +
"\n";;
376 msg +=
_(
"Restore") +
":\n";
377 msg +=
" --restore " +
_(
"Restore snapshot") +
"\n";
379 msg +=
" --snapshot <name> " +
_(
"Specify snapshot to restore") +
"\n";
380 msg +=
" --target[-device] <device> " +
_(
"Specify target device") +
"\n";
381 msg +=
" --grub[-device] <device> " +
_(
"Specify device for installing GRUB2 bootloader") +
"\n";
382 msg +=
" --skip-grub " +
_(
"Skip GRUB2 reinstall") +
"\n";
384 msg +=
_(
"Delete") +
":\n";
385 msg +=
" --delete " +
_(
"Delete snapshot") +
"\n";
386 msg +=
" --delete-all " +
_(
"Delete all snapshots") +
"\n";
388 msg +=
_(
"Global") +
":\n";
389 msg +=
" --snapshot-device <device> " +
_(
"Specify backup device (default: config)") +
"\n";
390 msg +=
" --yes " +
_(
"Answer YES to all confirmation prompts") +
"\n";
391 msg +=
" --btrfs " +
_(
"Switch to BTRFS mode (default: config)") +
"\n";
392 msg +=
" --rsync " +
_(
"Switch to RSYNC mode (default: config)") +
"\n";
393 msg +=
" --debug " +
_(
"Show additional debug messages") +
"\n";
394 msg +=
" --verbose " +
_(
"Show rsync output (default)") +
"\n";
395 msg +=
" --quiet " +
_(
"Hide rsync output") +
"\n";
396 msg +=
" --scripted " +
_(
"Run in non-interactive mode") +
"\n";
397 msg +=
" --help " +
_(
"Show all options") +
"\n";
398 msg +=
" --version " +
_(
"Print version number") +
"\n";
401 msg +=
_(
"Examples") +
":\n";
403 msg +=
"bastionguard-backup --list\n";
404 msg +=
"bastionguard-backup --list --snapshot-device /dev/sda1\n";
405 msg +=
"bastionguard-backup --create --comments \"after update\" --tags D\n";
406 msg +=
"bastionguard-backup --restore \n";
407 msg +=
"bastionguard-backup --restore --snapshot '2014-10-12_16-29-08' --target /dev/sda1\n";
408 msg +=
"bastionguard-backup --delete --snapshot '2014-10-12_16-29-08'\n";
409 msg +=
"bastionguard-backup --delete-all \n";
412 msg +=
_(
"Notes") +
":\n";
414 msg +=
" 1) --create will always create a new snapshot\n";
415 msg +=
" 2) --check will create a snapshot only if a scheduled snapshot is due\n";
416 msg +=
" 3) Use --restore without other options to select options interactively\n";
417 msg +=
" 4) UUID can be specified instead of device name\n";
418 msg +=
" 5) Default values will be loaded from app config if options are not specified\n";
433 string[,] grid =
new string[count+1,5];
434 bool[] right_align = {
false,
false,
false,
false,
false};
438 grid[row, ++col] =
_(
"Num");
439 grid[row, ++col] =
"";
440 grid[row, ++col] =
_(
"Name");
441 grid[row, ++col] =
_(
"Tags");
442 grid[row, ++col] =
_(
"Description");
449 grid[row, ++col] =
"%d".printf(index);
450 grid[row, ++col] =
">";
451 grid[row, ++col] =
"%s".printf(bak.
name);
463 string[,] grid =
new string[device_list.size+1,6];
464 bool[] right_align = {
false,
false,
false,
true,
true,
false};
468 grid[row, ++col] =
_(
"Num");
469 grid[row, ++col] =
"";
470 grid[row, ++col] =
_(
"Device");
472 grid[row, ++col] =
_(
"Size");
473 grid[row, ++col] =
_(
"Type");
474 grid[row, ++col] =
_(
"Label");
477 foreach(var pi
in device_list) {
479 grid[row, ++col] =
"%d".printf(row - 1);
480 grid[row, ++col] =
">";
481 grid[row, ++col] =
"%s".printf(pi.device_name_with_parent);
483 grid[row, ++col] =
"%s".printf((pi.size_bytes > 0) ?
"%s".printf(pi.size) :
"?? GB");
484 grid[row, ++col] =
"%s".printf(pi.fstype);
485 grid[row, ++col] =
"%s".printf(pi.label);
495 var device_list =
new Gee.ArrayList<
Device>();
497 if (dev.has_linux_filesystem()){
498 device_list.add(dev);
502 string[,] grid =
new string[device_list.size+1,6];
503 bool[] right_align = {
false,
false,
false,
true,
true,
false};
507 grid[row, ++col] =
_(
"Num");
508 grid[row, ++col] =
"";
509 grid[row, ++col] =
_(
"Device");
511 grid[row, ++col] =
_(
"Size");
512 grid[row, ++col] =
_(
"Type");
513 grid[row, ++col] =
_(
"Label");
516 foreach(var pi
in device_list) {
518 grid[row, ++col] =
"%d".printf(row - 1);
519 grid[row, ++col] =
">";
520 grid[row, ++col] =
"%s".printf(pi.device_name_with_parent);
522 grid[row, ++col] =
"%s".printf((pi.size_bytes > 0) ?
format_file_size(pi.size_bytes):
"?? GB");
523 grid[row, ++col] =
"%s".printf(pi.fstype);
524 grid[row, ++col] =
"%s".printf(pi.label);
535 var grub_device_list =
new Gee.ArrayList<
Device>();
537 if (dev.type ==
"disk"){
538 grub_device_list.add(dev);
540 else if (dev.type ==
"part"){
541 if (dev.has_linux_filesystem()){
542 grub_device_list.add(dev);
550 string[,] grid =
new string[grub_device_list.size+1,4];
551 bool[] right_align = {
false,
false,
false,
false };
555 grid[row, ++col] =
_(
"Num");
556 grid[row, ++col] =
"";
557 grid[row, ++col] =
_(
"Device");
558 grid[row, ++col] =
_(
"Description");
562 foreach(
Device pi
in grub_device_list) {
564 grid[row, ++col] =
"%d".printf(row - 1);
565 grid[row, ++col] =
">";
566 grid[row, ++col] =
"%s".printf(pi.short_name_with_alias);
568 if (pi.type ==
"disk"){
569 desc =
"%s".printf(((pi.vendor.length > 0)||(pi.model.length > 0)) ? (pi.vendor +
" " + pi.model +
" [MBR]") :
"");
572 desc =
"%5s, ".printf(pi.fstype);
573 desc +=
"%10s".printf((pi.size_bytes > 0) ?
"%s GB".printf(pi.size) :
"?? GB");
574 desc +=
"%s".printf((pi.label.length > 0) ?
", " + pi.label :
"");
576 grid[row, ++col] =
"%s".printf(desc);
582 return grub_device_list;
585 private void print_grid(
string[,] grid,
bool[] right_align,
bool has_header =
true){
586 int[] col_width =
new int[grid.length[1]];
588 for(
int col=0; col<grid.length[1]; col++){
589 for(
int row=0; row<grid.length[0]; row++){
590 if (grid[row,col].length > col_width[col]){
591 col_width[col] = grid[row,col].length;
596 for(
int row=0; row<grid.length[0]; row++){
597 for(
int col=0; col<grid.length[1]; col++){
598 string fmt =
"%" + (right_align[col] ?
"+" :
"-") + col_width[col].to_string() +
"s ";
599 stdout.printf(fmt.printf(grid[row,col]));
603 if (has_header && (row == 0)){
604 stdout.printf(
string.nfill(78,
'-'));
626 stdout.printf(
"\n\n");
627 log_msg(
string.nfill(78,
'*'));
628 stdout.printf(
_(
"To restore with default options, press the ENTER key for all prompts!") +
"\n");
629 log_msg(
string.nfill(78,
'*'));
630 stdout.printf(
_(
"\nPress ENTER to continue..."));
660 var list =
new Gee.ArrayList<
Device>();
662 if (pi.has_linux_filesystem()){
676 string status_message =
_(
"Snapshot device not selected");
680 string status_message =
_(
"Snapshot device not available");
681 string status_details =
_(
"Device not found") +
": UUID='%s'".printf(
App.
backup_uuid);
690 log_msg(
_(
"Select backup device") +
":\n");
698 if (attempts > 3) {
break; }
700 _(
"Enter device name or number (a=Abort)") +
": ");
706 log_error(
_(
"Selected snapshot device is not a system disk"));
707 log_error(
_(
"Select BTRFS system disk with root subvolume (@)"));
715 log_error(
_(
"Failed to get input from user in 3 attempts"));
731 log_debug(
"AppConsole: select_snapshot()");
755 if (selected_snapshot ==
null){
764 log_error(
"No snapshots selected. Use --snapshot to specify snapshots");
770 log_msg(
_(
"Select snapshot") +
":\n");
775 while (selected_snapshot ==
null){
777 if (attempts > 3) {
break; }
778 stdout.printf(
_(
"Enter snapshot number (a=Abort, p=Previous, n=Next)") +
": ");
784 if (selected_snapshot ==
null){
785 log_error(
_(
"Failed to get input from user in 3 attempts"));
791 return selected_snapshot;
821 if (entry.device ==
null){
845 foreach(
string symlink
in pi.
symlinks){
868 string default_device =
"";
894 log_error(
"Failed to get device by name %s".printf(default_device));
902 log_msg(
_(
"Select '%s' device (default = %s)").printf(
910 if (attempts > 3) {
break; }
913 _(
"[ENTER = Default (%s), r = Root device, a = Abort]").printf(default_device) +
"\n\n");
916 _(
"Enter device name or number")
925 log_error(
_(
"Failed to get input from user in 3 attempts"));
937 log_msg(
string.nfill(78,
'*'));
946 log_msg(
_(
"'%s' will be on '%s'").printf(
951 log_msg(
string.nfill(78,
'*'));
961 App.reinstall_grub2 =
false;
962 App.grub_device =
"";
966 log_debug(
"Grub device is specified as command argument");
972 foreach(
Device dev
in device_list) {
977 App.grub_device = dev.
device;
982 if (dev.
type ==
"part"){
983 foreach(
string symlink
in dev.
symlinks){
985 App.grub_device = dev.
device;
1004 App.reinstall_grub2 =
true;
1011 App.reinstall_grub2 = grub_reinstall_default;
1015 App.reinstall_grub2 =
true;
1021 if (attempts > 3) {
break; }
1022 stdout.printf(
_(
"Re-install GRUB2 bootloader?") + (grub_reinstall_default ?
" (recommended)" :
"") +
" (y/n): ");
1028 log_error(
_(
"Failed to get input from user in 3 attempts"));
1039 log_msg(
_(
"Select GRUB device") +
":\n");
1048 log_error(
"Failed to get grub device by name %s".printf(grub_device_default));
1056 if (attempts > 3) {
break; }
1058 if (grub_device_default.length > 0){
1060 _(
"[ENTER = Default (%s), a = Abort]").printf(grub_device_default) +
"\n\n");
1063 stdout.printf(
_(
"Enter device name or number (a=Abort)") +
": ");
1066 var list =
new Gee.ArrayList<
Device>();
1068 if (pi.has_linux_filesystem()){
1074 if (dev !=
null) {
break; }
1079 log_error(
_(
"Failed to get input from user in 3 attempts"));
1085 if (dev !=
null) { App.grub_device = dev.
device; }
1092 log_msg(
string.nfill(78,
'*'));
1094 log_msg(
string.nfill(78,
'*'));
1097 log_msg(
string.nfill(78,
'*'));
1098 log_msg(
_(
"GRUB will NOT be reinstalled"));
1099 log_msg(
string.nfill(78,
'*'));
1107 string msg_devices =
"";
1108 string msg_reboot =
"";
1109 string msg_disclaimer =
"";
1112 false, out msg_devices, out msg_reboot,
1113 out msg_disclaimer);
1118 if (attempts > 3) {
break; }
1119 stdout.printf(
_(
"Continue with restore? (y/n): "));
1125 log_error(
_(
"Failed to get input from user in 3 attempts"));
1135 counter.exit_on_timeout();
1136 string? line = stdin.read_line();
1139 line = (line !=
null) ? line.strip() :
"";
1141 Device selected_device =
null;
1143 if (line.down() ==
"a"){
1147 else if ((line ==
null)||(line.length == 0)||(line.down() ==
"c")||(line.down() ==
"d")){
1148 if (device_default.length > 0){
1155 else if (line.contains(
"/")){
1157 if (selected_device ==
null){
1163 if (selected_device ==
null){
1168 return selected_device;
1173 counter.exit_on_timeout();
1174 string? line = stdin.read_line();
1177 line = (line !=
null) ? line.strip() :
"";
1179 Device selected_device =
null;
1181 if ((line ==
null)||(line.length == 0)||(line.down() ==
"c")||(line.down() ==
"d")){
1190 else if (line.down() ==
"a"){
1194 else if ((line.down() ==
"n")||(line.down() ==
"r")){
1197 else if (line.contains(
"/")){
1199 if (selected_device ==
null){
1205 if (selected_device ==
null){
1210 return selected_device;
1215 if (int64.try_parse(index_string, out index)){
1217 foreach(
Device pi
in device_list) {
1229 counter.exit_on_timeout();
1230 string? line = stdin.read_line();
1233 line = (line !=
null) ? line.strip() :
"";
1237 if (line.down() ==
"a"){
1241 else if (line.down() ==
"p"){
1250 else if (line.down() ==
"n"){
1258 else if (line.contains(
"_")||line.contains(
"-")){
1262 else if ((line ==
null)||(line.length == 0)){
1267 if (int64.try_parse(line, out index)){
1280 return selected_snapshot;
1285 counter.exit_on_timeout();
1286 string? line = stdin.read_line();
1289 line = (line !=
null) ? line.strip() : line;
1291 if ((line ==
null)||(line.length == 0)){
1292 App.reinstall_grub2 = reinstall_default;
1293 App.cmd_skip_grub = !reinstall_default;
1296 else if (line.down() ==
"a"){
1301 else if (line.down() ==
"y"){
1302 App.cmd_skip_grub =
false;
1303 App.reinstall_grub2 =
true;
1306 else if (line.down() ==
"n"){
1307 App.cmd_skip_grub =
true;
1308 App.reinstall_grub2 =
false;
1311 else if ((line ==
null)||(line.length == 0)){
1323 counter.exit_on_timeout();
1325 string? line = stdin.read_line();
1328 line = (line !=
null) ? line.strip() :
"";
1330 if ((line.down() ==
"a")||(line.down() ==
"n")){
1335 else if ((line ==
null)||(line.length == 0)){
1339 else if (line.down() ==
"y"){
1340 App.cmd_confirm =
true;
1343 else if ((line ==
null)||(line.length == 0)){
const string GETTEXT_PACKAGE
const string AppShortName
const string AppAuthorEmail
bool read_stdin_grub_install(bool reinstall_default)
Device? read_stdin_device(Gee.ArrayList< Device > device_list, string device_default)
Device? read_stdin_device_mounts(Gee.ArrayList< Device > device_list, MountEntry mnt)
static void check_if_admin()
int snapshot_list_start_index
void select_snapshot_device(bool prompt_if_empty)
static int main(string[] args)
void list_devices(Gee.ArrayList< Device > device_list)
void print_grid(string[,] grid, bool[] right_align, bool has_header=true)
void list_snapshots(bool paginate, int page_size=20)
static void parse_arguments(string[] args)
static string version_message()
static string help_message()
Snapshot read_stdin_snapshot()
bool delete_all_snapshots()
void select_snapshot_for_deletion()
void select_grub_device()
Snapshot? select_snapshot()
Device? get_device_from_index(Gee.ArrayList< Device > device_list, string index_string)
void select_snapshot_for_restore()
bool create_snapshot(bool ondemand)
Gee.ArrayList< Device > list_all_devices()
Gee.ArrayList< Device > list_grub_devices(bool print_to_console=true)
bool read_stdin_restore_confirm()
bool has_linux_filesystem()
string short_name_with_alias
Gee.ArrayList< string > symlinks
static Gee.ArrayList< Device > get_block_devices_using_lsblk(string dev_name="")
static ? Device get_device_by_name(string file_name)
Snapshot snapshot_to_restore
Main(string[] args, bool gui_mode)
Gee.ArrayList< Device > partitions
Gee.ArrayList< MountEntry > mount_list
Snapshot snapshot_to_delete
void get_restore_messages(bool formatted, out string msg_devices, out string msg_reboot, out string msg_disclaimer)
void exit_app(int exit_code=0)
bool restore_snapshot(Gtk.Window? parent_win)
bool mount_target_devices(Gtk.Window? parent_win=null)
bool create_snapshot(bool is_ondemand, Gtk.Window? parent_win)
bool check_device_for_backup(Device dev, bool unlock)
Gee.ArrayList< Snapshot?> snapshots
SnapshotRepo. from_device(Device dev, Gtk.Window? parent_win, bool btrfs_repo)
string format_file_size(uint64 size, bool binary_units=false, string unit="", bool show_units=true, int decimals=1)
void log_debug(string message)
void log_error(string message, bool highlight=false, bool is_warning=false)
void log_msg(string message, bool highlight=false)