3 * Program to check a Bacula database for consistency and to
6 * Kern E. Sibbald, August 2002
12 Copyright (C) 2002-2006 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 "lib/runscript.h"
29 #include "dird/dird_conf.h"
32 int generate_daemon_event(JCR *jcr, const char *event)
35 typedef struct s_id_ctx {
36 uint32_t *Id; /* ids to be modified */
37 int num_ids; /* ids stored */
38 int max_ids; /* size of array */
39 int num_del; /* number deleted */
40 int tot_ids; /* total to process */
43 typedef struct s_name_ctx {
44 char **name; /* list of names */
45 int num_ids; /* ids stored */
46 int max_ids; /* size of array */
47 int num_del; /* number deleted */
48 int tot_ids; /* total to process */
53 /* Global variables */
54 static bool fix = false;
55 static bool batch = false;
57 static ID_LIST id_list;
58 static NAME_LIST name_list;
59 static char buf[20000];
60 static bool quit = false;
62 #define MAX_ID_LIST_LEN 10000000
64 /* Forward referenced functions */
65 static int make_id_list(const char *query, ID_LIST *id_list);
66 static int delete_id_list(const char *query, ID_LIST *id_list);
67 static int make_name_list(const char *query, NAME_LIST *name_list);
68 static void print_name_list(NAME_LIST *name_list);
69 static void free_name_list(NAME_LIST *name_list);
70 static char *get_cmd(const char *prompt);
71 static void eliminate_duplicate_filenames();
72 static void eliminate_duplicate_paths();
73 static void eliminate_orphaned_jobmedia_records();
74 static void eliminate_orphaned_file_records();
75 static void eliminate_orphaned_path_records();
76 static void eliminate_orphaned_filename_records();
77 static void eliminate_orphaned_fileset_records();
78 static void eliminate_orphaned_client_records();
79 static void eliminate_orphaned_job_records();
80 static void eliminate_admin_records();
81 static void eliminate_restore_records();
82 static void repair_bad_paths();
83 static void repair_bad_filenames();
84 static void do_interactive_mode();
85 static bool yes_no(const char *prompt);
91 "Usage: dbcheck [-c config] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>]\n"
93 " -C catalog name in the director conf file\n"
94 " -c director conf filename\n"
95 " -dnn set debug level to nn\n"
96 " -f fix inconsistencies\n"
98 " -? print this message\n\n");
102 int main (int argc, char *argv[])
105 const char *user, *password, *db_name, *dbhost;
106 char *configfile = NULL;
107 char *catalogname = NULL;
109 setlocale(LC_ALL, "");
110 bindtextdomain("bacula", LOCALEDIR);
111 textdomain("bacula");
113 my_name_is(argc, argv, "dbcheck");
114 init_msg(NULL, NULL); /* setup message handler */
116 memset(&id_list, 0, sizeof(id_list));
117 memset(&name_list, 0, sizeof(name_list));
120 while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
122 case 'b': /* batch */
126 case 'C': /* CatalogName */
127 catalogname = optarg;
130 case 'c': /* configfile */
134 case 'd': /* debug level */
135 debug_level = atoi(optarg);
136 if (debug_level <= 0)
140 case 'f': /* fix inconsistencies */
162 Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
164 parse_config(configfile);
166 foreach_res(catalog, R_CATALOG) {
167 if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
170 } else if (!catalogname) { // stop on first if no catalogname is given
178 Pmsg2(0, _("Error can not find the Catalog name[%s] in the given config file [%s]\n"), catalogname, configfile);
180 Pmsg1(0, _("Error there is no Catalog section in the given config file [%s]\n"), configfile);
186 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
189 Pmsg0(0, _("Error no Director resource defined.\n"));
192 set_working_directory(director->working_directory);
193 db_name = catalog->db_name;
194 user = catalog->db_user;
195 password = catalog->db_password;
196 dbhost = catalog->db_address;
197 if (dbhost && dbhost[0] == 0) {
203 Pmsg0(0, _("Wrong number of arguments.\n"));
208 Pmsg0(0, _("Working directory not supplied.\n"));
212 /* This is needed by SQLite to find the db */
213 working_directory = argv[0];
222 } else if (argc == 3) {
225 } else if (argc == 4) {
229 } else if (argc == 5) {
238 db = db_init_database(NULL, db_name, user, password, dbhost, 0, NULL, 0);
239 if (!db_open_database(NULL, db)) {
240 Emsg1(M_FATAL, 0, "%s", db_strerror(db));
246 repair_bad_filenames();
247 eliminate_duplicate_filenames();
248 eliminate_duplicate_paths();
249 eliminate_orphaned_jobmedia_records();
250 eliminate_orphaned_file_records();
251 eliminate_orphaned_path_records();
252 eliminate_orphaned_filename_records();
253 eliminate_orphaned_fileset_records();
254 eliminate_orphaned_client_records();
255 eliminate_orphaned_job_records();
256 eliminate_admin_records();
257 eliminate_restore_records();
259 do_interactive_mode();
262 db_close_database(NULL, db);
268 static void do_interactive_mode()
272 printf(_("Hello, this is the database check/correct program.\n"));
274 printf(_("Modify database is on."));
276 printf(_("Modify database is off."));
278 printf(_(" Verbose is on.\n"));
280 printf(_(" Verbose is off.\n"));
282 printf(_("Please select the fuction you want to perform.\n"));
287 " 1) Toggle modify database flag\n"
288 " 2) Toggle verbose flag\n"
289 " 3) Repair bad Filename records\n"
290 " 4) Repair bad Path records\n"
291 " 5) Eliminate duplicate Filename records\n"
292 " 6) Eliminate duplicate Path records\n"
293 " 7) Eliminate orphaned Jobmedia records\n"
294 " 8) Eliminate orphaned File records\n"
295 " 9) Eliminate orphaned Path records\n"
296 " 10) Eliminate orphaned Filename records\n"
297 " 11) Eliminate orphaned FileSet records\n"
298 " 12) Eliminate orphaned Client records\n"
299 " 13) Eliminate orphaned Job records\n"
300 " 14) Eliminate all Admin records\n"
301 " 15) Eliminate all Restore records\n"
306 " 1) Toggle modify database flag\n"
307 " 2) Toggle verbose flag\n"
308 " 3) Check for bad Filename records\n"
309 " 4) Check for bad Path records\n"
310 " 5) Check for duplicate Filename records\n"
311 " 6) Check for duplicate Path records\n"
312 " 7) Check for orphaned Jobmedia records\n"
313 " 8) Check for orphaned File records\n"
314 " 9) Check for orphaned Path records\n"
315 " 10) Check for orphaned Filename records\n"
316 " 11) Check for orphaned FileSet records\n"
317 " 12) Check for orphaned Client records\n"
318 " 13) Check for orphaned Job records\n"
319 " 14) Check for all Admin records\n"
320 " 15) Check for all Restore records\n"
325 cmd = get_cmd(_("Select function number: "));
327 int item = atoi(cmd);
332 printf(_("Database will be modified.\n"));
334 printf(_("Database will NOT be modified.\n"));
337 verbose = verbose?0:1;
339 printf(_(" Verbose is on.\n"));
341 printf(_(" Verbose is off.\n"));
344 repair_bad_filenames();
350 eliminate_duplicate_filenames();
353 eliminate_duplicate_paths();
356 eliminate_orphaned_jobmedia_records();
359 eliminate_orphaned_file_records();
362 eliminate_orphaned_path_records();
365 eliminate_orphaned_filename_records();
368 eliminate_orphaned_fileset_records();
371 eliminate_orphaned_client_records();
374 eliminate_orphaned_job_records();
377 eliminate_admin_records();
380 eliminate_restore_records();
383 repair_bad_filenames();
385 eliminate_duplicate_filenames();
386 eliminate_duplicate_paths();
387 eliminate_orphaned_jobmedia_records();
388 eliminate_orphaned_file_records();
389 eliminate_orphaned_path_records();
390 eliminate_orphaned_filename_records();
391 eliminate_orphaned_fileset_records();
392 eliminate_orphaned_client_records();
393 eliminate_orphaned_job_records();
394 eliminate_admin_records();
395 eliminate_restore_records();
405 static int print_name_handler(void *ctx, int num_fields, char **row)
408 printf("%s\n", row[0]);
413 static int get_name_handler(void *ctx, int num_fields, char **row)
415 POOLMEM *buf = (POOLMEM *)ctx;
417 pm_strcpy(&buf, row[0]);
422 static int print_job_handler(void *ctx, int num_fields, char **row)
424 printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
425 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
430 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
432 printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
433 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
437 static int print_file_handler(void *ctx, int num_fields, char **row)
439 printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
440 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
444 static int print_fileset_handler(void *ctx, int num_fields, char **row)
446 printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
447 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
451 static int print_client_handler(void *ctx, int num_fields, char **row)
453 printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
454 NPRT(row[0]), NPRT(row[1]));
460 * Called here with each id to be added to the list
462 static int id_list_handler(void *ctx, int num_fields, char **row)
464 ID_LIST *lst = (ID_LIST *)ctx;
466 if (lst->num_ids == MAX_ID_LIST_LEN) {
469 if (lst->num_ids == lst->max_ids) {
470 if (lst->max_ids == 0) {
471 lst->max_ids = 10000;
472 lst->Id = (uint32_t *)bmalloc(sizeof(uint32_t) * lst->max_ids);
474 lst->max_ids = (lst->max_ids * 3) / 2;
475 lst->Id = (uint32_t *)brealloc(lst->Id, sizeof(uint32_t) * lst->max_ids);
478 lst->Id[lst->num_ids++] = (uint32_t)strtod(row[0], NULL);
483 * Construct record id list
485 static int make_id_list(const char *query, ID_LIST *id_list)
487 id_list->num_ids = 0;
488 id_list->num_del = 0;
489 id_list->tot_ids = 0;
491 if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
492 printf("%s", db_strerror(db));
499 * Delete all entries in the list
501 static int delete_id_list(const char *query, ID_LIST *id_list)
503 for (int i=0; i < id_list->num_ids; i++) {
504 bsnprintf(buf, sizeof(buf), query, id_list->Id[i]);
506 printf(_("Deleting: %s\n"), buf);
508 db_sql_query(db, buf, NULL, NULL);
514 * Called here with each name to be added to the list
516 static int name_list_handler(void *ctx, int num_fields, char **row)
518 NAME_LIST *name = (NAME_LIST *)ctx;
520 if (name->num_ids == MAX_ID_LIST_LEN) {
523 if (name->num_ids == name->max_ids) {
524 if (name->max_ids == 0) {
525 name->max_ids = 10000;
526 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
528 name->max_ids = (name->max_ids * 3) / 2;
529 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
532 name->name[name->num_ids++] = bstrdup(row[0]);
538 * Construct name list
540 static int make_name_list(const char *query, NAME_LIST *name_list)
542 name_list->num_ids = 0;
543 name_list->num_del = 0;
544 name_list->tot_ids = 0;
546 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
547 printf("%s", db_strerror(db));
554 * Print names in the list
556 static void print_name_list(NAME_LIST *name_list)
558 for (int i=0; i < name_list->num_ids; i++) {
559 printf("%s\n", name_list->name[i]);
565 * Free names in the list
567 static void free_name_list(NAME_LIST *name_list)
569 for (int i=0; i < name_list->num_ids; i++) {
570 free(name_list->name[i]);
572 name_list->num_ids = 0;
575 static void eliminate_duplicate_filenames()
580 printf(_("Checking for duplicate Filename entries.\n"));
582 /* Make list of duplicated names */
583 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
584 "HAVING count(Name) > 1";
586 if (!make_name_list(query, &name_list)) {
589 printf(_("Found %d duplicate Filename records.\n"), name_list.num_ids);
590 if (name_list.num_ids && verbose && yes_no(_("Print the list? (yes/no): "))) {
591 print_name_list(&name_list);
597 /* Loop through list of duplicate names */
598 for (int i=0; i<name_list.num_ids; i++) {
599 /* Get all the Ids of each name */
600 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
601 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
605 if (!make_id_list(buf, &id_list)) {
609 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
611 /* Force all records to use the first id then delete the other ids */
612 for (int j=1; j<id_list.num_ids; j++) {
613 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%u WHERE FilenameId=%u",
614 id_list.Id[0], id_list.Id[j]);
618 db_sql_query(db, buf, NULL, NULL);
619 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%u",
624 db_sql_query(db, buf, NULL, NULL);
628 free_name_list(&name_list);
631 static void eliminate_duplicate_paths()
636 printf(_("Checking for duplicate Path entries.\n"));
638 /* Make list of duplicated names */
640 query = "SELECT Path, count(Path) as Count FROM Path "
641 "GROUP BY Path HAVING count(Path) > 1";
643 if (!make_name_list(query, &name_list)) {
646 printf(_("Found %d duplicate Path records.\n"), name_list.num_ids);
647 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
648 print_name_list(&name_list);
654 /* Loop through list of duplicate names */
655 for (int i=0; i<name_list.num_ids; i++) {
656 /* Get all the Ids of each name */
657 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
658 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
662 if (!make_id_list(buf, &id_list)) {
666 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
668 /* Force all records to use the first id then delete the other ids */
669 for (int j=1; j<id_list.num_ids; j++) {
670 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%u WHERE PathId=%u",
671 id_list.Id[0], id_list.Id[j]);
675 db_sql_query(db, buf, NULL, NULL);
676 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%u",
681 db_sql_query(db, buf, NULL, NULL);
685 free_name_list(&name_list);
688 static void eliminate_orphaned_jobmedia_records()
692 printf(_("Checking for orphaned JobMedia entries.\n"));
693 query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
694 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
695 "WHERE Job.JobId IS NULL";
696 if (!make_id_list(query, &id_list)) {
699 printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
700 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
701 for (int i=0; i < id_list.num_ids; i++) {
702 bsnprintf(buf, sizeof(buf),
703 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
704 "WHERE JobMedia.JobMediaId=%u AND Media.MediaId=JobMedia.MediaId", id_list.Id[i]);
705 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
706 printf("%s\n", db_strerror(db));
714 if (fix && id_list.num_ids > 0) {
715 printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
716 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%u", &id_list);
720 static void eliminate_orphaned_file_records()
724 printf(_("Checking for orphaned File entries. This may take some time!\n"));
725 query = "SELECT File.FileId,Job.JobId FROM File "
726 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
727 "WHERE Job.JobId IS NULL";
729 printf("%s\n", query);
731 if (!make_id_list(query, &id_list)) {
734 printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
735 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
736 for (int i=0; i < id_list.num_ids; i++) {
737 bsnprintf(buf, sizeof(buf),
738 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
739 "WHERE File.FileId=%u AND File.FilenameId=Filename.FilenameId", id_list.Id[i]);
740 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
741 printf("%s\n", db_strerror(db));
748 if (fix && id_list.num_ids > 0) {
749 printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
750 delete_id_list("DELETE FROM File WHERE FileId=%u", &id_list);
754 static void eliminate_orphaned_path_records()
758 printf(_("Checking for orphaned Path entries. This may take some time!\n"));
759 query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
760 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
761 "WHERE File.PathId IS NULL";
763 printf("%s\n", query);
765 if (!make_id_list(query, &id_list)) {
768 printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
769 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
770 for (int i=0; i < id_list.num_ids; i++) {
771 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
772 db_sql_query(db, buf, print_name_handler, NULL);
778 if (fix && id_list.num_ids > 0) {
779 printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
780 delete_id_list("DELETE FROM Path WHERE PathId=%u", &id_list);
784 static void eliminate_orphaned_filename_records()
788 printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
789 query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
790 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
791 "WHERE File.FilenameId IS NULL";
793 printf("%s\n", query);
795 if (!make_id_list(query, &id_list)) {
798 printf(_("Found %d orphaned Filename 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 Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
802 db_sql_query(db, buf, print_name_handler, NULL);
808 if (fix && id_list.num_ids > 0) {
809 printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
810 delete_id_list("DELETE FROM Filename WHERE FilenameId=%u", &id_list);
814 static void eliminate_orphaned_fileset_records()
818 printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
819 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
820 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
821 "WHERE Job.FileSetId IS NULL";
823 printf("%s\n", query);
825 if (!make_id_list(query, &id_list)) {
828 printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
829 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
830 for (int i=0; i < id_list.num_ids; i++) {
831 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
832 "WHERE FileSetId=%u", id_list.Id[i]);
833 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
834 printf("%s\n", db_strerror(db));
841 if (fix && id_list.num_ids > 0) {
842 printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
843 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%u", &id_list);
847 static void eliminate_orphaned_client_records()
851 printf(_("Checking for orphaned Client entries.\n"));
853 * Wiffle through Client for every Client
854 * joining with the Job table including every Client even if
855 * there is not a match in Job (left outer join), then
856 * filter out only those where no Job points to a Client
857 * i.e. Job.Client is NULL
859 query = "SELECT Client.ClientId,Client.Name FROM Client "
860 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
861 "WHERE Job.ClientId IS NULL";
863 printf("%s\n", query);
865 if (!make_id_list(query, &id_list)) {
868 printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
869 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
870 for (int i=0; i < id_list.num_ids; i++) {
871 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
872 "WHERE ClientId=%u", id_list.Id[i]);
873 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
874 printf("%s\n", db_strerror(db));
881 if (fix && id_list.num_ids > 0) {
882 printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
883 delete_id_list("DELETE FROM Client WHERE ClientId=%u", &id_list);
887 static void eliminate_orphaned_job_records()
891 printf(_("Checking for orphaned Job entries.\n"));
893 * Wiffle through Job for every Job
894 * joining with the Client table including every Job even if
895 * there is not a match in Client (left outer join), then
896 * filter out only those where no Client exists
897 * i.e. Client.Name is NULL
899 query = "SELECT Job.JobId,Job.Name FROM Job "
900 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
901 "WHERE Client.Name IS NULL";
903 printf("%s\n", query);
905 if (!make_id_list(query, &id_list)) {
908 printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
909 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
910 for (int i=0; i < id_list.num_ids; i++) {
911 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
912 "WHERE JobId=%u", id_list.Id[i]);
913 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
914 printf("%s\n", db_strerror(db));
921 if (fix && id_list.num_ids > 0) {
922 printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
923 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
924 printf(_("Deleting JobMedia records of orphaned Job records.\n"));
925 delete_id_list("DELETE FROM JobMedia WHERE JobId=%u", &id_list);
926 printf(_("Deleting Log records of orphaned Job records.\n"));
927 delete_id_list("DELETE FROM Log WHERE JobId=%u", &id_list);
932 static void eliminate_admin_records()
936 printf(_("Checking for Admin Job entries.\n"));
937 query = "SELECT Job.JobId FROM Job "
938 "WHERE Job.Type='D'";
940 printf("%s\n", query);
942 if (!make_id_list(query, &id_list)) {
945 printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
946 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
947 for (int i=0; i < id_list.num_ids; i++) {
948 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
949 "WHERE JobId=%u", id_list.Id[i]);
950 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
951 printf("%s\n", db_strerror(db));
958 if (fix && id_list.num_ids > 0) {
959 printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
960 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
964 static void eliminate_restore_records()
968 printf(_("Checking for Restore Job entries.\n"));
969 query = "SELECT Job.JobId FROM Job "
970 "WHERE Job.Type='R'";
972 printf("%s\n", query);
974 if (!make_id_list(query, &id_list)) {
977 printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
978 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
979 for (int i=0; i < id_list.num_ids; i++) {
980 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
981 "WHERE JobId=%u", id_list.Id[i]);
982 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
983 printf("%s\n", db_strerror(db));
990 if (fix && id_list.num_ids > 0) {
991 printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
992 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
999 static void repair_bad_filenames()
1004 printf(_("Checking for Filenames with a trailing slash\n"));
1005 query = "SELECT FilenameId,Name from Filename "
1006 "WHERE Name LIKE '%/'";
1008 printf("%s\n", query);
1010 if (!make_id_list(query, &id_list)) {
1013 printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
1014 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1015 for (i=0; i < id_list.num_ids; i++) {
1016 bsnprintf(buf, sizeof(buf),
1017 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
1018 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1019 printf("%s\n", db_strerror(db));
1026 if (fix && id_list.num_ids > 0) {
1027 POOLMEM *name = get_pool_memory(PM_FNAME);
1028 char esc_name[5000];
1029 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1030 for (i=0; i < id_list.num_ids; i++) {
1032 bsnprintf(buf, sizeof(buf),
1033 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
1034 if (!db_sql_query(db, buf, get_name_handler, name)) {
1035 printf("%s\n", db_strerror(db));
1037 /* Strip trailing slash(es) */
1038 for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
1046 db_escape_string(esc_name, name, len);
1048 bsnprintf(buf, sizeof(buf),
1049 "UPDATE Filename SET Name='%s' WHERE FilenameId=%u",
1050 esc_name, id_list.Id[i]);
1052 printf("%s\n", buf);
1054 db_sql_query(db, buf, NULL, NULL);
1059 static void repair_bad_paths()
1064 printf(_("Checking for Paths without a trailing slash\n"));
1065 query = "SELECT PathId,Path from Path "
1066 "WHERE Path NOT LIKE '%/'";
1068 printf("%s\n", query);
1070 if (!make_id_list(query, &id_list)) {
1073 printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1074 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1075 for (i=0; i < id_list.num_ids; i++) {
1076 bsnprintf(buf, sizeof(buf),
1077 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1078 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1079 printf("%s\n", db_strerror(db));
1086 if (fix && id_list.num_ids > 0) {
1087 POOLMEM *name = get_pool_memory(PM_FNAME);
1088 char esc_name[5000];
1089 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1090 for (i=0; i < id_list.num_ids; i++) {
1092 bsnprintf(buf, sizeof(buf),
1093 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1094 if (!db_sql_query(db, buf, get_name_handler, name)) {
1095 printf("%s\n", db_strerror(db));
1097 /* Strip trailing blanks */
1098 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1101 /* Add trailing slash */
1102 len = pm_strcat(&name, "/");
1103 db_escape_string(esc_name, name, len);
1104 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%u",
1105 esc_name, id_list.Id[i]);
1107 printf("%s\n", buf);
1109 db_sql_query(db, buf, NULL, NULL);
1116 * Gen next input command from the terminal
1118 static char *get_cmd(const char *prompt)
1120 static char cmd[1000];
1122 printf("%s", prompt);
1123 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
1128 strip_trailing_junk(cmd);
1132 static bool yes_no(const char *prompt)
1135 cmd = get_cmd(prompt);
1140 return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1143 bool python_set_prog(JCR*, char const*) { return false; }