backup_paradise 1.2.40

Sign up to get free protection for your applications and to get access to all the features.

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 +390 -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 +1416 -0
  43. data/lib/backup_paradise/utility_scripts/backup/constants.rb +44 -0
  44. data/lib/backup_paradise/utility_scripts/backup/initialize.rb +71 -0
  45. data/lib/backup_paradise/utility_scripts/backup/menu.rb +361 -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,1416 @@
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
+ # === do_not_perform_the_regular_backup_routine
1090
+ # ========================================================================= #
1091
+ def do_not_perform_the_regular_backup_routine
1092
+ @internal_hash[:perform_backup] = false
1093
+ end
1094
+
1095
+ # ========================================================================= #
1096
+ # === perform_the_regular_backup_routine?
1097
+ # ========================================================================= #
1098
+ def perform_the_regular_backup_routine?
1099
+ @internal_hash[:perform_backup]
1100
+ end
1101
+
1102
+ # ========================================================================= #
1103
+ # === report_todays_date
1104
+ # ========================================================================= #
1105
+ def report_todays_date
1106
+ e "Today is the #{sfancy(date?)}."
1107
+ end
1108
+
1109
+ # ========================================================================= #
1110
+ # === create_the_log_last_backup_file (log tag)
1111
+ #
1112
+ # We also have to remove all prior log files should they exist.
1113
+ # ========================================================================= #
1114
+ def create_the_log_last_backup_file
1115
+ _ = return_all_log_files
1116
+ target = main_target?+'log_last_backup_'+use_this_as_timestamp?
1117
+ what = dd_mm_yyyy
1118
+ opnn; e "Keeping a backup file (a log) at"
1119
+ opnn; e "`#{sfile(target)}`."
1120
+ write_what_into(what, target)
1121
+ unless _.empty?
1122
+ # ===================================================================== #
1123
+ # Remove all old log files next.
1124
+ # ===================================================================== #
1125
+ remove_these_files(_)
1126
+ end
1127
+ end; alias create_log_file create_the_log_last_backup_file # === create_log_file
1128
+
1129
+ # ========================================================================= #
1130
+ # === last_backup_directory?
1131
+ # ========================================================================= #
1132
+ def last_backup_directory?
1133
+ @internal_hash[:last_backup_directory]
1134
+ end
1135
+
1136
+ # ========================================================================= #
1137
+ # === set_last_backup_directory
1138
+ # ========================================================================= #
1139
+ def set_last_backup_directory(i)
1140
+ @internal_hash[:last_backup_directory] = i
1141
+ end
1142
+
1143
+ # ========================================================================= #
1144
+ # === consider_creating_a_log_file
1145
+ #
1146
+ # This method will create a .log file.
1147
+ #
1148
+ # It accepts an argument, which should be an Integer or Float, denoting,
1149
+ # in seconds, how long it took to make a backup.
1150
+ # ========================================================================= #
1151
+ def consider_creating_a_log_file(
1152
+ n_seconds
1153
+ )
1154
+ if File.directory? '/Depot/Temp/'
1155
+ n_minutes = (n_seconds.to_f / 60).round(1).to_s
1156
+ # ===================================================================== #
1157
+ # Make the seconds a bit prettier next, via trailing '0'.
1158
+ # ===================================================================== #
1159
+ n_seconds = '%.3f' % n_seconds
1160
+ what = "#{return_full_date}: Backing up the system took #{n_seconds} "\
1161
+ "seconds (#{n_minutes} minutes).\n"
1162
+ into = FILE_BACKUP_LOG
1163
+ opnn; e "Storing the time it took to backup "\
1164
+ "into the file `#{sfile(into)}`."
1165
+ append_what_into(what, into)
1166
+ end
1167
+ end
1168
+
1169
+ # ========================================================================= #
1170
+ # === backup_the_audio_directory (audio tag, audio dir tag)
1171
+ #
1172
+ # This method can be used to backup my audio directory, which normally
1173
+ # resides at "/home/x/songs/".
1174
+ #
1175
+ # Two arguments can be supplied to this method:
1176
+ #
1177
+ # (1) The first argument specifies which directory is used as the
1178
+ # input directory, where the songs that are to be copied
1179
+ # should reside.
1180
+ #
1181
+ # (2) If the second argument is true then only those audio files
1182
+ # that are missing will be copied.
1183
+ #
1184
+ # ========================================================================= #
1185
+ def backup_the_audio_directory(
1186
+ i = DIRECTORY_CONTAINING_ALL_SONGS,
1187
+ copy_only_missing_audio_files = true
1188
+ )
1189
+ if File.directory? i
1190
+ copied_n_files = 0
1191
+ print rev
1192
+ cliner
1193
+ new_target = @mountpoint.squeeze('/')
1194
+ opnn; e "Now trying to backup the Audio directory "\
1195
+ "at `#{sdir(i)}`."
1196
+ colourized_target = sdir("#{new_target}#{File.basename(i)}/")
1197
+ opnn; e "The target for this will be at "\
1198
+ "`#{colourized_target}`."
1199
+ n_gigabytes = BackupParadise.size_in_gigabytes?(i).to_s
1200
+ opnn; e 'This directory has a total of '+
1201
+ sfancy(n_files_in?(i).to_s)+' entries '\
1202
+ '(Total size: '+n_gigabytes+' GB).'
1203
+ cliner
1204
+ unless File.directory? "#{new_target}#{File.basename(i)}/"
1205
+ opnn; verbose_create_a_directory_at_this_position(
1206
+ "#{new_target}#{File.basename(i)}/"
1207
+ )
1208
+ end
1209
+ tab_title('Backing up the main audio directory.')
1210
+ # ===================================================================== #
1211
+ # Else we will copy only the audio files that are missing. In order
1212
+ # to do so we have to obtain all audio-files - we will simply
1213
+ # pick all files, anyway. We will keep these entries sorted, too.
1214
+ # ===================================================================== #
1215
+ these_audio_files_may_be_copied = Dir[
1216
+ rds("#{i.delete('*')}/*")
1217
+ ].sort
1218
+ if copy_only_missing_audio_files
1219
+ these_audio_files_may_be_copied.each {|this_file|
1220
+ totally_new_target =
1221
+ "#{new_target}#{File.basename(i)}/#{File.basename(this_file)}"
1222
+ # ================================================================= #
1223
+ # We check whether the target-file exists. Only if it does not
1224
+ # exist will it be copied.
1225
+ # ================================================================= #
1226
+ unless File.exist? totally_new_target
1227
+ copied_n_files += 1
1228
+ # =============================================================== #
1229
+ # Pad the display.
1230
+ # =============================================================== #
1231
+ padded_file = this_file.ljust(40)
1232
+ padded_target = totally_new_target.ljust(40)
1233
+ e "Copying `#{sfile(padded_file)}` to"
1234
+ e " `#{sfile(padded_target)}`."
1235
+ copy_this_file(this_file, totally_new_target)
1236
+ end
1237
+ }
1238
+ # =================================================================== #
1239
+ # Ok, now, if we removed a file from /Depot/Audio, then the Audio/
1240
+ # directory at the mounted USB device may still have such a file.
1241
+ # So we will warn the user about this.
1242
+ # =================================================================== #
1243
+ target = "#{new_target}#{File.basename(i)}/" # This will be something like "/Mount/USB1/songs/"
1244
+ unless these_audio_files_may_be_copied.size == Dir["#{target}*"].size
1245
+ opnn; e 'It seems as if we have an unequal amount of Audio files in '
1246
+ opnn; e 'the two directories.'
1247
+ opnn; e 'This usually means that the original directory '+
1248
+ sdir(i)+' has some files'
1249
+ opnn; e 'deleted, but the target directory at '+sdir(target)+' does not.'
1250
+ opnn; e 'The old directory has '+simp(i.size.to_s)+' entries.'
1251
+ end
1252
+ else
1253
+ cpr(i, new_target)
1254
+ end
1255
+ _ = "#{new_target}#{File.basename(i)}/"
1256
+ if copied_n_files == 0
1257
+ opnn; e "No file was copied into the directory #{sdir(_)}."
1258
+ opnn; e 'This may indicate that the target audio-directory already'
1259
+ opnn; e 'contains all necessary audio-files.'
1260
+ else
1261
+ opnn; e "Finished backing up the directory "\
1262
+ "#{sdir(i.delete('*'))} into "\
1263
+ "#{sdir(_)}."
1264
+ end
1265
+ default_tab_title
1266
+ else
1267
+ can_not_backup_this_directory_as_it_does_not_exist(i)
1268
+ end
1269
+ end; alias try_to_backup_the_audio_directory backup_the_audio_directory # === alias try_to_backup_the_audio_directory
1270
+ alias backup_audio_directory backup_the_audio_directory # === backup_audio_directory
1271
+
1272
+ # ========================================================================= #
1273
+ # === unmount
1274
+ #
1275
+ # Use this to unmount something. Right now, we unmount only the HDD.
1276
+ # ========================================================================= #
1277
+ def unmount(
1278
+ what = :hdd
1279
+ )
1280
+ case what.to_sym
1281
+ when :hdd
1282
+ system "umount -l #{ENV['FIRST_HD']}"
1283
+ end
1284
+ end
1285
+
1286
+ # ========================================================================= #
1287
+ # === show_backup_complete_message
1288
+ #
1289
+ # This variant will show that the backup has been complete on the
1290
+ # commandline. Since September 2022 there is also a backup-complete
1291
+ # message for LibuiParadise.
1292
+ # ========================================================================= #
1293
+ def show_backup_complete_message
1294
+ e "#{BackupParadise.steelblue('Backup complete!')}#{rev}" # The old colour was cyan, up until 2021.
1295
+ if TRY_TO_SHOW_BACKUP_COMPLETE_MESSAGE_VIA_LIBUI
1296
+ try_to_show_backup_complete_message_via_libui
1297
+ end
1298
+ end; alias all_done show_backup_complete_message # === all_done
1299
+ alias show_all_is_done_message show_backup_complete_message # === show_all_is_done_message
1300
+ alias all_done_message show_backup_complete_message # === all_done_message
1301
+
1302
+ # ========================================================================= #
1303
+ # === try_to_show_backup_complete_message_via_libui
1304
+ #
1305
+ # This method will describe the libui-widget that we will use for
1306
+ # notifying the user.
1307
+ # ========================================================================= #
1308
+ def try_to_show_backup_complete_message_via_libui
1309
+ begin
1310
+ require 'libui_paradise'
1311
+ text = LibuiParadise.text('Backup complete!')
1312
+ ::LibuiParadise.run_in_the_background
1313
+ LibuiParadise.generic_window(
1314
+ text, :create_a_quit_button
1315
+ ) {{
1316
+ width: 420,
1317
+ height: 120,
1318
+ title: 'Backup is complete now!'
1319
+ }}
1320
+ rescue LoadError; end
1321
+ end
1322
+
1323
+ # ========================================================================= #
1324
+ # === do_perform_the_backup_tasks (all tag)
1325
+ #
1326
+ # This method is the main powerhouse of this class. It connects all the
1327
+ # various steps together, one after the other.
1328
+ # ========================================================================= #
1329
+ def do_perform_the_backup_tasks
1330
+ determine_the_starting_time
1331
+ show_welcome_message
1332
+ create_file_keeping_track_of_when_the_last_backup_happened
1333
+ determine_whether_the_target_harddisc_is_a_ntfs_system
1334
+ tab_title 'Backing up some data next, onto '+backup_to_this_target?.to_s+' ...'
1335
+ cd last_backup_directory?
1336
+ # ======================================================================= #
1337
+ # Remove some log-files next, if necessary.
1338
+ # ======================================================================= #
1339
+ consider_removing_all_but_one_log_file
1340
+ # ======================================================================= #
1341
+ # The next method will rename entries such as:
1342
+ #
1343
+ # "last_backup-13.10.2018-21:07:20/"
1344
+ #
1345
+ # to
1346
+ #
1347
+ # "backup-13.10.2018-21:07:20/"
1348
+ #
1349
+ # specifically.
1350
+ #
1351
+ # This is necessary so that we can only have one such directory
1352
+ # name, after we are done with this method here.
1353
+ # ======================================================================= #
1354
+ consider_renaming_all_old_last_backup_directories
1355
+ # ======================================================================= #
1356
+ # We will also create a log file, to denote when we started to
1357
+ # do this current backup-operation.
1358
+ # ======================================================================= #
1359
+ create_the_log_last_backup_file
1360
+ create_the_directory_containing_the_last_backup
1361
+ create_a_symlink_pointing_to_that_directory_that_was_just_created
1362
+ # ======================================================================= #
1363
+ # === Back up the AUTOGENERATED directory
1364
+ #
1365
+ # We will next attempt to backup the directory at /AUTOGENERATED/.
1366
+ # This must come before we create a new directory at the mounted
1367
+ # USB device, simply because we may not copy anything at all,
1368
+ # or some error may happen that causes an interrupt.
1369
+ # ======================================================================= #
1370
+ try_to_backup_the_autogenerated_directory
1371
+ # ======================================================================= #
1372
+ # === Back up the audio directory as well
1373
+ # ======================================================================= #
1374
+ try_to_backup_the_audio_directory
1375
+ unless File.directory? backup_to_this_target?
1376
+ FileUtils.mkdir_p(backup_to_this_target?)
1377
+ end
1378
+ # ======================================================================= #
1379
+ # Next, iterate over the directories that we wish to backup.
1380
+ # ======================================================================= #
1381
+ backup_which_directories?.each {|this_directory|
1382
+ this_directory = ensure_trailing_backslash_for(this_directory)
1383
+ tab_title("Backing up #{this_directory}.")
1384
+ this_directory = this_directory.dup if this_directory.frozen?
1385
+ # ===================================================================== #
1386
+ # Ensure that we have a trailing '/' character here.
1387
+ # ===================================================================== #
1388
+ this_directory << '/' unless this_directory.end_with?('/')
1389
+ e "#{rev}Now backing up the directory #{sdir(this_directory)}"+
1390
+ ' onto '+
1391
+ steelblue(
1392
+ backup_to_this_target?+
1393
+ File.basename(this_directory)
1394
+ )+'.'
1395
+ cpr(this_directory, backup_to_this_target?)
1396
+ }
1397
+ popup_notification(:backup_complete) if show_popup_notification?
1398
+ do_sync # Synchronize the USB devices here, before we determine the end time.
1399
+ determine_end_time
1400
+ report_time_difference
1401
+ try_to_unmount_the_device
1402
+ show_backup_complete_message
1403
+ tab_title('Backup is complete!')
1404
+ # or: tab_title ''
1405
+ end; alias start_backup do_perform_the_backup_tasks # === start_backup
1406
+ alias backup_everything do_perform_the_backup_tasks # === backup_everything
1407
+ alias do_backup do_perform_the_backup_tasks # === do_backup
1408
+
1409
+ end; end
1410
+
1411
+ if __FILE__ == $PROGRAM_NAME
1412
+ BackupParadise::Backup.new(ARGV)
1413
+ end # rbackup
1414
+ # rbackup --help
1415
+ # rbackup --data_only
1416
+ # rbackup data_only