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()
707 printf(_("Checking for orphaned JobMedia entries.\n"));
708 query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
709 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
710 "WHERE Job.JobId IS NULL";
711 if (!make_id_list(query, &id_list)) {
714 printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
715 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
716 for (int i=0; i < id_list.num_ids; i++) {
718 bsnprintf(buf, sizeof(buf),
719 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
720 "WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId",
721 edit_int64(id_list.Id[i], ed1));
722 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
723 printf("%s\n", db_strerror(db));
731 if (fix && id_list.num_ids > 0) {
732 printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
733 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%s", &id_list);
737 static void eliminate_orphaned_file_records()
741 printf(_("Checking for orphaned File entries. This may take some time!\n"));
742 query = "SELECT File.FileId,Job.JobId FROM File "
743 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
744 "WHERE Job.JobId IS NULL";
746 printf("%s\n", query);
748 if (!make_id_list(query, &id_list)) {
751 printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
752 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
753 for (int i=0; i < id_list.num_ids; i++) {
755 bsnprintf(buf, sizeof(buf),
756 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
757 "WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
758 edit_int64(id_list.Id[i], ed1));
759 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
760 printf("%s\n", db_strerror(db));
767 if (fix && id_list.num_ids > 0) {
768 printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
769 delete_id_list("DELETE FROM File WHERE FileId=%s", &id_list);
773 static void eliminate_orphaned_path_records()
777 printf(_("Checking for orphaned Path entries. This may take some time!\n"));
778 query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
779 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
780 "WHERE File.PathId IS NULL";
782 printf("%s\n", query);
784 if (!make_id_list(query, &id_list)) {
787 printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
788 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
789 for (int i=0; i < id_list.num_ids; i++) {
791 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
792 edit_int64(id_list.Id[i], ed1));
793 db_sql_query(db, buf, print_name_handler, NULL);
799 if (fix && id_list.num_ids > 0) {
800 printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
801 delete_id_list("DELETE FROM Path WHERE PathId=%s", &id_list);
805 static void eliminate_orphaned_filename_records()
809 printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
810 query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
811 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
812 "WHERE File.FilenameId IS NULL";
814 printf("%s\n", query);
816 if (!make_id_list(query, &id_list)) {
819 printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
820 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
821 for (int i=0; i < id_list.num_ids; i++) {
823 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
824 edit_int64(id_list.Id[i], ed1));
825 db_sql_query(db, buf, print_name_handler, NULL);
831 if (fix && id_list.num_ids > 0) {
832 printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
833 delete_id_list("DELETE FROM Filename WHERE FilenameId=%s", &id_list);
837 static void eliminate_orphaned_fileset_records()
841 printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
842 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
843 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
844 "WHERE Job.FileSetId IS NULL";
846 printf("%s\n", query);
848 if (!make_id_list(query, &id_list)) {
851 printf(_("Found %d orphaned FileSet 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 FileSetId,FileSet,MD5 FROM FileSet "
856 "WHERE FileSetId=%s", edit_int64(id_list.Id[i], ed1));
857 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
858 printf("%s\n", db_strerror(db));
865 if (fix && id_list.num_ids > 0) {
866 printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
867 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%s", &id_list);
871 static void eliminate_orphaned_client_records()
875 printf(_("Checking for orphaned Client entries.\n"));
877 * Wiffle through Client for every Client
878 * joining with the Job table including every Client even if
879 * there is not a match in Job (left outer join), then
880 * filter out only those where no Job points to a Client
881 * i.e. Job.Client is NULL
883 query = "SELECT Client.ClientId,Client.Name FROM Client "
884 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
885 "WHERE Job.ClientId IS NULL";
887 printf("%s\n", query);
889 if (!make_id_list(query, &id_list)) {
892 printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
893 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
894 for (int i=0; i < id_list.num_ids; i++) {
896 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
897 "WHERE ClientId=%s", edit_int64(id_list.Id[i], ed1));
898 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
899 printf("%s\n", db_strerror(db));
906 if (fix && id_list.num_ids > 0) {
907 printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
908 delete_id_list("DELETE FROM Client WHERE ClientId=%s", &id_list);
912 static void eliminate_orphaned_job_records()
916 printf(_("Checking for orphaned Job entries.\n"));
918 * Wiffle through Job for every Job
919 * joining with the Client table including every Job even if
920 * there is not a match in Client (left outer join), then
921 * filter out only those where no Client exists
922 * i.e. Client.Name is NULL
924 query = "SELECT Job.JobId,Job.Name FROM Job "
925 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
926 "WHERE Client.Name IS NULL";
928 printf("%s\n", query);
930 if (!make_id_list(query, &id_list)) {
933 printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
934 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
935 for (int i=0; i < id_list.num_ids; i++) {
937 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
938 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
939 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
940 printf("%s\n", db_strerror(db));
947 if (fix && id_list.num_ids > 0) {
948 printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
949 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
950 printf(_("Deleting JobMedia records of orphaned Job records.\n"));
951 delete_id_list("DELETE FROM JobMedia WHERE JobId=%s", &id_list);
952 printf(_("Deleting Log records of orphaned Job records.\n"));
953 delete_id_list("DELETE FROM Log WHERE JobId=%s", &id_list);
958 static void eliminate_admin_records()
962 printf(_("Checking for Admin Job entries.\n"));
963 query = "SELECT Job.JobId FROM Job "
964 "WHERE Job.Type='D'";
966 printf("%s\n", query);
968 if (!make_id_list(query, &id_list)) {
971 printf(_("Found %d Admin 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 Admin Job records.\n"), id_list.num_ids);
987 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
991 static void eliminate_restore_records()
995 printf(_("Checking for Restore Job entries.\n"));
996 query = "SELECT Job.JobId FROM Job "
997 "WHERE Job.Type='R'";
999 printf("%s\n", query);
1001 if (!make_id_list(query, &id_list)) {
1004 printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
1005 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1006 for (int i=0; i < id_list.num_ids; i++) {
1008 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1009 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1010 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1011 printf("%s\n", db_strerror(db));
1018 if (fix && id_list.num_ids > 0) {
1019 printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
1020 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1027 static void repair_bad_filenames()
1032 printf(_("Checking for Filenames with a trailing slash\n"));
1033 query = "SELECT FilenameId,Name from Filename "
1034 "WHERE Name LIKE '%/'";
1036 printf("%s\n", query);
1038 if (!make_id_list(query, &id_list)) {
1041 printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
1042 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1043 for (i=0; i < id_list.num_ids; i++) {
1045 bsnprintf(buf, sizeof(buf),
1046 "SELECT Name FROM Filename WHERE FilenameId=%s",
1047 edit_int64(id_list.Id[i], ed1));
1048 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1049 printf("%s\n", db_strerror(db));
1056 if (fix && id_list.num_ids > 0) {
1057 POOLMEM *name = get_pool_memory(PM_FNAME);
1058 char esc_name[5000];
1059 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1060 for (i=0; i < id_list.num_ids; i++) {
1063 bsnprintf(buf, sizeof(buf),
1064 "SELECT Name FROM Filename WHERE FilenameId=%s",
1065 edit_int64(id_list.Id[i], ed1));
1066 if (!db_sql_query(db, buf, get_name_handler, name)) {
1067 printf("%s\n", db_strerror(db));
1069 /* Strip trailing slash(es) */
1070 for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
1078 db_escape_string(esc_name, name, len);
1080 bsnprintf(buf, sizeof(buf),
1081 "UPDATE Filename SET Name='%s' WHERE FilenameId=%s",
1082 esc_name, edit_int64(id_list.Id[i], ed1));
1084 printf("%s\n", buf);
1086 db_sql_query(db, buf, NULL, NULL);
1091 static void repair_bad_paths()
1096 printf(_("Checking for Paths without a trailing slash\n"));
1097 query = "SELECT PathId,Path from Path "
1098 "WHERE Path NOT LIKE '%/'";
1100 printf("%s\n", query);
1102 if (!make_id_list(query, &id_list)) {
1105 printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1106 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1107 for (i=0; i < id_list.num_ids; i++) {
1109 bsnprintf(buf, sizeof(buf),
1110 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1111 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1112 printf("%s\n", db_strerror(db));
1119 if (fix && id_list.num_ids > 0) {
1120 POOLMEM *name = get_pool_memory(PM_FNAME);
1121 char esc_name[5000];
1122 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1123 for (i=0; i < id_list.num_ids; i++) {
1126 bsnprintf(buf, sizeof(buf),
1127 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1128 if (!db_sql_query(db, buf, get_name_handler, name)) {
1129 printf("%s\n", db_strerror(db));
1131 /* Strip trailing blanks */
1132 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1135 /* Add trailing slash */
1136 len = pm_strcat(&name, "/");
1137 db_escape_string(esc_name, name, len);
1138 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
1139 esc_name, edit_int64(id_list.Id[i], ed1));
1141 printf("%s\n", buf);
1143 db_sql_query(db, buf, NULL, NULL);
1150 * Gen next input command from the terminal
1152 static char *get_cmd(const char *prompt)
1154 static char cmd[1000];
1156 printf("%s", prompt);
1157 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
1162 strip_trailing_junk(cmd);
1166 static bool yes_no(const char *prompt)
1169 cmd = get_cmd(prompt);
1174 return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1177 bool python_set_prog(JCR*, char const*) { return false; }