backup_paradise 1.2.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of backup_paradise might be problematic. Click here for more details.

Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +375 -0
  3. data/backup_paradise.gemspec +50 -0
  4. data/bin/backup_for_ingrid +10 -0
  5. data/bin/backup_paradise +7 -0
  6. data/bin/windows_backup_paradise +9 -0
  7. data/doc/README.gen +347 -0
  8. data/doc/TODO.md +130 -0
  9. data/lib/backup_paradise/actions/README.md +2 -0
  10. data/lib/backup_paradise/actions/backup.rb +62 -0
  11. data/lib/backup_paradise/base/base.rb +493 -0
  12. data/lib/backup_paradise/base/colours.rb +137 -0
  13. data/lib/backup_paradise/base/namespace.rb +16 -0
  14. data/lib/backup_paradise/base/tab.rb +47 -0
  15. data/lib/backup_paradise/colours/colours.rb +88 -0
  16. data/lib/backup_paradise/constants/constants.rb +162 -0
  17. data/lib/backup_paradise/gui/glimmer/libui/backup_for_ingrid/backup_for_ingrid.rb +87 -0
  18. data/lib/backup_paradise/gui/gtk2/OLD_backup.rb +222 -0
  19. data/lib/backup_paradise/gui/gtk3/simple_backup_widget/create.rb +64 -0
  20. data/lib/backup_paradise/gui/gtk3/simple_backup_widget/misc.rb +34 -0
  21. data/lib/backup_paradise/gui/gtk3/simple_backup_widget/simple_backup_widget.rb +167 -0
  22. data/lib/backup_paradise/gui/libui/backup_for_ingrid/backup_for_ingrid.rb +99 -0
  23. data/lib/backup_paradise/gui/libui/simple_backup_widget/simple_backup_widget.rb +119 -0
  24. data/lib/backup_paradise/gui/shared_code/simple_backup_widget/simple_backup_widget_module.rb +595 -0
  25. data/lib/backup_paradise/gui/tk/backup.rb +108 -0
  26. data/lib/backup_paradise/images/BACKUP_IMAGE.png +0 -0
  27. data/lib/backup_paradise/images/right_arrow.png +0 -0
  28. data/lib/backup_paradise/project/project.rb +40 -0
  29. data/lib/backup_paradise/requires/require_the_backup_paradise_project.rb +18 -0
  30. data/lib/backup_paradise/requires/require_yaml.rb +7 -0
  31. data/lib/backup_paradise/tab/tab.rb +87 -0
  32. data/lib/backup_paradise/toplevel_methods/cliner.rb +16 -0
  33. data/lib/backup_paradise/toplevel_methods/config.rb +77 -0
  34. data/lib/backup_paradise/toplevel_methods/create_and_remove.rb +63 -0
  35. data/lib/backup_paradise/toplevel_methods/e.rb +16 -0
  36. data/lib/backup_paradise/toplevel_methods/esystem.rb +19 -0
  37. data/lib/backup_paradise/toplevel_methods/files_and_directories.rb +181 -0
  38. data/lib/backup_paradise/toplevel_methods/help.rb +93 -0
  39. data/lib/backup_paradise/toplevel_methods/misc.rb +153 -0
  40. data/lib/backup_paradise/toplevel_methods/mountpoint.rb +185 -0
  41. data/lib/backup_paradise/toplevel_methods/opnn.rb +25 -0
  42. data/lib/backup_paradise/utility_scripts/backup/backup.rb +1389 -0
  43. data/lib/backup_paradise/utility_scripts/backup/constants.rb +34 -0
  44. data/lib/backup_paradise/utility_scripts/backup/initialize.rb +71 -0
  45. data/lib/backup_paradise/utility_scripts/backup/menu.rb +360 -0
  46. data/lib/backup_paradise/utility_scripts/backup/run.rb +20 -0
  47. data/lib/backup_paradise/version/version.rb +19 -0
  48. data/lib/backup_paradise/windows/README.md +1 -0
  49. data/lib/backup_paradise/windows/windows.rb +101 -0
  50. data/lib/backup_paradise/www/backup.cgi +63 -0
  51. data/lib/backup_paradise/yaml/config.yml +78 -0
  52. data/lib/backup_paradise.rb +5 -0
  53. data/test/testing_toplevel_functionality.rb +11 -0
  54. metadata +198 -0
