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 plus additions
11 that are listed in the file LICENSE.
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 " -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 break; /* get out if not updating db */
738 if (!make_id_list(query, &id_list)) {
744 static void eliminate_orphaned_file_records()
746 const char *query = "SELECT File.FileId,Job.JobId FROM File "
747 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
748 "WHERE Job.JobId IS NULL LIMIT 300000";
750 printf(_("Checking for orphaned File entries. This may take some time!\n"));
752 printf("%s\n", query);
754 if (!make_id_list(query, &id_list)) {
757 /* Loop doing 300000 at a time */
758 while (id_list.num_ids != 0) {
759 printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
760 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
761 for (int i=0; i < id_list.num_ids; i++) {
763 bsnprintf(buf, sizeof(buf),
764 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
765 "WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
766 edit_int64(id_list.Id[i], ed1));
767 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
768 printf("%s\n", db_strerror(db));
775 if (fix && id_list.num_ids > 0) {
776 printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
777 delete_id_list("DELETE FROM File WHERE FileId=%s", &id_list);
779 break; /* get out if not updating db */
781 if (!make_id_list(query, &id_list)) {
787 static void eliminate_orphaned_path_records()
789 const char *query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
790 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
791 "WHERE File.PathId IS NULL LIMIT 300000";
793 printf(_("Checking for orphaned Path entries. This may take some time!\n"));
795 printf("%s\n", query);
797 if (!make_id_list(query, &id_list)) {
800 /* Loop doing 300000 at a time */
801 while (id_list.num_ids != 0) {
802 printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
803 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
804 for (int i=0; i < id_list.num_ids; i++) {
806 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
807 edit_int64(id_list.Id[i], ed1));
808 db_sql_query(db, buf, print_name_handler, NULL);
814 if (fix && id_list.num_ids > 0) {
815 printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
816 delete_id_list("DELETE FROM Path WHERE PathId=%s", &id_list);
818 break; /* get out if not updating db */
820 if (!make_id_list(query, &id_list)) {
826 static void eliminate_orphaned_filename_records()
828 const char *query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
829 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
830 "WHERE File.FilenameId IS NULL LIMIT 300000";
832 printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
834 printf("%s\n", query);
836 if (!make_id_list(query, &id_list)) {
839 /* Loop doing 300000 at a time */
840 while (id_list.num_ids != 0) {
841 printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
842 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
843 for (int i=0; i < id_list.num_ids; i++) {
845 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
846 edit_int64(id_list.Id[i], ed1));
847 db_sql_query(db, buf, print_name_handler, NULL);
853 if (fix && id_list.num_ids > 0) {
854 printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
855 delete_id_list("DELETE FROM Filename WHERE FilenameId=%s", &id_list);
857 break; /* get out if not updating db */
859 if (!make_id_list(query, &id_list)) {
865 static void eliminate_orphaned_fileset_records()
869 printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
870 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
871 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
872 "WHERE Job.FileSetId IS NULL";
874 printf("%s\n", query);
876 if (!make_id_list(query, &id_list)) {
879 printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
880 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
881 for (int i=0; i < id_list.num_ids; i++) {
883 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
884 "WHERE FileSetId=%s", edit_int64(id_list.Id[i], ed1));
885 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
886 printf("%s\n", db_strerror(db));
893 if (fix && id_list.num_ids > 0) {
894 printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
895 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%s", &id_list);
899 static void eliminate_orphaned_client_records()
903 printf(_("Checking for orphaned Client entries.\n"));
905 * Wiffle through Client for every Client
906 * joining with the Job table including every Client even if
907 * there is not a match in Job (left outer join), then
908 * filter out only those where no Job points to a Client
909 * i.e. Job.Client is NULL
911 query = "SELECT Client.ClientId,Client.Name FROM Client "
912 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
913 "WHERE Job.ClientId IS NULL";
915 printf("%s\n", query);
917 if (!make_id_list(query, &id_list)) {
920 printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
921 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
922 for (int i=0; i < id_list.num_ids; i++) {
924 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
925 "WHERE ClientId=%s", edit_int64(id_list.Id[i], ed1));
926 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
927 printf("%s\n", db_strerror(db));
934 if (fix && id_list.num_ids > 0) {
935 printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
936 delete_id_list("DELETE FROM Client WHERE ClientId=%s", &id_list);
940 static void eliminate_orphaned_job_records()
944 printf(_("Checking for orphaned Job entries.\n"));
946 * Wiffle through Job for every Job
947 * joining with the Client table including every Job even if
948 * there is not a match in Client (left outer join), then
949 * filter out only those where no Client exists
950 * i.e. Client.Name is NULL
952 query = "SELECT Job.JobId,Job.Name FROM Job "
953 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
954 "WHERE Client.Name IS NULL";
956 printf("%s\n", query);
958 if (!make_id_list(query, &id_list)) {
961 printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
962 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
963 for (int i=0; i < id_list.num_ids; i++) {
965 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
966 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
967 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
968 printf("%s\n", db_strerror(db));
975 if (fix && id_list.num_ids > 0) {
976 printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
977 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
978 printf(_("Deleting JobMedia records of orphaned Job records.\n"));
979 delete_id_list("DELETE FROM JobMedia WHERE JobId=%s", &id_list);
980 printf(_("Deleting Log records of orphaned Job records.\n"));
981 delete_id_list("DELETE FROM Log WHERE JobId=%s", &id_list);
986 static void eliminate_admin_records()
990 printf(_("Checking for Admin Job entries.\n"));
991 query = "SELECT Job.JobId FROM Job "
992 "WHERE Job.Type='D'";
994 printf("%s\n", query);
996 if (!make_id_list(query, &id_list)) {
999 printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
1000 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1001 for (int i=0; i < id_list.num_ids; i++) {
1003 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1004 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1005 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1006 printf("%s\n", db_strerror(db));
1013 if (fix && id_list.num_ids > 0) {
1014 printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
1015 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1019 static void eliminate_restore_records()
1023 printf(_("Checking for Restore Job entries.\n"));
1024 query = "SELECT Job.JobId FROM Job "
1025 "WHERE Job.Type='R'";
1027 printf("%s\n", query);
1029 if (!make_id_list(query, &id_list)) {
1032 printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
1033 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1034 for (int i=0; i < id_list.num_ids; i++) {
1036 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1037 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1038 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1039 printf("%s\n", db_strerror(db));
1046 if (fix && id_list.num_ids > 0) {
1047 printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
1048 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1055 static void repair_bad_filenames()
1060 printf(_("Checking for Filenames with a trailing slash\n"));
1061 query = "SELECT FilenameId,Name from Filename "
1062 "WHERE Name LIKE '%/'";
1064 printf("%s\n", query);
1066 if (!make_id_list(query, &id_list)) {
1069 printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
1070 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1071 for (i=0; i < id_list.num_ids; i++) {
1073 bsnprintf(buf, sizeof(buf),
1074 "SELECT Name FROM Filename WHERE FilenameId=%s",
1075 edit_int64(id_list.Id[i], ed1));
1076 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1077 printf("%s\n", db_strerror(db));
1084 if (fix && id_list.num_ids > 0) {
1085 POOLMEM *name = get_pool_memory(PM_FNAME);
1086 char esc_name[5000];
1087 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1088 for (i=0; i < id_list.num_ids; i++) {
1091 bsnprintf(buf, sizeof(buf),
1092 "SELECT Name FROM Filename WHERE FilenameId=%s",
1093 edit_int64(id_list.Id[i], ed1));
1094 if (!db_sql_query(db, buf, get_name_handler, name)) {
1095 printf("%s\n", db_strerror(db));
1097 /* Strip trailing slash(es) */
1098 for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
1106 db_escape_string(esc_name, name, len);
1108 bsnprintf(buf, sizeof(buf),
1109 "UPDATE Filename SET Name='%s' WHERE FilenameId=%s",
1110 esc_name, edit_int64(id_list.Id[i], ed1));
1112 printf("%s\n", buf);
1114 db_sql_query(db, buf, NULL, NULL);
1119 static void repair_bad_paths()
1124 printf(_("Checking for Paths without a trailing slash\n"));
1125 query = "SELECT PathId,Path from Path "
1126 "WHERE Path NOT LIKE '%/'";
1128 printf("%s\n", query);
1130 if (!make_id_list(query, &id_list)) {
1133 printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1134 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1135 for (i=0; i < id_list.num_ids; i++) {
1137 bsnprintf(buf, sizeof(buf),
1138 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1139 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1140 printf("%s\n", db_strerror(db));
1147 if (fix && id_list.num_ids > 0) {
1148 POOLMEM *name = get_pool_memory(PM_FNAME);
1149 char esc_name[5000];
1150 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1151 for (i=0; i < id_list.num_ids; i++) {
1154 bsnprintf(buf, sizeof(buf),
1155 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1156 if (!db_sql_query(db, buf, get_name_handler, name)) {
1157 printf("%s\n", db_strerror(db));
1159 /* Strip trailing blanks */
1160 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1163 /* Add trailing slash */
1164 len = pm_strcat(&name, "/");
1165 db_escape_string(esc_name, name, len);
1166 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
1167 esc_name, edit_int64(id_list.Id[i], ed1));
1169 printf("%s\n", buf);
1171 db_sql_query(db, buf, NULL, NULL);
1178 * Gen next input command from the terminal
1180 static char *get_cmd(const char *prompt)
1182 static char cmd[1000];
1184 printf("%s", prompt);
1185 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
1190 strip_trailing_junk(cmd);
1194 static bool yes_no(const char *prompt)
1197 cmd = get_cmd(prompt);
1202 return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1205 bool python_set_prog(JCR*, char const*) { return false; }