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
16 version 2 as amended with additional clauses defined in the
17 file LICENSE in the main source directory.
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
22 the file LICENSE for additional details.
27 #include "cats/cats.h"
28 #include "dird/dird_conf.h"
31 int generate_daemon_event(JCR *jcr, const char *event)
34 typedef struct s_id_ctx {
35 uint32_t *Id; /* ids to be modified */
36 int num_ids; /* ids stored */
37 int max_ids; /* size of array */
38 int num_del; /* number deleted */
39 int tot_ids; /* total to process */
42 typedef struct s_name_ctx {
43 char **name; /* list of names */
44 int num_ids; /* ids stored */
45 int max_ids; /* size of array */
46 int num_del; /* number deleted */
47 int tot_ids; /* total to process */
52 /* Global variables */
53 static bool fix = false;
54 static bool batch = false;
56 static ID_LIST id_list;
57 static NAME_LIST name_list;
58 static char buf[2000];
60 #define MAX_ID_LIST_LEN 10000000
62 /* Forward referenced functions */
63 static int make_id_list(const char *query, ID_LIST *id_list);
64 static int delete_id_list(const char *query, ID_LIST *id_list);
65 static int make_name_list(const char *query, NAME_LIST *name_list);
66 static void print_name_list(NAME_LIST *name_list);
67 static void free_name_list(NAME_LIST *name_list);
68 static char *get_cmd(const char *prompt);
69 static void eliminate_duplicate_filenames();
70 static void eliminate_duplicate_paths();
71 static void eliminate_orphaned_jobmedia_records();
72 static void eliminate_orphaned_file_records();
73 static void eliminate_orphaned_path_records();
74 static void eliminate_orphaned_filename_records();
75 static void eliminate_orphaned_fileset_records();
76 static void eliminate_orphaned_client_records();
77 static void eliminate_orphaned_job_records();
78 static void eliminate_admin_records();
79 static void eliminate_restore_records();
80 static void repair_bad_paths();
81 static void repair_bad_filenames();
82 static void do_interactive_mode();
83 static int yes_no(const char *prompt);
89 "Usage: dbcheck [-c config] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>]\n"
91 " -C catalog name in the director conf file\n"
92 " -c director conf filename\n"
93 " -dnn set debug level to nn\n"
94 " -f fix inconsistencies\n"
96 " -? print this message\n\n");
100 int main (int argc, char *argv[])
103 const char *user, *password, *db_name, *dbhost;
104 char *configfile = NULL;
105 char *catalogname = NULL;
107 my_name_is(argc, argv, "dbcheck");
108 init_msg(NULL, NULL); /* setup message handler */
110 memset(&id_list, 0, sizeof(id_list));
111 memset(&name_list, 0, sizeof(name_list));
114 while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
116 case 'b': /* batch */
120 case 'C': /* CatalogName */
121 catalogname = optarg;
124 case 'c': /* configfile */
128 case 'd': /* debug level */
129 debug_level = atoi(optarg);
130 if (debug_level <= 0)
134 case 'f': /* fix inconsistencies */
154 Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
156 parse_config(configfile);
158 foreach_res(catalog, R_CATALOG) {
159 if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
162 } else if (!catalogname) { // stop on first if no catalogname is given
170 Pmsg2(0, "Error can not find the Catalog name[%s] in the given config file [%s]\n", catalogname, configfile);
172 Pmsg1(0, "Error there is no Catalog section in the given config file [%s]\n", configfile);
178 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
181 Pmsg0(0, "Error no Director resource defined.\n");
184 set_working_directory(director->working_directory);
185 db_name = catalog->db_name;
186 user = catalog->db_user;
187 password = catalog->db_password;
188 dbhost = catalog->db_address;
189 if (dbhost && dbhost[0] == 0) {
195 Pmsg0(0, _("Wrong number of arguments.\n"));
200 Pmsg0(0, _("Working directory not supplied.\n"));
204 /* This is needed by SQLite to find the db */
205 working_directory = argv[0];
214 } else if (argc == 3) {
217 } else if (argc == 4) {
221 } else if (argc == 5) {
230 db = db_init_database(NULL, db_name, user, password, dbhost, 0, NULL, 0);
231 if (!db_open_database(NULL, db)) {
232 Emsg1(M_FATAL, 0, "%s", db_strerror(db));
238 repair_bad_filenames();
239 eliminate_duplicate_filenames();
240 eliminate_duplicate_paths();
241 eliminate_orphaned_jobmedia_records();
242 eliminate_orphaned_file_records();
243 eliminate_orphaned_path_records();
244 eliminate_orphaned_filename_records();
245 eliminate_orphaned_fileset_records();
246 eliminate_orphaned_client_records();
247 eliminate_orphaned_job_records();
248 eliminate_admin_records();
249 eliminate_restore_records();
251 do_interactive_mode();
254 db_close_database(NULL, db);
260 static void do_interactive_mode()
265 printf("Hello, this is the database check/correct program.\n"
266 "Modify database is %s. Verbose is %s.\n"
267 "Please select the fuction you want to perform.\n",
268 fix?"On":"Off", verbose?"On":"Off");
273 " 1) Toggle modify database flag\n"
274 " 2) Toggle verbose flag\n"
275 " 3) Repair bad Filename records\n"
276 " 4) Repair bad Path records\n"
277 " 5) Eliminate duplicate Filename records\n"
278 " 6) Eliminate duplicate Path records\n"
279 " 7) Eliminate orphaned Jobmedia records\n"
280 " 8) Eliminate orphaned File records\n"
281 " 9) Eliminate orphaned Path records\n"
282 " 10) Eliminate orphaned Filename records\n"
283 " 11) Eliminate orphaned FileSet records\n"
284 " 12) Eliminate orphaned Client records\n"
285 " 13) Eliminate orphaned Job records\n"
286 " 14) Eliminate all Admin records\n"
287 " 15) Eliminate all Restore records\n"
292 " 1) Toggle modify database flag\n"
293 " 2) Toggle verbose flag\n"
294 " 3) Check for bad Filename records\n"
295 " 4) Check for bad Path records\n"
296 " 5) Check for duplicate Filename records\n"
297 " 6) Check for duplicate Path records\n"
298 " 7) Check for orphaned Jobmedia records\n"
299 " 8) Check for orphaned File records\n"
300 " 9) Check for orphaned Path records\n"
301 " 10) Check for orphaned Filename records\n"
302 " 11) Check for orphaned FileSet records\n"
303 " 12) Check for orphaned Client records\n"
304 " 13) Check for orphaned Job records\n"
305 " 14) Check for all Admin records\n"
306 " 15) Check for all Restore records\n"
311 cmd = get_cmd(_("Select function number: "));
313 int item = atoi(cmd);
317 printf(_("Database will %sbe modified.\n"), fix?"":_("NOT "));
320 verbose = verbose?0:1;
321 printf(_("Verbose is %s\n"), verbose?_("On"):_("Off"));
324 repair_bad_filenames();
330 eliminate_duplicate_filenames();
333 eliminate_duplicate_paths();
336 eliminate_orphaned_jobmedia_records();
339 eliminate_orphaned_file_records();
342 eliminate_orphaned_path_records();
345 eliminate_orphaned_filename_records();
348 eliminate_orphaned_fileset_records();
351 eliminate_orphaned_client_records();
354 eliminate_orphaned_job_records();
357 eliminate_admin_records();
360 eliminate_restore_records();
363 repair_bad_filenames();
365 eliminate_duplicate_filenames();
366 eliminate_duplicate_paths();
367 eliminate_orphaned_jobmedia_records();
368 eliminate_orphaned_file_records();
369 eliminate_orphaned_path_records();
370 eliminate_orphaned_filename_records();
371 eliminate_orphaned_fileset_records();
372 eliminate_orphaned_client_records();
373 eliminate_orphaned_job_records();
374 eliminate_admin_records();
375 eliminate_restore_records();
385 static int print_name_handler(void *ctx, int num_fields, char **row)
388 printf("%s\n", row[0]);
393 static int get_name_handler(void *ctx, int num_fields, char **row)
395 POOLMEM *buf = (POOLMEM *)ctx;
397 pm_strcpy(&buf, row[0]);
402 static int print_job_handler(void *ctx, int num_fields, char **row)
404 printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
405 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
410 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
412 printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
413 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
417 static int print_file_handler(void *ctx, int num_fields, char **row)
419 printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
420 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
424 static int print_fileset_handler(void *ctx, int num_fields, char **row)
426 printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
427 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
431 static int print_client_handler(void *ctx, int num_fields, char **row)
433 printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
434 NPRT(row[0]), NPRT(row[1]));
440 * Called here with each id to be added to the list
442 static int id_list_handler(void *ctx, int num_fields, char **row)
444 ID_LIST *lst = (ID_LIST *)ctx;
446 if (lst->num_ids == MAX_ID_LIST_LEN) {
449 if (lst->num_ids == lst->max_ids) {
450 if (lst->max_ids == 0) {
452 lst->Id = (uint32_t *)bmalloc(sizeof(uint32_t) * lst->max_ids);
454 lst->max_ids = (lst->max_ids * 3) / 2;
455 lst->Id = (uint32_t *)brealloc(lst->Id, sizeof(uint32_t) * lst->max_ids);
458 lst->Id[lst->num_ids++] = (uint32_t)strtod(row[0], NULL);
463 * Construct record id list
465 static int make_id_list(const char *query, ID_LIST *id_list)
467 id_list->num_ids = 0;
468 id_list->num_del = 0;
469 id_list->tot_ids = 0;
471 if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
472 printf("%s", db_strerror(db));
479 * Delete all entries in the list
481 static int delete_id_list(const char *query, ID_LIST *id_list)
483 for (int i=0; i < id_list->num_ids; i++) {
484 bsnprintf(buf, sizeof(buf), query, id_list->Id[i]);
486 printf("Deleting: %s\n", buf);
488 db_sql_query(db, buf, NULL, NULL);
494 * Called here with each name to be added to the list
496 static int name_list_handler(void *ctx, int num_fields, char **row)
498 NAME_LIST *name = (NAME_LIST *)ctx;
500 if (name->num_ids == MAX_ID_LIST_LEN) {
503 if (name->num_ids == name->max_ids) {
504 if (name->max_ids == 0) {
505 name->max_ids = 1000;
506 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
508 name->max_ids = (name->max_ids * 3) / 2;
509 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
512 name->name[name->num_ids++] = bstrdup(row[0]);
518 * Construct name list
520 static int make_name_list(const char *query, NAME_LIST *name_list)
522 name_list->num_ids = 0;
523 name_list->num_del = 0;
524 name_list->tot_ids = 0;
526 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
527 printf("%s", db_strerror(db));
534 * Print names in the list
536 static void print_name_list(NAME_LIST *name_list)
538 for (int i=0; i < name_list->num_ids; i++) {
539 printf("%s\n", name_list->name[i]);
545 * Free names in the list
547 static void free_name_list(NAME_LIST *name_list)
549 for (int i=0; i < name_list->num_ids; i++) {
550 free(name_list->name[i]);
552 name_list->num_ids = 0;
555 static void eliminate_duplicate_filenames()
560 printf("Checking for duplicate Filename entries.\n");
562 /* Make list of duplicated names */
563 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
564 "HAVING count(Name) > 1";
566 if (!make_name_list(query, &name_list)) {
569 printf("Found %d duplicate Filename records.\n", name_list.num_ids);
570 if (name_list.num_ids && verbose && yes_no("Print the list? (yes/no): ")) {
571 print_name_list(&name_list);
574 /* Loop through list of duplicate names */
575 for (int i=0; i<name_list.num_ids; i++) {
576 /* Get all the Ids of each name */
577 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
578 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
582 if (!make_id_list(buf, &id_list)) {
586 printf("Found %d for: %s\n", id_list.num_ids, name_list.name[i]);
588 /* Force all records to use the first id then delete the other ids */
589 for (int j=1; j<id_list.num_ids; j++) {
590 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%u WHERE FilenameId=%u",
591 id_list.Id[0], id_list.Id[j]);
595 db_sql_query(db, buf, NULL, NULL);
596 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%u",
601 db_sql_query(db, buf, NULL, NULL);
605 free_name_list(&name_list);
608 static void eliminate_duplicate_paths()
613 printf(_("Checking for duplicate Path entries.\n"));
615 /* Make list of duplicated names */
617 query = "SELECT Path, count(Path) as Count FROM Path "
618 "GROUP BY Path HAVING count(Path) > 1";
620 if (!make_name_list(query, &name_list)) {
623 printf("Found %d duplicate Path records.\n", name_list.num_ids);
624 if (name_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
625 print_name_list(&name_list);
628 /* Loop through list of duplicate names */
629 for (int i=0; i<name_list.num_ids; i++) {
630 /* Get all the Ids of each name */
631 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
632 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
636 if (!make_id_list(buf, &id_list)) {
640 printf("Found %d for: %s\n", id_list.num_ids, name_list.name[i]);
642 /* Force all records to use the first id then delete the other ids */
643 for (int j=1; j<id_list.num_ids; j++) {
644 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%u WHERE PathId=%u",
645 id_list.Id[0], id_list.Id[j]);
649 db_sql_query(db, buf, NULL, NULL);
650 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%u",
655 db_sql_query(db, buf, NULL, NULL);
659 free_name_list(&name_list);
662 static void eliminate_orphaned_jobmedia_records()
666 printf("Checking for orphaned JobMedia entries.\n");
667 query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
668 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
669 "WHERE Job.JobId IS NULL";
670 if (!make_id_list(query, &id_list)) {
673 printf("Found %d orphaned JobMedia records.\n", id_list.num_ids);
674 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
675 for (int i=0; i < id_list.num_ids; i++) {
676 bsnprintf(buf, sizeof(buf),
677 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
678 "WHERE JobMedia.JobMediaId=%u AND Media.MediaId=JobMedia.MediaId", id_list.Id[i]);
679 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
680 printf("%s\n", db_strerror(db));
685 if (fix && id_list.num_ids > 0) {
686 printf("Deleting %d orphaned JobMedia records.\n", id_list.num_ids);
687 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%u", &id_list);
691 static void eliminate_orphaned_file_records()
695 printf("Checking for orphaned File entries. This may take some time!\n");
696 query = "SELECT File.FileId,Job.JobId FROM File "
697 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
698 "WHERE Job.JobId IS NULL";
700 printf("%s\n", query);
702 if (!make_id_list(query, &id_list)) {
705 printf("Found %d orphaned File records.\n", id_list.num_ids);
706 if (name_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
707 for (int i=0; i < id_list.num_ids; i++) {
708 bsnprintf(buf, sizeof(buf),
709 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
710 "WHERE File.FileId=%u AND File.FilenameId=Filename.FilenameId", id_list.Id[i]);
711 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
712 printf("%s\n", db_strerror(db));
717 if (fix && id_list.num_ids > 0) {
718 printf("Deleting %d orphaned File records.\n", id_list.num_ids);
719 delete_id_list("DELETE FROM File WHERE FileId=%u", &id_list);
723 static void eliminate_orphaned_path_records()
727 printf("Checking for orphaned Path entries. This may take some time!\n");
728 query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
729 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
730 "WHERE File.PathId IS NULL";
732 printf("%s\n", query);
734 if (!make_id_list(query, &id_list)) {
737 printf("Found %d orphaned Path records.\n", id_list.num_ids);
738 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
739 for (int i=0; i < id_list.num_ids; i++) {
740 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
741 db_sql_query(db, buf, print_name_handler, NULL);
745 if (fix && id_list.num_ids > 0) {
746 printf("Deleting %d orphaned Path records.\n", id_list.num_ids);
747 delete_id_list("DELETE FROM Path WHERE PathId=%u", &id_list);
751 static void eliminate_orphaned_filename_records()
755 printf("Checking for orphaned Filename entries. This may take some time!\n");
756 query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
757 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
758 "WHERE File.FilenameId IS NULL";
760 printf("%s\n", query);
762 if (!make_id_list(query, &id_list)) {
765 printf("Found %d orphaned Filename records.\n", id_list.num_ids);
766 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
767 for (int i=0; i < id_list.num_ids; i++) {
768 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
769 db_sql_query(db, buf, print_name_handler, NULL);
773 if (fix && id_list.num_ids > 0) {
774 printf("Deleting %d orphaned Filename records.\n", id_list.num_ids);
775 delete_id_list("DELETE FROM Filename WHERE FilenameId=%u", &id_list);
779 static void eliminate_orphaned_fileset_records()
783 printf("Checking for orphaned FileSet entries. This takes some time!\n");
784 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
785 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
786 "WHERE Job.FileSetId IS NULL";
788 printf("%s\n", query);
790 if (!make_id_list(query, &id_list)) {
793 printf("Found %d orphaned FileSet records.\n", id_list.num_ids);
794 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
795 for (int i=0; i < id_list.num_ids; i++) {
796 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
797 "WHERE FileSetId=%u", id_list.Id[i]);
798 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
799 printf("%s\n", db_strerror(db));
804 if (fix && id_list.num_ids > 0) {
805 printf("Deleting %d orphaned FileSet records.\n", id_list.num_ids);
806 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%u", &id_list);
810 static void eliminate_orphaned_client_records()
814 printf("Checking for orphaned Client entries.\n");
816 * Wiffle through Client for every Client
817 * joining with the Job table including every Client even if
818 * there is not a match in Job (left outer join), then
819 * filter out only those where no Job points to a Client
820 * i.e. Job.Client is NULL
822 query = "SELECT Client.ClientId,Client.Name FROM Client "
823 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
824 "WHERE Job.ClientId IS NULL";
826 printf("%s\n", query);
828 if (!make_id_list(query, &id_list)) {
831 printf("Found %d orphaned Client records.\n", id_list.num_ids);
832 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
833 for (int i=0; i < id_list.num_ids; i++) {
834 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
835 "WHERE ClientId=%u", id_list.Id[i]);
836 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
837 printf("%s\n", db_strerror(db));
842 if (fix && id_list.num_ids > 0) {
843 printf("Deleting %d orphaned Client records.\n", id_list.num_ids);
844 delete_id_list("DELETE FROM Client WHERE ClientId=%u", &id_list);
848 static void eliminate_orphaned_job_records()
852 printf("Checking for orphaned Job entries.\n");
854 * Wiffle through Job for every Job
855 * joining with the Client table including every Job even if
856 * there is not a match in Client (left outer join), then
857 * filter out only those where no Client exists
858 * i.e. Client.Name is NULL
860 query = "SELECT Job.JobId,Job.Name FROM Job "
861 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
862 "WHERE Client.Name IS NULL";
864 printf("%s\n", query);
866 if (!make_id_list(query, &id_list)) {
869 printf("Found %d orphaned Job records.\n", id_list.num_ids);
870 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
871 for (int i=0; i < id_list.num_ids; i++) {
872 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
873 "WHERE JobId=%u", id_list.Id[i]);
874 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
875 printf("%s\n", db_strerror(db));
880 if (fix && id_list.num_ids > 0) {
881 printf("Deleting %d orphaned Job records.\n", id_list.num_ids);
882 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
887 static void eliminate_admin_records()
891 printf("Checking for Admin Job entries.\n");
892 query = "SELECT Job.JobId FROM Job "
893 "WHERE Job.Type='D'";
895 printf("%s\n", query);
897 if (!make_id_list(query, &id_list)) {
900 printf("Found %d Admin Job records.\n", id_list.num_ids);
901 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
902 for (int i=0; i < id_list.num_ids; i++) {
903 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
904 "WHERE JobId=%u", id_list.Id[i]);
905 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
906 printf("%s\n", db_strerror(db));
911 if (fix && id_list.num_ids > 0) {
912 printf("Deleting %d Admin Job records.\n", id_list.num_ids);
913 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
917 static void eliminate_restore_records()
921 printf("Checking for Restore Job entries.\n");
922 query = "SELECT Job.JobId FROM Job "
923 "WHERE Job.Type='R'";
925 printf("%s\n", query);
927 if (!make_id_list(query, &id_list)) {
930 printf("Found %d Restore Job records.\n", id_list.num_ids);
931 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
932 for (int i=0; i < id_list.num_ids; i++) {
933 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
934 "WHERE JobId=%u", id_list.Id[i]);
935 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
936 printf("%s\n", db_strerror(db));
941 if (fix && id_list.num_ids > 0) {
942 printf("Deleting %d Restore Job records.\n", id_list.num_ids);
943 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
950 static void repair_bad_filenames()
955 printf("Checking for Filenames with a trailing slash\n");
956 query = "SELECT FilenameId,Name from Filename "
957 "WHERE Name LIKE '%/'";
959 printf("%s\n", query);
961 if (!make_id_list(query, &id_list)) {
964 printf("Found %d bad Filename records.\n", id_list.num_ids);
965 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
966 for (i=0; i < id_list.num_ids; i++) {
967 bsnprintf(buf, sizeof(buf),
968 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
969 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
970 printf("%s\n", db_strerror(db));
975 if (fix && id_list.num_ids > 0) {
976 POOLMEM *name = get_pool_memory(PM_FNAME);
978 printf("Reparing %d bad Filename records.\n", id_list.num_ids);
979 for (i=0; i < id_list.num_ids; i++) {
981 bsnprintf(buf, sizeof(buf),
982 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
983 if (!db_sql_query(db, buf, get_name_handler, name)) {
984 printf("%s\n", db_strerror(db));
986 /* Strip trailing slash(es) */
987 for (len=strlen(name); len > 0 && name[len-1]=='/'; len--)
995 db_escape_string(esc_name, name, len);
997 bsnprintf(buf, sizeof(buf),
998 "UPDATE Filename SET Name='%s' WHERE FilenameId=%u",
999 esc_name, id_list.Id[i]);
1001 printf("%s\n", buf);
1003 db_sql_query(db, buf, NULL, NULL);
1008 static void repair_bad_paths()
1013 printf("Checking for Paths without a trailing slash\n");
1014 query = "SELECT PathId,Path from Path "
1015 "WHERE Path NOT LIKE '%/'";
1017 printf("%s\n", query);
1019 if (!make_id_list(query, &id_list)) {
1022 printf("Found %d bad Path records.\n", id_list.num_ids);
1023 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
1024 for (i=0; i < id_list.num_ids; i++) {
1025 bsnprintf(buf, sizeof(buf),
1026 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1027 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1028 printf("%s\n", db_strerror(db));
1033 if (fix && id_list.num_ids > 0) {
1034 POOLMEM *name = get_pool_memory(PM_FNAME);
1035 char esc_name[5000];
1036 printf("Reparing %d bad Filename records.\n", id_list.num_ids);
1037 for (i=0; i < id_list.num_ids; i++) {
1039 bsnprintf(buf, sizeof(buf),
1040 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1041 if (!db_sql_query(db, buf, get_name_handler, name)) {
1042 printf("%s\n", db_strerror(db));
1044 /* Strip trailing blanks */
1045 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1048 /* Add trailing slash */
1049 len = pm_strcat(&name, "/");
1050 db_escape_string(esc_name, name, len);
1051 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%u",
1052 esc_name, id_list.Id[i]);
1054 printf("%s\n", buf);
1056 db_sql_query(db, buf, NULL, NULL);
1063 * Gen next input command from the terminal
1065 static char *get_cmd(const char *prompt)
1067 static char cmd[1000];
1069 printf("%s", prompt);
1070 if (fgets(cmd, sizeof(cmd), stdin) == NULL)
1073 strip_trailing_junk(cmd);
1077 static int yes_no(const char *prompt)
1080 cmd = get_cmd(prompt);
1081 return strcasecmp(cmd, "yes") == 0;
1084 bool python_set_prog(JCR*, char const*) { return false; }