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-2006 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)
516 for (int i=0; i < id_list->num_ids; i++) {
517 bsnprintf(buf, sizeof(buf), query, id_list->Id[i]);
519 printf(_("Deleting: %s\n"), buf);
521 db_sql_query(db, buf, NULL, NULL);
527 * Called here with each name to be added to the list
529 static int name_list_handler(void *ctx, int num_fields, char **row)
531 NAME_LIST *name = (NAME_LIST *)ctx;
533 if (name->num_ids == MAX_ID_LIST_LEN) {
536 if (name->num_ids == name->max_ids) {
537 if (name->max_ids == 0) {
538 name->max_ids = 10000;
539 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
541 name->max_ids = (name->max_ids * 3) / 2;
542 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
545 name->name[name->num_ids++] = bstrdup(row[0]);
551 * Construct name list
553 static int make_name_list(const char *query, NAME_LIST *name_list)
555 name_list->num_ids = 0;
556 name_list->num_del = 0;
557 name_list->tot_ids = 0;
559 if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
560 printf("%s", db_strerror(db));
567 * Print names in the list
569 static void print_name_list(NAME_LIST *name_list)
571 for (int i=0; i < name_list->num_ids; i++) {
572 printf("%s\n", name_list->name[i]);
578 * Free names in the list
580 static void free_name_list(NAME_LIST *name_list)
582 for (int i=0; i < name_list->num_ids; i++) {
583 free(name_list->name[i]);
585 name_list->num_ids = 0;
588 static void eliminate_duplicate_filenames()
593 printf(_("Checking for duplicate Filename entries.\n"));
595 /* Make list of duplicated names */
596 query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
597 "HAVING count(Name) > 1";
599 if (!make_name_list(query, &name_list)) {
602 printf(_("Found %d duplicate Filename records.\n"), name_list.num_ids);
603 if (name_list.num_ids && verbose && yes_no(_("Print the list? (yes/no): "))) {
604 print_name_list(&name_list);
610 /* Loop through list of duplicate names */
611 for (int i=0; i<name_list.num_ids; i++) {
612 /* Get all the Ids of each name */
613 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
614 bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
618 if (!make_id_list(buf, &id_list)) {
622 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
624 /* Force all records to use the first id then delete the other ids */
625 for (int j=1; j<id_list.num_ids; j++) {
626 char ed1[50], ed2[50];
627 bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%s WHERE FilenameId=%s",
628 edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
632 db_sql_query(db, buf, NULL, NULL);
633 bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%s",
638 db_sql_query(db, buf, NULL, NULL);
642 free_name_list(&name_list);
645 static void eliminate_duplicate_paths()
650 printf(_("Checking for duplicate Path entries.\n"));
652 /* Make list of duplicated names */
654 query = "SELECT Path, count(Path) as Count FROM Path "
655 "GROUP BY Path HAVING count(Path) > 1";
657 if (!make_name_list(query, &name_list)) {
660 printf(_("Found %d duplicate Path records.\n"), name_list.num_ids);
661 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
662 print_name_list(&name_list);
668 /* Loop through list of duplicate names */
669 for (int i=0; i<name_list.num_ids; i++) {
670 /* Get all the Ids of each name */
671 db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
672 bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
676 if (!make_id_list(buf, &id_list)) {
680 printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
682 /* Force all records to use the first id then delete the other ids */
683 for (int j=1; j<id_list.num_ids; j++) {
684 char ed1[50], ed2[50];
685 bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%s WHERE PathId=%s",
686 edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
690 db_sql_query(db, buf, NULL, NULL);
691 bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%s", ed2);
695 db_sql_query(db, buf, NULL, NULL);
699 free_name_list(&name_list);
702 static void eliminate_orphaned_jobmedia_records()
706 printf(_("Checking for orphaned JobMedia entries.\n"));
707 query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
708 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
709 "WHERE Job.JobId IS NULL";
710 if (!make_id_list(query, &id_list)) {
713 printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
714 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
715 for (int i=0; i < id_list.num_ids; i++) {
717 bsnprintf(buf, sizeof(buf),
718 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
719 "WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId",
720 edit_int64(id_list.Id[i], ed1));
721 if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
722 printf("%s\n", db_strerror(db));
730 if (fix && id_list.num_ids > 0) {
731 printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
732 delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%s", &id_list);
736 static void eliminate_orphaned_file_records()
740 printf(_("Checking for orphaned File entries. This may take some time!\n"));
741 query = "SELECT File.FileId,Job.JobId FROM File "
742 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
743 "WHERE Job.JobId IS NULL";
745 printf("%s\n", query);
747 if (!make_id_list(query, &id_list)) {
750 printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
751 if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
752 for (int i=0; i < id_list.num_ids; i++) {
754 bsnprintf(buf, sizeof(buf),
755 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
756 "WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
757 edit_int64(id_list.Id[i], ed1));
758 if (!db_sql_query(db, buf, print_file_handler, NULL)) {
759 printf("%s\n", db_strerror(db));
766 if (fix && id_list.num_ids > 0) {
767 printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
768 delete_id_list("DELETE FROM File WHERE FileId=%s", &id_list);
772 static void eliminate_orphaned_path_records()
776 printf(_("Checking for orphaned Path entries. This may take some time!\n"));
777 query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
778 "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
779 "WHERE File.PathId IS NULL";
781 printf("%s\n", query);
783 if (!make_id_list(query, &id_list)) {
786 printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
787 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
788 for (int i=0; i < id_list.num_ids; i++) {
790 bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
791 edit_int64(id_list.Id[i], ed1));
792 db_sql_query(db, buf, print_name_handler, NULL);
798 if (fix && id_list.num_ids > 0) {
799 printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
800 delete_id_list("DELETE FROM Path WHERE PathId=%s", &id_list);
804 static void eliminate_orphaned_filename_records()
808 printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
809 query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
810 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
811 "WHERE File.FilenameId IS NULL";
813 printf("%s\n", query);
815 if (!make_id_list(query, &id_list)) {
818 printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
819 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
820 for (int i=0; i < id_list.num_ids; i++) {
822 bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
823 edit_int64(id_list.Id[i], ed1));
824 db_sql_query(db, buf, print_name_handler, NULL);
830 if (fix && id_list.num_ids > 0) {
831 printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
832 delete_id_list("DELETE FROM Filename WHERE FilenameId=%s", &id_list);
836 static void eliminate_orphaned_fileset_records()
840 printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
841 query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
842 "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
843 "WHERE Job.FileSetId IS NULL";
845 printf("%s\n", query);
847 if (!make_id_list(query, &id_list)) {
850 printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
851 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
852 for (int i=0; i < id_list.num_ids; i++) {
854 bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
855 "WHERE FileSetId=%s", edit_int64(id_list.Id[i], ed1));
856 if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
857 printf("%s\n", db_strerror(db));
864 if (fix && id_list.num_ids > 0) {
865 printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
866 delete_id_list("DELETE FROM FileSet WHERE FileSetId=%s", &id_list);
870 static void eliminate_orphaned_client_records()
874 printf(_("Checking for orphaned Client entries.\n"));
876 * Wiffle through Client for every Client
877 * joining with the Job table including every Client even if
878 * there is not a match in Job (left outer join), then
879 * filter out only those where no Job points to a Client
880 * i.e. Job.Client is NULL
882 query = "SELECT Client.ClientId,Client.Name FROM Client "
883 "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
884 "WHERE Job.ClientId IS NULL";
886 printf("%s\n", query);
888 if (!make_id_list(query, &id_list)) {
891 printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
892 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
893 for (int i=0; i < id_list.num_ids; i++) {
895 bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
896 "WHERE ClientId=%s", edit_int64(id_list.Id[i], ed1));
897 if (!db_sql_query(db, buf, print_client_handler, NULL)) {
898 printf("%s\n", db_strerror(db));
905 if (fix && id_list.num_ids > 0) {
906 printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
907 delete_id_list("DELETE FROM Client WHERE ClientId=%s", &id_list);
911 static void eliminate_orphaned_job_records()
915 printf(_("Checking for orphaned Job entries.\n"));
917 * Wiffle through Job for every Job
918 * joining with the Client table including every Job even if
919 * there is not a match in Client (left outer join), then
920 * filter out only those where no Client exists
921 * i.e. Client.Name is NULL
923 query = "SELECT Job.JobId,Job.Name FROM Job "
924 "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
925 "WHERE Client.Name IS NULL";
927 printf("%s\n", query);
929 if (!make_id_list(query, &id_list)) {
932 printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
933 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
934 for (int i=0; i < id_list.num_ids; i++) {
936 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
937 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
938 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
939 printf("%s\n", db_strerror(db));
946 if (fix && id_list.num_ids > 0) {
947 printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
948 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
949 printf(_("Deleting JobMedia records of orphaned Job records.\n"));
950 delete_id_list("DELETE FROM JobMedia WHERE JobId=%s", &id_list);
951 printf(_("Deleting Log records of orphaned Job records.\n"));
952 delete_id_list("DELETE FROM Log WHERE JobId=%s", &id_list);
957 static void eliminate_admin_records()
961 printf(_("Checking for Admin Job entries.\n"));
962 query = "SELECT Job.JobId FROM Job "
963 "WHERE Job.Type='D'";
965 printf("%s\n", query);
967 if (!make_id_list(query, &id_list)) {
970 printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
971 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
972 for (int i=0; i < id_list.num_ids; i++) {
974 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
975 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
976 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
977 printf("%s\n", db_strerror(db));
984 if (fix && id_list.num_ids > 0) {
985 printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
986 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
990 static void eliminate_restore_records()
994 printf(_("Checking for Restore Job entries.\n"));
995 query = "SELECT Job.JobId FROM Job "
996 "WHERE Job.Type='R'";
998 printf("%s\n", query);
1000 if (!make_id_list(query, &id_list)) {
1003 printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
1004 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1005 for (int i=0; i < id_list.num_ids; i++) {
1007 bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1008 "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1009 if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1010 printf("%s\n", db_strerror(db));
1017 if (fix && id_list.num_ids > 0) {
1018 printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
1019 delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1026 static void repair_bad_filenames()
1031 printf(_("Checking for Filenames with a trailing slash\n"));
1032 query = "SELECT FilenameId,Name from Filename "
1033 "WHERE Name LIKE '%/'";
1035 printf("%s\n", query);
1037 if (!make_id_list(query, &id_list)) {
1040 printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
1041 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1042 for (i=0; i < id_list.num_ids; i++) {
1044 bsnprintf(buf, sizeof(buf),
1045 "SELECT Name FROM Filename WHERE FilenameId=%s",
1046 edit_int64(id_list.Id[i], ed1));
1047 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1048 printf("%s\n", db_strerror(db));
1055 if (fix && id_list.num_ids > 0) {
1056 POOLMEM *name = get_pool_memory(PM_FNAME);
1057 char esc_name[5000];
1058 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1059 for (i=0; i < id_list.num_ids; i++) {
1062 bsnprintf(buf, sizeof(buf),
1063 "SELECT Name FROM Filename WHERE FilenameId=%s",
1064 edit_int64(id_list.Id[i], ed1));
1065 if (!db_sql_query(db, buf, get_name_handler, name)) {
1066 printf("%s\n", db_strerror(db));
1068 /* Strip trailing slash(es) */
1069 for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
1077 db_escape_string(esc_name, name, len);
1079 bsnprintf(buf, sizeof(buf),
1080 "UPDATE Filename SET Name='%s' WHERE FilenameId=%s",
1081 esc_name, edit_int64(id_list.Id[i], ed1));
1083 printf("%s\n", buf);
1085 db_sql_query(db, buf, NULL, NULL);
1090 static void repair_bad_paths()
1095 printf(_("Checking for Paths without a trailing slash\n"));
1096 query = "SELECT PathId,Path from Path "
1097 "WHERE Path NOT LIKE '%/'";
1099 printf("%s\n", query);
1101 if (!make_id_list(query, &id_list)) {
1104 printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1105 if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1106 for (i=0; i < id_list.num_ids; i++) {
1108 bsnprintf(buf, sizeof(buf),
1109 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1110 if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1111 printf("%s\n", db_strerror(db));
1118 if (fix && id_list.num_ids > 0) {
1119 POOLMEM *name = get_pool_memory(PM_FNAME);
1120 char esc_name[5000];
1121 printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1122 for (i=0; i < id_list.num_ids; i++) {
1125 bsnprintf(buf, sizeof(buf),
1126 "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1127 if (!db_sql_query(db, buf, get_name_handler, name)) {
1128 printf("%s\n", db_strerror(db));
1130 /* Strip trailing blanks */
1131 for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1134 /* Add trailing slash */
1135 len = pm_strcat(&name, "/");
1136 db_escape_string(esc_name, name, len);
1137 bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
1138 esc_name, edit_int64(id_list.Id[i], ed1));
1140 printf("%s\n", buf);
1142 db_sql_query(db, buf, NULL, NULL);
1149 * Gen next input command from the terminal
1151 static char *get_cmd(const char *prompt)
1153 static char cmd[1000];
1155 printf("%s", prompt);
1156 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
1161 strip_trailing_junk(cmd);
1165 static bool yes_no(const char *prompt)
1168 cmd = get_cmd(prompt);
1173 return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1176 bool python_set_prog(JCR*, char const*) { return false; }