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[2000];
61 #define MAX_ID_LIST_LEN 10000000
63 /* Forward referenced functions */
64 static int make_id_list(const char *query, ID_LIST *id_list);
65 static int delete_id_list(const char *query, ID_LIST *id_list);
66 static int make_name_list(const char *query, NAME_LIST *name_list);
67 static void print_name_list(NAME_LIST *name_list);
68 static void free_name_list(NAME_LIST *name_list);
69 static char *get_cmd(const char *prompt);
70 static void eliminate_duplicate_filenames();
71 static void eliminate_duplicate_paths();
72 static void eliminate_orphaned_jobmedia_records();
73 static void eliminate_orphaned_file_records();
74 static void eliminate_orphaned_path_records();
75 static void eliminate_orphaned_filename_records();
76 static void eliminate_orphaned_fileset_records();
77 static void eliminate_orphaned_client_records();
78 static void eliminate_orphaned_job_records();
79 static void eliminate_admin_records();
80 static void eliminate_restore_records();
81 static void repair_bad_paths();
82 static void repair_bad_filenames();
83 static void do_interactive_mode();
84 static int yes_no(const char *prompt);
90 "Usage: dbcheck [-c config] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>]\n"
92 " -C catalog name in the director conf file\n"
93 " -c director conf filename\n"
94 " -dnn set debug level to nn\n"
95 " -f fix inconsistencies\n"
97 " -? print this message\n\n");
101 int main (int argc, char *argv[])
104 const char *user, *password, *db_name, *dbhost;
105 char *configfile = NULL;
106 char *catalogname = NULL;
108 setlocale(LC_ALL, "");
109 bindtextdomain("bacula", LOCALEDIR);
110 textdomain("bacula");
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"));
272 printf(_("Modify database is on."));
274 printf(_("Modify database is off."));
276 printf(_(" Verbose is on.\n"));
278 printf(_(" Verbose is off.\n"));
280 printf(_("Please select the fuction you want to perform.\n"));
285 " 1) Toggle modify database flag\n"
286 " 2) Toggle verbose flag\n"
287 " 3) Repair bad Filename records\n"
288 " 4) Repair bad Path records\n"
289 " 5) Eliminate duplicate Filename records\n"
290 " 6) Eliminate duplicate Path records\n"
291 " 7) Eliminate orphaned Jobmedia records\n"
292 " 8) Eliminate orphaned File records\n"
293 " 9) Eliminate orphaned Path records\n"
294 " 10) Eliminate orphaned Filename records\n"
295 " 11) Eliminate orphaned FileSet records\n"
296 " 12) Eliminate orphaned Client records\n"
297 " 13) Eliminate orphaned Job records\n"
298 " 14) Eliminate all Admin records\n"
299 " 15) Eliminate all Restore records\n"
304 " 1) Toggle modify database flag\n"
305 " 2) Toggle verbose flag\n"
306 " 3) Check for bad Filename records\n"
307 " 4) Check for bad Path records\n"
308 " 5) Check for duplicate Filename records\n"
309 " 6) Check for duplicate Path records\n"
310 " 7) Check for orphaned Jobmedia records\n"
311 " 8) Check for orphaned File records\n"
312 " 9) Check for orphaned Path records\n"
313 " 10) Check for orphaned Filename records\n"
314 " 11) Check for orphaned FileSet records\n"
315 " 12) Check for orphaned Client records\n"
316 " 13) Check for orphaned Job records\n"
317 " 14) Check for all Admin records\n"
318 " 15) Check for all Restore records\n"
323 cmd = get_cmd(_("Select function number: "));
325 int item = atoi(cmd);
330 printf(_("Database will be modified.\n"));
332 printf(_("Database will NOT be modified.\n"));
335 verbose = verbose?0:1;
337 printf(_(" Verbose is on.\n"));
339 printf(_(" Verbose is off.\n"));
342 repair_bad_filenames();
348 eliminate_duplicate_filenames();
351 eliminate_duplicate_paths();
354 eliminate_orphaned_jobmedia_records();
357 eliminate_orphaned_file_records();
360 eliminate_orphaned_path_records();
363 eliminate_orphaned_filename_records();
366 eliminate_orphaned_fileset_records();
369 eliminate_orphaned_client_records();
372 eliminate_orphaned_job_records();
375 eliminate_admin_records();
378 eliminate_restore_records();
381 repair_bad_filenames();
383 eliminate_duplicate_filenames();
384 eliminate_duplicate_paths();
385 eliminate_orphaned_jobmedia_records();
386 eliminate_orphaned_file_records();
387 eliminate_orphaned_path_records();
388 eliminate_orphaned_filename_records();
389 eliminate_orphaned_fileset_records();
390 eliminate_orphaned_client_records();
391 eliminate_orphaned_job_records();
392 eliminate_admin_records();
393 eliminate_restore_records();
403 static int print_name_handler(void *ctx, int num_fields, char **row)
406 printf("%s\n", row[0]);
411 static int get_name_handler(void *ctx, int num_fields, char **row)
413 POOLMEM *buf = (POOLMEM *)ctx;
415 pm_strcpy(&buf, row[0]);
420 static int print_job_handler(void *ctx, int num_fields, char **row)
422 printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
423 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
428 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
430 printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
431 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
435 static int print_file_handler(void *ctx, int num_fields, char **row)
437 printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
438 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
442 static int print_fileset_handler(void *ctx, int num_fields, char **row)
444 printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
445 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
449 static int print_client_handler(void *ctx, int num_fields, char **row)
451 printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
452 NPRT(row[0]), NPRT(row[1]));
458 * Called here with each id to be added to the list
460 static int id_list_handler(void *ctx, int num_fields, char **row)
462 ID_LIST *lst = (ID_LIST *)ctx;
464 if (lst->num_ids == MAX_ID_LIST_LEN) {
467 if (lst->num_ids == lst->max_ids) {
468 if (lst->max_ids == 0) {
470 lst->Id = (uint32_t *)bmalloc(sizeof(uint32_t) * lst->max_ids);
472 lst->max_ids = (lst->max_ids * 3) / 2;
473 lst->Id = (uint32_t *)brealloc(lst->Id, sizeof(uint32_t) * lst->max_ids);
476 lst->Id[lst->num_ids++] = (uint32_t)strtod(row[0], NULL);
481 * Construct record id list
483 static int make_id_list(const char *query, ID_LIST *id_list)
485 id_list->num_ids = 0;
486 id_list->num_del = 0;
487 id_list->tot_ids = 0;
489 if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
490 printf("%s", db_strerror(db));
497 * Delete all entries in the list
499 static int delete_id_list(const char *query, ID_LIST *id_list)
501 for (int i=0; i < id_list->num_ids; i++) {
502 bsnprintf(buf, sizeof(buf), query, id_list->Id[i]);
504 printf(_("Deleting: %s\n"), buf);
506 db_sql_query(db, buf, NULL, NULL);
512 * Called here with each name to be added to the list
514 static int name_list_handler(void *ctx, int num_fields, char **row)
516 NAME_LIST *name = (NAME_LIST *)ctx;
518 if (name->num_ids == MAX_ID_LIST_LEN) {
521 if (name->num_ids == name->max_ids) {
522 if (name->max_ids == 0) {
523 name->max_ids = 1000;
524 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
526 name->max_ids = (name->max_ids * 3) / 2;
527 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
530 name->name[name->num_ids++] = bstrdup(row[0]);
536 * Construct name list
538 static int make_name_list(const char *query, NAME_LIST *name_list)
540 name_list->num_ids = 0;
541 name_list->num_del = 0;
542 name_list->tot_ids = 0;
544 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
545 printf("%s", db_strerror(db));
552 * Print names in the list
554 static void print_name_list(NAME_LIST *name_list)
556 for (int i=0; i < name_list->num_ids; i++) {
557 printf("%s\n", name_list->name[i]);
563 * Free names in the list
565 static void free_name_list(NAME_LIST *name_list)
567 for (int i=0; i < name_list->num_ids; i++) {
568 free(name_list->name[i]);
570 name_list->num_ids = 0;
573 static void eliminate_duplicate_filenames()
578 printf(_("Checking for duplicate Filename entries.\n"));
580 /* Make list of duplicated names */
581 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
582 "HAVING count(Name) > 1";
584 if (!make_name_list(query, &name_list)) {
587 printf(_("Found %d duplicate Filename records.\n"), name_list.num_ids);
588 if (name_list.num_ids && verbose && yes_no(_("Print the list? (yes/no): "))) {
589 print_name_list(&name_list);
592 /* Loop through list of duplicate names */
593 for (int i=0; i<name_list.num_ids; i++) {
594 /* Get all the Ids of each name */
595 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
596 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
600 if (!make_id_list(buf, &id_list)) {
604 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
606 /* Force all records to use the first id then delete the other ids */
607 for (int j=1; j<id_list.num_ids; j++) {
608 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%u WHERE FilenameId=%u",
609 id_list.Id[0], id_list.Id[j]);
613 db_sql_query(db, buf, NULL, NULL);
614 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%u",
619 db_sql_query(db, buf, NULL, NULL);
623 free_name_list(&name_list);
626 static void eliminate_duplicate_paths()
631 printf(_("Checking for duplicate Path entries.\n"));
633 /* Make list of duplicated names */
635 query = "SELECT Path, count(Path) as Count FROM Path "
636 "GROUP BY Path HAVING count(Path) > 1";
638 if (!make_name_list(query, &name_list)) {
641 printf(_("Found %d duplicate Path records.\n"), name_list.num_ids);
642 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
643 print_name_list(&name_list);
646 /* Loop through list of duplicate names */
647 for (int i=0; i<name_list.num_ids; i++) {
648 /* Get all the Ids of each name */
649 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
650 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
654 if (!make_id_list(buf, &id_list)) {
658 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
660 /* Force all records to use the first id then delete the other ids */
661 for (int j=1; j<id_list.num_ids; j++) {
662 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%u WHERE PathId=%u",
663 id_list.Id[0], id_list.Id[j]);
667 db_sql_query(db, buf, NULL, NULL);
668 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%u",
673 db_sql_query(db, buf, NULL, NULL);
677 free_name_list(&name_list);
680 static void eliminate_orphaned_jobmedia_records()
684 printf(_("Checking for orphaned JobMedia entries.\n"));
685 query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
686 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
687 "WHERE Job.JobId IS NULL";
688 if (!make_id_list(query, &id_list)) {
691 printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
692 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
693 for (int i=0; i < id_list.num_ids; i++) {
694 bsnprintf(buf, sizeof(buf),
695 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
696 "WHERE JobMedia.JobMediaId=%u AND Media.MediaId=JobMedia.MediaId", id_list.Id[i]);
697 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
698 printf("%s\n", db_strerror(db));
703 if (fix && id_list.num_ids > 0) {
704 printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
705 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%u", &id_list);
709 static void eliminate_orphaned_file_records()
713 printf(_("Checking for orphaned File entries. This may take some time!\n"));
714 query = "SELECT File.FileId,Job.JobId FROM File "
715 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
716 "WHERE Job.JobId IS NULL";
718 printf("%s\n", query);
720 if (!make_id_list(query, &id_list)) {
723 printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
724 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
725 for (int i=0; i < id_list.num_ids; i++) {
726 bsnprintf(buf, sizeof(buf),
727 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
728 "WHERE File.FileId=%u AND File.FilenameId=Filename.FilenameId", id_list.Id[i]);
729 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
730 printf("%s\n", db_strerror(db));
735 if (fix && id_list.num_ids > 0) {
736 printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
737 delete_id_list("DELETE FROM File WHERE FileId=%u", &id_list);
741 static void eliminate_orphaned_path_records()
745 printf(_("Checking for orphaned Path entries. This may take some time!\n"));
746 query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
747 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
748 "WHERE File.PathId IS NULL";
750 printf("%s\n", query);
752 if (!make_id_list(query, &id_list)) {
755 printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
756 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
757 for (int i=0; i < id_list.num_ids; i++) {
758 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
759 db_sql_query(db, buf, print_name_handler, NULL);
763 if (fix && id_list.num_ids > 0) {
764 printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
765 delete_id_list("DELETE FROM Path WHERE PathId=%u", &id_list);
769 static void eliminate_orphaned_filename_records()
773 printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
774 query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
775 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
776 "WHERE File.FilenameId IS NULL";
778 printf("%s\n", query);
780 if (!make_id_list(query, &id_list)) {
783 printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
784 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
785 for (int i=0; i < id_list.num_ids; i++) {
786 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
787 db_sql_query(db, buf, print_name_handler, NULL);
791 if (fix && id_list.num_ids > 0) {
792 printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
793 delete_id_list("DELETE FROM Filename WHERE FilenameId=%u", &id_list);
797 static void eliminate_orphaned_fileset_records()
801 printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
802 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
803 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
804 "WHERE Job.FileSetId IS NULL";
806 printf("%s\n", query);
808 if (!make_id_list(query, &id_list)) {
811 printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
812 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
813 for (int i=0; i < id_list.num_ids; i++) {
814 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
815 "WHERE FileSetId=%u", id_list.Id[i]);
816 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
817 printf("%s\n", db_strerror(db));
822 if (fix && id_list.num_ids > 0) {
823 printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
824 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%u", &id_list);
828 static void eliminate_orphaned_client_records()
832 printf(_("Checking for orphaned Client entries.\n"));
834 * Wiffle through Client for every Client
835 * joining with the Job table including every Client even if
836 * there is not a match in Job (left outer join), then
837 * filter out only those where no Job points to a Client
838 * i.e. Job.Client is NULL
840 query = "SELECT Client.ClientId,Client.Name FROM Client "
841 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
842 "WHERE Job.ClientId IS NULL";
844 printf("%s\n", query);
846 if (!make_id_list(query, &id_list)) {
849 printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
850 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
851 for (int i=0; i < id_list.num_ids; i++) {
852 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
853 "WHERE ClientId=%u", id_list.Id[i]);
854 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
855 printf("%s\n", db_strerror(db));
860 if (fix && id_list.num_ids > 0) {
861 printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
862 delete_id_list("DELETE FROM Client WHERE ClientId=%u", &id_list);
866 static void eliminate_orphaned_job_records()
870 printf(_("Checking for orphaned Job entries.\n"));
872 * Wiffle through Job for every Job
873 * joining with the Client table including every Job even if
874 * there is not a match in Client (left outer join), then
875 * filter out only those where no Client exists
876 * i.e. Client.Name is NULL
878 query = "SELECT Job.JobId,Job.Name FROM Job "
879 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
880 "WHERE Client.Name IS NULL";
882 printf("%s\n", query);
884 if (!make_id_list(query, &id_list)) {
887 printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
888 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
889 for (int i=0; i < id_list.num_ids; i++) {
890 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
891 "WHERE JobId=%u", id_list.Id[i]);
892 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
893 printf("%s\n", db_strerror(db));
898 if (fix && id_list.num_ids > 0) {
899 printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
900 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
905 static void eliminate_admin_records()
909 printf(_("Checking for Admin Job entries.\n"));
910 query = "SELECT Job.JobId FROM Job "
911 "WHERE Job.Type='D'";
913 printf("%s\n", query);
915 if (!make_id_list(query, &id_list)) {
918 printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
919 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
920 for (int i=0; i < id_list.num_ids; i++) {
921 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
922 "WHERE JobId=%u", id_list.Id[i]);
923 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
924 printf("%s\n", db_strerror(db));
929 if (fix && id_list.num_ids > 0) {
930 printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
931 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
935 static void eliminate_restore_records()
939 printf(_("Checking for Restore Job entries.\n"));
940 query = "SELECT Job.JobId FROM Job "
941 "WHERE Job.Type='R'";
943 printf("%s\n", query);
945 if (!make_id_list(query, &id_list)) {
948 printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
949 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
950 for (int i=0; i < id_list.num_ids; i++) {
951 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
952 "WHERE JobId=%u", id_list.Id[i]);
953 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
954 printf("%s\n", db_strerror(db));
959 if (fix && id_list.num_ids > 0) {
960 printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
961 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
968 static void repair_bad_filenames()
973 printf(_("Checking for Filenames with a trailing slash\n"));
974 query = "SELECT FilenameId,Name from Filename "
975 "WHERE Name LIKE '%/'";
977 printf("%s\n", query);
979 if (!make_id_list(query, &id_list)) {
982 printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
983 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
984 for (i=0; i < id_list.num_ids; i++) {
985 bsnprintf(buf, sizeof(buf),
986 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
987 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
988 printf("%s\n", db_strerror(db));
993 if (fix && id_list.num_ids > 0) {
994 POOLMEM *name = get_pool_memory(PM_FNAME);
996 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
997 for (i=0; i < id_list.num_ids; i++) {
999 bsnprintf(buf, sizeof(buf),
1000 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
1001 if (!db_sql_query(db, buf, get_name_handler, name)) {
1002 printf("%s\n", db_strerror(db));
1004 /* Strip trailing slash(es) */
1005 for (len=strlen(name); len > 0 && name[len-1]=='/'; len--)
1013 db_escape_string(esc_name, name, len);
1015 bsnprintf(buf, sizeof(buf),
1016 "UPDATE Filename SET Name='%s' WHERE FilenameId=%u",
1017 esc_name, id_list.Id[i]);
1019 printf("%s\n", buf);
1021 db_sql_query(db, buf, NULL, NULL);
1026 static void repair_bad_paths()
1031 printf(_("Checking for Paths without a trailing slash\n"));
1032 query = "SELECT PathId,Path from Path "
1033 "WHERE Path NOT LIKE '%/'";
1035 printf("%s\n", query);
1037 if (!make_id_list(query, &id_list)) {
1040 printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1041 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1042 for (i=0; i < id_list.num_ids; i++) {
1043 bsnprintf(buf, sizeof(buf),
1044 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1045 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1046 printf("%s\n", db_strerror(db));
1051 if (fix && id_list.num_ids > 0) {
1052 POOLMEM *name = get_pool_memory(PM_FNAME);
1053 char esc_name[5000];
1054 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1055 for (i=0; i < id_list.num_ids; i++) {
1057 bsnprintf(buf, sizeof(buf),
1058 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1059 if (!db_sql_query(db, buf, get_name_handler, name)) {
1060 printf("%s\n", db_strerror(db));
1062 /* Strip trailing blanks */
1063 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1066 /* Add trailing slash */
1067 len = pm_strcat(&name, "/");
1068 db_escape_string(esc_name, name, len);
1069 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%u",
1070 esc_name, id_list.Id[i]);
1072 printf("%s\n", buf);
1074 db_sql_query(db, buf, NULL, NULL);
1081 * Gen next input command from the terminal
1083 static char *get_cmd(const char *prompt)
1085 static char cmd[1000];
1087 printf("%s", prompt);
1088 if (fgets(cmd, sizeof(cmd), stdin) == NULL)
1091 strip_trailing_junk(cmd);
1095 static int yes_no(const char *prompt)
1098 cmd = get_cmd(prompt);
1099 return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1102 bool python_set_prog(JCR*, char const*) { return false; }