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 setlocale(LC_ALL, "");
108 bindtextdomain("bacula", LOCALEDIR);
109 textdomain("bacula");
111 my_name_is(argc, argv, "dbcheck");
112 init_msg(NULL, NULL); /* setup message handler */
114 memset(&id_list, 0, sizeof(id_list));
115 memset(&name_list, 0, sizeof(name_list));
118 while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
120 case 'b': /* batch */
124 case 'C': /* CatalogName */
125 catalogname = optarg;
128 case 'c': /* configfile */
132 case 'd': /* debug level */
133 debug_level = atoi(optarg);
134 if (debug_level <= 0)
138 case 'f': /* fix inconsistencies */
158 Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
160 parse_config(configfile);
162 foreach_res(catalog, R_CATALOG) {
163 if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
166 } else if (!catalogname) { // stop on first if no catalogname is given
174 Pmsg2(0, _("Error can not find the Catalog name[%s] in the given config file [%s]\n"), catalogname, configfile);
176 Pmsg1(0, _("Error there is no Catalog section in the given config file [%s]\n"), configfile);
182 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
185 Pmsg0(0, _("Error no Director resource defined.\n"));
188 set_working_directory(director->working_directory);
189 db_name = catalog->db_name;
190 user = catalog->db_user;
191 password = catalog->db_password;
192 dbhost = catalog->db_address;
193 if (dbhost && dbhost[0] == 0) {
199 Pmsg0(0, _("Wrong number of arguments.\n"));
204 Pmsg0(0, _("Working directory not supplied.\n"));
208 /* This is needed by SQLite to find the db */
209 working_directory = argv[0];
218 } else if (argc == 3) {
221 } else if (argc == 4) {
225 } else if (argc == 5) {
234 db = db_init_database(NULL, db_name, user, password, dbhost, 0, NULL, 0);
235 if (!db_open_database(NULL, db)) {
236 Emsg1(M_FATAL, 0, "%s", db_strerror(db));
242 repair_bad_filenames();
243 eliminate_duplicate_filenames();
244 eliminate_duplicate_paths();
245 eliminate_orphaned_jobmedia_records();
246 eliminate_orphaned_file_records();
247 eliminate_orphaned_path_records();
248 eliminate_orphaned_filename_records();
249 eliminate_orphaned_fileset_records();
250 eliminate_orphaned_client_records();
251 eliminate_orphaned_job_records();
252 eliminate_admin_records();
253 eliminate_restore_records();
255 do_interactive_mode();
258 db_close_database(NULL, db);
264 static void do_interactive_mode()
269 printf(_("Hello, this is the database check/correct program.\n"));
271 printf(_("Modify database is on."));
273 printf(_("Modify database is off."));
275 printf(_(" Verbose is on.\n"));
277 printf(_(" Verbose is off.\n"));
279 printf(_("Please select the fuction you want to perform.\n"));
284 " 1) Toggle modify database flag\n"
285 " 2) Toggle verbose flag\n"
286 " 3) Repair bad Filename records\n"
287 " 4) Repair bad Path records\n"
288 " 5) Eliminate duplicate Filename records\n"
289 " 6) Eliminate duplicate Path records\n"
290 " 7) Eliminate orphaned Jobmedia records\n"
291 " 8) Eliminate orphaned File records\n"
292 " 9) Eliminate orphaned Path records\n"
293 " 10) Eliminate orphaned Filename records\n"
294 " 11) Eliminate orphaned FileSet records\n"
295 " 12) Eliminate orphaned Client records\n"
296 " 13) Eliminate orphaned Job records\n"
297 " 14) Eliminate all Admin records\n"
298 " 15) Eliminate all Restore records\n"
303 " 1) Toggle modify database flag\n"
304 " 2) Toggle verbose flag\n"
305 " 3) Check for bad Filename records\n"
306 " 4) Check for bad Path records\n"
307 " 5) Check for duplicate Filename records\n"
308 " 6) Check for duplicate Path records\n"
309 " 7) Check for orphaned Jobmedia records\n"
310 " 8) Check for orphaned File records\n"
311 " 9) Check for orphaned Path records\n"
312 " 10) Check for orphaned Filename records\n"
313 " 11) Check for orphaned FileSet records\n"
314 " 12) Check for orphaned Client records\n"
315 " 13) Check for orphaned Job records\n"
316 " 14) Check for all Admin records\n"
317 " 15) Check for all Restore records\n"
322 cmd = get_cmd(_("Select function number: "));
324 int item = atoi(cmd);
329 printf(_("Database will be modified.\n"));
331 printf(_("Database will NOT be modified.\n"));
334 verbose = verbose?0:1;
336 printf(_(" Verbose is on.\n"));
338 printf(_(" Verbose is off.\n"));
341 repair_bad_filenames();
347 eliminate_duplicate_filenames();
350 eliminate_duplicate_paths();
353 eliminate_orphaned_jobmedia_records();
356 eliminate_orphaned_file_records();
359 eliminate_orphaned_path_records();
362 eliminate_orphaned_filename_records();
365 eliminate_orphaned_fileset_records();
368 eliminate_orphaned_client_records();
371 eliminate_orphaned_job_records();
374 eliminate_admin_records();
377 eliminate_restore_records();
380 repair_bad_filenames();
382 eliminate_duplicate_filenames();
383 eliminate_duplicate_paths();
384 eliminate_orphaned_jobmedia_records();
385 eliminate_orphaned_file_records();
386 eliminate_orphaned_path_records();
387 eliminate_orphaned_filename_records();
388 eliminate_orphaned_fileset_records();
389 eliminate_orphaned_client_records();
390 eliminate_orphaned_job_records();
391 eliminate_admin_records();
392 eliminate_restore_records();
402 static int print_name_handler(void *ctx, int num_fields, char **row)
405 printf("%s\n", row[0]);
410 static int get_name_handler(void *ctx, int num_fields, char **row)
412 POOLMEM *buf = (POOLMEM *)ctx;
414 pm_strcpy(&buf, row[0]);
419 static int print_job_handler(void *ctx, int num_fields, char **row)
421 printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
422 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
427 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
429 printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
430 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
434 static int print_file_handler(void *ctx, int num_fields, char **row)
436 printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
437 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
441 static int print_fileset_handler(void *ctx, int num_fields, char **row)
443 printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
444 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
448 static int print_client_handler(void *ctx, int num_fields, char **row)
450 printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
451 NPRT(row[0]), NPRT(row[1]));
457 * Called here with each id to be added to the list
459 static int id_list_handler(void *ctx, int num_fields, char **row)
461 ID_LIST *lst = (ID_LIST *)ctx;
463 if (lst->num_ids == MAX_ID_LIST_LEN) {
466 if (lst->num_ids == lst->max_ids) {
467 if (lst->max_ids == 0) {
469 lst->Id = (uint32_t *)bmalloc(sizeof(uint32_t) * lst->max_ids);
471 lst->max_ids = (lst->max_ids * 3) / 2;
472 lst->Id = (uint32_t *)brealloc(lst->Id, sizeof(uint32_t) * lst->max_ids);
475 lst->Id[lst->num_ids++] = (uint32_t)strtod(row[0], NULL);
480 * Construct record id list
482 static int make_id_list(const char *query, ID_LIST *id_list)
484 id_list->num_ids = 0;
485 id_list->num_del = 0;
486 id_list->tot_ids = 0;
488 if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
489 printf("%s", db_strerror(db));
496 * Delete all entries in the list
498 static int delete_id_list(const char *query, ID_LIST *id_list)
500 for (int i=0; i < id_list->num_ids; i++) {
501 bsnprintf(buf, sizeof(buf), query, id_list->Id[i]);
503 printf(_("Deleting: %s\n"), buf);
505 db_sql_query(db, buf, NULL, NULL);
511 * Called here with each name to be added to the list
513 static int name_list_handler(void *ctx, int num_fields, char **row)
515 NAME_LIST *name = (NAME_LIST *)ctx;
517 if (name->num_ids == MAX_ID_LIST_LEN) {
520 if (name->num_ids == name->max_ids) {
521 if (name->max_ids == 0) {
522 name->max_ids = 1000;
523 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
525 name->max_ids = (name->max_ids * 3) / 2;
526 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
529 name->name[name->num_ids++] = bstrdup(row[0]);
535 * Construct name list
537 static int make_name_list(const char *query, NAME_LIST *name_list)
539 name_list->num_ids = 0;
540 name_list->num_del = 0;
541 name_list->tot_ids = 0;
543 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
544 printf("%s", db_strerror(db));
551 * Print names in the list
553 static void print_name_list(NAME_LIST *name_list)
555 for (int i=0; i < name_list->num_ids; i++) {
556 printf("%s\n", name_list->name[i]);
562 * Free names in the list
564 static void free_name_list(NAME_LIST *name_list)
566 for (int i=0; i < name_list->num_ids; i++) {
567 free(name_list->name[i]);
569 name_list->num_ids = 0;
572 static void eliminate_duplicate_filenames()
577 printf(_("Checking for duplicate Filename entries.\n"));
579 /* Make list of duplicated names */
580 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
581 "HAVING count(Name) > 1";
583 if (!make_name_list(query, &name_list)) {
586 printf(_("Found %d duplicate Filename records.\n"), name_list.num_ids);
587 if (name_list.num_ids && verbose && yes_no(_("Print the list? (yes/no): "))) {
588 print_name_list(&name_list);
591 /* Loop through list of duplicate names */
592 for (int i=0; i<name_list.num_ids; i++) {
593 /* Get all the Ids of each name */
594 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
595 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
599 if (!make_id_list(buf, &id_list)) {
603 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
605 /* Force all records to use the first id then delete the other ids */
606 for (int j=1; j<id_list.num_ids; j++) {
607 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%u WHERE FilenameId=%u",
608 id_list.Id[0], id_list.Id[j]);
612 db_sql_query(db, buf, NULL, NULL);
613 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%u",
618 db_sql_query(db, buf, NULL, NULL);
622 free_name_list(&name_list);
625 static void eliminate_duplicate_paths()
630 printf(_("Checking for duplicate Path entries.\n"));
632 /* Make list of duplicated names */
634 query = "SELECT Path, count(Path) as Count FROM Path "
635 "GROUP BY Path HAVING count(Path) > 1";
637 if (!make_name_list(query, &name_list)) {
640 printf(_("Found %d duplicate Path records.\n"), name_list.num_ids);
641 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
642 print_name_list(&name_list);
645 /* Loop through list of duplicate names */
646 for (int i=0; i<name_list.num_ids; i++) {
647 /* Get all the Ids of each name */
648 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
649 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
653 if (!make_id_list(buf, &id_list)) {
657 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
659 /* Force all records to use the first id then delete the other ids */
660 for (int j=1; j<id_list.num_ids; j++) {
661 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%u WHERE PathId=%u",
662 id_list.Id[0], id_list.Id[j]);
666 db_sql_query(db, buf, NULL, NULL);
667 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%u",
672 db_sql_query(db, buf, NULL, NULL);
676 free_name_list(&name_list);
679 static void eliminate_orphaned_jobmedia_records()
683 printf(_("Checking for orphaned JobMedia entries.\n"));
684 query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
685 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
686 "WHERE Job.JobId IS NULL";
687 if (!make_id_list(query, &id_list)) {
690 printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
691 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
692 for (int i=0; i < id_list.num_ids; i++) {
693 bsnprintf(buf, sizeof(buf),
694 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
695 "WHERE JobMedia.JobMediaId=%u AND Media.MediaId=JobMedia.MediaId", id_list.Id[i]);
696 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
697 printf("%s\n", db_strerror(db));
702 if (fix && id_list.num_ids > 0) {
703 printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
704 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%u", &id_list);
708 static void eliminate_orphaned_file_records()
712 printf(_("Checking for orphaned File entries. This may take some time!\n"));
713 query = "SELECT File.FileId,Job.JobId FROM File "
714 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
715 "WHERE Job.JobId IS NULL";
717 printf("%s\n", query);
719 if (!make_id_list(query, &id_list)) {
722 printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
723 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
724 for (int i=0; i < id_list.num_ids; i++) {
725 bsnprintf(buf, sizeof(buf),
726 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
727 "WHERE File.FileId=%u AND File.FilenameId=Filename.FilenameId", id_list.Id[i]);
728 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
729 printf("%s\n", db_strerror(db));
734 if (fix && id_list.num_ids > 0) {
735 printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
736 delete_id_list("DELETE FROM File WHERE FileId=%u", &id_list);
740 static void eliminate_orphaned_path_records()
744 printf(_("Checking for orphaned Path entries. This may take some time!\n"));
745 query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
746 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
747 "WHERE File.PathId IS NULL";
749 printf("%s\n", query);
751 if (!make_id_list(query, &id_list)) {
754 printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
755 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
756 for (int i=0; i < id_list.num_ids; i++) {
757 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
758 db_sql_query(db, buf, print_name_handler, NULL);
762 if (fix && id_list.num_ids > 0) {
763 printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
764 delete_id_list("DELETE FROM Path WHERE PathId=%u", &id_list);
768 static void eliminate_orphaned_filename_records()
772 printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
773 query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
774 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
775 "WHERE File.FilenameId IS NULL";
777 printf("%s\n", query);
779 if (!make_id_list(query, &id_list)) {
782 printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
783 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
784 for (int i=0; i < id_list.num_ids; i++) {
785 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
786 db_sql_query(db, buf, print_name_handler, NULL);
790 if (fix && id_list.num_ids > 0) {
791 printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
792 delete_id_list("DELETE FROM Filename WHERE FilenameId=%u", &id_list);
796 static void eliminate_orphaned_fileset_records()
800 printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
801 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
802 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
803 "WHERE Job.FileSetId IS NULL";
805 printf("%s\n", query);
807 if (!make_id_list(query, &id_list)) {
810 printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
811 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
812 for (int i=0; i < id_list.num_ids; i++) {
813 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
814 "WHERE FileSetId=%u", id_list.Id[i]);
815 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
816 printf("%s\n", db_strerror(db));
821 if (fix && id_list.num_ids > 0) {
822 printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
823 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%u", &id_list);
827 static void eliminate_orphaned_client_records()
831 printf(_("Checking for orphaned Client entries.\n"));
833 * Wiffle through Client for every Client
834 * joining with the Job table including every Client even if
835 * there is not a match in Job (left outer join), then
836 * filter out only those where no Job points to a Client
837 * i.e. Job.Client is NULL
839 query = "SELECT Client.ClientId,Client.Name FROM Client "
840 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
841 "WHERE Job.ClientId IS NULL";
843 printf("%s\n", query);
845 if (!make_id_list(query, &id_list)) {
848 printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
849 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
850 for (int i=0; i < id_list.num_ids; i++) {
851 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
852 "WHERE ClientId=%u", id_list.Id[i]);
853 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
854 printf("%s\n", db_strerror(db));
859 if (fix && id_list.num_ids > 0) {
860 printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
861 delete_id_list("DELETE FROM Client WHERE ClientId=%u", &id_list);
865 static void eliminate_orphaned_job_records()
869 printf(_("Checking for orphaned Job entries.\n"));
871 * Wiffle through Job for every Job
872 * joining with the Client table including every Job even if
873 * there is not a match in Client (left outer join), then
874 * filter out only those where no Client exists
875 * i.e. Client.Name is NULL
877 query = "SELECT Job.JobId,Job.Name FROM Job "
878 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
879 "WHERE Client.Name IS NULL";
881 printf("%s\n", query);
883 if (!make_id_list(query, &id_list)) {
886 printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
887 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
888 for (int i=0; i < id_list.num_ids; i++) {
889 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
890 "WHERE JobId=%u", id_list.Id[i]);
891 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
892 printf("%s\n", db_strerror(db));
897 if (fix && id_list.num_ids > 0) {
898 printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
899 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
904 static void eliminate_admin_records()
908 printf(_("Checking for Admin Job entries.\n"));
909 query = "SELECT Job.JobId FROM Job "
910 "WHERE Job.Type='D'";
912 printf("%s\n", query);
914 if (!make_id_list(query, &id_list)) {
917 printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
918 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
919 for (int i=0; i < id_list.num_ids; i++) {
920 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
921 "WHERE JobId=%u", id_list.Id[i]);
922 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
923 printf("%s\n", db_strerror(db));
928 if (fix && id_list.num_ids > 0) {
929 printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
930 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
934 static void eliminate_restore_records()
938 printf(_("Checking for Restore Job entries.\n"));
939 query = "SELECT Job.JobId FROM Job "
940 "WHERE Job.Type='R'";
942 printf("%s\n", query);
944 if (!make_id_list(query, &id_list)) {
947 printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
948 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
949 for (int i=0; i < id_list.num_ids; i++) {
950 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
951 "WHERE JobId=%u", id_list.Id[i]);
952 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
953 printf("%s\n", db_strerror(db));
958 if (fix && id_list.num_ids > 0) {
959 printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
960 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
967 static void repair_bad_filenames()
972 printf(_("Checking for Filenames with a trailing slash\n"));
973 query = "SELECT FilenameId,Name from Filename "
974 "WHERE Name LIKE '%/'";
976 printf("%s\n", query);
978 if (!make_id_list(query, &id_list)) {
981 printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
982 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
983 for (i=0; i < id_list.num_ids; i++) {
984 bsnprintf(buf, sizeof(buf),
985 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
986 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
987 printf("%s\n", db_strerror(db));
992 if (fix && id_list.num_ids > 0) {
993 POOLMEM *name = get_pool_memory(PM_FNAME);
995 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
996 for (i=0; i < id_list.num_ids; i++) {
998 bsnprintf(buf, sizeof(buf),
999 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
1000 if (!db_sql_query(db, buf, get_name_handler, name)) {
1001 printf("%s\n", db_strerror(db));
1003 /* Strip trailing slash(es) */
1004 for (len=strlen(name); len > 0 && name[len-1]=='/'; len--)
1012 db_escape_string(esc_name, name, len);
1014 bsnprintf(buf, sizeof(buf),
1015 "UPDATE Filename SET Name='%s' WHERE FilenameId=%u",
1016 esc_name, id_list.Id[i]);
1018 printf("%s\n", buf);
1020 db_sql_query(db, buf, NULL, NULL);
1025 static void repair_bad_paths()
1030 printf(_("Checking for Paths without a trailing slash\n"));
1031 query = "SELECT PathId,Path from Path "
1032 "WHERE Path NOT LIKE '%/'";
1034 printf("%s\n", query);
1036 if (!make_id_list(query, &id_list)) {
1039 printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1040 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1041 for (i=0; i < id_list.num_ids; i++) {
1042 bsnprintf(buf, sizeof(buf),
1043 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1044 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1045 printf("%s\n", db_strerror(db));
1050 if (fix && id_list.num_ids > 0) {
1051 POOLMEM *name = get_pool_memory(PM_FNAME);
1052 char esc_name[5000];
1053 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1054 for (i=0; i < id_list.num_ids; i++) {
1056 bsnprintf(buf, sizeof(buf),
1057 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1058 if (!db_sql_query(db, buf, get_name_handler, name)) {
1059 printf("%s\n", db_strerror(db));
1061 /* Strip trailing blanks */
1062 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1065 /* Add trailing slash */
1066 len = pm_strcat(&name, "/");
1067 db_escape_string(esc_name, name, len);
1068 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%u",
1069 esc_name, id_list.Id[i]);
1071 printf("%s\n", buf);
1073 db_sql_query(db, buf, NULL, NULL);
1080 * Gen next input command from the terminal
1082 static char *get_cmd(const char *prompt)
1084 static char cmd[1000];
1086 printf("%s", prompt);
1087 if (fgets(cmd, sizeof(cmd), stdin) == NULL)
1090 strip_trailing_junk(cmd);
1094 static int yes_no(const char *prompt)
1097 cmd = get_cmd(prompt);
1098 return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1101 bool python_set_prog(JCR*, char const*) { return false; }