2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Program to check a Bacula database for consistency and to
33 * Kern E. Sibbald, August 2002
40 #include "cats/cats.h"
41 #include "lib/runscript.h"
42 #include "dird/dird_conf.h"
45 int generate_daemon_event(JCR *jcr, const char *event)
48 typedef struct s_id_ctx {
49 int64_t *Id; /* ids to be modified */
50 int num_ids; /* ids stored */
51 int max_ids; /* size of array */
52 int num_del; /* number deleted */
53 int tot_ids; /* total to process */
56 typedef struct s_name_ctx {
57 char **name; /* list of names */
58 int num_ids; /* ids stored */
59 int max_ids; /* size of array */
60 int num_del; /* number deleted */
61 int tot_ids; /* total to process */
66 /* Global variables */
67 static bool fix = false;
68 static bool batch = false;
70 static ID_LIST id_list;
71 static NAME_LIST name_list;
72 static char buf[20000];
73 static bool quit = false;
75 #define MAX_ID_LIST_LEN 10000000
77 /* Forward referenced functions */
78 static int make_id_list(const char *query, ID_LIST *id_list);
79 static int delete_id_list(const char *query, ID_LIST *id_list);
80 static int make_name_list(const char *query, NAME_LIST *name_list);
81 static void print_name_list(NAME_LIST *name_list);
82 static void free_name_list(NAME_LIST *name_list);
83 static char *get_cmd(const char *prompt);
84 static void eliminate_duplicate_filenames();
85 static void eliminate_duplicate_paths();
86 static void eliminate_orphaned_jobmedia_records();
87 static void eliminate_orphaned_file_records();
88 static void eliminate_orphaned_path_records();
89 static void eliminate_orphaned_filename_records();
90 static void eliminate_orphaned_fileset_records();
91 static void eliminate_orphaned_client_records();
92 static void eliminate_orphaned_job_records();
93 static void eliminate_admin_records();
94 static void eliminate_restore_records();
95 static void repair_bad_paths();
96 static void repair_bad_filenames();
97 static void do_interactive_mode();
98 static bool yes_no(const char *prompt);
104 "Usage: dbcheck [-c config] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>]\n"
106 " -C catalog name in the director conf file\n"
107 " -c director conf filename\n"
108 " -d <nn> set debug level to <nn>\n"
109 " -dt print timestamp in debug output\n"
110 " -f fix inconsistencies\n"
112 " -? print this message\n\n");
116 int main (int argc, char *argv[])
119 const char *user, *password, *db_name, *dbhost;
120 char *configfile = NULL;
121 char *catalogname = NULL;
123 setlocale(LC_ALL, "");
124 bindtextdomain("bacula", LOCALEDIR);
125 textdomain("bacula");
127 my_name_is(argc, argv, "dbcheck");
128 init_msg(NULL, NULL); /* setup message handler */
130 memset(&id_list, 0, sizeof(id_list));
131 memset(&name_list, 0, sizeof(name_list));
134 while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
136 case 'b': /* batch */
140 case 'C': /* CatalogName */
141 catalogname = optarg;
144 case 'c': /* configfile */
148 case 'd': /* debug level */
149 if (*optarg == 't') {
150 dbg_timestamp = true;
152 debug_level = atoi(optarg);
153 if (debug_level <= 0) {
159 case 'f': /* fix inconsistencies */
181 Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
183 parse_config(configfile);
185 foreach_res(catalog, R_CATALOG) {
186 if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
189 } else if (!catalogname) { // stop on first if no catalogname is given
197 Pmsg2(0, _("Error can not find the Catalog name[%s] in the given config file [%s]\n"), catalogname, configfile);
199 Pmsg1(0, _("Error there is no Catalog section in the given config file [%s]\n"), configfile);
205 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
208 Pmsg0(0, _("Error no Director resource defined.\n"));
211 set_working_directory(director->working_directory);
212 db_name = catalog->db_name;
213 user = catalog->db_user;
214 password = catalog->db_password;
215 dbhost = catalog->db_address;
216 if (dbhost && dbhost[0] == 0) {
222 Pmsg0(0, _("Wrong number of arguments.\n"));
227 Pmsg0(0, _("Working directory not supplied.\n"));
231 /* This is needed by SQLite to find the db */
232 working_directory = argv[0];
241 } else if (argc == 3) {
244 } else if (argc == 4) {
248 } else if (argc == 5) {
257 db = db_init_database(NULL, db_name, user, password, dbhost, 0, NULL, 0);
258 if (!db_open_database(NULL, db)) {
259 Emsg1(M_FATAL, 0, "%s", db_strerror(db));
265 repair_bad_filenames();
266 eliminate_duplicate_filenames();
267 eliminate_duplicate_paths();
268 eliminate_orphaned_jobmedia_records();
269 eliminate_orphaned_file_records();
270 eliminate_orphaned_path_records();
271 eliminate_orphaned_filename_records();
272 eliminate_orphaned_fileset_records();
273 eliminate_orphaned_client_records();
274 eliminate_orphaned_job_records();
275 eliminate_admin_records();
276 eliminate_restore_records();
278 do_interactive_mode();
281 db_close_database(NULL, db);
287 static void do_interactive_mode()
291 printf(_("Hello, this is the database check/correct program.\n"));
293 printf(_("Modify database is on."));
295 printf(_("Modify database is off."));
297 printf(_(" Verbose is on.\n"));
299 printf(_(" Verbose is off.\n"));
301 printf(_("Please select the fuction you want to perform.\n"));
306 " 1) Toggle modify database flag\n"
307 " 2) Toggle verbose flag\n"
308 " 3) Repair bad Filename records\n"
309 " 4) Repair bad Path records\n"
310 " 5) Eliminate duplicate Filename records\n"
311 " 6) Eliminate duplicate Path records\n"
312 " 7) Eliminate orphaned Jobmedia records\n"
313 " 8) Eliminate orphaned File records\n"
314 " 9) Eliminate orphaned Path records\n"
315 " 10) Eliminate orphaned Filename records\n"
316 " 11) Eliminate orphaned FileSet records\n"
317 " 12) Eliminate orphaned Client records\n"
318 " 13) Eliminate orphaned Job records\n"
319 " 14) Eliminate all Admin records\n"
320 " 15) Eliminate all Restore records\n"
325 " 1) Toggle modify database flag\n"
326 " 2) Toggle verbose flag\n"
327 " 3) Check for bad Filename records\n"
328 " 4) Check for bad Path records\n"
329 " 5) Check for duplicate Filename records\n"
330 " 6) Check for duplicate Path records\n"
331 " 7) Check for orphaned Jobmedia records\n"
332 " 8) Check for orphaned File records\n"
333 " 9) Check for orphaned Path records\n"
334 " 10) Check for orphaned Filename records\n"
335 " 11) Check for orphaned FileSet records\n"
336 " 12) Check for orphaned Client records\n"
337 " 13) Check for orphaned Job records\n"
338 " 14) Check for all Admin records\n"
339 " 15) Check for all Restore records\n"
344 cmd = get_cmd(_("Select function number: "));
346 int item = atoi(cmd);
351 printf(_("Database will be modified.\n"));
353 printf(_("Database will NOT be modified.\n"));
356 verbose = verbose?0:1;
358 printf(_(" Verbose is on.\n"));
360 printf(_(" Verbose is off.\n"));
363 repair_bad_filenames();
369 eliminate_duplicate_filenames();
372 eliminate_duplicate_paths();
375 eliminate_orphaned_jobmedia_records();
378 eliminate_orphaned_file_records();
381 eliminate_orphaned_path_records();
384 eliminate_orphaned_filename_records();
387 eliminate_orphaned_fileset_records();
390 eliminate_orphaned_client_records();
393 eliminate_orphaned_job_records();
396 eliminate_admin_records();
399 eliminate_restore_records();
402 repair_bad_filenames();
404 eliminate_duplicate_filenames();
405 eliminate_duplicate_paths();
406 eliminate_orphaned_jobmedia_records();
407 eliminate_orphaned_file_records();
408 eliminate_orphaned_path_records();
409 eliminate_orphaned_filename_records();
410 eliminate_orphaned_fileset_records();
411 eliminate_orphaned_client_records();
412 eliminate_orphaned_job_records();
413 eliminate_admin_records();
414 eliminate_restore_records();
424 static int print_name_handler(void *ctx, int num_fields, char **row)
427 printf("%s\n", row[0]);
432 static int get_name_handler(void *ctx, int num_fields, char **row)
434 POOLMEM *buf = (POOLMEM *)ctx;
436 pm_strcpy(&buf, row[0]);
441 static int print_job_handler(void *ctx, int num_fields, char **row)
443 printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
444 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
449 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
451 printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
452 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
456 static int print_file_handler(void *ctx, int num_fields, char **row)
458 printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
459 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
463 static int print_fileset_handler(void *ctx, int num_fields, char **row)
465 printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
466 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
470 static int print_client_handler(void *ctx, int num_fields, char **row)
472 printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
473 NPRT(row[0]), NPRT(row[1]));
479 * Called here with each id to be added to the list
481 static int id_list_handler(void *ctx, int num_fields, char **row)
483 ID_LIST *lst = (ID_LIST *)ctx;
485 if (lst->num_ids == MAX_ID_LIST_LEN) {
488 if (lst->num_ids == lst->max_ids) {
489 if (lst->max_ids == 0) {
490 lst->max_ids = 10000;
491 lst->Id = (int64_t *)bmalloc(sizeof(int64_t) * lst->max_ids);
493 lst->max_ids = (lst->max_ids * 3) / 2;
494 lst->Id = (int64_t *)brealloc(lst->Id, sizeof(int64_t) * lst->max_ids);
497 lst->Id[lst->num_ids++] = str_to_int64(row[0]);
502 * Construct record id list
504 static int make_id_list(const char *query, ID_LIST *id_list)
506 id_list->num_ids = 0;
507 id_list->num_del = 0;
508 id_list->tot_ids = 0;
510 if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
511 printf("%s", db_strerror(db));
518 * Delete all entries in the list
520 static int delete_id_list(const char *query, ID_LIST *id_list)
523 for (int i=0; i < id_list->num_ids; i++) {
524 bsnprintf(buf, sizeof(buf), query, edit_int64(id_list->Id[i], ed1));
526 printf(_("Deleting: %s\n"), buf);
528 db_sql_query(db, buf, NULL, NULL);
534 * Called here with each name to be added to the list
536 static int name_list_handler(void *ctx, int num_fields, char **row)
538 NAME_LIST *name = (NAME_LIST *)ctx;
540 if (name->num_ids == MAX_ID_LIST_LEN) {
543 if (name->num_ids == name->max_ids) {
544 if (name->max_ids == 0) {
545 name->max_ids = 10000;
546 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
548 name->max_ids = (name->max_ids * 3) / 2;
549 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
552 name->name[name->num_ids++] = bstrdup(row[0]);
558 * Construct name list
560 static int make_name_list(const char *query, NAME_LIST *name_list)
562 name_list->num_ids = 0;
563 name_list->num_del = 0;
564 name_list->tot_ids = 0;
566 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
567 printf("%s", db_strerror(db));
574 * Print names in the list
576 static void print_name_list(NAME_LIST *name_list)
578 for (int i=0; i < name_list->num_ids; i++) {
579 printf("%s\n", name_list->name[i]);
585 * Free names in the list
587 static void free_name_list(NAME_LIST *name_list)
589 for (int i=0; i < name_list->num_ids; i++) {
590 free(name_list->name[i]);
592 name_list->num_ids = 0;
595 static void eliminate_duplicate_filenames()
600 printf(_("Checking for duplicate Filename entries.\n"));
602 /* Make list of duplicated names */
603 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
604 "HAVING count(Name) > 1";
606 if (!make_name_list(query, &name_list)) {
609 printf(_("Found %d duplicate Filename records.\n"), name_list.num_ids);
610 if (name_list.num_ids && verbose && yes_no(_("Print the list? (yes/no): "))) {
611 print_name_list(&name_list);
617 /* Loop through list of duplicate names */
618 for (int i=0; i<name_list.num_ids; i++) {
619 /* Get all the Ids of each name */
620 db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
621 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
625 if (!make_id_list(buf, &id_list)) {
629 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
631 /* Force all records to use the first id then delete the other ids */
632 for (int j=1; j<id_list.num_ids; j++) {
633 char ed1[50], ed2[50];
634 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%s WHERE FilenameId=%s",
635 edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
639 db_sql_query(db, buf, NULL, NULL);
640 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%s",
645 db_sql_query(db, buf, NULL, NULL);
649 free_name_list(&name_list);
652 static void eliminate_duplicate_paths()
657 printf(_("Checking for duplicate Path entries.\n"));
659 /* Make list of duplicated names */
661 query = "SELECT Path, count(Path) as Count FROM Path "
662 "GROUP BY Path HAVING count(Path) > 1";
664 if (!make_name_list(query, &name_list)) {
667 printf(_("Found %d duplicate Path records.\n"), name_list.num_ids);
668 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
669 print_name_list(&name_list);
675 /* Loop through list of duplicate names */
676 for (int i=0; i<name_list.num_ids; i++) {
677 /* Get all the Ids of each name */
678 db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
679 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
683 if (!make_id_list(buf, &id_list)) {
687 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
689 /* Force all records to use the first id then delete the other ids */
690 for (int j=1; j<id_list.num_ids; j++) {
691 char ed1[50], ed2[50];
692 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%s WHERE PathId=%s",
693 edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
697 db_sql_query(db, buf, NULL, NULL);
698 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%s", ed2);
702 db_sql_query(db, buf, NULL, NULL);
706 free_name_list(&name_list);
709 static void eliminate_orphaned_jobmedia_records()
711 const char *query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
712 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
713 "WHERE Job.JobId IS NULL LIMIT 300000";
715 printf(_("Checking for orphaned JobMedia entries.\n"));
716 if (!make_id_list(query, &id_list)) {
719 /* Loop doing 300000 at a time */
720 while (id_list.num_ids != 0) {
721 printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
722 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
723 for (int i=0; i < id_list.num_ids; i++) {
725 bsnprintf(buf, sizeof(buf),
726 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
727 "WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId",
728 edit_int64(id_list.Id[i], ed1));
729 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
730 printf("%s\n", db_strerror(db));
738 if (fix && id_list.num_ids > 0) {
739 printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
740 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%s", &id_list);
742 break; /* get out if not updating db */
744 if (!make_id_list(query, &id_list)) {
750 static void eliminate_orphaned_file_records()
752 const char *query = "SELECT File.FileId,Job.JobId FROM File "
753 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
754 "WHERE Job.JobId IS NULL LIMIT 300000";
756 printf(_("Checking for orphaned File entries. This may take some time!\n"));
758 printf("%s\n", query);
760 if (!make_id_list(query, &id_list)) {
763 /* Loop doing 300000 at a time */
764 while (id_list.num_ids != 0) {
765 printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
766 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
767 for (int i=0; i < id_list.num_ids; i++) {
769 bsnprintf(buf, sizeof(buf),
770 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
771 "WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
772 edit_int64(id_list.Id[i], ed1));
773 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
774 printf("%s\n", db_strerror(db));
781 if (fix && id_list.num_ids > 0) {
782 printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
783 delete_id_list("DELETE FROM File WHERE FileId=%s", &id_list);
785 break; /* get out if not updating db */
787 if (!make_id_list(query, &id_list)) {
793 static void eliminate_orphaned_path_records()
795 const char *query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
796 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
797 "WHERE File.PathId IS NULL LIMIT 300000";
799 printf(_("Checking for orphaned Path entries. This may take some time!\n"));
801 printf("%s\n", query);
803 if (!make_id_list(query, &id_list)) {
806 /* Loop doing 300000 at a time */
807 while (id_list.num_ids != 0) {
808 printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
809 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
810 for (int i=0; i < id_list.num_ids; i++) {
812 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
813 edit_int64(id_list.Id[i], ed1));
814 db_sql_query(db, buf, print_name_handler, NULL);
820 if (fix && id_list.num_ids > 0) {
821 printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
822 delete_id_list("DELETE FROM Path WHERE PathId=%s", &id_list);
824 break; /* get out if not updating db */
826 if (!make_id_list(query, &id_list)) {
832 static void eliminate_orphaned_filename_records()
834 const char *query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
835 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
836 "WHERE File.FilenameId IS NULL LIMIT 300000";
838 printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
840 printf("%s\n", query);
842 if (!make_id_list(query, &id_list)) {
845 /* Loop doing 300000 at a time */
846 while (id_list.num_ids != 0) {
847 printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
848 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
849 for (int i=0; i < id_list.num_ids; i++) {
851 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
852 edit_int64(id_list.Id[i], ed1));
853 db_sql_query(db, buf, print_name_handler, NULL);
859 if (fix && id_list.num_ids > 0) {
860 printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
861 delete_id_list("DELETE FROM Filename WHERE FilenameId=%s", &id_list);
863 break; /* get out if not updating db */
865 if (!make_id_list(query, &id_list)) {
871 static void eliminate_orphaned_fileset_records()
875 printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
876 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
877 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
878 "WHERE Job.FileSetId IS NULL";
880 printf("%s\n", query);
882 if (!make_id_list(query, &id_list)) {
885 printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
886 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
887 for (int i=0; i < id_list.num_ids; i++) {
889 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
890 "WHERE FileSetId=%s", edit_int64(id_list.Id[i], ed1));
891 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
892 printf("%s\n", db_strerror(db));
899 if (fix && id_list.num_ids > 0) {
900 printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
901 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%s", &id_list);
905 static void eliminate_orphaned_client_records()
909 printf(_("Checking for orphaned Client entries.\n"));
911 * Wiffle through Client for every Client
912 * joining with the Job table including every Client even if
913 * there is not a match in Job (left outer join), then
914 * filter out only those where no Job points to a Client
915 * i.e. Job.Client is NULL
917 query = "SELECT Client.ClientId,Client.Name FROM Client "
918 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
919 "WHERE Job.ClientId IS NULL";
921 printf("%s\n", query);
923 if (!make_id_list(query, &id_list)) {
926 printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
927 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
928 for (int i=0; i < id_list.num_ids; i++) {
930 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
931 "WHERE ClientId=%s", edit_int64(id_list.Id[i], ed1));
932 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
933 printf("%s\n", db_strerror(db));
940 if (fix && id_list.num_ids > 0) {
941 printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
942 delete_id_list("DELETE FROM Client WHERE ClientId=%s", &id_list);
946 static void eliminate_orphaned_job_records()
950 printf(_("Checking for orphaned Job entries.\n"));
952 * Wiffle through Job for every Job
953 * joining with the Client table including every Job even if
954 * there is not a match in Client (left outer join), then
955 * filter out only those where no Client exists
956 * i.e. Client.Name is NULL
958 query = "SELECT Job.JobId,Job.Name FROM Job "
959 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
960 "WHERE Client.Name IS NULL";
962 printf("%s\n", query);
964 if (!make_id_list(query, &id_list)) {
967 printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
968 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
969 for (int i=0; i < id_list.num_ids; i++) {
971 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
972 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
973 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
974 printf("%s\n", db_strerror(db));
981 if (fix && id_list.num_ids > 0) {
982 printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
983 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
984 printf(_("Deleting JobMedia records of orphaned Job records.\n"));
985 delete_id_list("DELETE FROM JobMedia WHERE JobId=%s", &id_list);
986 printf(_("Deleting Log records of orphaned Job records.\n"));
987 delete_id_list("DELETE FROM Log WHERE JobId=%s", &id_list);
992 static void eliminate_admin_records()
996 printf(_("Checking for Admin Job entries.\n"));
997 query = "SELECT Job.JobId FROM Job "
998 "WHERE Job.Type='D'";
1000 printf("%s\n", query);
1002 if (!make_id_list(query, &id_list)) {
1005 printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
1006 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1007 for (int i=0; i < id_list.num_ids; i++) {
1009 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1010 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1011 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1012 printf("%s\n", db_strerror(db));
1019 if (fix && id_list.num_ids > 0) {
1020 printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
1021 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1025 static void eliminate_restore_records()
1029 printf(_("Checking for Restore Job entries.\n"));
1030 query = "SELECT Job.JobId FROM Job "
1031 "WHERE Job.Type='R'";
1033 printf("%s\n", query);
1035 if (!make_id_list(query, &id_list)) {
1038 printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
1039 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1040 for (int i=0; i < id_list.num_ids; i++) {
1042 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1043 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1044 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1045 printf("%s\n", db_strerror(db));
1052 if (fix && id_list.num_ids > 0) {
1053 printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
1054 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1061 static void repair_bad_filenames()
1066 printf(_("Checking for Filenames with a trailing slash\n"));
1067 query = "SELECT FilenameId,Name from Filename "
1068 "WHERE Name LIKE '%/'";
1070 printf("%s\n", query);
1072 if (!make_id_list(query, &id_list)) {
1075 printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
1076 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1077 for (i=0; i < id_list.num_ids; i++) {
1079 bsnprintf(buf, sizeof(buf),
1080 "SELECT Name FROM Filename WHERE FilenameId=%s",
1081 edit_int64(id_list.Id[i], ed1));
1082 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1083 printf("%s\n", db_strerror(db));
1090 if (fix && id_list.num_ids > 0) {
1091 POOLMEM *name = get_pool_memory(PM_FNAME);
1092 char esc_name[5000];
1093 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1094 for (i=0; i < id_list.num_ids; i++) {
1097 bsnprintf(buf, sizeof(buf),
1098 "SELECT Name FROM Filename WHERE FilenameId=%s",
1099 edit_int64(id_list.Id[i], ed1));
1100 if (!db_sql_query(db, buf, get_name_handler, name)) {
1101 printf("%s\n", db_strerror(db));
1103 /* Strip trailing slash(es) */
1104 for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
1112 db_escape_string(NULL, db, esc_name, name, len);
1114 bsnprintf(buf, sizeof(buf),
1115 "UPDATE Filename SET Name='%s' WHERE FilenameId=%s",
1116 esc_name, edit_int64(id_list.Id[i], ed1));
1118 printf("%s\n", buf);
1120 db_sql_query(db, buf, NULL, NULL);
1125 static void repair_bad_paths()
1130 printf(_("Checking for Paths without a trailing slash\n"));
1131 query = "SELECT PathId,Path from Path "
1132 "WHERE Path NOT LIKE '%/'";
1134 printf("%s\n", query);
1136 if (!make_id_list(query, &id_list)) {
1139 printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1140 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1141 for (i=0; i < id_list.num_ids; i++) {
1143 bsnprintf(buf, sizeof(buf),
1144 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1145 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1146 printf("%s\n", db_strerror(db));
1153 if (fix && id_list.num_ids > 0) {
1154 POOLMEM *name = get_pool_memory(PM_FNAME);
1155 char esc_name[5000];
1156 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1157 for (i=0; i < id_list.num_ids; i++) {
1160 bsnprintf(buf, sizeof(buf),
1161 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1162 if (!db_sql_query(db, buf, get_name_handler, name)) {
1163 printf("%s\n", db_strerror(db));
1165 /* Strip trailing blanks */
1166 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1169 /* Add trailing slash */
1170 len = pm_strcat(&name, "/");
1171 db_escape_string(NULL, db, esc_name, name, len);
1172 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
1173 esc_name, edit_int64(id_list.Id[i], ed1));
1175 printf("%s\n", buf);
1177 db_sql_query(db, buf, NULL, NULL);
1184 * Gen next input command from the terminal
1186 static char *get_cmd(const char *prompt)
1188 static char cmd[1000];
1190 printf("%s", prompt);
1191 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
1196 strip_trailing_junk(cmd);
1200 static bool yes_no(const char *prompt)
1203 cmd = get_cmd(prompt);
1208 return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1211 bool python_set_prog(JCR*, char const*) { return false; }