3 * Program to check a Bacula database for consistency and to
6 * Kern E. Sibbald, August 2002
12 Bacula® - The Network Backup Solution
14 Copyright (C) 2002-2007 Free Software Foundation Europe e.V.
16 The main author of Bacula is Kern Sibbald, with contributions from
17 many others, a complete list can be found in the file AUTHORS.
18 This program is Free Software; you can redistribute it and/or
19 modify it under the terms of version two of the GNU General Public
20 License as published by the Free Software Foundation plus additions
21 that are listed in the file LICENSE.
23 This program is distributed in the hope that it will be useful, but
24 WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program; if not, write to the Free Software
30 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
33 Bacula® is a registered trademark of John Walker.
34 The licensor of Bacula is the Free Software Foundation Europe
35 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
36 Switzerland, email:ftf@fsfeurope.org.
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 " -dnn set debug level to nn\n"
109 " -f fix inconsistencies\n"
111 " -? print this message\n\n");
115 int main (int argc, char *argv[])
118 const char *user, *password, *db_name, *dbhost;
119 char *configfile = NULL;
120 char *catalogname = NULL;
122 setlocale(LC_ALL, "");
123 bindtextdomain("bacula", LOCALEDIR);
124 textdomain("bacula");
126 my_name_is(argc, argv, "dbcheck");
127 init_msg(NULL, NULL); /* setup message handler */
129 memset(&id_list, 0, sizeof(id_list));
130 memset(&name_list, 0, sizeof(name_list));
133 while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
135 case 'b': /* batch */
139 case 'C': /* CatalogName */
140 catalogname = optarg;
143 case 'c': /* configfile */
147 case 'd': /* debug level */
148 debug_level = atoi(optarg);
149 if (debug_level <= 0)
153 case 'f': /* fix inconsistencies */
175 Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
177 parse_config(configfile);
179 foreach_res(catalog, R_CATALOG) {
180 if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
183 } else if (!catalogname) { // stop on first if no catalogname is given
191 Pmsg2(0, _("Error can not find the Catalog name[%s] in the given config file [%s]\n"), catalogname, configfile);
193 Pmsg1(0, _("Error there is no Catalog section in the given config file [%s]\n"), configfile);
199 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
202 Pmsg0(0, _("Error no Director resource defined.\n"));
205 set_working_directory(director->working_directory);
206 db_name = catalog->db_name;
207 user = catalog->db_user;
208 password = catalog->db_password;
209 dbhost = catalog->db_address;
210 if (dbhost && dbhost[0] == 0) {
216 Pmsg0(0, _("Wrong number of arguments.\n"));
221 Pmsg0(0, _("Working directory not supplied.\n"));
225 /* This is needed by SQLite to find the db */
226 working_directory = argv[0];
235 } else if (argc == 3) {
238 } else if (argc == 4) {
242 } else if (argc == 5) {
251 db = db_init_database(NULL, db_name, user, password, dbhost, 0, NULL, 0);
252 if (!db_open_database(NULL, db)) {
253 Emsg1(M_FATAL, 0, "%s", db_strerror(db));
259 repair_bad_filenames();
260 eliminate_duplicate_filenames();
261 eliminate_duplicate_paths();
262 eliminate_orphaned_jobmedia_records();
263 eliminate_orphaned_file_records();
264 eliminate_orphaned_path_records();
265 eliminate_orphaned_filename_records();
266 eliminate_orphaned_fileset_records();
267 eliminate_orphaned_client_records();
268 eliminate_orphaned_job_records();
269 eliminate_admin_records();
270 eliminate_restore_records();
272 do_interactive_mode();
275 db_close_database(NULL, db);
281 static void do_interactive_mode()
285 printf(_("Hello, this is the database check/correct program.\n"));
287 printf(_("Modify database is on."));
289 printf(_("Modify database is off."));
291 printf(_(" Verbose is on.\n"));
293 printf(_(" Verbose is off.\n"));
295 printf(_("Please select the fuction you want to perform.\n"));
300 " 1) Toggle modify database flag\n"
301 " 2) Toggle verbose flag\n"
302 " 3) Repair bad Filename records\n"
303 " 4) Repair bad Path records\n"
304 " 5) Eliminate duplicate Filename records\n"
305 " 6) Eliminate duplicate Path records\n"
306 " 7) Eliminate orphaned Jobmedia records\n"
307 " 8) Eliminate orphaned File records\n"
308 " 9) Eliminate orphaned Path records\n"
309 " 10) Eliminate orphaned Filename records\n"
310 " 11) Eliminate orphaned FileSet records\n"
311 " 12) Eliminate orphaned Client records\n"
312 " 13) Eliminate orphaned Job records\n"
313 " 14) Eliminate all Admin records\n"
314 " 15) Eliminate all Restore records\n"
319 " 1) Toggle modify database flag\n"
320 " 2) Toggle verbose flag\n"
321 " 3) Check for bad Filename records\n"
322 " 4) Check for bad Path records\n"
323 " 5) Check for duplicate Filename records\n"
324 " 6) Check for duplicate Path records\n"
325 " 7) Check for orphaned Jobmedia records\n"
326 " 8) Check for orphaned File records\n"
327 " 9) Check for orphaned Path records\n"
328 " 10) Check for orphaned Filename records\n"
329 " 11) Check for orphaned FileSet records\n"
330 " 12) Check for orphaned Client records\n"
331 " 13) Check for orphaned Job records\n"
332 " 14) Check for all Admin records\n"
333 " 15) Check for all Restore records\n"
338 cmd = get_cmd(_("Select function number: "));
340 int item = atoi(cmd);
345 printf(_("Database will be modified.\n"));
347 printf(_("Database will NOT be modified.\n"));
350 verbose = verbose?0:1;
352 printf(_(" Verbose is on.\n"));
354 printf(_(" Verbose is off.\n"));
357 repair_bad_filenames();
363 eliminate_duplicate_filenames();
366 eliminate_duplicate_paths();
369 eliminate_orphaned_jobmedia_records();
372 eliminate_orphaned_file_records();
375 eliminate_orphaned_path_records();
378 eliminate_orphaned_filename_records();
381 eliminate_orphaned_fileset_records();
384 eliminate_orphaned_client_records();
387 eliminate_orphaned_job_records();
390 eliminate_admin_records();
393 eliminate_restore_records();
396 repair_bad_filenames();
398 eliminate_duplicate_filenames();
399 eliminate_duplicate_paths();
400 eliminate_orphaned_jobmedia_records();
401 eliminate_orphaned_file_records();
402 eliminate_orphaned_path_records();
403 eliminate_orphaned_filename_records();
404 eliminate_orphaned_fileset_records();
405 eliminate_orphaned_client_records();
406 eliminate_orphaned_job_records();
407 eliminate_admin_records();
408 eliminate_restore_records();
418 static int print_name_handler(void *ctx, int num_fields, char **row)
421 printf("%s\n", row[0]);
426 static int get_name_handler(void *ctx, int num_fields, char **row)
428 POOLMEM *buf = (POOLMEM *)ctx;
430 pm_strcpy(&buf, row[0]);
435 static int print_job_handler(void *ctx, int num_fields, char **row)
437 printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
438 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
443 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
445 printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
446 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
450 static int print_file_handler(void *ctx, int num_fields, char **row)
452 printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
453 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
457 static int print_fileset_handler(void *ctx, int num_fields, char **row)
459 printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
460 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
464 static int print_client_handler(void *ctx, int num_fields, char **row)
466 printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
467 NPRT(row[0]), NPRT(row[1]));
473 * Called here with each id to be added to the list
475 static int id_list_handler(void *ctx, int num_fields, char **row)
477 ID_LIST *lst = (ID_LIST *)ctx;
479 if (lst->num_ids == MAX_ID_LIST_LEN) {
482 if (lst->num_ids == lst->max_ids) {
483 if (lst->max_ids == 0) {
484 lst->max_ids = 10000;
485 lst->Id = (int64_t *)bmalloc(sizeof(int64_t) * lst->max_ids);
487 lst->max_ids = (lst->max_ids * 3) / 2;
488 lst->Id = (int64_t *)brealloc(lst->Id, sizeof(int64_t) * lst->max_ids);
491 lst->Id[lst->num_ids++] = str_to_int64(row[0]);
496 * Construct record id list
498 static int make_id_list(const char *query, ID_LIST *id_list)
500 id_list->num_ids = 0;
501 id_list->num_del = 0;
502 id_list->tot_ids = 0;
504 if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
505 printf("%s", db_strerror(db));
512 * Delete all entries in the list
514 static int delete_id_list(const char *query, ID_LIST *id_list)
517 for (int i=0; i < id_list->num_ids; i++) {
518 bsnprintf(buf, sizeof(buf), query, edit_int64(id_list->Id[i], ed1));
520 printf(_("Deleting: %s\n"), buf);
522 db_sql_query(db, buf, NULL, NULL);
528 * Called here with each name to be added to the list
530 static int name_list_handler(void *ctx, int num_fields, char **row)
532 NAME_LIST *name = (NAME_LIST *)ctx;
534 if (name->num_ids == MAX_ID_LIST_LEN) {
537 if (name->num_ids == name->max_ids) {
538 if (name->max_ids == 0) {
539 name->max_ids = 10000;
540 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
542 name->max_ids = (name->max_ids * 3) / 2;
543 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
546 name->name[name->num_ids++] = bstrdup(row[0]);
552 * Construct name list
554 static int make_name_list(const char *query, NAME_LIST *name_list)
556 name_list->num_ids = 0;
557 name_list->num_del = 0;
558 name_list->tot_ids = 0;
560 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
561 printf("%s", db_strerror(db));
568 * Print names in the list
570 static void print_name_list(NAME_LIST *name_list)
572 for (int i=0; i < name_list->num_ids; i++) {
573 printf("%s\n", name_list->name[i]);
579 * Free names in the list
581 static void free_name_list(NAME_LIST *name_list)
583 for (int i=0; i < name_list->num_ids; i++) {
584 free(name_list->name[i]);
586 name_list->num_ids = 0;
589 static void eliminate_duplicate_filenames()
594 printf(_("Checking for duplicate Filename entries.\n"));
596 /* Make list of duplicated names */
597 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
598 "HAVING count(Name) > 1";
600 if (!make_name_list(query, &name_list)) {
603 printf(_("Found %d duplicate Filename records.\n"), name_list.num_ids);
604 if (name_list.num_ids && verbose && yes_no(_("Print the list? (yes/no): "))) {
605 print_name_list(&name_list);
611 /* Loop through list of duplicate names */
612 for (int i=0; i<name_list.num_ids; i++) {
613 /* Get all the Ids of each name */
614 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
615 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
619 if (!make_id_list(buf, &id_list)) {
623 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
625 /* Force all records to use the first id then delete the other ids */
626 for (int j=1; j<id_list.num_ids; j++) {
627 char ed1[50], ed2[50];
628 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%s WHERE FilenameId=%s",
629 edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
633 db_sql_query(db, buf, NULL, NULL);
634 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%s",
639 db_sql_query(db, buf, NULL, NULL);
643 free_name_list(&name_list);
646 static void eliminate_duplicate_paths()
651 printf(_("Checking for duplicate Path entries.\n"));
653 /* Make list of duplicated names */
655 query = "SELECT Path, count(Path) as Count FROM Path "
656 "GROUP BY Path HAVING count(Path) > 1";
658 if (!make_name_list(query, &name_list)) {
661 printf(_("Found %d duplicate Path records.\n"), name_list.num_ids);
662 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
663 print_name_list(&name_list);
669 /* Loop through list of duplicate names */
670 for (int i=0; i<name_list.num_ids; i++) {
671 /* Get all the Ids of each name */
672 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
673 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
677 if (!make_id_list(buf, &id_list)) {
681 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
683 /* Force all records to use the first id then delete the other ids */
684 for (int j=1; j<id_list.num_ids; j++) {
685 char ed1[50], ed2[50];
686 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%s WHERE PathId=%s",
687 edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
691 db_sql_query(db, buf, NULL, NULL);
692 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%s", ed2);
696 db_sql_query(db, buf, NULL, NULL);
700 free_name_list(&name_list);
703 static void eliminate_orphaned_jobmedia_records()
705 const char *query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
706 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
707 "WHERE Job.JobId IS NULL LIMIT 300000";
709 printf(_("Checking for orphaned JobMedia entries.\n"));
710 if (!make_id_list(query, &id_list)) {
713 /* Loop doing 300000 at a time */
714 while (id_list.num_ids != 0) {
715 printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
716 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
717 for (int i=0; i < id_list.num_ids; i++) {
719 bsnprintf(buf, sizeof(buf),
720 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
721 "WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId",
722 edit_int64(id_list.Id[i], ed1));
723 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
724 printf("%s\n", db_strerror(db));
732 if (fix && id_list.num_ids > 0) {
733 printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
734 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%s", &id_list);
736 if (!make_id_list(query, &id_list)) {
742 static void eliminate_orphaned_file_records()
744 const char *query = "SELECT File.FileId,Job.JobId FROM File "
745 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
746 "WHERE Job.JobId IS NULL LIMIT 300000";
748 printf(_("Checking for orphaned File entries. This may take some time!\n"));
750 printf("%s\n", query);
752 if (!make_id_list(query, &id_list)) {
755 /* Loop doing 300000 at a time */
756 while (id_list.num_ids != 0) {
757 printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
758 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
759 for (int i=0; i < id_list.num_ids; i++) {
761 bsnprintf(buf, sizeof(buf),
762 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
763 "WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
764 edit_int64(id_list.Id[i], ed1));
765 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
766 printf("%s\n", db_strerror(db));
773 if (fix && id_list.num_ids > 0) {
774 printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
775 delete_id_list("DELETE FROM File WHERE FileId=%s", &id_list);
777 if (!make_id_list(query, &id_list)) {
783 static void eliminate_orphaned_path_records()
785 const char *query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
786 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
787 "WHERE File.PathId IS NULL LIMIT 300000";
789 printf(_("Checking for orphaned Path entries. This may take some time!\n"));
791 printf("%s\n", query);
793 if (!make_id_list(query, &id_list)) {
796 /* Loop doing 300000 at a time */
797 while (id_list.num_ids != 0) {
798 printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
799 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
800 for (int i=0; i < id_list.num_ids; i++) {
802 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
803 edit_int64(id_list.Id[i], ed1));
804 db_sql_query(db, buf, print_name_handler, NULL);
810 if (fix && id_list.num_ids > 0) {
811 printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
812 delete_id_list("DELETE FROM Path WHERE PathId=%s", &id_list);
814 if (!make_id_list(query, &id_list)) {
820 static void eliminate_orphaned_filename_records()
822 const char *query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
823 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
824 "WHERE File.FilenameId IS NULL LIMIT 300000";
826 printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
828 printf("%s\n", query);
830 if (!make_id_list(query, &id_list)) {
833 /* Loop doing 300000 at a time */
834 while (id_list.num_ids != 0) {
835 printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
836 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
837 for (int i=0; i < id_list.num_ids; i++) {
839 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
840 edit_int64(id_list.Id[i], ed1));
841 db_sql_query(db, buf, print_name_handler, NULL);
847 if (fix && id_list.num_ids > 0) {
848 printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
849 delete_id_list("DELETE FROM Filename WHERE FilenameId=%s", &id_list);
851 if (!make_id_list(query, &id_list)) {
857 static void eliminate_orphaned_fileset_records()
861 printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
862 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
863 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
864 "WHERE Job.FileSetId IS NULL";
866 printf("%s\n", query);
868 if (!make_id_list(query, &id_list)) {
871 printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
872 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
873 for (int i=0; i < id_list.num_ids; i++) {
875 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
876 "WHERE FileSetId=%s", edit_int64(id_list.Id[i], ed1));
877 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
878 printf("%s\n", db_strerror(db));
885 if (fix && id_list.num_ids > 0) {
886 printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
887 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%s", &id_list);
891 static void eliminate_orphaned_client_records()
895 printf(_("Checking for orphaned Client entries.\n"));
897 * Wiffle through Client for every Client
898 * joining with the Job table including every Client even if
899 * there is not a match in Job (left outer join), then
900 * filter out only those where no Job points to a Client
901 * i.e. Job.Client is NULL
903 query = "SELECT Client.ClientId,Client.Name FROM Client "
904 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
905 "WHERE Job.ClientId IS NULL";
907 printf("%s\n", query);
909 if (!make_id_list(query, &id_list)) {
912 printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
913 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
914 for (int i=0; i < id_list.num_ids; i++) {
916 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
917 "WHERE ClientId=%s", edit_int64(id_list.Id[i], ed1));
918 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
919 printf("%s\n", db_strerror(db));
926 if (fix && id_list.num_ids > 0) {
927 printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
928 delete_id_list("DELETE FROM Client WHERE ClientId=%s", &id_list);
932 static void eliminate_orphaned_job_records()
936 printf(_("Checking for orphaned Job entries.\n"));
938 * Wiffle through Job for every Job
939 * joining with the Client table including every Job even if
940 * there is not a match in Client (left outer join), then
941 * filter out only those where no Client exists
942 * i.e. Client.Name is NULL
944 query = "SELECT Job.JobId,Job.Name FROM Job "
945 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
946 "WHERE Client.Name IS NULL";
948 printf("%s\n", query);
950 if (!make_id_list(query, &id_list)) {
953 printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
954 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
955 for (int i=0; i < id_list.num_ids; i++) {
957 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
958 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
959 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
960 printf("%s\n", db_strerror(db));
967 if (fix && id_list.num_ids > 0) {
968 printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
969 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
970 printf(_("Deleting JobMedia records of orphaned Job records.\n"));
971 delete_id_list("DELETE FROM JobMedia WHERE JobId=%s", &id_list);
972 printf(_("Deleting Log records of orphaned Job records.\n"));
973 delete_id_list("DELETE FROM Log WHERE JobId=%s", &id_list);
978 static void eliminate_admin_records()
982 printf(_("Checking for Admin Job entries.\n"));
983 query = "SELECT Job.JobId FROM Job "
984 "WHERE Job.Type='D'";
986 printf("%s\n", query);
988 if (!make_id_list(query, &id_list)) {
991 printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
992 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
993 for (int i=0; i < id_list.num_ids; i++) {
995 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
996 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
997 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
998 printf("%s\n", db_strerror(db));
1005 if (fix && id_list.num_ids > 0) {
1006 printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
1007 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1011 static void eliminate_restore_records()
1015 printf(_("Checking for Restore Job entries.\n"));
1016 query = "SELECT Job.JobId FROM Job "
1017 "WHERE Job.Type='R'";
1019 printf("%s\n", query);
1021 if (!make_id_list(query, &id_list)) {
1024 printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
1025 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1026 for (int i=0; i < id_list.num_ids; i++) {
1028 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1029 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1030 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1031 printf("%s\n", db_strerror(db));
1038 if (fix && id_list.num_ids > 0) {
1039 printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
1040 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1047 static void repair_bad_filenames()
1052 printf(_("Checking for Filenames with a trailing slash\n"));
1053 query = "SELECT FilenameId,Name from Filename "
1054 "WHERE Name LIKE '%/'";
1056 printf("%s\n", query);
1058 if (!make_id_list(query, &id_list)) {
1061 printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
1062 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1063 for (i=0; i < id_list.num_ids; i++) {
1065 bsnprintf(buf, sizeof(buf),
1066 "SELECT Name FROM Filename WHERE FilenameId=%s",
1067 edit_int64(id_list.Id[i], ed1));
1068 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1069 printf("%s\n", db_strerror(db));
1076 if (fix && id_list.num_ids > 0) {
1077 POOLMEM *name = get_pool_memory(PM_FNAME);
1078 char esc_name[5000];
1079 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1080 for (i=0; i < id_list.num_ids; i++) {
1083 bsnprintf(buf, sizeof(buf),
1084 "SELECT Name FROM Filename WHERE FilenameId=%s",
1085 edit_int64(id_list.Id[i], ed1));
1086 if (!db_sql_query(db, buf, get_name_handler, name)) {
1087 printf("%s\n", db_strerror(db));
1089 /* Strip trailing slash(es) */
1090 for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
1098 db_escape_string(esc_name, name, len);
1100 bsnprintf(buf, sizeof(buf),
1101 "UPDATE Filename SET Name='%s' WHERE FilenameId=%s",
1102 esc_name, edit_int64(id_list.Id[i], ed1));
1104 printf("%s\n", buf);
1106 db_sql_query(db, buf, NULL, NULL);
1111 static void repair_bad_paths()
1116 printf(_("Checking for Paths without a trailing slash\n"));
1117 query = "SELECT PathId,Path from Path "
1118 "WHERE Path NOT LIKE '%/'";
1120 printf("%s\n", query);
1122 if (!make_id_list(query, &id_list)) {
1125 printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1126 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1127 for (i=0; i < id_list.num_ids; i++) {
1129 bsnprintf(buf, sizeof(buf),
1130 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1131 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1132 printf("%s\n", db_strerror(db));
1139 if (fix && id_list.num_ids > 0) {
1140 POOLMEM *name = get_pool_memory(PM_FNAME);
1141 char esc_name[5000];
1142 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1143 for (i=0; i < id_list.num_ids; i++) {
1146 bsnprintf(buf, sizeof(buf),
1147 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1148 if (!db_sql_query(db, buf, get_name_handler, name)) {
1149 printf("%s\n", db_strerror(db));
1151 /* Strip trailing blanks */
1152 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1155 /* Add trailing slash */
1156 len = pm_strcat(&name, "/");
1157 db_escape_string(esc_name, name, len);
1158 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
1159 esc_name, edit_int64(id_list.Id[i], ed1));
1161 printf("%s\n", buf);
1163 db_sql_query(db, buf, NULL, NULL);
1170 * Gen next input command from the terminal
1172 static char *get_cmd(const char *prompt)
1174 static char cmd[1000];
1176 printf("%s", prompt);
1177 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
1182 strip_trailing_junk(cmd);
1186 static bool yes_no(const char *prompt)
1189 cmd = get_cmd(prompt);
1194 return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1197 bool python_set_prog(JCR*, char const*) { return false; }