3 * Program to check a Bacula database for consistency and to
6 * Kern E. Sibbald, August 2002
12 Copyright (C) 2002-2005 Kern Sibbald
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of
17 the License, or (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
24 You should have received a copy of the GNU General Public
25 License along with this program; if not, write to the Free
26 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
32 #include "cats/cats.h"
33 #include "dird/dird_conf.h"
36 int generate_daemon_event(JCR *jcr, const char *event)
39 typedef struct s_id_ctx {
40 uint32_t *Id; /* ids to be modified */
41 int num_ids; /* ids stored */
42 int max_ids; /* size of array */
43 int num_del; /* number deleted */
44 int tot_ids; /* total to process */
47 typedef struct s_name_ctx {
48 char **name; /* list of names */
49 int num_ids; /* ids stored */
50 int max_ids; /* size of array */
51 int num_del; /* number deleted */
52 int tot_ids; /* total to process */
57 /* Global variables */
58 static bool fix = false;
59 static bool batch = false;
61 static ID_LIST id_list;
62 static NAME_LIST name_list;
63 static char buf[2000];
65 #define MAX_ID_LIST_LEN 10000000
67 /* Forward referenced functions */
68 static int make_id_list(const char *query, ID_LIST *id_list);
69 static int delete_id_list(const char *query, ID_LIST *id_list);
70 static int make_name_list(const char *query, NAME_LIST *name_list);
71 static void print_name_list(NAME_LIST *name_list);
72 static void free_name_list(NAME_LIST *name_list);
73 static char *get_cmd(const char *prompt);
74 static void eliminate_duplicate_filenames();
75 static void eliminate_duplicate_paths();
76 static void eliminate_orphaned_jobmedia_records();
77 static void eliminate_orphaned_file_records();
78 static void eliminate_orphaned_path_records();
79 static void eliminate_orphaned_filename_records();
80 static void eliminate_orphaned_fileset_records();
81 static void eliminate_orphaned_client_records();
82 static void eliminate_orphaned_job_records();
83 static void eliminate_admin_records();
84 static void eliminate_restore_records();
85 static void repair_bad_paths();
86 static void repair_bad_filenames();
87 static void do_interactive_mode();
88 static int yes_no(const char *prompt);
94 "Usage: dbcheck [-c config] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>]\n"
96 " -C catalog name in the director conf file\n"
97 " -c director conf filename\n"
98 " -dnn set debug level to nn\n"
99 " -f fix inconsistencies\n"
101 " -? print this message\n\n");
105 int main (int argc, char *argv[])
108 const char *user, *password, *db_name, *dbhost;
109 char *configfile = NULL;
110 char *catalogname = NULL;
112 my_name_is(argc, argv, "dbcheck");
113 init_msg(NULL, NULL); /* setup message handler */
115 memset(&id_list, 0, sizeof(id_list));
116 memset(&name_list, 0, sizeof(name_list));
119 while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
121 case 'b': /* batch */
125 case 'C': /* CatalogName */
126 catalogname = optarg;
129 case 'c': /* configfile */
133 case 'd': /* debug level */
134 debug_level = atoi(optarg);
135 if (debug_level <= 0)
139 case 'f': /* fix inconsistencies */
159 Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
161 parse_config(configfile);
163 foreach_res(catalog, R_CATALOG) {
164 if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
167 } else if (!catalogname) { // stop on first if no catalogname is given
175 Pmsg2(0, "Error can not find the Catalog name[%s] in the given config file [%s]\n", catalogname, configfile);
177 Pmsg1(0, "Error there is no Catalog section in the given config file [%s]\n", configfile);
183 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
186 Pmsg0(0, "Error no Director resource defined.\n");
189 set_working_directory(director->working_directory);
190 db_name = catalog->db_name;
191 user = catalog->db_user;
192 password = catalog->db_password;
193 dbhost = catalog->db_address;
194 if (dbhost && dbhost[0] == 0) {
200 Pmsg0(0, _("Wrong number of arguments.\n"));
205 Pmsg0(0, _("Working directory not supplied.\n"));
209 /* This is needed by SQLite to find the db */
210 working_directory = argv[0];
219 } else if (argc == 3) {
222 } else if (argc == 4) {
226 } else if (argc == 5) {
235 db = db_init_database(NULL, db_name, user, password, dbhost, 0, NULL, 0);
236 if (!db_open_database(NULL, db)) {
237 Emsg1(M_FATAL, 0, "%s", db_strerror(db));
243 repair_bad_filenames();
244 eliminate_duplicate_filenames();
245 eliminate_duplicate_paths();
246 eliminate_orphaned_jobmedia_records();
247 eliminate_orphaned_file_records();
248 eliminate_orphaned_path_records();
249 eliminate_orphaned_filename_records();
250 eliminate_orphaned_fileset_records();
251 eliminate_orphaned_client_records();
252 eliminate_orphaned_job_records();
253 eliminate_admin_records();
254 eliminate_restore_records();
256 do_interactive_mode();
259 db_close_database(NULL, db);
265 static void do_interactive_mode()
270 printf("Hello, this is the database check/correct program.\n"
271 "Modify database is %s. Verbose is %s.\n"
272 "Please select the fuction you want to perform.\n",
273 fix?"On":"Off", verbose?"On":"Off");
278 " 1) Toggle modify database flag\n"
279 " 2) Toggle verbose flag\n"
280 " 3) Repair bad Filename records\n"
281 " 4) Repair bad Path records\n"
282 " 5) Eliminate duplicate Filename records\n"
283 " 6) Eliminate duplicate Path records\n"
284 " 7) Eliminate orphaned Jobmedia records\n"
285 " 8) Eliminate orphaned File records\n"
286 " 9) Eliminate orphaned Path records\n"
287 " 10) Eliminate orphaned Filename records\n"
288 " 11) Eliminate orphaned FileSet records\n"
289 " 12) Eliminate orphaned Client records\n"
290 " 13) Eliminate orphaned Job records\n"
291 " 14) Eliminate all Admin records\n"
292 " 15) Eliminate all Restore records\n"
297 " 1) Toggle modify database flag\n"
298 " 2) Toggle verbose flag\n"
299 " 3) Check for bad Filename records\n"
300 " 4) Check for bad Path records\n"
301 " 5) Check for duplicate Filename records\n"
302 " 6) Check for duplicate Path records\n"
303 " 7) Check for orphaned Jobmedia records\n"
304 " 8) Check for orphaned File records\n"
305 " 9) Check for orphaned Path records\n"
306 " 10) Check for orphaned Filename records\n"
307 " 11) Check for orphaned FileSet records\n"
308 " 12) Check for orphaned Client records\n"
309 " 13) Check for orphaned Job records\n"
310 " 14) Check for all Admin records\n"
311 " 15) Check for all Restore records\n"
316 cmd = get_cmd(_("Select function number: "));
318 int item = atoi(cmd);
322 printf(_("Database will %sbe modified.\n"), fix?"":_("NOT "));
325 verbose = verbose?0:1;
326 printf(_("Verbose is %s\n"), verbose?_("On"):_("Off"));
329 repair_bad_filenames();
335 eliminate_duplicate_filenames();
338 eliminate_duplicate_paths();
341 eliminate_orphaned_jobmedia_records();
344 eliminate_orphaned_file_records();
347 eliminate_orphaned_path_records();
350 eliminate_orphaned_filename_records();
353 eliminate_orphaned_fileset_records();
356 eliminate_orphaned_client_records();
359 eliminate_orphaned_job_records();
362 eliminate_admin_records();
365 eliminate_restore_records();
368 repair_bad_filenames();
370 eliminate_duplicate_filenames();
371 eliminate_duplicate_paths();
372 eliminate_orphaned_jobmedia_records();
373 eliminate_orphaned_file_records();
374 eliminate_orphaned_path_records();
375 eliminate_orphaned_filename_records();
376 eliminate_orphaned_fileset_records();
377 eliminate_orphaned_client_records();
378 eliminate_orphaned_job_records();
379 eliminate_admin_records();
380 eliminate_restore_records();
390 static int print_name_handler(void *ctx, int num_fields, char **row)
393 printf("%s\n", row[0]);
398 static int get_name_handler(void *ctx, int num_fields, char **row)
400 POOLMEM *buf = (POOLMEM *)ctx;
402 pm_strcpy(&buf, row[0]);
407 static int print_job_handler(void *ctx, int num_fields, char **row)
409 printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
410 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
415 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
417 printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
418 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
422 static int print_file_handler(void *ctx, int num_fields, char **row)
424 printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
425 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
429 static int print_fileset_handler(void *ctx, int num_fields, char **row)
431 printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
432 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
436 static int print_client_handler(void *ctx, int num_fields, char **row)
438 printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
439 NPRT(row[0]), NPRT(row[1]));
445 * Called here with each id to be added to the list
447 static int id_list_handler(void *ctx, int num_fields, char **row)
449 ID_LIST *lst = (ID_LIST *)ctx;
451 if (lst->num_ids == MAX_ID_LIST_LEN) {
454 if (lst->num_ids == lst->max_ids) {
455 if (lst->max_ids == 0) {
457 lst->Id = (uint32_t *)bmalloc(sizeof(uint32_t) * lst->max_ids);
459 lst->max_ids = (lst->max_ids * 3) / 2;
460 lst->Id = (uint32_t *)brealloc(lst->Id, sizeof(uint32_t) * lst->max_ids);
463 lst->Id[lst->num_ids++] = (uint32_t)strtod(row[0], NULL);
468 * Construct record id list
470 static int make_id_list(const char *query, ID_LIST *id_list)
472 id_list->num_ids = 0;
473 id_list->num_del = 0;
474 id_list->tot_ids = 0;
476 if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
477 printf("%s", db_strerror(db));
484 * Delete all entries in the list
486 static int delete_id_list(const char *query, ID_LIST *id_list)
488 for (int i=0; i < id_list->num_ids; i++) {
489 bsnprintf(buf, sizeof(buf), query, id_list->Id[i]);
491 printf("Deleting: %s\n", buf);
493 db_sql_query(db, buf, NULL, NULL);
499 * Called here with each name to be added to the list
501 static int name_list_handler(void *ctx, int num_fields, char **row)
503 NAME_LIST *name = (NAME_LIST *)ctx;
505 if (name->num_ids == MAX_ID_LIST_LEN) {
508 if (name->num_ids == name->max_ids) {
509 if (name->max_ids == 0) {
510 name->max_ids = 1000;
511 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
513 name->max_ids = (name->max_ids * 3) / 2;
514 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
517 name->name[name->num_ids++] = bstrdup(row[0]);
523 * Construct name list
525 static int make_name_list(const char *query, NAME_LIST *name_list)
527 name_list->num_ids = 0;
528 name_list->num_del = 0;
529 name_list->tot_ids = 0;
531 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
532 printf("%s", db_strerror(db));
539 * Print names in the list
541 static void print_name_list(NAME_LIST *name_list)
543 for (int i=0; i < name_list->num_ids; i++) {
544 printf("%s\n", name_list->name[i]);
550 * Free names in the list
552 static void free_name_list(NAME_LIST *name_list)
554 for (int i=0; i < name_list->num_ids; i++) {
555 free(name_list->name[i]);
557 name_list->num_ids = 0;
560 static void eliminate_duplicate_filenames()
565 printf("Checking for duplicate Filename entries.\n");
567 /* Make list of duplicated names */
568 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
569 "HAVING count(Name) > 1";
571 if (!make_name_list(query, &name_list)) {
574 printf("Found %d duplicate Filename records.\n", name_list.num_ids);
575 if (name_list.num_ids && verbose && yes_no("Print the list? (yes/no): ")) {
576 print_name_list(&name_list);
579 /* Loop through list of duplicate names */
580 for (int i=0; i<name_list.num_ids; i++) {
581 /* Get all the Ids of each name */
582 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
583 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
587 if (!make_id_list(buf, &id_list)) {
591 printf("Found %d for: %s\n", id_list.num_ids, name_list.name[i]);
593 /* Force all records to use the first id then delete the other ids */
594 for (int j=1; j<id_list.num_ids; j++) {
595 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%u WHERE FilenameId=%u",
596 id_list.Id[0], id_list.Id[j]);
600 db_sql_query(db, buf, NULL, NULL);
601 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%u",
606 db_sql_query(db, buf, NULL, NULL);
610 free_name_list(&name_list);
613 static void eliminate_duplicate_paths()
618 printf(_("Checking for duplicate Path entries.\n"));
620 /* Make list of duplicated names */
622 query = "SELECT Path, count(Path) as Count FROM Path "
623 "GROUP BY Path HAVING count(Path) > 1";
625 if (!make_name_list(query, &name_list)) {
628 printf("Found %d duplicate Path records.\n", name_list.num_ids);
629 if (name_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
630 print_name_list(&name_list);
633 /* Loop through list of duplicate names */
634 for (int i=0; i<name_list.num_ids; i++) {
635 /* Get all the Ids of each name */
636 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
637 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
641 if (!make_id_list(buf, &id_list)) {
645 printf("Found %d for: %s\n", id_list.num_ids, name_list.name[i]);
647 /* Force all records to use the first id then delete the other ids */
648 for (int j=1; j<id_list.num_ids; j++) {
649 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%u WHERE PathId=%u",
650 id_list.Id[0], id_list.Id[j]);
654 db_sql_query(db, buf, NULL, NULL);
655 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%u",
660 db_sql_query(db, buf, NULL, NULL);
664 free_name_list(&name_list);
667 static void eliminate_orphaned_jobmedia_records()
671 printf("Checking for orphaned JobMedia entries.\n");
672 query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
673 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
674 "WHERE Job.JobId IS NULL";
675 if (!make_id_list(query, &id_list)) {
678 printf("Found %d orphaned JobMedia records.\n", id_list.num_ids);
679 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
680 for (int i=0; i < id_list.num_ids; i++) {
681 bsnprintf(buf, sizeof(buf),
682 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
683 "WHERE JobMedia.JobMediaId=%u AND Media.MediaId=JobMedia.MediaId", id_list.Id[i]);
684 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
685 printf("%s\n", db_strerror(db));
690 if (fix && id_list.num_ids > 0) {
691 printf("Deleting %d orphaned JobMedia records.\n", id_list.num_ids);
692 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%u", &id_list);
696 static void eliminate_orphaned_file_records()
700 printf("Checking for orphaned File entries. This may take some time!\n");
701 query = "SELECT File.FileId,Job.JobId FROM File "
702 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
703 "WHERE Job.JobId IS NULL";
705 printf("%s\n", query);
707 if (!make_id_list(query, &id_list)) {
710 printf("Found %d orphaned File records.\n", id_list.num_ids);
711 if (name_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
712 for (int i=0; i < id_list.num_ids; i++) {
713 bsnprintf(buf, sizeof(buf),
714 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
715 "WHERE File.FileId=%u AND File.FilenameId=Filename.FilenameId", id_list.Id[i]);
716 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
717 printf("%s\n", db_strerror(db));
722 if (fix && id_list.num_ids > 0) {
723 printf("Deleting %d orphaned File records.\n", id_list.num_ids);
724 delete_id_list("DELETE FROM File WHERE FileId=%u", &id_list);
728 static void eliminate_orphaned_path_records()
732 printf("Checking for orphaned Path entries. This may take some time!\n");
733 query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
734 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
735 "WHERE File.PathId IS NULL";
737 printf("%s\n", query);
739 if (!make_id_list(query, &id_list)) {
742 printf("Found %d orphaned Path records.\n", id_list.num_ids);
743 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
744 for (int i=0; i < id_list.num_ids; i++) {
745 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
746 db_sql_query(db, buf, print_name_handler, NULL);
750 if (fix && id_list.num_ids > 0) {
751 printf("Deleting %d orphaned Path records.\n", id_list.num_ids);
752 delete_id_list("DELETE FROM Path WHERE PathId=%u", &id_list);
756 static void eliminate_orphaned_filename_records()
760 printf("Checking for orphaned Filename entries. This may take some time!\n");
761 query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
762 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
763 "WHERE File.FilenameId IS NULL";
765 printf("%s\n", query);
767 if (!make_id_list(query, &id_list)) {
770 printf("Found %d orphaned Filename records.\n", id_list.num_ids);
771 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
772 for (int i=0; i < id_list.num_ids; i++) {
773 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
774 db_sql_query(db, buf, print_name_handler, NULL);
778 if (fix && id_list.num_ids > 0) {
779 printf("Deleting %d orphaned Filename records.\n", id_list.num_ids);
780 delete_id_list("DELETE FROM Filename WHERE FilenameId=%u", &id_list);
784 static void eliminate_orphaned_fileset_records()
788 printf("Checking for orphaned FileSet entries. This takes some time!\n");
789 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
790 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
791 "WHERE Job.FileSetId IS NULL";
793 printf("%s\n", query);
795 if (!make_id_list(query, &id_list)) {
798 printf("Found %d orphaned FileSet records.\n", id_list.num_ids);
799 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
800 for (int i=0; i < id_list.num_ids; i++) {
801 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
802 "WHERE FileSetId=%u", id_list.Id[i]);
803 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
804 printf("%s\n", db_strerror(db));
809 if (fix && id_list.num_ids > 0) {
810 printf("Deleting %d orphaned FileSet records.\n", id_list.num_ids);
811 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%u", &id_list);
815 static void eliminate_orphaned_client_records()
819 printf("Checking for orphaned Client entries.\n");
821 * Wiffle through Client for every Client
822 * joining with the Job table including every Client even if
823 * there is not a match in Job (left outer join), then
824 * filter out only those where no Job points to a Client
825 * i.e. Job.Client is NULL
827 query = "SELECT Client.ClientId,Client.Name FROM Client "
828 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
829 "WHERE Job.ClientId IS NULL";
831 printf("%s\n", query);
833 if (!make_id_list(query, &id_list)) {
836 printf("Found %d orphaned Client records.\n", id_list.num_ids);
837 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
838 for (int i=0; i < id_list.num_ids; i++) {
839 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
840 "WHERE ClientId=%u", id_list.Id[i]);
841 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
842 printf("%s\n", db_strerror(db));
847 if (fix && id_list.num_ids > 0) {
848 printf("Deleting %d orphaned Client records.\n", id_list.num_ids);
849 delete_id_list("DELETE FROM Client WHERE ClientId=%u", &id_list);
853 static void eliminate_orphaned_job_records()
857 printf("Checking for orphaned Job entries.\n");
859 * Wiffle through Job for every Job
860 * joining with the Client table including every Job even if
861 * there is not a match in Client (left outer join), then
862 * filter out only those where no Client exists
863 * i.e. Client.Name is NULL
865 query = "SELECT Job.JobId,Job.Name FROM Job "
866 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
867 "WHERE Client.Name IS NULL";
869 printf("%s\n", query);
871 if (!make_id_list(query, &id_list)) {
874 printf("Found %d orphaned Job records.\n", id_list.num_ids);
875 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
876 for (int i=0; i < id_list.num_ids; i++) {
877 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
878 "WHERE JobId=%u", id_list.Id[i]);
879 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
880 printf("%s\n", db_strerror(db));
885 if (fix && id_list.num_ids > 0) {
886 printf("Deleting %d orphaned Job records.\n", id_list.num_ids);
887 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
892 static void eliminate_admin_records()
896 printf("Checking for Admin Job entries.\n");
897 query = "SELECT Job.JobId FROM Job "
898 "WHERE Job.Type='D'";
900 printf("%s\n", query);
902 if (!make_id_list(query, &id_list)) {
905 printf("Found %d Admin Job records.\n", id_list.num_ids);
906 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
907 for (int i=0; i < id_list.num_ids; i++) {
908 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
909 "WHERE JobId=%u", id_list.Id[i]);
910 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
911 printf("%s\n", db_strerror(db));
916 if (fix && id_list.num_ids > 0) {
917 printf("Deleting %d Admin Job records.\n", id_list.num_ids);
918 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
922 static void eliminate_restore_records()
926 printf("Checking for Restore Job entries.\n");
927 query = "SELECT Job.JobId FROM Job "
928 "WHERE Job.Type='R'";
930 printf("%s\n", query);
932 if (!make_id_list(query, &id_list)) {
935 printf("Found %d Restore Job records.\n", id_list.num_ids);
936 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
937 for (int i=0; i < id_list.num_ids; i++) {
938 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
939 "WHERE JobId=%u", id_list.Id[i]);
940 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
941 printf("%s\n", db_strerror(db));
946 if (fix && id_list.num_ids > 0) {
947 printf("Deleting %d Restore Job records.\n", id_list.num_ids);
948 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
955 static void repair_bad_filenames()
960 printf("Checking for Filenames with a trailing slash\n");
961 query = "SELECT FilenameId,Name from Filename "
962 "WHERE Name LIKE '%/'";
964 printf("%s\n", query);
966 if (!make_id_list(query, &id_list)) {
969 printf("Found %d bad Filename records.\n", id_list.num_ids);
970 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
971 for (i=0; i < id_list.num_ids; i++) {
972 bsnprintf(buf, sizeof(buf),
973 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
974 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
975 printf("%s\n", db_strerror(db));
980 if (fix && id_list.num_ids > 0) {
981 POOLMEM *name = get_pool_memory(PM_FNAME);
983 printf("Reparing %d bad Filename records.\n", id_list.num_ids);
984 for (i=0; i < id_list.num_ids; i++) {
986 bsnprintf(buf, sizeof(buf),
987 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
988 if (!db_sql_query(db, buf, get_name_handler, name)) {
989 printf("%s\n", db_strerror(db));
991 /* Strip trailing slash(es) */
992 for (len=strlen(name); len > 0 && name[len-1]=='/'; len--)
1000 db_escape_string(esc_name, name, len);
1002 bsnprintf(buf, sizeof(buf),
1003 "UPDATE Filename SET Name='%s' WHERE FilenameId=%u",
1004 esc_name, id_list.Id[i]);
1006 printf("%s\n", buf);
1008 db_sql_query(db, buf, NULL, NULL);
1013 static void repair_bad_paths()
1018 printf("Checking for Paths without a trailing slash\n");
1019 query = "SELECT PathId,Path from Path "
1020 "WHERE Path NOT LIKE '%/'";
1022 printf("%s\n", query);
1024 if (!make_id_list(query, &id_list)) {
1027 printf("Found %d bad Path records.\n", id_list.num_ids);
1028 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
1029 for (i=0; i < id_list.num_ids; i++) {
1030 bsnprintf(buf, sizeof(buf),
1031 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1032 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1033 printf("%s\n", db_strerror(db));
1038 if (fix && id_list.num_ids > 0) {
1039 POOLMEM *name = get_pool_memory(PM_FNAME);
1040 char esc_name[5000];
1041 printf("Reparing %d bad Filename records.\n", id_list.num_ids);
1042 for (i=0; i < id_list.num_ids; i++) {
1044 bsnprintf(buf, sizeof(buf),
1045 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1046 if (!db_sql_query(db, buf, get_name_handler, name)) {
1047 printf("%s\n", db_strerror(db));
1049 /* Strip trailing blanks */
1050 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1053 /* Add trailing slash */
1054 len = pm_strcat(&name, "/");
1055 db_escape_string(esc_name, name, len);
1056 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%u",
1057 esc_name, id_list.Id[i]);
1059 printf("%s\n", buf);
1061 db_sql_query(db, buf, NULL, NULL);
1068 * Gen next input command from the terminal
1070 static char *get_cmd(const char *prompt)
1072 static char cmd[1000];
1074 printf("%s", prompt);
1075 if (fgets(cmd, sizeof(cmd), stdin) == NULL)
1078 strip_trailing_junk(cmd);
1082 static int yes_no(const char *prompt)
1085 cmd = get_cmd(prompt);
1086 return strcasecmp(cmd, "yes") == 0;
1089 bool python_set_prog(JCR*, char const*) { return false; }