@@ -0,0 +1,1389 @@
1
+ #!/usr/bin/ruby -w
2
+ # Encoding: UTF-8
3
+ # frozen_string_literal: true
4
+ # =========================================================================== #
5
+ # === BackupParadise::Backup
6
+ #
7
+ # This is the main class that will be used for making a backup, usually
8
+ # onto an external USB device, but optionally also to another (second)
9
+ # harddisc (HDD).
10
+ #
11
+ # Note that this class will still store some information on the toplevel
12
+ # namespace where we will backup to.
13
+ #
14
+ # For a listing of specific options have a look at the file "help.rb",
15
+ # or do "rbackup help" from the commandline.
16
+ #
17
+ # Usage example:
18
+ #
19
+ # BackupParadise::Backup.new(ARGV)
20
+ #
21
+ # =========================================================================== #
22
+ # require 'backup_paradise/utility_scripts/backup/backup.rb'
23
+ # BackupParadise::Backup.new(hash)
24
+ # =========================================================================== #
25
+ require 'backup_paradise/base/base.rb'
26
+
27
+ module BackupParadise
28
+
29
+ class Backup < ::BackupParadise::Base # === BackupParadise::Backup
30
+
31
+ require 'backup_paradise/utility_scripts/backup/constants.rb'
32
+ require 'backup_paradise/utility_scripts/backup/initialize.rb'
33
+ require 'backup_paradise/utility_scripts/backup/menu.rb'
34
+ require 'backup_paradise/utility_scripts/backup/run.rb'
35
+ require 'backup_paradise/toplevel_methods/config.rb'
36
+ require 'backup_paradise/toplevel_methods/help.rb'
37
+ require 'backup_paradise/toplevel_methods/mountpoint.rb'
38
+
39
+ # ========================================================================= #
40
+ # === reset (reset tag)
41
+ # ========================================================================= #
42
+ def reset
43
+ super()
44
+ # ======================================================================= #
45
+ # === @mountpoint
46
+ # ======================================================================= #
47
+ @mountpoint = nil
48
+ # ======================================================================= #
49
+ # === @internal_hash
50
+ # ======================================================================= #
51
+ @internal_hash = {}
52
+ # ======================================================================= #
53
+ # === :simplify_the_directory_layout
54
+ #
55
+ # If the following variable is set to true then this means that the
56
+ # target directory will be slightly simplified. There will not be
57
+ # any ':' characters; these will become '_' characters.
58
+ #
59
+ # This was necessary because windows NTFS seems to struggle with
60
+ # ':' characters.
61
+ # ======================================================================= #
62
+ @internal_hash[:simplify_the_directory_layout] = true
63
+ # ======================================================================= #
64
+ # === :perform_backup
65
+ #
66
+ # The next variable determines whether we will perform a backup
67
+ # job or not. By default this will be false - it will be set
68
+ # to true through the menu() interface, via its else clause.
69
+ # ======================================================================= #
70
+ @internal_hash[:perform_backup] = true
71
+ # ======================================================================= #
72
+ # === :are_we_on_windows
73
+ #
74
+ # This can be used to overrule any other windows-specific checks.
75
+ # ======================================================================= #
76
+ @internal_hash[:are_we_on_windows] = nil
77
+ # ======================================================================= #
78
+ # === :date
79
+ # ======================================================================= #
80
+ @internal_hash[:date] = dd_mm_yyyy
81
+ # ======================================================================= #
82
+ # === :show_popup_notification
83
+ # ======================================================================= #
84
+ @internal_hash[:show_popup_notification] =
85
+ BackupParadise.show_popup_notification?
86
+ # ======================================================================= #
87
+ # === :last_backup_directory
88
+ # ======================================================================= #
89
+ @internal_hash[:last_backup_directory] = nil
90
+ # ======================================================================= #
91
+ # === :array_store_times
92
+ #
93
+ # The next Array will keep track of how long it took to backup
94
+ # the data.
95
+ # ======================================================================= #
96
+ @internal_hash[:array_store_times] = []
97
+ # ======================================================================= #
98
+ # === :use_this_as_timestamp
99
+ #
100
+ # The next variable will keep the timestamp as-is.
101
+ #
102
+ # This may include ':' characters, which Windows NTFS does not seem
103
+ # to like. Thus, since as of September 2020, a slightly simpler
104
+ # variant is used here.
105
+ # ======================================================================= #
106
+ determine_the_current_timestamp
107
+ # ======================================================================= #
108
+ # === :backup_these_directories
109
+ #
110
+ # Determine the directories that we wish to back up.
111
+ # ======================================================================= #
112
+ @internal_hash[:backup_these_directories] = all_important_directories?
113
+ end
114
+
115
+ # ========================================================================= #
116
+ # === start_the_gtk3_bindings
117
+ #
118
+ # To invoke this method, try:
119
+ #
120
+ # rbackup --gui
121
+ # rbackup --gtk
122
+ #
123
+ # ========================================================================= #
124
+ def start_the_gtk3_bindings
125
+ require 'backup_paradise/gui/gtk3/simple_backup_widget/simple_backup_widget.rb'
126
+ BackupParadise::GUI::Gtk::SimpleBackupWidget.run
127
+ end
128
+
129
+ # ========================================================================= #
130
+ # === default_tab_title
131
+ # ========================================================================= #
132
+ def default_tab_title
133
+ tab_title('Backup is complete!')
134
+ end
135
+
136
+ # ========================================================================= #
137
+ # === backup_which_directories?
138
+ # ========================================================================= #
139
+ def backup_which_directories?
140
+ @internal_hash[:backup_these_directories]
141
+ end
142
+
143
+ # ========================================================================= #
144
+ # === return_this_absolute_directory
145
+ # ========================================================================= #
146
+ def return_this_absolute_directory(i)
147
+ i = case i
148
+ # ======================================================================= #
149
+ # === DATA_DIRECTORY
150
+ # ======================================================================= #
151
+ when 'DATA_DIRECTORY'
152
+ DATA_DIRECTORY
153
+ # ======================================================================= #
154
+ # === SRC_DIRECTORY
155
+ # ======================================================================= #
156
+ when 'SRC_DIRECTORY',
157
+ 'SOURCE_DIRECTORY'
158
+ SOURCE_DIRECTORY
159
+ # ======================================================================= #
160
+ # === STUDIUM_DIRECTORY
161
+ # ======================================================================= #
162
+ when 'STUDIUM_DIRECTORY'
163
+ STUDIUM_DIRECTORY
164
+ else
165
+ i
166
+ end
167
+ i
168
+ end
169
+
170
+ # ========================================================================= #
171
+ # === return_the_songs_directory
172
+ # ========================================================================= #
173
+ def return_the_songs_directory
174
+ DIRECTORY_CONTAINING_ALL_SONGS
175
+ end
176
+
177
+ # ========================================================================= #
178
+ # === show_help
179
+ # ========================================================================= #
180
+ def show_help
181
+ BackupParadise.show_help
182
+ end; alias display_help show_help # === display_help
183
+
184
+ # ========================================================================= #
185
+ # === simplify_the_directory_layout?
186
+ # ========================================================================= #
187
+ def simplify_the_directory_layout?
188
+ @internal_hash[:simplify_the_directory_layout]
189
+ end
190
+
191
+ # ========================================================================= #
192
+ # === set_commandline_arguments
193
+ # ========================================================================= #
194
+ def set_commandline_arguments(i = ARGV)
195
+ @commandline_arguments = i
196
+ end
197
+
198
+ # ========================================================================= #
199
+ # === commandline_arguments?
200
+ # ========================================================================= #
201
+ def commandline_arguments?
202
+ @commandline_arguments
203
+ end
204
+
205
+ # ========================================================================= #
206
+ # === first_argument?
207
+ # ========================================================================= #
208
+ def first_argument?
209
+ @commandline_arguments.first
210
+ end; alias first? first_argument? # === first?
211
+
212
+ # ========================================================================= #
213
+ # === is_target_harddisc_a_ntfs_system?
214
+ #
215
+ # This method will return a boolean - true or false.
216
+ #
217
+ # It will return true if the target harddisc is a NTFS system, and
218
+ # false otherwise.
219
+ # ========================================================================= #
220
+ def is_target_harddisc_a_ntfs_system?
221
+ return_value = false
222
+ _ = '/Mount'
223
+ if mount_target?.include? _
224
+ # ===================================================================== #
225
+ # Next, determine what is mounted.
226
+ # ===================================================================== #
227
+ these_may_be_mounted = `df -T -ah`.split(N).select {|line| line.include? _ }
228
+ if these_may_be_mounted and these_may_be_mounted.first and
229
+ these_may_be_mounted.first.include?('fuseblk') # This may mean a NTFS system.
230
+ return_value = true
231
+ end
232
+ end
233
+ return return_value
234
+ end
235
+
236
+ # ========================================================================= #
237
+ # === determine_whether_the_target_harddisc_is_a_ntfs_system
238
+ # ========================================================================= #
239
+ def determine_whether_the_target_harddisc_is_a_ntfs_system
240
+ if is_target_harddisc_a_ntfs_system?
241
+ do_use_simplified_directory
242
+ end
243
+ end
244
+
245
+ # ========================================================================= #
246
+ # === return_the_assumed_mountpoint_from_this_input
247
+ # ========================================================================= #
248
+ def return_the_assumed_mountpoint_from_this_input(i)
249
+ BackupParadise.return_the_assumed_mountpoint_from_this_input(i)
250
+ end
251
+
252
+ # ========================================================================= #
253
+ # === set_mountpoint
254
+ # ========================================================================= #
255
+ def set_mountpoint(i)
256
+ i = i.dup
257
+ if BackupParadise.is_this_a_shortcut?(i)
258
+ i = return_the_assumed_mountpoint_from_this_input(i)
259
+ end
260
+ i = i.dup if i.frozen?
261
+ i << '/' unless i.end_with? '/'
262
+ @mountpoint = i
263
+ # ======================================================================= #
264
+ # Sync it back to the toplevel as well:
265
+ # ======================================================================= #
266
+ determine_the_target_mountpoint(@mountpoint)
267
+ determine_where_to_backup_to_exactly
268
+ end; alias set_backup_to_this_target set_mountpoint # === set_backup_to_this_target
269
+ alias set_target_mountpoint set_mountpoint # === set_target_mountpoint
270
+
271
+ # ========================================================================= #
272
+ # === determine_the_current_timestamp
273
+ # ========================================================================= #
274
+ def determine_the_current_timestamp
275
+ if @internal_hash[:simplify_the_directory_layout]
276
+ @internal_hash[:use_this_as_timestamp] =
277
+ "#{date?}-#{current_time.tr(':','_')}"
278
+ else
279
+ @internal_hash[:use_this_as_timestamp] =
280
+ "#{date?}-#{current_time}"
281
+ end
282
+ end
283
+
284
+ # ========================================================================= #
285
+ # === date?
286
+ # ========================================================================= #
287
+ def date?
288
+ @internal_hash[:date]
289
+ end
290
+
291
+ # ========================================================================= #
292
+ # === use_this_as_timestamp?
293
+ # ========================================================================= #
294
+ def use_this_as_timestamp?
295
+ @internal_hash[:use_this_as_timestamp]
296
+ end
297
+
298
+ # ========================================================================= #
299
+ # === sanitize_the_commandline_arguments
300
+ # ========================================================================= #
301
+ def sanitize_the_commandline_arguments(
302
+ hash = commandline_arguments?
303
+ )
304
+ copy = hash.dup
305
+ # ======================================================================= #
306
+ # === :commandline_arguments
307
+ # ======================================================================= #
308
+ if hash.is_a?(Hash) and hash.has_key?(:commandline_arguments)
309
+ hash[:commandline_arguments].each {|entry|
310
+ if BackupParadise.is_this_a_shortcut?(entry)
311
+ copy[:backup_to_this_target] =
312
+ BackupParadise.return_the_assumed_mountpoint_from_this_input(entry)
313
+ copy[:commandline_arguments].reject! {|inner_entry|
314
+ inner_entry == entry
315
+ }
316
+ end
317
+ }
318
+ set_commandline_arguments(copy)
319
+ end
320
+ end
321
+
322
+ # ========================================================================= #
323
+ # === can_not_backup_this_directory_as_it_does_not_exist
324
+ # ========================================================================= #
325
+ def can_not_backup_this_directory_as_it_does_not_exist(i)
326
+ opnn; e "Can not backup the directory at #{sdir(i)}"
327
+ opnn; e 'as it does not exist.'
328
+ end
329
+
330
+ # ========================================================================= #
331
+ # === try_to_unmount_the_device
332
+ #
333
+ # This method can be used to automatically unmount the target device
334
+ # again.
335
+ # ========================================================================= #
336
+ def try_to_unmount_the_device
337
+ this_is_the_mounted_device = backup_to_this_directory?
338
+ # ======================================================================= #
339
+ # Change directory to another directory where we can safely
340
+ # umount the directory.
341
+ # ======================================================================= #
342
+ if has_superuser_abilities?
343
+ if BackupParadise.target_mountpoint?.include?('HDD1')
344
+ # =================================================================== #
345
+ # Unmount harddiscs quickly.
346
+ # =================================================================== #
347
+ unmount :hdd # Since as of 17.06.2008 we will unmount the harddisc as well.
348
+ end
349
+ if File.directory? '/Depot/j/'
350
+ cd '/Depot/j/'
351
+ elsif File.directory? '/tmp'
352
+ cd '/tmp'
353
+ elsif File.directory? '/home/Temp'
354
+ cd '/home/Temp'
355
+ else
356
+ cd '/'
357
+ end
358
+ opnn; e "Next unmounting the device at "\
359
+ "`#{sfancy(this_is_the_mounted_device)}`."
360
+ system "umount #{this_is_the_mounted_device}"
361
+ end
362
+ end
363
+
364
+ # ========================================================================= #
365
+ # === consider_renaming_all_old_last_backup_directories
366
+ #
367
+ # This will rename all "last_directory*" directory entries to their
368
+ # corresponding equivalent without the leading 'last_' string.
369
+ # ========================================================================= #
370
+ def consider_renaming_all_old_last_backup_directories
371
+ # ======================================================================= #
372
+ # First grab all possible entries, and check that these are all existing
373
+ # directories. If there is at the least one entry we will continue to
374
+ # operate on it.
375
+ # ======================================================================= #
376
+ all_possible_entries = Dir["#{mount_target?}last_backup-*"].select {|entry|
377
+ File.directory?(entry)
378
+ }
379
+ if all_possible_entries.size > 0
380
+ # ===================================================================== #
381
+ # Batch-rename these directories in that case:
382
+ # ===================================================================== #
383
+ all_possible_entries.each {|this_directory|
384
+ new_name_of_the_new_target_directory = this_directory.sub(/^last_/,'')
385
+ opnn; e "Now renaming `#{sdir(this_directory)}` "\
386
+ "to `#{sdir(new_name_of_the_new_target_directory)}`."
387
+ rename(this_directory, new_name_of_the_new_target_directory)
388
+ }
389
+ end
390
+ end
391
+
392
+ # ========================================================================= #
393
+ # === verbose_create_a_directory_at_this_position
394
+ #
395
+ # The first argument to this method should be the target directory,
396
+ # a path, that is where you wish to create that directory.
397
+ #
398
+ # The second argument to this method, which is optional, denotes the
399
+ # permission that is to be used.
400
+ # ========================================================================= #
401
+ def verbose_create_a_directory_at_this_position(
402
+ this_position, use_this_permission = 0754
403
+ )
404
+ e "Creating a directory at `#{sdir(this_position)}` next."
405
+ mkdir(this_position, use_this_permission)
406
+ end
407
+
408
+ # ========================================================================= #
409
+ # === show_the_logfile
410
+ #
411
+ # To invoke this method from the commandline, try:
412
+ #
413
+ # advanced_backup --logfile?
414
+ #
415
+ # ========================================================================= #
416
+ def show_the_logfile
417
+ _ = FILE_BACKUP_LOG
418
+ if File.exist? _
419
+ opnn; e "Now reading the dataset from the file `#{sfile(_)}`:"
420
+ e
421
+ File.readlines(_).each {|line|
422
+ e orangered(" #{line.chomp}")
423
+ }
424
+ e
425
+ else
426
+ opnn; e no_file_exists_at(_)
427
+ end
428
+ end
429
+
430
+ # ========================================================================= #
431
+ # === show_popup_notification?
432
+ # ========================================================================= #
433
+ def show_popup_notification?
434
+ @internal_hash[:show_popup_notification]
435
+ end
436
+
437
+ # ========================================================================= #
438
+ # === do_show_popup
439
+ # ========================================================================= #
440
+ def do_show_popup
441
+ @internal_hash[:show_popup_notification] = true
442
+ end; alias show_popup do_show_popup # === show_popup
443
+
444
+ # ========================================================================= #
445
+ # === determine_where_to_backup_to_exactly
446
+ #
447
+ # This is automatically called whenever set_mountpoint() is called.
448
+ # ========================================================================= #
449
+ def determine_where_to_backup_to_exactly(
450
+ i = @mountpoint
451
+ )
452
+ backup_to_this_target = i
453
+ @backup_to_this_target = backup_to_this_target.dup
454
+ @backup_to_this_target << "/backup-#{return_current_date_and_time}/"
455
+ @backup_to_this_target = @backup_to_this_target.squeeze('/')
456
+ @backup_to_this_target.tr!(':','_') if simplify_the_directory_layout?
457
+ set_last_backup_directory(@backup_to_this_target)
458
+ end
459
+
460
+ # ========================================================================= #
461
+ # === mountpoint?
462
+ # ========================================================================= #
463
+ def mountpoint?
464
+ @mountpoint
465
+ end; alias mounted_at? mountpoint? # === mounted_at?
466
+ alias target_base_directory? mountpoint? # === target_base_directory?
467
+
468
+ # ========================================================================= #
469
+ # === opnn
470
+ # ========================================================================= #
471
+ def opnn(i = NAMESPACE)
472
+ if i.is_a? String
473
+ i = { namespace: i }
474
+ end
475
+ super(i)
476
+ end
477
+
478
+ # ========================================================================= #
479
+ # === backup_the_audio_directory_then_exit
480
+ #
481
+ # Backup the audio directory, then exit the program.
482
+ # ========================================================================= #
483
+ def backup_the_audio_directory_then_exit
484
+ backup_the_audio_directory
485
+ exit_program
486
+ end
487
+
488
+ # ========================================================================= #
489
+ # === do_use_simplified_directory
490
+ #
491
+ # The old code used to be like this:
492
+ #
493
+ # set_last_backup_directory(mount_point?+'last_backup')
494
+ #
495
+ # ========================================================================= #
496
+ def do_use_simplified_directory
497
+ @internal_hash[:simplify_the_directory_layout] = true
498
+ determine_the_current_timestamp # Must sync it back again.
499
+ end
500
+
501
+ # ========================================================================= #
502
+ # === create_file_keeping_track_of_when_the_last_backup_happened
503
+ #
504
+ # This method will store date-information into a file, when the last
505
+ # backup happened.
506
+ #
507
+ # It is normally found in a place such as:
508
+ #
509
+ # /home/x/data/LAST_BACKUP.md
510
+ #
511
+ # Since Dec 2012, we will also append the date to the name of said file.
512
+ # ========================================================================= #
513
+ def create_file_keeping_track_of_when_the_last_backup_happened(
514
+ use_this_as_timestamp = use_this_as_timestamp?
515
+ )
516
+ base_target = "#{data_directory?}LAST_BACKUP"
517
+ target = "#{base_target}_#{date?}.md" # Append the date here.
518
+ e N+'Storing '+sfile(use_this_as_timestamp)+' into '+
519
+ sfile(target)+' to note'
520
+ e 'down when the last backup was performed.'
521
+ write_what_into(use_this_as_timestamp, target)
522
+ # ======================================================================= #
523
+ # Next, obtain an array with potential similar names. We will have
524
+ # to delete stray entries so as to not clutter our original home
525
+ # directory there.
526
+ # ======================================================================= #
527
+ array_all_last_backup_files = Dir["#{base_target}*"]
528
+ if array_all_last_backup_files.size > 1
529
+ array_all_last_backup_files = array_all_last_backup_files.sort_by {|d|
530
+ d.split('.').reverse
531
+ } # Keep it sorted.
532
+ e N+'We found more than one entry in '+
533
+ sdir(File.dirname(base_target)+'/')+', thus removing'
534
+ e 'the extra-entries next.'
535
+ array_all_last_backup_files[0..-2].each {|my_file|
536
+ e "Removing `#{sfile(my_file)}` next."
537
+ remove_file(my_file)
538
+ }
539
+ end
540
+ end; alias store_when_was_the_last_backup create_file_keeping_track_of_when_the_last_backup_happened # === store_when_was_the_last_backup
541
+
542
+ # ========================================================================= #
543
+ # === show_available_mountpoints
544
+ #
545
+ # This helper-method can show the available mountpoints, at the least
546
+ # on a linux system.
547
+ #
548
+ # To invoke this, try:
549
+ #
550
+ # rbackup --mountpoints
551
+ #
552
+ # ========================================================================= #
553
+ def show_available_mountpoints
554
+ begin
555
+ require 'mountpoints'
556
+ rescue LoadError; end
557
+ if Mountpoints.is_any_mountpoint_available?
558
+ opnn; e 'The available (and mounted) USB devices are:'
559
+ e
560
+ Mountpoints[].each {|this_usb_device|
561
+ e " #{sfancy(this_usb_device)}"
562
+ }
563
+ e
564
+ else
565
+ opnn; e 'No external USB devices appear to be mounted.'
566
+ end
567
+ end
568
+
569
+ # ========================================================================= #
570
+ # === show_available_mountpoints_then_exit
571
+ # ========================================================================= #
572
+ def show_available_mountpoints_then_exit
573
+ show_available_mountpoints
574
+ exit_program
575
+ end
576
+
577
+ # ========================================================================= #
578
+ # === report_time_difference
579
+ #
580
+ # This method will report to the user how long it took this class
581
+ # to backup the system, e. g. 30 minutes or something like that.
582
+ # ========================================================================= #
583
+ def report_time_difference
584
+ if @internal_hash[:array_store_times].size > 1
585
+ start_time = @internal_hash[:array_store_times][-2]
586
+ end_time = @internal_hash[:array_store_times].last
587
+ time_difference = end_time - start_time # This will result in a Float.
588
+ if time_difference.respond_to? :round
589
+ time_difference = time_difference.round(3)
590
+ end
591
+ n_minutes = (time_difference.to_f / 60.0).round(2).to_s
592
+ opnn; e "#{tomato('Time required')} for this backup-operation: "\
593
+ "#{sfancy(time_difference.to_s)}"\
594
+ " seconds (#{royalblue(n_minutes)} minutes)."
595
+ consider_creating_a_log_file(time_difference)
596
+ end
597
+ end
598
+
599
+ # ========================================================================= #
600
+ # === show_welcome_message
601
+ # ========================================================================= #
602
+ def show_welcome_message
603
+ print_rev
604
+ cliner
605
+ e "Welcome to the #{lightcoral('BackupParadise')} #{rev}project!"
606
+ report_todays_date
607
+ cliner
608
+ e "Starting to backup into the target at `#{sdir(main_target?)}`."
609
+ cliner
610
+ end
611
+
612
+ # ========================================================================= #
613
+ # === consider_removing_all_but_one_log_file
614
+ # ========================================================================= #
615
+ def consider_removing_all_but_one_log_file
616
+ log_files = return_all_log_files
617
+ if log_files.size > 1
618
+ opnn; e 'More than one log file has been found. We will delete'
619
+ opnn; e 'all but one next.'
620
+ log_files.pop
621
+ delete_files(log_files, :be_verbose)
622
+ end
623
+ end
624
+
625
+ # ========================================================================= #
626
+ # === return_all_log_files
627
+ # ========================================================================= #
628
+ def return_all_log_files(
629
+ from_where = mounted_at?
630
+ )
631
+ Dir["#{from_where}log_last_backup_*"].select {|entry|
632
+ File.file? entry # We only want files.
633
+ }
634
+ end
635
+
636
+ # ========================================================================= #
637
+ # === popup_notification
638
+ #
639
+ # Use this method to send a graphical popup to the user. Only invoke
640
+ # it when we set @show_popup_notification to true.
641
+ # ========================================================================= #
642
+ def popup_notification(
643
+ what = :backup_complete
644
+ )
645
+ case what
646
+ when :backup_complete
647
+ esystem 'xmessage -center Backup finished!'
648
+ end
649
+ end
650
+
651
+ # ========================================================================= #
652
+ # === determine_the_starting_time
653
+ # ========================================================================= #
654
+ def determine_the_starting_time
655
+ @internal_hash[:array_store_times] << Time.now
656
+ end; alias determine_starting_time determine_the_starting_time # === determine_starting_time
657
+
658
+ # ========================================================================= #
659
+ # === determine_end_time
660
+ # ========================================================================= #
661
+ def determine_end_time
662
+ @internal_hash[:array_store_times] << Time.now
663
+ end
664
+
665
+ # ========================================================================= #
666
+ # === backup_the_programs_directory
667
+ #
668
+ # This method can be used to backup the /Programs directory.
669
+ # ========================================================================= #
670
+ def backup_the_programs_directory(
671
+ target_directory = PROGRAMS_DIRECTORY
672
+ )
673
+ if File.directory? target_directory
674
+ cliner
675
+ e 'Now backing up the Programs/ directory from '+
676
+ sfancy(target_directory)+' into '+
677
+ sfile(main_target?+File.basename(target_directory))
678
+ cliner
679
+ report_total_size_of(target_directory)
680
+ if target_hdd_does_not_have_enough_space_left?
681
+ opnn; e "We have to skip backing up #{sdir(target_directory)}"
682
+ opnn; e 'as it does not have enough space left (Threshold: '+
683
+ MINIMAL_FILESIZE+' bytes)'
684
+ else
685
+ backup_this_directory_if_it_exists(
686
+ target_directory, :default, :do_not_continue
687
+ )
688
+ end
689
+ else
690
+ can_not_backup_this_directory_as_it_does_not_exist(target_directory)
691
+ end
692
+ end
693
+
694
+ # ========================================================================= #
695
+ # === return_default_target_directory_from_this_input
696
+ # ========================================================================= #
697
+ def return_default_target_directory_from_this_input(i)
698
+ rds(
699
+ main_target?+
700
+ File.basename(i)+
701
+ '/'
702
+ )
703
+ end
704
+
705
+ # ========================================================================= #
706
+ # === determine_the_target_mountpoint
707
+ #
708
+ # This method will determine the target base mountpoint that
709
+ # is to be used.
710
+ # ========================================================================= #
711
+ def determine_the_target_mountpoint(
712
+ i = '/Mount/USB1/'
713
+ )
714
+ BackupParadise.target_mountpoint = i
715
+ end
716
+
717
+ # ========================================================================= #
718
+ # === create_the_directory_containing_the_last_backup
719
+ # ========================================================================= #
720
+ def create_the_directory_containing_the_last_backup
721
+ target = "#{main_target?}backup-#{use_this_as_timestamp?}"
722
+ # ======================================================================= #
723
+ # Next, create the last_backup directory:
724
+ # ======================================================================= #
725
+ begin
726
+ mkdir(target, 0755)
727
+ rescue Errno::EINVAL
728
+ target.tr!(':','_')
729
+ mkdir(target, 0755)
730
+ end
731
+ @internal_hash[:last_backup_directory] = rds(target+'/')
732
+ opnn; e "Creating a directory at "\
733
+ "`#{sdir(@internal_hash[:last_backup_directory])}`."
734
+ end
735
+
736
+ # ========================================================================= #
737
+ # === create_a_symlink_pointing_to_that_directory_that_was_just_created
738
+ #
739
+ # This method will create a symlink to the last_backup/ directory.
740
+ #
741
+ # This is synonymous to the following ruby code:
742
+ #
743
+ # File.symlink('last_backup-13.08.2018-22:12:26/','last_backup')
744
+ #
745
+ # ========================================================================= #
746
+ def create_a_symlink_pointing_to_that_directory_that_was_just_created
747
+ cd_to_the_mounted_device
748
+ create_a_symlink_here = "#{backup_to_this_directory?}last_backup"
749
+ from_this_target = File.basename(@internal_hash[:last_backup_directory])
750
+ from_this_target.chop! if from_this_target.end_with? '/'
751
+ # ========================================================================= #
752
+ # First, delete the old symlink if it exists. The method delete_symlink()
753
+ # will do that check for us.
754
+ # ========================================================================= #
755
+ delete_symlink(create_a_symlink_here)
756
+ # ========================================================================= #
757
+ # The next action can fail on e. g. vfat filesystems, with the error
758
+ # being Errno::EPERM. Hence we must rescue here.
759
+ # ========================================================================= #
760
+ begin
761
+ do_symlink(from_this_target, create_a_symlink_here)
762
+ if File.exist? create_a_symlink_here
763
+ points_towards = File.readlink(create_a_symlink_here)
764
+ opnn; e 'Next setting a symlink at `'+sfancy(create_a_symlink_here)+
765
+ '` pointing'
766
+ opnn; e "towards `#{sfile(points_towards)}`."
767
+ else
768
+ opnn; e no_file_exists_at(create_a_symlink_here)
769
+ end
770
+ rescue Errno::EPERM
771
+ end
772
+ end; alias create_symlink_to_last_backup_directory create_a_symlink_pointing_to_that_directory_that_was_just_created # === create_symlink_to_last_backup_directory
773
+
774
+ # ========================================================================= #
775
+ # === do_sync
776
+ #
777
+ # The do_sync() method, also aliased towards synchronize(), will sync
778
+ # the data onto USB items, by using the sync command.
779
+ # ========================================================================= #
780
+ def do_sync
781
+ opnn; e 'Starting to '+steelblue('sync data')+' ...'
782
+ system 'sync' # Be silent since May 2014.
783
+ opnn; e "> Syncing finished. (At: "\
784
+ "#{rosybrown(return_current_date_and_time.to_s)})"
785
+ end; alias synchronize do_sync # === synchronize
786
+
787
+ # ========================================================================= #
788
+ # === backup_this_directory
789
+ #
790
+ # This method will quickly backup a given directory.
791
+ #
792
+ # The second argument will be the target-directory onto which
793
+ # we will copy our files.
794
+ # ========================================================================= #
795
+ def backup_this_directory(
796
+ i,
797
+ copy_to_this_target_directory = nil,
798
+ shall_we_sync = true
799
+ )
800
+ case shall_we_sync
801
+ when :do_not_sync
802
+ shall_we_sync = false
803
+ end
804
+ case copy_to_this_target_directory
805
+ when nil,
806
+ :default
807
+ copy_to_this_target_directory = return_default_target_directory_from_this_input(i)
808
+ end
809
+ unless File.directory? copy_to_this_target_directory
810
+ mkdir(copy_to_this_target_directory)
811
+ end
812
+ cd copy_to_this_target_directory
813
+ opnn; e 'Now copying '+sdir(i)+
814
+ ' to '+sdir(copy_to_this_target_directory)+'.'
815
+ if File.directory?(i)
816
+ i << '*'
817
+ end
818
+ cpr(
819
+ i, copy_to_this_target_directory
820
+ )
821
+ do_sync if shall_we_sync
822
+ end
823
+
824
+ # ========================================================================= #
825
+ # === backup_the_system_directory
826
+ #
827
+ # This method can be used to backup the "/System/Settings/" directory,
828
+ # as it may be found in GoboLinux.
829
+ # ========================================================================= #
830
+ def backup_the_system_directory(
831
+ target_directory = SYSTEM_SETTINGS_DIRECTORY
832
+ )
833
+ if File.directory? target_directory
834
+ cliner
835
+ e 'Now backing up the system-settings directory from '+
836
+ sfancy(target_directory)+' into '+
837
+ sfile(main_target?+File.basename(target_directory))
838
+ cliner
839
+ report_total_size_of(target_directory)
840
+ cpr(
841
+ target_directory,
842
+ backup_to_this_target?
843
+ )
844
+ else
845
+ can_not_backup_this_directory_as_it_does_not_exist(target_directory)
846
+ end
847
+ end; alias backup_system_directory backup_the_system_directory # === backup_system_directory
848
+
849
+ # ========================================================================= #
850
+ # === backup_to_this_target?
851
+ # ========================================================================= #
852
+ def backup_to_this_target?
853
+ @backup_to_this_target
854
+ end
855
+
856
+ # ========================================================================= #
857
+ # === backup_the_data_directory_then_exit
858
+ #
859
+ # Backup only Data directory.
860
+ # ========================================================================= #
861
+ def backup_the_data_directory_then_exit
862
+ backup_data_directory
863
+ exit_program
864
+ end; alias backup_data_directory_then_exit backup_the_data_directory_then_exit # === backup_data_directory_then_exit
865
+
866
+ # ========================================================================= #
867
+ # === backup_the_data_directory (data tag)
868
+ #
869
+ # This method will backup the DATA directory, e. g. the one that is
870
+ # at "/home/x/DATA/" on my home system.
871
+ # ========================================================================= #
872
+ def backup_the_data_directory(
873
+ target_directory = DATA_DIRECTORY
874
+ )
875
+ if File.directory? target_directory
876
+ mountpoint_target = main_target?+File.basename(target_directory)
877
+ cliner
878
+ e "Now backing up the DATA directory from "\
879
+ "#{sfancy(target_directory)} into "\
880
+ "#{sfile(mountpoint_target)}."
881
+ cliner
882
+ report_total_size_of(target_directory)
883
+ unless File.directory? mountpoint_target
884
+ create_directory(mountpoint_target, 0755)
885
+ end
886
+ mkdir(backup_to_this_target?+File.basename(target_directory))
887
+ cpr(
888
+ target_directory,
889
+ backup_to_this_target?
890
+ )
891
+ else
892
+ can_not_backup_this_directory_as_it_does_not_exist(target_directory)
893
+ end
894
+ end; alias backup_only_data_directory backup_the_data_directory # === backup_only_data_directory
895
+ alias backup_data_directory backup_the_data_directory # === backup_data_directory
896
+
897
+ # ========================================================================= #
898
+ # === ensure_trailing_backslash_for
899
+ # ========================================================================= #
900
+ def ensure_trailing_backslash_for(i)
901
+ i = i.dup
902
+ i << '/' unless i.end_with? '/'
903
+ return i
904
+ end
905
+
906
+ # ========================================================================= #
907
+ # === backup_the_studium_directory
908
+ # ========================================================================= #
909
+ def backup_the_studium_directory(
910
+ target_directory = STUDIUM_DIRECTORY
911
+ )
912
+ target_directory = ensure_trailing_backslash_for(target_directory)
913
+ if File.directory? target_directory
914
+ tab_title('Backing up the '+target_directory+' directory.')
915
+ cliner
916
+ e 'Now backing up the studium/ directory from '+
917
+ sfancy(target_directory)+' into '+
918
+ sfile(main_target?+File.basename(target_directory))
919
+ cliner
920
+ report_total_size_of(target_directory)
921
+ mkdir(backup_to_this_target?+File.basename(target_directory))
922
+ cpr(
923
+ target_directory,
924
+ backup_to_this_target?
925
+ )
926
+ else
927
+ can_not_backup_this_directory_as_it_does_not_exist(target_directory)
928
+ end
929
+ end
930
+
931
+ # ========================================================================= #
932
+ # === backup_the_video_directory
933
+ # ========================================================================= #
934
+ def backup_the_video_directory(
935
+ target_directory = VIDEO_DIRECTORY
936
+ )
937
+ target_directory = ensure_trailing_backslash_for(target_directory)
938
+ if File.directory? target_directory
939
+ cliner
940
+ e 'Now backing up the Video directory from '+
941
+ sfancy(target_directory)+' into '+
942
+ sfile(main_target?+File.basename(target_directory))
943
+ cliner
944
+ tab_title('Backing up the directory '+target_directory+' next.')
945
+ report_total_size_of(target_directory)
946
+ set_backup_to_this_target(target_base_directory?)
947
+ mkdir(
948
+ target_base_directory?+File.basename(target_directory)
949
+ )
950
+ cpr(
951
+ target_directory,
952
+ target_base_directory?
953
+ )
954
+ show_all_is_done_message
955
+ default_tab_title
956
+ else
957
+ can_not_backup_this_directory_as_it_does_not_exist(target_directory)
958
+ end
959
+ end
960
+
961
+ # ========================================================================= #
962
+ # === BackupParadise::Backup[]
963
+ # ========================================================================= #
964
+ def self.[](i = '')
965
+ new(i)
966
+ end
967
+
968
+ # ========================================================================= #
969
+ # === try_to_backup_the_autogenerated_directory
970
+ # ========================================================================= #
971
+ def try_to_backup_the_autogenerated_directory(
972
+ i = AUTOGENERATED_DIRECTORY
973
+ )
974
+ i = ensure_trailing_backslash_for(i)
975
+ if File.directory? i
976
+ opnn; e 'Now trying to backup the AUTOGENERATED '\
977
+ 'directory at `'+sdir(i)+'`.'
978
+ new_target = @mountpoint.squeeze('/')
979
+ n_gigabytes = BackupParadise.size_in_gigabytes?(i).to_s
980
+ opnn; e 'This directory has a total of '+
981
+ sfancy(n_files_in?(i).to_s)+' entries '\
982
+ '(Total size: '+n_gigabytes+' GB).'
983
+ unless File.directory? new_target
984
+ verbose_create_a_directory_at_this_position(new_target)
985
+ end
986
+ tab_title('Backing up the AUTOGENERATED directory.')
987
+ cpr(i, new_target)
988
+ else
989
+ can_not_backup_this_directory_as_it_does_not_exist(i)
990
+ end
991
+ end; alias backup_autogenerated_directory try_to_backup_the_autogenerated_directory # === backup_autogenerated_directory
992
+ alias backup_the_autogenerated_directory try_to_backup_the_autogenerated_directory # === backup_the_autogenerated_directory
993
+
994
+ # ========================================================================= #
995
+ # === backup_into_the_default_chroot_directory (chroot tag)
996
+ # ========================================================================= #
997
+ def backup_into_the_default_chroot_directory(
998
+ i = :chroot
999
+ )
1000
+ e
1001
+ e "#{rev}Setting the target to #{sdir(i)} next."
1002
+ e
1003
+ set_target_mountpoint(i) # Set to the above Chroot target. We could also use :chroot.
1004
+ set_main_target(
1005
+ "#{main_target?.dup}#{home_dir_of_user_x?.dup}"
1006
+ )
1007
+ # ======================================================================= #
1008
+ # Must copy the autogenerated directory first:
1009
+ # ======================================================================= #
1010
+ try_to_backup_the_autogenerated_directory
1011
+ all_important_directories?.each {|this_directory|
1012
+ tab_title 'Now backing up the '+this_directory+' directory ...'
1013
+ new_target = main_target?+File.basename(this_directory)
1014
+ mkdir(new_target)
1015
+ cpr(this_directory, main_target?)
1016
+ }
1017
+ all_done_message
1018
+ end
1019
+
1020
+ # ========================================================================= #
1021
+ # === backup_src_directory (src tag)
1022
+ #
1023
+ # This method will backup the SRC/ directory, aka "/home/x/src/". It
1024
+ # is evidently geared towards the system at my home setup.
1025
+ #
1026
+ # To invoke this method from the commandline, try:
1027
+ #
1028
+ # rbackup --src-dir
1029
+ #
1030
+ # ========================================================================= #
1031
+ def backup_the_source_directory(
1032
+ target_directory = SOURCE_DIRECTORY
1033
+ )
1034
+ target_directory = ensure_trailing_backslash_for(target_directory)
1035
+ if File.directory? target_directory
1036
+ tab_title('Backing up the '+target_directory+' directory.')
1037
+ cliner
1038
+ e 'Now backing up the '+sdir('src/')+' directory from '+
1039
+ sfancy(target_directory)+' into '+
1040
+ sfile(main_target?+File.basename(target_directory))
1041
+ cliner
1042
+ report_total_size_of(target_directory)
1043
+ mkdir(backup_to_this_target?+File.basename(target_directory))
1044
+ cpr(
1045
+ target_directory,
1046
+ backup_to_this_target?
1047
+ )
1048
+ else
1049
+ can_not_backup_this_directory_as_it_does_not_exist(target_directory)
1050
+ end
1051
+ end
1052
+
1053
+ # ========================================================================= #
1054
+ # === backup_the_books_directory (data tag)
1055
+ #
1056
+ # This method will backup the BOOKS directory, e. g. the one that is
1057
+ # at "/home/x/books/" on my home system.
1058
+ # ========================================================================= #
1059
+ def backup_the_books_directory(
1060
+ target_directory = BOOKS_DIRECTORY
1061
+ )
1062
+ target_directory = ensure_trailing_backslash_for(target_directory)
1063
+ if File.directory? target_directory
1064
+ tab_title('Backing up the '+target_directory+' directory.')
1065
+ cliner
1066
+ e 'Now backing up the books/ directory from '+
1067
+ sfancy(target_directory)+' into '+
1068
+ sfile(main_target?+File.basename(target_directory))
1069
+ cliner
1070
+ report_total_size_of(target_directory)
1071
+ unless File.directory? backup_to_this_target?+File.basename(target_directory)
1072
+ opnn; verbose_create_a_directory_at_this_position(
1073
+ backup_to_this_target?+File.basename(target_directory),
1074
+ 0755
1075
+ )
1076
+ end
1077
+ cpr(
1078
+ target_directory,
1079
+ backup_to_this_target?
1080
+ )
1081
+ rename_tab('.')
1082
+ else
1083
+ can_not_backup_this_directory_as_it_does_not_exist(target_directory)
1084
+ end
1085
+ end; alias backup_only_books_directory backup_the_books_directory # === backup_only_books_directory
1086
+ alias backup_books_directory backup_the_books_directory # === backup_books_directory
1087
+
1088
+ # ========================================================================= #
1089
+ # === show_backup_complete_message
1090
+ # ========================================================================= #
1091
+ def show_backup_complete_message
1092
+ e "#{BackupParadise.steelblue('Backup complete!')}#{rev}" # The old colour was cyan, up until 2021.
1093
+ end; alias all_done show_backup_complete_message # === all_done
1094
+ alias show_all_is_done_message show_backup_complete_message # === show_all_is_done_message
1095
+ alias all_done_message show_backup_complete_message # === all_done_message
1096
+
1097
+ # ========================================================================= #
1098
+ # === do_not_perform_the_regular_backup_routine
1099
+ # ========================================================================= #
1100
+ def do_not_perform_the_regular_backup_routine
1101
+ @internal_hash[:perform_backup] = false
1102
+ end
1103
+
1104
+ # ========================================================================= #
1105
+ # === perform_the_regular_backup_routine?
1106
+ # ========================================================================= #
1107
+ def perform_the_regular_backup_routine?
1108
+ @internal_hash[:perform_backup]
1109
+ end
1110
+
1111
+ # ========================================================================= #
1112
+ # === report_todays_date
1113
+ # ========================================================================= #
1114
+ def report_todays_date
1115
+ e "Today is the #{sfancy(date?)}."
1116
+ end
1117
+
1118
+ # ========================================================================= #
1119
+ # === create_the_log_last_backup_file (log tag)
1120
+ #
1121
+ # We also have to remove all prior log files should they exist.
1122
+ # ========================================================================= #
1123
+ def create_the_log_last_backup_file
1124
+ _ = return_all_log_files
1125
+ target = main_target?+'log_last_backup_'+use_this_as_timestamp?
1126
+ what = dd_mm_yyyy
1127
+ opnn; e "Keeping a backup file (a log) at"
1128
+ opnn; e "`#{sfile(target)}`."
1129
+ write_what_into(what, target)
1130
+ unless _.empty?
1131
+ # ===================================================================== #
1132
+ # Remove all old log files next.
1133
+ # ===================================================================== #
1134
+ remove_these_files(_)
1135
+ end
1136
+ end; alias create_log_file create_the_log_last_backup_file # === create_log_file
1137
+
1138
+ # ========================================================================= #
1139
+ # === last_backup_directory?
1140
+ # ========================================================================= #
1141
+ def last_backup_directory?
1142
+ @internal_hash[:last_backup_directory]
1143
+ end
1144
+
1145
+ # ========================================================================= #
1146
+ # === set_last_backup_directory
1147
+ # ========================================================================= #
1148
+ def set_last_backup_directory(i)
1149
+ @internal_hash[:last_backup_directory] = i
1150
+ end
1151
+
1152
+ # ========================================================================= #
1153
+ # === consider_creating_a_log_file
1154
+ #
1155
+ # This method will create a .log file.
1156
+ #
1157
+ # It accepts an argument, which should be an Integer or Float, denoting,
1158
+ # in seconds, how long it took to make a backup.
1159
+ # ========================================================================= #
1160
+ def consider_creating_a_log_file(
1161
+ n_seconds
1162
+ )
1163
+ if File.directory? '/Depot/Temp/'
1164
+ n_minutes = (n_seconds.to_f / 60).round(1).to_s
1165
+ # ===================================================================== #
1166
+ # Make the seconds a bit prettier next, via trailing '0'.
1167
+ # ===================================================================== #
1168
+ n_seconds = '%.3f' % n_seconds
1169
+ what = "#{return_full_date}: Backing up the system took #{n_seconds} "\
1170
+ "seconds (#{n_minutes} minutes).\n"
1171
+ into = FILE_BACKUP_LOG
1172
+ opnn; e "Storing the time it took to backup "\
1173
+ "into the file `#{sfile(into)}`."
1174
+ append_what_into(what, into)
1175
+ end
1176
+ end
1177
+
1178
+ # ========================================================================= #
1179
+ # === backup_the_audio_directory (audio tag, audio dir tag)
1180
+ #
1181
+ # This method can be used to backup my audio directory, which normally
1182
+ # resides at "/home/x/songs/".
1183
+ #
1184
+ # Two arguments can be supplied to this method:
1185
+ #
1186
+ # (1) The first argument specifies which directory is used as the
1187
+ # input directory, where the songs that are to be copied
1188
+ # should reside.
1189
+ #
1190
+ # (2) If the second argument is true then only those audio files
1191
+ # that are missing will be copied.
1192
+ #
1193
+ # ========================================================================= #
1194
+ def backup_the_audio_directory(
1195
+ i = DIRECTORY_CONTAINING_ALL_SONGS,
1196
+ copy_only_missing_audio_files = true
1197
+ )
1198
+ if File.directory? i
1199
+ copied_n_files = 0
1200
+ print rev
1201
+ cliner
1202
+ new_target = @mountpoint.squeeze('/')
1203
+ opnn; e "Now trying to backup the Audio directory "\
1204
+ "at `#{sdir(i)}`."
1205
+ colourized_target = sdir("#{new_target}#{File.basename(i)}/")
1206
+ opnn; e "The target for this will be at "\
1207
+ "`#{colourized_target}`."
1208
+ n_gigabytes = BackupParadise.size_in_gigabytes?(i).to_s
1209
+ opnn; e 'This directory has a total of '+
1210
+ sfancy(n_files_in?(i).to_s)+' entries '\
1211
+ '(Total size: '+n_gigabytes+' GB).'
1212
+ cliner
1213
+ unless File.directory? "#{new_target}#{File.basename(i)}/"
1214
+ opnn; verbose_create_a_directory_at_this_position(
1215
+ "#{new_target}#{File.basename(i)}/"
1216
+ )
1217
+ end
1218
+ tab_title('Backing up the main audio directory.')
1219
+ # ===================================================================== #
1220
+ # Else we will copy only the audio files that are missing. In order
1221
+ # to do so we have to obtain all audio-files - we will simply
1222
+ # pick all files, anyway. We will keep these entries sorted, too.
1223
+ # ===================================================================== #
1224
+ these_audio_files_may_be_copied = Dir[
1225
+ rds("#{i.delete('*')}/*")
1226
+ ].sort
1227
+ if copy_only_missing_audio_files
1228
+ these_audio_files_may_be_copied.each {|this_file|
1229
+ totally_new_target =
1230
+ "#{new_target}#{File.basename(i)}/#{File.basename(this_file)}"
1231
+ # ================================================================= #
1232
+ # We check whether the target-file exists. Only if it does not
1233
+ # exist will it be copied.
1234
+ # ================================================================= #
1235
+ unless File.exist? totally_new_target
1236
+ copied_n_files += 1
1237
+ # =============================================================== #
1238
+ # Pad the display.
1239
+ # =============================================================== #
1240
+ padded_file = this_file.ljust(40)
1241
+ padded_target = totally_new_target.ljust(40)
1242
+ e "Copying `#{sfile(padded_file)}` to"
1243
+ e " `#{sfile(padded_target)}`."
1244
+ copy_this_file(this_file, totally_new_target)
1245
+ end
1246
+ }
1247
+ # =================================================================== #
1248
+ # Ok, now, if we removed a file from /Depot/Audio, then the Audio/
1249
+ # directory at the mounted USB device may still have such a file.
1250
+ # So we will warn the user about this.
1251
+ # =================================================================== #
1252
+ target = "#{new_target}#{File.basename(i)}/" # This will be something like "/Mount/USB1/songs/"
1253
+ unless these_audio_files_may_be_copied.size == Dir["#{target}*"].size
1254
+ opnn; e 'It seems as if we have an unequal amount of Audio files in '
1255
+ opnn; e 'the two directories.'
1256
+ opnn; e 'This usually means that the original directory '+
1257
+ sdir(i)+' has some files'
1258
+ opnn; e 'deleted, but the target directory at '+sdir(target)+' does not.'
1259
+ opnn; e 'The old directory has '+simp(i.size.to_s)+' entries.'
1260
+ end
1261
+ else
1262
+ cpr(i, new_target)
1263
+ end
1264
+ _ = "#{new_target}#{File.basename(i)}/"
1265
+ if copied_n_files == 0
1266
+ opnn; e "No file was copied into the directory #{sdir(_)}."
1267
+ opnn; e 'This may indicate that the target audio-directory already'
1268
+ opnn; e 'contains all necessary audio-files.'
1269
+ else
1270
+ opnn; e "Finished backing up the directory "\
1271
+ "#{sdir(i.delete('*'))} into "\
1272
+ "#{sdir(_)}."
1273
+ end
1274
+ default_tab_title
1275
+ else
1276
+ can_not_backup_this_directory_as_it_does_not_exist(i)
1277
+ end
1278
+ end; alias try_to_backup_the_audio_directory backup_the_audio_directory # === alias try_to_backup_the_audio_directory
1279
+ alias backup_audio_directory backup_the_audio_directory # === backup_audio_directory
1280
+
1281
+ # ========================================================================= #
1282
+ # === unmount
1283
+ #
1284
+ # Use this to unmount something. Right now, we unmount only the HDD.
1285
+ # ========================================================================= #
1286
+ def unmount(
1287
+ what = :hdd
1288
+ )
1289
+ case what.to_sym
1290
+ when :hdd
1291
+ system "umount -l #{ENV['FIRST_HD']}"
1292
+ end
1293
+ end
1294
+
1295
+ # ========================================================================= #
1296
+ # === do_perform_the_backup_tasks (all tag)
1297
+ #
1298
+ # This method is the main powerhorse of this class. It connects all the
1299
+ # various steps together, one after the other.
1300
+ # ========================================================================= #
1301
+ def do_perform_the_backup_tasks
1302
+ determine_the_starting_time
1303
+ show_welcome_message
1304
+ create_file_keeping_track_of_when_the_last_backup_happened
1305
+ determine_whether_the_target_harddisc_is_a_ntfs_system
1306
+ tab_title 'Backing up some data next, onto '+backup_to_this_target?.to_s+' ...'
1307
+ cd last_backup_directory?
1308
+ # ======================================================================= #
1309
+ # Remove some log-files next, if necessary.
1310
+ # ======================================================================= #
1311
+ consider_removing_all_but_one_log_file
1312
+ # ======================================================================= #
1313
+ # The next method will rename entries such as:
1314
+ #
1315
+ # "last_backup-13.10.2018-21:07:20/"
1316
+ #
1317
+ # to
1318
+ #
1319
+ # "backup-13.10.2018-21:07:20/"
1320
+ #
1321
+ # specifically.
1322
+ #
1323
+ # This is necessary so that we can only have one such directory
1324
+ # name, after we are done with this method here.
1325
+ # ======================================================================= #
1326
+ consider_renaming_all_old_last_backup_directories
1327
+ # ======================================================================= #
1328
+ # We will also create a log file, to denote when we started to
1329
+ # do this current backup-operation.
1330
+ # ======================================================================= #
1331
+ create_the_log_last_backup_file
1332
+ create_the_directory_containing_the_last_backup
1333
+ create_a_symlink_pointing_to_that_directory_that_was_just_created
1334
+ # ======================================================================= #
1335
+ # === Back up the AUTOGENERATED directory
1336
+ #
1337
+ # We will next attempt to backup the directory at /AUTOGENERATED/.
1338
+ # This must come before we create a new directory at the mounted
1339
+ # USB device, simply because we may not copy anything at all,
1340
+ # or some error may happen that causes an interrupt.
1341
+ # ======================================================================= #
1342
+ try_to_backup_the_autogenerated_directory
1343
+ # ======================================================================= #
1344
+ # === Back up the audio directory as well
1345
+ # ======================================================================= #
1346
+ try_to_backup_the_audio_directory
1347
+ unless File.directory? backup_to_this_target?
1348
+ FileUtils.mkdir_p(backup_to_this_target?)
1349
+ end
1350
+ # ======================================================================= #
1351
+ # Next, iterate over the directories that we wish to backup.
1352
+ # ======================================================================= #
1353
+ backup_which_directories?.each {|this_directory|
1354
+ this_directory = ensure_trailing_backslash_for(this_directory)
1355
+ tab_title("Backing up #{this_directory}.")
1356
+ this_directory = this_directory.dup if this_directory.frozen?
1357
+ # ===================================================================== #
1358
+ # Ensure that we have a trailing '/' character here.
1359
+ # ===================================================================== #
1360
+ this_directory << '/' unless this_directory.end_with?('/')
1361
+ e "#{::Colours.rev}Now backing up the directory "+
1362
+ sdir(this_directory)+
1363
+ ' onto '+
1364
+ steelblue(
1365
+ backup_to_this_target?+
1366
+ File.basename(this_directory)
1367
+ )+'.'
1368
+ cpr(this_directory, backup_to_this_target?)
1369
+ }
1370
+ popup_notification(:backup_complete) if show_popup_notification?
1371
+ do_sync # Synchronize the USB devices here, before we determine the end time.
1372
+ determine_end_time
1373
+ report_time_difference
1374
+ try_to_unmount_the_device
1375
+ show_backup_complete_message
1376
+ tab_title('Backup is complete!')
1377
+ # or: tab_title ''
1378
+ end; alias start_backup do_perform_the_backup_tasks # === start_backup
1379
+ alias backup_everything do_perform_the_backup_tasks # === backup_everything
1380
+ alias do_backup do_perform_the_backup_tasks # === do_backup
1381
+
1382
+ end; end
1383
+
1384
+ if __FILE__ == $PROGRAM_NAME
1385
+ BackupParadise::Backup.new(ARGV)
1386
+ end # rbackup
1387
+ # rbackup --help
1388
+ # rbackup --data_only
1389
+ # rbackup data_only