2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2008 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 Kern Sibbald.
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"
44 extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
47 int generate_daemon_event(JCR *jcr, const char *event)
50 typedef struct s_id_ctx {
51 int64_t *Id; /* ids to be modified */
52 int num_ids; /* ids stored */
53 int max_ids; /* size of array */
54 int num_del; /* number deleted */
55 int tot_ids; /* total to process */
58 typedef struct s_name_ctx {
59 char **name; /* list of names */
60 int num_ids; /* ids stored */
61 int max_ids; /* size of array */
62 int num_del; /* number deleted */
63 int tot_ids; /* total to process */
68 /* Global variables */
69 static bool fix = false;
70 static bool batch = false;
72 static ID_LIST id_list;
73 static NAME_LIST name_list;
74 static char buf[20000];
75 static bool quit = false;
76 static CONFIG *config;
78 #define MAX_ID_LIST_LEN 10000000
80 /* Forward referenced functions */
81 static int make_id_list(const char *query, ID_LIST *id_list);
82 static int delete_id_list(const char *query, ID_LIST *id_list);
83 static int make_name_list(const char *query, NAME_LIST *name_list);
84 static void print_name_list(NAME_LIST *name_list);
85 static void free_name_list(NAME_LIST *name_list);
86 static char *get_cmd(const char *prompt);
87 static void eliminate_duplicate_filenames();
88 static void eliminate_duplicate_paths();
89 static void eliminate_orphaned_jobmedia_records();
90 static void eliminate_orphaned_file_records();
91 static void eliminate_orphaned_path_records();
92 static void eliminate_orphaned_filename_records();
93 static void eliminate_orphaned_fileset_records();
94 static void eliminate_orphaned_client_records();
95 static void eliminate_orphaned_job_records();
96 static void eliminate_admin_records();
97 static void eliminate_restore_records();
98 static void repair_bad_paths();
99 static void repair_bad_filenames();
100 static void do_interactive_mode();
101 static bool yes_no(const char *prompt);
107 "Usage: dbcheck [-c config] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>] [<dbport>]\n"
109 " -C catalog name in the director conf file\n"
110 " -c Director conf filename\n"
111 " -d <nn> set debug level to <nn>\n"
112 " -dt print timestamp in debug output\n"
113 " -f fix inconsistencies\n"
115 " -? print this message\n\n");
119 int main (int argc, char *argv[])
122 const char *user, *password, *db_name, *dbhost;
124 char *configfile = NULL;
125 char *catalogname = NULL;
128 setlocale(LC_ALL, "");
129 bindtextdomain("bacula", LOCALEDIR);
130 textdomain("bacula");
132 my_name_is(argc, argv, "dbcheck");
133 init_msg(NULL, NULL); /* setup message handler */
135 memset(&id_list, 0, sizeof(id_list));
136 memset(&name_list, 0, sizeof(name_list));
139 while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
141 case 'b': /* batch */
145 case 'C': /* CatalogName */
146 catalogname = optarg;
149 case 'c': /* configfile */
153 case 'd': /* debug level */
154 if (*optarg == 't') {
155 dbg_timestamp = true;
157 debug_level = atoi(optarg);
158 if (debug_level <= 0) {
164 case 'f': /* fix inconsistencies */
186 Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
188 config = new_config_parser();
189 parse_dir_config(config, configfile, M_ERROR_TERM);
191 foreach_res(catalog, R_CATALOG) {
192 if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
195 } else if (!catalogname) { // stop on first if no catalogname is given
203 Pmsg2(0, _("Error can not find the Catalog name[%s] in the given config file [%s]\n"), catalogname, configfile);
205 Pmsg1(0, _("Error there is no Catalog section in the given config file [%s]\n"), configfile);
211 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
214 Pmsg0(0, _("Error no Director resource defined.\n"));
217 set_working_directory(director->working_directory);
218 db_name = catalog->db_name;
219 user = catalog->db_user;
220 password = catalog->db_password;
221 dbhost = catalog->db_address;
222 if (dbhost && dbhost[0] == 0) {
225 dbport = catalog->db_port;
229 Pmsg0(0, _("Wrong number of arguments.\n"));
234 Pmsg0(0, _("Working directory not supplied.\n"));
238 /* This is needed by SQLite to find the db */
239 working_directory = argv[0];
248 } else if (argc == 3) {
251 } else if (argc == 4) {
255 } else if (argc == 5) {
260 } else if (argc == 6) {
265 dbport = strtol(argv[5], &endptr, 10);
266 if (*endptr != '\0') {
267 Pmsg0(0, _("Database port must be a numeric value.\n"));
269 } else if (dbport == LONG_MIN || dbport == LONG_MAX) {
270 Pmsg0(0, _("Database port must be a int value.\n"));
277 db = db_init_database(NULL, db_name, user, password, dbhost, dbport, NULL, 0);
278 if (!db_open_database(NULL, db)) {
279 Emsg1(M_FATAL, 0, "%s", db_strerror(db));
285 repair_bad_filenames();
286 eliminate_duplicate_filenames();
287 eliminate_duplicate_paths();
288 eliminate_orphaned_jobmedia_records();
289 eliminate_orphaned_file_records();
290 eliminate_orphaned_path_records();
291 eliminate_orphaned_filename_records();
292 eliminate_orphaned_fileset_records();
293 eliminate_orphaned_client_records();
294 eliminate_orphaned_job_records();
295 eliminate_admin_records();
296 eliminate_restore_records();
298 do_interactive_mode();
301 db_close_database(NULL, db);
307 static void do_interactive_mode()
311 printf(_("Hello, this is the database check/correct program.\n"));
313 printf(_("Modify database is on."));
315 printf(_("Modify database is off."));
317 printf(_(" Verbose is on.\n"));
319 printf(_(" Verbose is off.\n"));
321 printf(_("Please select the fuction you want to perform.\n"));
326 " 1) Toggle modify database flag\n"
327 " 2) Toggle verbose flag\n"
328 " 3) Repair bad Filename records\n"
329 " 4) Repair bad Path records\n"
330 " 5) Eliminate duplicate Filename records\n"
331 " 6) Eliminate duplicate Path records\n"
332 " 7) Eliminate orphaned Jobmedia records\n"
333 " 8) Eliminate orphaned File records\n"
334 " 9) Eliminate orphaned Path records\n"
335 " 10) Eliminate orphaned Filename records\n"
336 " 11) Eliminate orphaned FileSet records\n"
337 " 12) Eliminate orphaned Client records\n"
338 " 13) Eliminate orphaned Job records\n"
339 " 14) Eliminate all Admin records\n"
340 " 15) Eliminate all Restore records\n"
345 " 1) Toggle modify database flag\n"
346 " 2) Toggle verbose flag\n"
347 " 3) Check for bad Filename records\n"
348 " 4) Check for bad Path records\n"
349 " 5) Check for duplicate Filename records\n"
350 " 6) Check for duplicate Path records\n"
351 " 7) Check for orphaned Jobmedia records\n"
352 " 8) Check for orphaned File records\n"
353 " 9) Check for orphaned Path records\n"
354 " 10) Check for orphaned Filename records\n"
355 " 11) Check for orphaned FileSet records\n"
356 " 12) Check for orphaned Client records\n"
357 " 13) Check for orphaned Job records\n"
358 " 14) Check for all Admin records\n"
359 " 15) Check for all Restore records\n"
364 cmd = get_cmd(_("Select function number: "));
366 int item = atoi(cmd);
371 printf(_("Database will be modified.\n"));
373 printf(_("Database will NOT be modified.\n"));
376 verbose = verbose?0:1;
378 printf(_(" Verbose is on.\n"));
380 printf(_(" Verbose is off.\n"));
383 repair_bad_filenames();
389 eliminate_duplicate_filenames();
392 eliminate_duplicate_paths();
395 eliminate_orphaned_jobmedia_records();
398 eliminate_orphaned_file_records();
401 eliminate_orphaned_path_records();
404 eliminate_orphaned_filename_records();
407 eliminate_orphaned_fileset_records();
410 eliminate_orphaned_client_records();
413 eliminate_orphaned_job_records();
416 eliminate_admin_records();
419 eliminate_restore_records();
422 repair_bad_filenames();
424 eliminate_duplicate_filenames();
425 eliminate_duplicate_paths();
426 eliminate_orphaned_jobmedia_records();
427 eliminate_orphaned_file_records();
428 eliminate_orphaned_path_records();
429 eliminate_orphaned_filename_records();
430 eliminate_orphaned_fileset_records();
431 eliminate_orphaned_client_records();
432 eliminate_orphaned_job_records();
433 eliminate_admin_records();
434 eliminate_restore_records();
444 static int print_name_handler(void *ctx, int num_fields, char **row)
447 printf("%s\n", row[0]);
452 static int get_name_handler(void *ctx, int num_fields, char **row)
454 POOLMEM *buf = (POOLMEM *)ctx;
456 pm_strcpy(&buf, row[0]);
461 static int print_job_handler(void *ctx, int num_fields, char **row)
463 printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
464 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
469 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
471 printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
472 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
476 static int print_file_handler(void *ctx, int num_fields, char **row)
478 printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
479 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
483 static int print_fileset_handler(void *ctx, int num_fields, char **row)
485 printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
486 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
490 static int print_client_handler(void *ctx, int num_fields, char **row)
492 printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
493 NPRT(row[0]), NPRT(row[1]));
499 * Called here with each id to be added to the list
501 static int id_list_handler(void *ctx, int num_fields, char **row)
503 ID_LIST *lst = (ID_LIST *)ctx;
505 if (lst->num_ids == MAX_ID_LIST_LEN) {
508 if (lst->num_ids == lst->max_ids) {
509 if (lst->max_ids == 0) {
510 lst->max_ids = 10000;
511 lst->Id = (int64_t *)bmalloc(sizeof(int64_t) * lst->max_ids);
513 lst->max_ids = (lst->max_ids * 3) / 2;
514 lst->Id = (int64_t *)brealloc(lst->Id, sizeof(int64_t) * lst->max_ids);
517 lst->Id[lst->num_ids++] = str_to_int64(row[0]);
522 * Construct record id list
524 static int make_id_list(const char *query, ID_LIST *id_list)
526 id_list->num_ids = 0;
527 id_list->num_del = 0;
528 id_list->tot_ids = 0;
530 if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
531 printf("%s", db_strerror(db));
538 * Delete all entries in the list
540 static int delete_id_list(const char *query, ID_LIST *id_list)
543 for (int i=0; i < id_list->num_ids; i++) {
544 bsnprintf(buf, sizeof(buf), query, edit_int64(id_list->Id[i], ed1));
546 printf(_("Deleting: %s\n"), buf);
548 db_sql_query(db, buf, NULL, NULL);
554 * Called here with each name to be added to the list
556 static int name_list_handler(void *ctx, int num_fields, char **row)
558 NAME_LIST *name = (NAME_LIST *)ctx;
560 if (name->num_ids == MAX_ID_LIST_LEN) {
563 if (name->num_ids == name->max_ids) {
564 if (name->max_ids == 0) {
565 name->max_ids = 10000;
566 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
568 name->max_ids = (name->max_ids * 3) / 2;
569 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
572 name->name[name->num_ids++] = bstrdup(row[0]);
578 * Construct name list
580 static int make_name_list(const char *query, NAME_LIST *name_list)
582 name_list->num_ids = 0;
583 name_list->num_del = 0;
584 name_list->tot_ids = 0;
586 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
587 printf("%s", db_strerror(db));
594 * Print names in the list
596 static void print_name_list(NAME_LIST *name_list)
598 for (int i=0; i < name_list->num_ids; i++) {
599 printf("%s\n", name_list->name[i]);
605 * Free names in the list
607 static void free_name_list(NAME_LIST *name_list)
609 for (int i=0; i < name_list->num_ids; i++) {
610 free(name_list->name[i]);
612 name_list->num_ids = 0;
615 static void eliminate_duplicate_filenames()
620 printf(_("Checking for duplicate Filename entries.\n"));
622 /* Make list of duplicated names */
623 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
624 "HAVING count(Name) > 1";
626 if (!make_name_list(query, &name_list)) {
629 printf(_("Found %d duplicate Filename records.\n"), name_list.num_ids);
630 if (name_list.num_ids && verbose && yes_no(_("Print the list? (yes/no): "))) {
631 print_name_list(&name_list);
637 /* Loop through list of duplicate names */
638 for (int i=0; i<name_list.num_ids; i++) {
639 /* Get all the Ids of each name */
640 db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
641 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
645 if (!make_id_list(buf, &id_list)) {
649 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
651 /* Force all records to use the first id then delete the other ids */
652 for (int j=1; j<id_list.num_ids; j++) {
653 char ed1[50], ed2[50];
654 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%s WHERE FilenameId=%s",
655 edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
659 db_sql_query(db, buf, NULL, NULL);
660 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%s",
665 db_sql_query(db, buf, NULL, NULL);
669 free_name_list(&name_list);
672 static void eliminate_duplicate_paths()
677 printf(_("Checking for duplicate Path entries.\n"));
679 /* Make list of duplicated names */
681 query = "SELECT Path, count(Path) as Count FROM Path "
682 "GROUP BY Path HAVING count(Path) > 1";
684 if (!make_name_list(query, &name_list)) {
687 printf(_("Found %d duplicate Path records.\n"), name_list.num_ids);
688 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
689 print_name_list(&name_list);
695 /* Loop through list of duplicate names */
696 for (int i=0; i<name_list.num_ids; i++) {
697 /* Get all the Ids of each name */
698 db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
699 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
703 if (!make_id_list(buf, &id_list)) {
707 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
709 /* Force all records to use the first id then delete the other ids */
710 for (int j=1; j<id_list.num_ids; j++) {
711 char ed1[50], ed2[50];
712 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%s WHERE PathId=%s",
713 edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
717 db_sql_query(db, buf, NULL, NULL);
718 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%s", ed2);
722 db_sql_query(db, buf, NULL, NULL);
726 free_name_list(&name_list);
729 static void eliminate_orphaned_jobmedia_records()
731 const char *query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
732 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
733 "WHERE Job.JobId IS NULL LIMIT 300000";
735 printf(_("Checking for orphaned JobMedia entries.\n"));
736 if (!make_id_list(query, &id_list)) {
739 /* Loop doing 300000 at a time */
740 while (id_list.num_ids != 0) {
741 printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
742 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
743 for (int i=0; i < id_list.num_ids; i++) {
745 bsnprintf(buf, sizeof(buf),
746 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
747 "WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId",
748 edit_int64(id_list.Id[i], ed1));
749 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
750 printf("%s\n", db_strerror(db));
758 if (fix && id_list.num_ids > 0) {
759 printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
760 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%s", &id_list);
762 break; /* get out if not updating db */
764 if (!make_id_list(query, &id_list)) {
770 static void eliminate_orphaned_file_records()
772 const char *query = "SELECT File.FileId,Job.JobId FROM File "
773 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
774 "WHERE Job.JobId IS NULL LIMIT 300000";
776 printf(_("Checking for orphaned File entries. This may take some time!\n"));
778 printf("%s\n", query);
780 if (!make_id_list(query, &id_list)) {
783 /* Loop doing 300000 at a time */
784 while (id_list.num_ids != 0) {
785 printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
786 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
787 for (int i=0; i < id_list.num_ids; i++) {
789 bsnprintf(buf, sizeof(buf),
790 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
791 "WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
792 edit_int64(id_list.Id[i], ed1));
793 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
794 printf("%s\n", db_strerror(db));
801 if (fix && id_list.num_ids > 0) {
802 printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
803 delete_id_list("DELETE FROM File WHERE FileId=%s", &id_list);
805 break; /* get out if not updating db */
807 if (!make_id_list(query, &id_list)) {
813 static void eliminate_orphaned_path_records()
815 const char *query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
816 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
817 "WHERE File.PathId IS NULL LIMIT 300000";
819 printf(_("Checking for orphaned Path entries. This may take some time!\n"));
821 printf("%s\n", query);
823 if (!make_id_list(query, &id_list)) {
826 /* Loop doing 300000 at a time */
827 while (id_list.num_ids != 0) {
828 printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
829 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
830 for (int i=0; i < id_list.num_ids; i++) {
832 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
833 edit_int64(id_list.Id[i], ed1));
834 db_sql_query(db, buf, print_name_handler, NULL);
840 if (fix && id_list.num_ids > 0) {
841 printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
842 delete_id_list("DELETE FROM Path WHERE PathId=%s", &id_list);
844 break; /* get out if not updating db */
846 if (!make_id_list(query, &id_list)) {
852 static void eliminate_orphaned_filename_records()
854 const char *query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
855 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
856 "WHERE File.FilenameId IS NULL LIMIT 300000";
858 printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
860 printf("%s\n", query);
862 if (!make_id_list(query, &id_list)) {
865 /* Loop doing 300000 at a time */
866 while (id_list.num_ids != 0) {
867 printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
868 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
869 for (int i=0; i < id_list.num_ids; i++) {
871 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
872 edit_int64(id_list.Id[i], ed1));
873 db_sql_query(db, buf, print_name_handler, NULL);
879 if (fix && id_list.num_ids > 0) {
880 printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
881 delete_id_list("DELETE FROM Filename WHERE FilenameId=%s", &id_list);
883 break; /* get out if not updating db */
885 if (!make_id_list(query, &id_list)) {
891 static void eliminate_orphaned_fileset_records()
895 printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
896 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
897 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
898 "WHERE Job.FileSetId IS NULL";
900 printf("%s\n", query);
902 if (!make_id_list(query, &id_list)) {
905 printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
906 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
907 for (int i=0; i < id_list.num_ids; i++) {
909 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
910 "WHERE FileSetId=%s", edit_int64(id_list.Id[i], ed1));
911 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
912 printf("%s\n", db_strerror(db));
919 if (fix && id_list.num_ids > 0) {
920 printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
921 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%s", &id_list);
925 static void eliminate_orphaned_client_records()
929 printf(_("Checking for orphaned Client entries.\n"));
931 * Wiffle through Client for every Client
932 * joining with the Job table including every Client even if
933 * there is not a match in Job (left outer join), then
934 * filter out only those where no Job points to a Client
935 * i.e. Job.Client is NULL
937 query = "SELECT Client.ClientId,Client.Name FROM Client "
938 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
939 "WHERE Job.ClientId IS NULL";
941 printf("%s\n", query);
943 if (!make_id_list(query, &id_list)) {
946 printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
947 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
948 for (int i=0; i < id_list.num_ids; i++) {
950 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
951 "WHERE ClientId=%s", edit_int64(id_list.Id[i], ed1));
952 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
953 printf("%s\n", db_strerror(db));
960 if (fix && id_list.num_ids > 0) {
961 printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
962 delete_id_list("DELETE FROM Client WHERE ClientId=%s", &id_list);
966 static void eliminate_orphaned_job_records()
970 printf(_("Checking for orphaned Job entries.\n"));
972 * Wiffle through Job for every Job
973 * joining with the Client table including every Job even if
974 * there is not a match in Client (left outer join), then
975 * filter out only those where no Client exists
976 * i.e. Client.Name is NULL
978 query = "SELECT Job.JobId,Job.Name FROM Job "
979 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
980 "WHERE Client.Name IS NULL";
982 printf("%s\n", query);
984 if (!make_id_list(query, &id_list)) {
987 printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
988 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
989 for (int i=0; i < id_list.num_ids; i++) {
991 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
992 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
993 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
994 printf("%s\n", db_strerror(db));
1001 if (fix && id_list.num_ids > 0) {
1002 printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
1003 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1004 printf(_("Deleting JobMedia records of orphaned Job records.\n"));
1005 delete_id_list("DELETE FROM JobMedia WHERE JobId=%s", &id_list);
1006 printf(_("Deleting Log records of orphaned Job records.\n"));
1007 delete_id_list("DELETE FROM Log WHERE JobId=%s", &id_list);
1012 static void eliminate_admin_records()
1016 printf(_("Checking for Admin Job entries.\n"));
1017 query = "SELECT Job.JobId FROM Job "
1018 "WHERE Job.Type='D'";
1020 printf("%s\n", query);
1022 if (!make_id_list(query, &id_list)) {
1025 printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
1026 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1027 for (int i=0; i < id_list.num_ids; i++) {
1029 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1030 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1031 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1032 printf("%s\n", db_strerror(db));
1039 if (fix && id_list.num_ids > 0) {
1040 printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
1041 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1045 static void eliminate_restore_records()
1049 printf(_("Checking for Restore Job entries.\n"));
1050 query = "SELECT Job.JobId FROM Job "
1051 "WHERE Job.Type='R'";
1053 printf("%s\n", query);
1055 if (!make_id_list(query, &id_list)) {
1058 printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
1059 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1060 for (int i=0; i < id_list.num_ids; i++) {
1062 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1063 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1064 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1065 printf("%s\n", db_strerror(db));
1072 if (fix && id_list.num_ids > 0) {
1073 printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
1074 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1081 static void repair_bad_filenames()
1086 printf(_("Checking for Filenames with a trailing slash\n"));
1087 query = "SELECT FilenameId,Name from Filename "
1088 "WHERE Name LIKE '%/'";
1090 printf("%s\n", query);
1092 if (!make_id_list(query, &id_list)) {
1095 printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
1096 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1097 for (i=0; i < id_list.num_ids; i++) {
1099 bsnprintf(buf, sizeof(buf),
1100 "SELECT Name FROM Filename WHERE FilenameId=%s",
1101 edit_int64(id_list.Id[i], ed1));
1102 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1103 printf("%s\n", db_strerror(db));
1110 if (fix && id_list.num_ids > 0) {
1111 POOLMEM *name = get_pool_memory(PM_FNAME);
1112 char esc_name[5000];
1113 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1114 for (i=0; i < id_list.num_ids; i++) {
1117 bsnprintf(buf, sizeof(buf),
1118 "SELECT Name FROM Filename WHERE FilenameId=%s",
1119 edit_int64(id_list.Id[i], ed1));
1120 if (!db_sql_query(db, buf, get_name_handler, name)) {
1121 printf("%s\n", db_strerror(db));
1123 /* Strip trailing slash(es) */
1124 for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
1132 db_escape_string(NULL, db, esc_name, name, len);
1134 bsnprintf(buf, sizeof(buf),
1135 "UPDATE Filename SET Name='%s' WHERE FilenameId=%s",
1136 esc_name, edit_int64(id_list.Id[i], ed1));
1138 printf("%s\n", buf);
1140 db_sql_query(db, buf, NULL, NULL);
1145 static void repair_bad_paths()
1150 printf(_("Checking for Paths without a trailing slash\n"));
1151 query = "SELECT PathId,Path from Path "
1152 "WHERE Path NOT LIKE '%/'";
1154 printf("%s\n", query);
1156 if (!make_id_list(query, &id_list)) {
1159 printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1160 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1161 for (i=0; i < id_list.num_ids; i++) {
1163 bsnprintf(buf, sizeof(buf),
1164 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1165 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1166 printf("%s\n", db_strerror(db));
1173 if (fix && id_list.num_ids > 0) {
1174 POOLMEM *name = get_pool_memory(PM_FNAME);
1175 char esc_name[5000];
1176 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1177 for (i=0; i < id_list.num_ids; i++) {
1180 bsnprintf(buf, sizeof(buf),
1181 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1182 if (!db_sql_query(db, buf, get_name_handler, name)) {
1183 printf("%s\n", db_strerror(db));
1185 /* Strip trailing blanks */
1186 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1189 /* Add trailing slash */
1190 len = pm_strcat(&name, "/");
1191 db_escape_string(NULL, db, esc_name, name, len);
1192 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
1193 esc_name, edit_int64(id_list.Id[i], ed1));
1195 printf("%s\n", buf);
1197 db_sql_query(db, buf, NULL, NULL);
1204 * Gen next input command from the terminal
1206 static char *get_cmd(const char *prompt)
1208 static char cmd[1000];
1210 printf("%s", prompt);
1211 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
1216 strip_trailing_junk(cmd);
1220 static bool yes_no(const char *prompt)
1223 cmd = get_cmd(prompt);
1228 return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1231 bool python_set_prog(JCR*, char const*) { return false; }