16
16
#include <dirent.h>
17
17
#include <fcntl.h>
18
18
#include <sys/stat.h>
19
+ #include <time.h>
19
20
20
21
#include "common/hashfn.h"
21
22
#include "common/logging.h"
22
23
#include "fe_utils/simple_list.h"
23
24
#include "getopt_long.h"
24
25
#include "parse_manifest.h"
26
+ #include "pgtime.h"
25
27
26
28
/*
27
29
* For efficiency, we'd like our hash table containing information about the
@@ -58,6 +60,9 @@ typedef struct manifest_file
58
60
bool bad ;
59
61
} manifest_file ;
60
62
63
+ #define should_verify_checksum (m ) \
64
+ (((m)->matched) && !((m)->bad) && (((m)->checksum_type) != CHECKSUM_TYPE_NONE))
65
+
61
66
/*
62
67
* Define a hash table which we can use to store information about the files
63
68
* mentioned in the backup manifest.
@@ -147,10 +152,19 @@ static void report_fatal_error(const char *pg_restrict fmt,...)
147
152
pg_attribute_printf (1 , 2 ) pg_attribute_noreturn ();
148
153
static bool should_ignore_relpath (verifier_context * context , char * relpath );
149
154
155
+ static void progress_report (bool finished );
150
156
static void usage (void );
151
157
152
158
static const char * progname ;
153
159
160
+ /* options */
161
+ static bool show_progress = false;
162
+ static bool skip_checksums = false;
163
+
164
+ /* Progress indicators */
165
+ static uint64 total_size = 0 ;
166
+ static uint64 done_size = 0 ;
167
+
154
168
/*
155
169
* Main entry point.
156
170
*/
@@ -162,6 +176,7 @@ main(int argc, char **argv)
162
176
{"ignore" , required_argument , NULL , 'i' },
163
177
{"manifest-path" , required_argument , NULL , 'm' },
164
178
{"no-parse-wal" , no_argument , NULL , 'n' },
179
+ {"progress" , no_argument , NULL , 'P' },
165
180
{"quiet" , no_argument , NULL , 'q' },
166
181
{"skip-checksums" , no_argument , NULL , 's' },
167
182
{"wal-directory" , required_argument , NULL , 'w' },
@@ -174,7 +189,6 @@ main(int argc, char **argv)
174
189
char * manifest_path = NULL ;
175
190
bool no_parse_wal = false;
176
191
bool quiet = false;
177
- bool skip_checksums = false;
178
192
char * wal_directory = NULL ;
179
193
char * pg_waldump_path = NULL ;
180
194
@@ -219,7 +233,7 @@ main(int argc, char **argv)
219
233
simple_string_list_append (& context .ignore_list , "recovery.signal" );
220
234
simple_string_list_append (& context .ignore_list , "standby.signal" );
221
235
222
- while ((c = getopt_long (argc , argv , "ei:m:nqsw :" , long_options , NULL )) != -1 )
236
+ while ((c = getopt_long (argc , argv , "ei:m:nPqsw :" , long_options , NULL )) != -1 )
223
237
{
224
238
switch (c )
225
239
{
@@ -241,6 +255,9 @@ main(int argc, char **argv)
241
255
case 'n' :
242
256
no_parse_wal = true;
243
257
break ;
258
+ case 'P' :
259
+ show_progress = true;
260
+ break ;
244
261
case 'q' :
245
262
quiet = true;
246
263
break ;
@@ -277,6 +294,11 @@ main(int argc, char **argv)
277
294
exit (1 );
278
295
}
279
296
297
+ /* Complain if the specified arguments conflict */
298
+ if (show_progress && quiet )
299
+ pg_fatal ("cannot specify both %s and %s" ,
300
+ "-P/--progress" , "-q/--quiet" );
301
+
280
302
/* Unless --no-parse-wal was specified, we will need pg_waldump. */
281
303
if (!no_parse_wal )
282
304
{
@@ -638,6 +660,10 @@ verify_backup_file(verifier_context *context, char *relpath, char *fullpath)
638
660
m -> bad = true;
639
661
}
640
662
663
+ /* Update statistics for progress report, if necessary */
664
+ if (show_progress && !skip_checksums && should_verify_checksum (m ))
665
+ total_size += m -> size ;
666
+
641
667
/*
642
668
* We don't verify checksums at this stage. We first finish verifying that
643
669
* we have the expected set of files with the expected sizes, and only
@@ -675,10 +701,12 @@ verify_backup_checksums(verifier_context *context)
675
701
manifest_files_iterator it ;
676
702
manifest_file * m ;
677
703
704
+ progress_report (false);
705
+
678
706
manifest_files_start_iterate (context -> ht , & it );
679
707
while ((m = manifest_files_iterate (context -> ht , & it )) != NULL )
680
708
{
681
- if (m -> matched && ! m -> bad && m -> checksum_type != CHECKSUM_TYPE_NONE &&
709
+ if (should_verify_checksum ( m ) &&
682
710
!should_ignore_relpath (context , m -> pathname ))
683
711
{
684
712
char * fullpath ;
@@ -694,6 +722,8 @@ verify_backup_checksums(verifier_context *context)
694
722
pfree (fullpath );
695
723
}
696
724
}
725
+
726
+ progress_report (true);
697
727
}
698
728
699
729
/*
@@ -740,6 +770,10 @@ verify_file_checksum(verifier_context *context, manifest_file *m,
740
770
close (fd );
741
771
return ;
742
772
}
773
+
774
+ /* Report progress */
775
+ done_size += rc ;
776
+ progress_report (false);
743
777
}
744
778
if (rc < 0 )
745
779
report_backup_error (context , "could not read file \"%s\": %m" ,
@@ -894,6 +928,51 @@ hash_string_pointer(char *s)
894
928
return hash_bytes (ss , strlen (s ));
895
929
}
896
930
931
+ /*
932
+ * Print a progress report based on the global variables.
933
+ *
934
+ * Progress report is written at maximum once per second, unless the finished
935
+ * parameter is set to true.
936
+ *
937
+ * If finished is set to true, this is the last progress report. The cursor
938
+ * is moved to the next line.
939
+ */
940
+ static void
941
+ progress_report (bool finished )
942
+ {
943
+ static pg_time_t last_progress_report = 0 ;
944
+ pg_time_t now ;
945
+ int percent_size = 0 ;
946
+ char totalsize_str [32 ];
947
+ char donesize_str [32 ];
948
+
949
+ if (!show_progress )
950
+ return ;
951
+
952
+ now = time (NULL );
953
+ if (now == last_progress_report && !finished )
954
+ return ; /* Max once per second */
955
+
956
+ last_progress_report = now ;
957
+ percent_size = total_size ? (int ) ((done_size * 100 / total_size )) : 0 ;
958
+
959
+ snprintf (totalsize_str , sizeof (totalsize_str ), UINT64_FORMAT ,
960
+ total_size / 1024 );
961
+ snprintf (donesize_str , sizeof (donesize_str ), UINT64_FORMAT ,
962
+ done_size / 1024 );
963
+
964
+ fprintf (stderr ,
965
+ _ ("%*s/%s kB (%d%%) verified" ),
966
+ (int ) strlen (totalsize_str ),
967
+ donesize_str , totalsize_str , percent_size );
968
+
969
+ /*
970
+ * Stay on the same line if reporting to a terminal and we're not done
971
+ * yet.
972
+ */
973
+ fputc ((!finished && isatty (fileno (stderr ))) ? '\r' : '\n' , stderr );
974
+ }
975
+
897
976
/*
898
977
* Print out usage information and exit.
899
978
*/
@@ -907,6 +986,7 @@ usage(void)
907
986
printf (_ (" -i, --ignore=RELATIVE_PATH ignore indicated path\n" ));
908
987
printf (_ (" -m, --manifest-path=PATH use specified path for manifest\n" ));
909
988
printf (_ (" -n, --no-parse-wal do not try to parse WAL files\n" ));
989
+ printf (_ (" -P, --progress show progress information\n" ));
910
990
printf (_ (" -q, --quiet do not print any output, except for errors\n" ));
911
991
printf (_ (" -s, --skip-checksums skip checksum verification\n" ));
912
992
printf (_ (" -w, --wal-directory=PATH use specified path for WAL files\n" ));
0 commit comments