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 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"
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>]\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;
123 char *configfile = NULL;
124 char *catalogname = NULL;
126 setlocale(LC_ALL, "");
127 bindtextdomain("bacula", LOCALEDIR);
128 textdomain("bacula");
130 my_name_is(argc, argv, "dbcheck");
131 init_msg(NULL, NULL); /* setup message handler */
133 memset(&id_list, 0, sizeof(id_list));
134 memset(&name_list, 0, sizeof(name_list));
137 while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
139 case 'b': /* batch */
143 case 'C': /* CatalogName */
144 catalogname = optarg;
147 case 'c': /* configfile */
151 case 'd': /* debug level */
152 if (*optarg == 't') {
153 dbg_timestamp = true;
155 debug_level = atoi(optarg);
156 if (debug_level <= 0) {
162 case 'f': /* fix inconsistencies */
184 Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
186 config = new_config_parser();
187 parse_dir_config(config, configfile, M_ERROR_TERM);
189 foreach_res(catalog, R_CATALOG) {
190 if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
193 } else if (!catalogname) { // stop on first if no catalogname is given
201 Pmsg2(0, _("Error can not find the Catalog name[%s] in the given config file [%s]\n"), catalogname, configfile);
203 Pmsg1(0, _("Error there is no Catalog section in the given config file [%s]\n"), configfile);
209 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
212 Pmsg0(0, _("Error no Director resource defined.\n"));
215 set_working_directory(director->working_directory);
216 db_name = catalog->db_name;
217 user = catalog->db_user;
218 password = catalog->db_password;
219 dbhost = catalog->db_address;
220 if (dbhost && dbhost[0] == 0) {
226 Pmsg0(0, _("Wrong number of arguments.\n"));
231 Pmsg0(0, _("Working directory not supplied.\n"));
235 /* This is needed by SQLite to find the db */
236 working_directory = argv[0];
245 } else if (argc == 3) {
248 } else if (argc == 4) {
252 } else if (argc == 5) {
261 db = db_init_database(NULL, db_name, user, password, dbhost, 0, NULL, 0);
262 if (!db_open_database(NULL, db)) {
263 Emsg1(M_FATAL, 0, "%s", db_strerror(db));
269 repair_bad_filenames();
270 eliminate_duplicate_filenames();
271 eliminate_duplicate_paths();
272 eliminate_orphaned_jobmedia_records();
273 eliminate_orphaned_file_records();
274 eliminate_orphaned_path_records();
275 eliminate_orphaned_filename_records();
276 eliminate_orphaned_fileset_records();
277 eliminate_orphaned_client_records();
278 eliminate_orphaned_job_records();
279 eliminate_admin_records();
280 eliminate_restore_records();
282 do_interactive_mode();
285 db_close_database(NULL, db);
291 static void do_interactive_mode()
295 printf(_("Hello, this is the database check/correct program.\n"));
297 printf(_("Modify database is on."));
299 printf(_("Modify database is off."));
301 printf(_(" Verbose is on.\n"));
303 printf(_(" Verbose is off.\n"));
305 printf(_("Please select the fuction you want to perform.\n"));
310 " 1) Toggle modify database flag\n"
311 " 2) Toggle verbose flag\n"
312 " 3) Repair bad Filename records\n"
313 " 4) Repair bad Path records\n"
314 " 5) Eliminate duplicate Filename records\n"
315 " 6) Eliminate duplicate Path records\n"
316 " 7) Eliminate orphaned Jobmedia records\n"
317 " 8) Eliminate orphaned File records\n"
318 " 9) Eliminate orphaned Path records\n"
319 " 10) Eliminate orphaned Filename records\n"
320 " 11) Eliminate orphaned FileSet records\n"
321 " 12) Eliminate orphaned Client records\n"
322 " 13) Eliminate orphaned Job records\n"
323 " 14) Eliminate all Admin records\n"
324 " 15) Eliminate all Restore records\n"
329 " 1) Toggle modify database flag\n"
330 " 2) Toggle verbose flag\n"
331 " 3) Check for bad Filename records\n"
332 " 4) Check for bad Path records\n"
333 " 5) Check for duplicate Filename records\n"
334 " 6) Check for duplicate Path records\n"
335 " 7) Check for orphaned Jobmedia records\n"
336 " 8) Check for orphaned File records\n"
337 " 9) Check for orphaned Path records\n"
338 " 10) Check for orphaned Filename records\n"
339 " 11) Check for orphaned FileSet records\n"
340 " 12) Check for orphaned Client records\n"
341 " 13) Check for orphaned Job records\n"
342 " 14) Check for all Admin records\n"
343 " 15) Check for all Restore records\n"
348 cmd = get_cmd(_("Select function number: "));
350 int item = atoi(cmd);
355 printf(_("Database will be modified.\n"));
357 printf(_("Database will NOT be modified.\n"));
360 verbose = verbose?0:1;
362 printf(_(" Verbose is on.\n"));
364 printf(_(" Verbose is off.\n"));
367 repair_bad_filenames();
373 eliminate_duplicate_filenames();
376 eliminate_duplicate_paths();
379 eliminate_orphaned_jobmedia_records();
382 eliminate_orphaned_file_records();
385 eliminate_orphaned_path_records();
388 eliminate_orphaned_filename_records();
391 eliminate_orphaned_fileset_records();
394 eliminate_orphaned_client_records();
397 eliminate_orphaned_job_records();
400 eliminate_admin_records();
403 eliminate_restore_records();
406 repair_bad_filenames();
408 eliminate_duplicate_filenames();
409 eliminate_duplicate_paths();
410 eliminate_orphaned_jobmedia_records();
411 eliminate_orphaned_file_records();
412 eliminate_orphaned_path_records();
413 eliminate_orphaned_filename_records();
414 eliminate_orphaned_fileset_records();
415 eliminate_orphaned_client_records();
416 eliminate_orphaned_job_records();
417 eliminate_admin_records();
418 eliminate_restore_records();
428 static int print_name_handler(void *ctx, int num_fields, char **row)
431 printf("%s\n", row[0]);
436 static int get_name_handler(void *ctx, int num_fields, char **row)
438 POOLMEM *buf = (POOLMEM *)ctx;
440 pm_strcpy(&buf, row[0]);
445 static int print_job_handler(void *ctx, int num_fields, char **row)
447 printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
448 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
453 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
455 printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
456 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
460 static int print_file_handler(void *ctx, int num_fields, char **row)
462 printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
463 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
467 static int print_fileset_handler(void *ctx, int num_fields, char **row)
469 printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
470 NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
474 static int print_client_handler(void *ctx, int num_fields, char **row)
476 printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
477 NPRT(row[0]), NPRT(row[1]));
483 * Called here with each id to be added to the list
485 static int id_list_handler(void *ctx, int num_fields, char **row)
487 ID_LIST *lst = (ID_LIST *)ctx;
489 if (lst->num_ids == MAX_ID_LIST_LEN) {
492 if (lst->num_ids == lst->max_ids) {
493 if (lst->max_ids == 0) {
494 lst->max_ids = 10000;
495 lst->Id = (int64_t *)bmalloc(sizeof(int64_t) * lst->max_ids);
497 lst->max_ids = (lst->max_ids * 3) / 2;
498 lst->Id = (int64_t *)brealloc(lst->Id, sizeof(int64_t) * lst->max_ids);
501 lst->Id[lst->num_ids++] = str_to_int64(row[0]);
506 * Construct record id list
508 static int make_id_list(const char *query, ID_LIST *id_list)
510 id_list->num_ids = 0;
511 id_list->num_del = 0;
512 id_list->tot_ids = 0;
514 if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
515 printf("%s", db_strerror(db));
522 * Delete all entries in the list
524 static int delete_id_list(const char *query, ID_LIST *id_list)
527 for (int i=0; i < id_list->num_ids; i++) {
528 bsnprintf(buf, sizeof(buf), query, edit_int64(id_list->Id[i], ed1));
530 printf(_("Deleting: %s\n"), buf);
532 db_sql_query(db, buf, NULL, NULL);
538 * Called here with each name to be added to the list
540 static int name_list_handler(void *ctx, int num_fields, char **row)
542 NAME_LIST *name = (NAME_LIST *)ctx;
544 if (name->num_ids == MAX_ID_LIST_LEN) {
547 if (name->num_ids == name->max_ids) {
548 if (name->max_ids == 0) {
549 name->max_ids = 10000;
550 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
552 name->max_ids = (name->max_ids * 3) / 2;
553 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
556 name->name[name->num_ids++] = bstrdup(row[0]);
562 * Construct name list
564 static int make_name_list(const char *query, NAME_LIST *name_list)
566 name_list->num_ids = 0;
567 name_list->num_del = 0;
568 name_list->tot_ids = 0;
570 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
571 printf("%s", db_strerror(db));
578 * Print names in the list
580 static void print_name_list(NAME_LIST *name_list)
582 for (int i=0; i < name_list->num_ids; i++) {
583 printf("%s\n", name_list->name[i]);
589 * Free names in the list
591 static void free_name_list(NAME_LIST *name_list)
593 for (int i=0; i < name_list->num_ids; i++) {
594 free(name_list->name[i]);
596 name_list->num_ids = 0;
599 static void eliminate_duplicate_filenames()
604 printf(_("Checking for duplicate Filename entries.\n"));
606 /* Make list of duplicated names */
607 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
608 "HAVING count(Name) > 1";
610 if (!make_name_list(query, &name_list)) {
613 printf(_("Found %d duplicate Filename records.\n"), name_list.num_ids);
614 if (name_list.num_ids && verbose && yes_no(_("Print the list? (yes/no): "))) {
615 print_name_list(&name_list);
621 /* Loop through list of duplicate names */
622 for (int i=0; i<name_list.num_ids; i++) {
623 /* Get all the Ids of each name */
624 db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
625 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
629 if (!make_id_list(buf, &id_list)) {
633 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
635 /* Force all records to use the first id then delete the other ids */
636 for (int j=1; j<id_list.num_ids; j++) {
637 char ed1[50], ed2[50];
638 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%s WHERE FilenameId=%s",
639 edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
643 db_sql_query(db, buf, NULL, NULL);
644 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%s",
649 db_sql_query(db, buf, NULL, NULL);
653 free_name_list(&name_list);
656 static void eliminate_duplicate_paths()
661 printf(_("Checking for duplicate Path entries.\n"));
663 /* Make list of duplicated names */
665 query = "SELECT Path, count(Path) as Count FROM Path "
666 "GROUP BY Path HAVING count(Path) > 1";
668 if (!make_name_list(query, &name_list)) {
671 printf(_("Found %d duplicate Path records.\n"), name_list.num_ids);
672 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
673 print_name_list(&name_list);
679 /* Loop through list of duplicate names */
680 for (int i=0; i<name_list.num_ids; i++) {
681 /* Get all the Ids of each name */
682 db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
683 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
687 if (!make_id_list(buf, &id_list)) {
691 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
693 /* Force all records to use the first id then delete the other ids */
694 for (int j=1; j<id_list.num_ids; j++) {
695 char ed1[50], ed2[50];
696 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%s WHERE PathId=%s",
697 edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
701 db_sql_query(db, buf, NULL, NULL);
702 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%s", ed2);
706 db_sql_query(db, buf, NULL, NULL);
710 free_name_list(&name_list);
713 static void eliminate_orphaned_jobmedia_records()
715 const char *query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
716 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
717 "WHERE Job.JobId IS NULL LIMIT 300000";
719 printf(_("Checking for orphaned JobMedia entries.\n"));
720 if (!make_id_list(query, &id_list)) {
723 /* Loop doing 300000 at a time */
724 while (id_list.num_ids != 0) {
725 printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
726 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
727 for (int i=0; i < id_list.num_ids; i++) {
729 bsnprintf(buf, sizeof(buf),
730 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
731 "WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId",
732 edit_int64(id_list.Id[i], ed1));
733 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
734 printf("%s\n", db_strerror(db));
742 if (fix && id_list.num_ids > 0) {
743 printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
744 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%s", &id_list);
746 break; /* get out if not updating db */
748 if (!make_id_list(query, &id_list)) {
754 static void eliminate_orphaned_file_records()
756 const char *query = "SELECT File.FileId,Job.JobId FROM File "
757 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
758 "WHERE Job.JobId IS NULL LIMIT 300000";
760 printf(_("Checking for orphaned File entries. This may take some time!\n"));
762 printf("%s\n", query);
764 if (!make_id_list(query, &id_list)) {
767 /* Loop doing 300000 at a time */
768 while (id_list.num_ids != 0) {
769 printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
770 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
771 for (int i=0; i < id_list.num_ids; i++) {
773 bsnprintf(buf, sizeof(buf),
774 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
775 "WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
776 edit_int64(id_list.Id[i], ed1));
777 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
778 printf("%s\n", db_strerror(db));
785 if (fix && id_list.num_ids > 0) {
786 printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
787 delete_id_list("DELETE FROM File WHERE FileId=%s", &id_list);
789 break; /* get out if not updating db */
791 if (!make_id_list(query, &id_list)) {
797 static void eliminate_orphaned_path_records()
799 const char *query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
800 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
801 "WHERE File.PathId IS NULL LIMIT 300000";
803 printf(_("Checking for orphaned Path entries. This may take some time!\n"));
805 printf("%s\n", query);
807 if (!make_id_list(query, &id_list)) {
810 /* Loop doing 300000 at a time */
811 while (id_list.num_ids != 0) {
812 printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
813 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
814 for (int i=0; i < id_list.num_ids; i++) {
816 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
817 edit_int64(id_list.Id[i], ed1));
818 db_sql_query(db, buf, print_name_handler, NULL);
824 if (fix && id_list.num_ids > 0) {
825 printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
826 delete_id_list("DELETE FROM Path WHERE PathId=%s", &id_list);
828 break; /* get out if not updating db */
830 if (!make_id_list(query, &id_list)) {
836 static void eliminate_orphaned_filename_records()
838 const char *query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
839 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
840 "WHERE File.FilenameId IS NULL LIMIT 300000";
842 printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
844 printf("%s\n", query);
846 if (!make_id_list(query, &id_list)) {
849 /* Loop doing 300000 at a time */
850 while (id_list.num_ids != 0) {
851 printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
852 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
853 for (int i=0; i < id_list.num_ids; i++) {
855 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
856 edit_int64(id_list.Id[i], ed1));
857 db_sql_query(db, buf, print_name_handler, NULL);
863 if (fix && id_list.num_ids > 0) {
864 printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
865 delete_id_list("DELETE FROM Filename WHERE FilenameId=%s", &id_list);
867 break; /* get out if not updating db */
869 if (!make_id_list(query, &id_list)) {
875 static void eliminate_orphaned_fileset_records()
879 printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
880 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
881 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
882 "WHERE Job.FileSetId IS NULL";
884 printf("%s\n", query);
886 if (!make_id_list(query, &id_list)) {
889 printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
890 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
891 for (int i=0; i < id_list.num_ids; i++) {
893 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
894 "WHERE FileSetId=%s", edit_int64(id_list.Id[i], ed1));
895 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
896 printf("%s\n", db_strerror(db));
903 if (fix && id_list.num_ids > 0) {
904 printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
905 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%s", &id_list);
909 static void eliminate_orphaned_client_records()
913 printf(_("Checking for orphaned Client entries.\n"));
915 * Wiffle through Client for every Client
916 * joining with the Job table including every Client even if
917 * there is not a match in Job (left outer join), then
918 * filter out only those where no Job points to a Client
919 * i.e. Job.Client is NULL
921 query = "SELECT Client.ClientId,Client.Name FROM Client "
922 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
923 "WHERE Job.ClientId IS NULL";
925 printf("%s\n", query);
927 if (!make_id_list(query, &id_list)) {
930 printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
931 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
932 for (int i=0; i < id_list.num_ids; i++) {
934 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
935 "WHERE ClientId=%s", edit_int64(id_list.Id[i], ed1));
936 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
937 printf("%s\n", db_strerror(db));
944 if (fix && id_list.num_ids > 0) {
945 printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
946 delete_id_list("DELETE FROM Client WHERE ClientId=%s", &id_list);
950 static void eliminate_orphaned_job_records()
954 printf(_("Checking for orphaned Job entries.\n"));
956 * Wiffle through Job for every Job
957 * joining with the Client table including every Job even if
958 * there is not a match in Client (left outer join), then
959 * filter out only those where no Client exists
960 * i.e. Client.Name is NULL
962 query = "SELECT Job.JobId,Job.Name FROM Job "
963 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
964 "WHERE Client.Name IS NULL";
966 printf("%s\n", query);
968 if (!make_id_list(query, &id_list)) {
971 printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
972 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
973 for (int i=0; i < id_list.num_ids; i++) {
975 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
976 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
977 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
978 printf("%s\n", db_strerror(db));
985 if (fix && id_list.num_ids > 0) {
986 printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
987 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
988 printf(_("Deleting JobMedia records of orphaned Job records.\n"));
989 delete_id_list("DELETE FROM JobMedia WHERE JobId=%s", &id_list);
990 printf(_("Deleting Log records of orphaned Job records.\n"));
991 delete_id_list("DELETE FROM Log WHERE JobId=%s", &id_list);
996 static void eliminate_admin_records()
1000 printf(_("Checking for Admin Job entries.\n"));
1001 query = "SELECT Job.JobId FROM Job "
1002 "WHERE Job.Type='D'";
1004 printf("%s\n", query);
1006 if (!make_id_list(query, &id_list)) {
1009 printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
1010 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1011 for (int i=0; i < id_list.num_ids; i++) {
1013 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1014 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1015 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1016 printf("%s\n", db_strerror(db));
1023 if (fix && id_list.num_ids > 0) {
1024 printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
1025 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1029 static void eliminate_restore_records()
1033 printf(_("Checking for Restore Job entries.\n"));
1034 query = "SELECT Job.JobId FROM Job "
1035 "WHERE Job.Type='R'";
1037 printf("%s\n", query);
1039 if (!make_id_list(query, &id_list)) {
1042 printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
1043 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1044 for (int i=0; i < id_list.num_ids; i++) {
1046 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1047 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1048 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1049 printf("%s\n", db_strerror(db));
1056 if (fix && id_list.num_ids > 0) {
1057 printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
1058 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1065 static void repair_bad_filenames()
1070 printf(_("Checking for Filenames with a trailing slash\n"));
1071 query = "SELECT FilenameId,Name from Filename "
1072 "WHERE Name LIKE '%/'";
1074 printf("%s\n", query);
1076 if (!make_id_list(query, &id_list)) {
1079 printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
1080 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1081 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, print_name_handler, NULL)) {
1087 printf("%s\n", db_strerror(db));
1094 if (fix && id_list.num_ids > 0) {
1095 POOLMEM *name = get_pool_memory(PM_FNAME);
1096 char esc_name[5000];
1097 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1098 for (i=0; i < id_list.num_ids; i++) {
1101 bsnprintf(buf, sizeof(buf),
1102 "SELECT Name FROM Filename WHERE FilenameId=%s",
1103 edit_int64(id_list.Id[i], ed1));
1104 if (!db_sql_query(db, buf, get_name_handler, name)) {
1105 printf("%s\n", db_strerror(db));
1107 /* Strip trailing slash(es) */
1108 for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
1116 db_escape_string(NULL, db, esc_name, name, len);
1118 bsnprintf(buf, sizeof(buf),
1119 "UPDATE Filename SET Name='%s' WHERE FilenameId=%s",
1120 esc_name, edit_int64(id_list.Id[i], ed1));
1122 printf("%s\n", buf);
1124 db_sql_query(db, buf, NULL, NULL);
1129 static void repair_bad_paths()
1134 printf(_("Checking for Paths without a trailing slash\n"));
1135 query = "SELECT PathId,Path from Path "
1136 "WHERE Path NOT LIKE '%/'";
1138 printf("%s\n", query);
1140 if (!make_id_list(query, &id_list)) {
1143 printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1144 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1145 for (i=0; i < id_list.num_ids; i++) {
1147 bsnprintf(buf, sizeof(buf),
1148 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1149 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1150 printf("%s\n", db_strerror(db));
1157 if (fix && id_list.num_ids > 0) {
1158 POOLMEM *name = get_pool_memory(PM_FNAME);
1159 char esc_name[5000];
1160 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1161 for (i=0; i < id_list.num_ids; i++) {
1164 bsnprintf(buf, sizeof(buf),
1165 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1166 if (!db_sql_query(db, buf, get_name_handler, name)) {
1167 printf("%s\n", db_strerror(db));
1169 /* Strip trailing blanks */
1170 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1173 /* Add trailing slash */
1174 len = pm_strcat(&name, "/");
1175 db_escape_string(NULL, db, esc_name, name, len);
1176 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
1177 esc_name, edit_int64(id_list.Id[i], ed1));
1179 printf("%s\n", buf);
1181 db_sql_query(db, buf, NULL, NULL);
1188 * Gen next input command from the terminal
1190 static char *get_cmd(const char *prompt)
1192 static char cmd[1000];
1194 printf("%s", prompt);
1195 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
1200 strip_trailing_junk(cmd);
1204 static bool yes_no(const char *prompt)
1207 cmd = get_cmd(prompt);
1212 return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1215 bool python_set_prog(JCR*, char const*) { return false; }