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;