3 * Program to check a Bacula database for consistency and to
6 * Kern E. Sibbald, August 2002
12 Copyright (C) 2002-2004 Kern Sibbald and John Walker
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of
17 the License, or (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
24 You should have received a copy of the GNU General Public
25 License along with this program; if not, write to the Free
26 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
32 #include "cats/cats.h"
33 #include "dird/dird_conf.h"
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 my_name_is(argc, argv, "dbcheck");
109 init_msg(NULL, NULL); /* setup message handler */
111 memset(&id_list, 0, sizeof(id_list));
112 memset(&name_list, 0, sizeof(name_list));
115 while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
117 case 'b': /* batch */
121 case 'C': /* CatalogName */
122 catalogname = optarg;
125 case 'c': /* configfile */
129 case 'd': /* debug level */
130 debug_level = atoi(optarg);
131 if (debug_level <= 0)
135 case 'f': /* fix inconsistencies */
155 Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
157 parse_config(configfile);
159 foreach_res(catalog, R_CATALOG) {
160 if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
163 } else if (!catalogname) { // stop on first if no catalogname is given
171 Pmsg2(0, "Error can not find the Catalog name[%s] in the given config file [%s]\n", catalogname, configfile);
173 Pmsg1(0, "Error there is no Catalog section in the given config file [%s]\n", configfile);
179 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
182 Pmsg0(0, "Error no Director resource defined.\n");
185 set_working_directory(director->working_directory);
186 db_name = catalog->db_name;
187 user = catalog->db_user;
188 password = catalog->db_password;
189 dbhost = catalog->db_address;
190 if (dbhost && dbhost[0] == 0) {
196 Pmsg0(0, _("Wrong number of arguments.\n"));
201 Pmsg0(0, _("Working directory not supplied.\n"));
205 /* This is needed by SQLite to find the db */
206 working_directory = argv[0];
215 } else if (argc == 3) {
218 } else if (argc == 4) {
222 } else if (argc == 5) {
231 db = db_init_database(NULL, db_name, user, password, dbhost, 0, NULL, 0);
232 if (!db_open_database(NULL, db)) {
233 Emsg1(M_FATAL, 0, "%s", db_strerror(db));
239 repair_bad_filenames();
240 eliminate_duplicate_filenames();
241 eliminate_duplicate_paths();
242 eliminate_orphaned_jobmedia_records();
243 eliminate_orphaned_file_records();
244 eliminate_orphaned_path_records();
245 eliminate_orphaned_filename_records();
246 eliminate_orphaned_fileset_records();
247 eliminate_orphaned_client_records();
248 eliminate_orphaned_job_records();
249 eliminate_admin_records();
250 eliminate_restore_records();
252 do_interactive_mode();
255 db_close_database(NULL, db);
261 static void do_interactive_mode()
266 printf("Hello, this is the database check/correct program.\n"
267 "Modify database is %s. Verbose is %s.\n"
268 "Please select the fuction you want to perform.\n",
269 fix?"On":"Off", verbose?"On":"Off");
274 " 1) Toggle modify database flag\n"
275 " 2) Toggle verbose flag\n"
276 " 3) Repair bad Filename records\n"
277 " 4) Repair bad Path records\n"
278 " 5) Eliminate duplicate Filename records\n"
279 " 6) Eliminate duplicate Path records\n"
280 " 7) Eliminate orphaned Jobmedia records\n"
281 " 8) Eliminate orphaned File records\n"
282 " 9) Eliminate orphaned Path records\n"
283 " 10) Eliminate orphaned Filename records\n"
284 " 11) Eliminate orphaned FileSet records\n"
285 " 12) Eliminate orphaned Client records\n"
286 " 13) Eliminate orphaned Job records\n"
287 " 14) Eliminate all Admin records\n"
288 " 15) Eliminate all Restore records\n"
293 " 1) Toggle modify database flag\n"
294 " 2) Toggle verbose flag\n"
295 " 3) Check for bad Filename records\n"
296 " 4) Check for bad Path records\n"
297 " 5) Check for duplicate Filename records\n"
298 " 6) Check for duplicate Path records\n"
299 " 7) Check for orphaned Jobmedia records\n"
300 " 8) Check for orphaned File records\n"
301 " 9) Check for orphaned Path records\n"
302 " 10) Check for orphaned Filename records\n"
303 " 11) Check for orphaned FileSet records\n"
304 " 12) Check for orphaned Client records\n"
305 " 13) Check for orphaned Job records\n"
306 " 14) Check for all Admin records\n"
307 " 15) Check for all Restore records\n"
312 cmd = get_cmd(_("Select function number: "));
314 int item = atoi(cmd);
318 printf(_("Database will %sbe modified.\n"), fix?"":_("NOT "));
321 verbose = verbose?0:1;
322 printf(_("Verbose is %s\n"), verbose?_("On"):_("Off"));
325 repair_bad_filenames();
331 eliminate_duplicate_filenames();
334 eliminate_duplicate_paths();
337 eliminate_orphaned_jobmedia_records();
340 eliminate_orphaned_file_records();
343 eliminate_orphaned_path_records();
346 eliminate_orphaned_filename_records();
349 eliminate_orphaned_fileset_records();
352 eliminate_orphaned_client_records();
355 eliminate_orphaned_job_records();
358 eliminate_admin_records();
361 eliminate_restore_records();
364 repair_bad_filenames();
366 eliminate_duplicate_filenames();
367 eliminate_duplicate_paths();
368 eliminate_orphaned_jobmedia_records();
369 eliminate_orphaned_file_records();
370 eliminate_orphaned_path_records();
371 eliminate_orphaned_filename_records();
372 eliminate_orphaned_fileset_records();
373 eliminate_orphaned_client_records();
374 eliminate_orphaned_job_records();
375 eliminate_admin_records();
376 eliminate_restore_records();
386 static int print_name_handler(void *ctx, int num_fields, char **row)
389 printf("%s\n", row[0]);
394 static int get_name_handler(void *ctx, int num_fields, char **row)
396 POOLMEM *buf = (POOLMEM *)ctx;
398 pm_strcpy(&buf, row[0]);
403 static int print_job_handler(void *ctx, int num_fields, char **row)
405 printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
406 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
411 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
413 printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
414 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
418 static int print_file_handler(void *ctx, int num_fields, char **row)
420 printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
421 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
425 static int print_fileset_handler(void *ctx, int num_fields, char **row)
427 printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
428 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
432 static int print_client_handler(void *ctx, int num_fields, char **row)
434 printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
435 NPRT(row[0]), NPRT(row[1]));
441 * Called here with each id to be added to the list
443 static int id_list_handler(void *ctx, int num_fields, char **row)
445 ID_LIST *lst = (ID_LIST *)ctx;
447 if (lst->num_ids == MAX_ID_LIST_LEN) {
450 if (lst->num_ids == lst->max_ids) {
451 if (lst->max_ids == 0) {
453 lst->Id = (uint32_t *)bmalloc(sizeof(uint32_t) * lst->max_ids);
455 lst->max_ids = (lst->max_ids * 3) / 2;
456 lst->Id = (uint32_t *)brealloc(lst->Id, sizeof(uint32_t) * lst->max_ids);
459 lst->Id[lst->num_ids++] = (uint32_t)strtod(row[0], NULL);
464 * Construct record id list
466 static int make_id_list(const char *query, ID_LIST *id_list)
468 id_list->num_ids = 0;
469 id_list->num_del = 0;
470 id_list->tot_ids = 0;
472 if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
473 printf("%s", db_strerror(db));
480 * Delete all entries in the list
482 static int delete_id_list(const char *query, ID_LIST *id_list)
484 for (int i=0; i < id_list->num_ids; i++) {
485 bsnprintf(buf, sizeof(buf), query, id_list->Id[i]);
487 printf("Deleting: %s\n", buf);
489 db_sql_query(db, buf, NULL, NULL);
495 * Called here with each name to be added to the list
497 static int name_list_handler(void *ctx, int num_fields, char **row)
499 NAME_LIST *name = (NAME_LIST *)ctx;
501 if (name->num_ids == MAX_ID_LIST_LEN) {
504 if (name->num_ids == name->max_ids) {
505 if (name->max_ids == 0) {
506 name->max_ids = 1000;
507 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
509 name->max_ids = (name->max_ids * 3) / 2;
510 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
513 name->name[name->num_ids++] = bstrdup(row[0]);
519 * Construct name list
521 static int make_name_list(const char *query, NAME_LIST *name_list)
523 name_list->num_ids = 0;
524 name_list->num_del = 0;
525 name_list->tot_ids = 0;
527 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
528 printf("%s", db_strerror(db));
535 * Print names in the list
537 static void print_name_list(NAME_LIST *name_list)
539 for (int i=0; i < name_list->num_ids; i++) {
540 printf("%s\n", name_list->name[i]);
546 * Free names in the list
548 static void free_name_list(NAME_LIST *name_list)
550 for (int i=0; i < name_list->num_ids; i++) {
551 free(name_list->name[i]);
553 name_list->num_ids = 0;
556 static void eliminate_duplicate_filenames()
561 printf("Checking for duplicate Filename entries.\n");
563 /* Make list of duplicated names */
564 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
565 "HAVING count(Name) > 1";
567 if (!make_name_list(query, &name_list)) {
570 printf("Found %d duplicate Filename records.\n", name_list.num_ids);
571 if (name_list.num_ids && verbose && yes_no("Print the list? (yes/no): ")) {
572 print_name_list(&name_list);
575 /* Loop through list of duplicate names */
576 for (int i=0; i<name_list.num_ids; i++) {
577 /* Get all the Ids of each name */
578 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
579 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
583 if (!make_id_list(buf, &id_list)) {
587 printf("Found %d for: %s\n", id_list.num_ids, name_list.name[i]);
589 /* Force all records to use the first id then delete the other ids */
590 for (int j=1; j<id_list.num_ids; j++) {
591 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%u WHERE FilenameId=%u",
592 id_list.Id[0], id_list.Id[j]);
596 db_sql_query(db, buf, NULL, NULL);
597 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%u",
602 db_sql_query(db, buf, NULL, NULL);
606 free_name_list(&name_list);
609 static void eliminate_duplicate_paths()
614 printf(_("Checking for duplicate Path entries.\n"));
616 /* Make list of duplicated names */
618 query = "SELECT Path, count(Path) as Count FROM Path "
619 "GROUP BY Path HAVING count(Path) > 1";
621 if (!make_name_list(query, &name_list)) {
624 printf("Found %d duplicate Path records.\n", name_list.num_ids);
625 if (name_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
626 print_name_list(&name_list);
629 /* Loop through list of duplicate names */
630 for (int i=0; i<name_list.num_ids; i++) {
631 /* Get all the Ids of each name */
632 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
633 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
637 if (!make_id_list(buf, &id_list)) {
641 printf("Found %d for: %s\n", id_list.num_ids, name_list.name[i]);
643 /* Force all records to use the first id then delete the other ids */
644 for (int j=1; j<id_list.num_ids; j++) {
645 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%u WHERE PathId=%u",
646 id_list.Id[0], id_list.Id[j]);
650 db_sql_query(db, buf, NULL, NULL);
651 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%u",
656 db_sql_query(db, buf, NULL, NULL);
660 free_name_list(&name_list);
663 static void eliminate_orphaned_jobmedia_records()
667 printf("Checking for orphaned JobMedia entries.\n");
668 query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
669 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
670 "WHERE Job.JobId IS NULL";
671 if (!make_id_list(query, &id_list)) {
674 printf("Found %d orphaned JobMedia records.\n", id_list.num_ids);
675 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
676 for (int i=0; i < id_list.num_ids; i++) {
677 bsnprintf(buf, sizeof(buf),
678 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
679 "WHERE JobMedia.JobMediaId=%u AND Media.MediaId=JobMedia.MediaId", id_list.Id[i]);
680 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
681 printf("%s\n", db_strerror(db));
686 if (fix && id_list.num_ids > 0) {
687 printf("Deleting %d orphaned JobMedia records.\n", id_list.num_ids);
688 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%u", &id_list);
692 static void eliminate_orphaned_file_records()
696 printf("Checking for orphaned File entries. This may take some time!\n");
697 query = "SELECT File.FileId,Job.JobId FROM File "
698 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
699 "WHERE Job.JobId IS NULL";
701 printf("%s\n", query);
703 if (!make_id_list(query, &id_list)) {
706 printf("Found %d orphaned File records.\n", id_list.num_ids);
707 if (name_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
708 for (int i=0; i < id_list.num_ids; i++) {
709 bsnprintf(buf, sizeof(buf),
710 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
711 "WHERE File.FileId=%u AND File.FilenameId=Filename.FilenameId", id_list.Id[i]);
712 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
713 printf("%s\n", db_strerror(db));
718 if (fix && id_list.num_ids > 0) {
719 printf("Deleting %d orphaned File records.\n", id_list.num_ids);
720 delete_id_list("DELETE FROM File WHERE FileId=%u", &id_list);
724 static void eliminate_orphaned_path_records()
728 printf("Checking for orphaned Path entries. This may take some time!\n");
729 query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
730 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
731 "WHERE File.PathId IS NULL";
733 printf("%s\n", query);
735 if (!make_id_list(query, &id_list)) {
738 printf("Found %d orphaned Path records.\n", id_list.num_ids);
739 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
740 for (int i=0; i < id_list.num_ids; i++) {
741 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
742 db_sql_query(db, buf, print_name_handler, NULL);
746 if (fix && id_list.num_ids > 0) {
747 printf("Deleting %d orphaned Path records.\n", id_list.num_ids);
748 delete_id_list("DELETE FROM Path WHERE PathId=%u", &id_list);
752 static void eliminate_orphaned_filename_records()
756 printf("Checking for orphaned Filename entries. This may take some time!\n");
757 query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
758 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
759 "WHERE File.FilenameId IS NULL";
761 printf("%s\n", query);
763 if (!make_id_list(query, &id_list)) {
766 printf("Found %d orphaned Filename records.\n", id_list.num_ids);
767 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
768 for (int i=0; i < id_list.num_ids; i++) {
769 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
770 db_sql_query(db, buf, print_name_handler, NULL);
774 if (fix && id_list.num_ids > 0) {
775 printf("Deleting %d orphaned Filename records.\n", id_list.num_ids);
776 delete_id_list("DELETE FROM Filename WHERE FilenameId=%u", &id_list);
780 static void eliminate_orphaned_fileset_records()
784 printf("Checking for orphaned FileSet entries. This takes some time!\n");
785 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
786 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
787 "WHERE Job.FileSetId IS NULL";
789 printf("%s\n", query);
791 if (!make_id_list(query, &id_list)) {
794 printf("Found %d orphaned FileSet records.\n", id_list.num_ids);
795 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
796 for (int i=0; i < id_list.num_ids; i++) {
797 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
798 "WHERE FileSetId=%u", id_list.Id[i]);
799 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
800 printf("%s\n", db_strerror(db));
805 if (fix && id_list.num_ids > 0) {
806 printf("Deleting %d orphaned FileSet records.\n", id_list.num_ids);
807 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%u", &id_list);
811 static void eliminate_orphaned_client_records()
815 printf("Checking for orphaned Client entries.\n");
817 * Wiffle through Client for every Client
818 * joining with the Job table including every Client even if
819 * there is not a match in Job (left outer join), then
820 * filter out only those where no Job points to a Client
821 * i.e. Job.Client is NULL
823 query = "SELECT Client.ClientId,Client.Name FROM Client "
824 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
825 "WHERE Job.ClientId IS NULL";
827 printf("%s\n", query);
829 if (!make_id_list(query, &id_list)) {
832 printf("Found %d orphaned Client records.\n", id_list.num_ids);
833 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
834 for (int i=0; i < id_list.num_ids; i++) {
835 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
836 "WHERE ClientId=%u", id_list.Id[i]);
837 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
838 printf("%s\n", db_strerror(db));
843 if (fix && id_list.num_ids > 0) {
844 printf("Deleting %d orphaned Client records.\n", id_list.num_ids);
845 delete_id_list("DELETE FROM Client WHERE ClientId=%u", &id_list);
849 static void eliminate_orphaned_job_records()
853 printf("Checking for orphaned Job entries.\n");
855 * Wiffle through Job for every Job
856 * joining with the Client table including every Job even if
857 * there is not a match in Client (left outer join), then
858 * filter out only those where no Client exists
859 * i.e. Client.Name is NULL
861 query = "SELECT Job.JobId,Job.Name FROM Job "
862 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
863 "WHERE Client.Name IS NULL";
865 printf("%s\n", query);
867 if (!make_id_list(query, &id_list)) {
870 printf("Found %d orphaned Job records.\n", id_list.num_ids);
871 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
872 for (int i=0; i < id_list.num_ids; i++) {
873 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
874 "WHERE JobId=%u", id_list.Id[i]);
875 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
876 printf("%s\n", db_strerror(db));
881 if (fix && id_list.num_ids > 0) {
882 printf("Deleting %d orphaned Job records.\n", id_list.num_ids);
883 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
888 static void eliminate_admin_records()
892 printf("Checking for Admin Job entries.\n");
893 query = "SELECT Job.JobId FROM Job "
894 "WHERE Job.Type='D'";
896 printf("%s\n", query);
898 if (!make_id_list(query, &id_list)) {
901 printf("Found %d Admin Job records.\n", id_list.num_ids);
902 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
903 for (int i=0; i < id_list.num_ids; i++) {
904 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
905 "WHERE JobId=%u", id_list.Id[i]);
906 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
907 printf("%s\n", db_strerror(db));
912 if (fix && id_list.num_ids > 0) {
913 printf("Deleting %d Admin Job records.\n", id_list.num_ids);
914 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
918 static void eliminate_restore_records()
922 printf("Checking for Restore Job entries.\n");
923 query = "SELECT Job.JobId FROM Job "
924 "WHERE Job.Type='R'";
926 printf("%s\n", query);
928 if (!make_id_list(query, &id_list)) {
931 printf("Found %d Restore Job records.\n", id_list.num_ids);
932 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
933 for (int i=0; i < id_list.num_ids; i++) {
934 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
935 "WHERE JobId=%u", id_list.Id[i]);
936 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
937 printf("%s\n", db_strerror(db));
942 if (fix && id_list.num_ids > 0) {
943 printf("Deleting %d Restore Job records.\n", id_list.num_ids);
944 delete_id_list("DELETE FROM Job WHERE JobId=%u", &id_list);
951 static void repair_bad_filenames()
956 printf("Checking for Filenames with a trailing slash\n");
957 query = "SELECT FilenameId,Name from Filename "
958 "WHERE Name LIKE '%/'";
960 printf("%s\n", query);
962 if (!make_id_list(query, &id_list)) {
965 printf("Found %d bad Filename records.\n", id_list.num_ids);
966 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
967 for (i=0; i < id_list.num_ids; i++) {
968 bsnprintf(buf, sizeof(buf),
969 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
970 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
971 printf("%s\n", db_strerror(db));
976 if (fix && id_list.num_ids > 0) {
977 POOLMEM *name = get_pool_memory(PM_FNAME);
979 printf("Reparing %d bad Filename records.\n", id_list.num_ids);
980 for (i=0; i < id_list.num_ids; i++) {
982 bsnprintf(buf, sizeof(buf),
983 "SELECT Name FROM Filename WHERE FilenameId=%u", id_list.Id[i]);
984 if (!db_sql_query(db, buf, get_name_handler, name)) {
985 printf("%s\n", db_strerror(db));
987 /* Strip trailing slash(es) */
988 for (len=strlen(name); len > 0 && name[len-1]=='/'; len--)
996 db_escape_string(esc_name, name, len);
998 bsnprintf(buf, sizeof(buf),
999 "UPDATE Filename SET Name='%s' WHERE FilenameId=%u",
1000 esc_name, id_list.Id[i]);
1002 printf("%s\n", buf);
1004 db_sql_query(db, buf, NULL, NULL);
1009 static void repair_bad_paths()
1014 printf("Checking for Paths without a trailing slash\n");
1015 query = "SELECT PathId,Path from Path "
1016 "WHERE Path NOT LIKE '%/'";
1018 printf("%s\n", query);
1020 if (!make_id_list(query, &id_list)) {
1023 printf("Found %d bad Path records.\n", id_list.num_ids);
1024 if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
1025 for (i=0; i < id_list.num_ids; i++) {
1026 bsnprintf(buf, sizeof(buf),
1027 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1028 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1029 printf("%s\n", db_strerror(db));
1034 if (fix && id_list.num_ids > 0) {
1035 POOLMEM *name = get_pool_memory(PM_FNAME);
1036 char esc_name[5000];
1037 printf("Reparing %d bad Filename records.\n", id_list.num_ids);
1038 for (i=0; i < id_list.num_ids; i++) {
1040 bsnprintf(buf, sizeof(buf),
1041 "SELECT Path FROM Path WHERE PathId=%u", id_list.Id[i]);
1042 if (!db_sql_query(db, buf, get_name_handler, name)) {
1043 printf("%s\n", db_strerror(db));
1045 /* Strip trailing blanks */
1046 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1049 /* Add trailing slash */
1050 len = pm_strcat(&name, "/");
1051 db_escape_string(esc_name, name, len);
1052 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%u",
1053 esc_name, id_list.Id[i]);
1055 printf("%s\n", buf);
1057 db_sql_query(db, buf, NULL, NULL);
1066 * Gen next input command from the terminal
1068 static char *get_cmd(const char *prompt)
1070 static char cmd[1000];
1072 printf("%s", prompt);
1073 if (fgets(cmd, sizeof(cmd), stdin) == NULL)
1076 strip_trailing_junk(cmd);
1080 static int yes_no(const char *prompt)
1083 cmd = get_cmd(prompt);
1084 return strcasecmp(cmd, "yes") == 0;