1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *  Program to check a Bacula database for consistency and to
22  *   make repairs
23  *
24  *   Kern E. Sibbald, August 2002
25  *
26  */
27 
28 #include "bacula.h"
29 #include "cats/cats.h"
30 #include "lib/runscript.h"
31 #include "dird/dird_conf.h"
32 
33 extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
34 
35 typedef struct s_id_ctx {
36    int64_t *Id;                       /* ids to be modified */
37    int num_ids;                       /* ids stored */
38    int max_ids;                       /* size of array */
39    int num_del;                       /* number deleted */
40    int tot_ids;                       /* total to process */
41 } ID_LIST;
42 
43 typedef struct s_name_ctx {
44    char **name;                       /* list of names */
45    int num_ids;                       /* ids stored */
46    int max_ids;                       /* size of array */
47    int num_del;                       /* number deleted */
48    int tot_ids;                       /* total to process */
49 } NAME_LIST;
50 
51 /* Global variables */
52 
53 static bool fix = false;
54 static bool batch = false;
55 static BDB *db;
56 static ID_LIST id_list;
57 static NAME_LIST name_list;
58 static char buf[20000];
59 static bool quit = false;
60 static CONFIG *config;
61 static const char *idx_tmp_name;
62 
63 #define MAX_ID_LIST_LEN 10000000
64 
65 /* Forward referenced functions */
66 static int make_id_list(const char *query, ID_LIST *id_list);
67 static int delete_id_list(const char *query, ID_LIST *id_list);
68 static int make_name_list(const char *query, NAME_LIST *name_list);
69 static void print_name_list(NAME_LIST *name_list);
70 static void free_name_list(NAME_LIST *name_list);
71 static char *get_cmd(const char *prompt);
72 static void eliminate_duplicate_filenames();
73 static void eliminate_duplicate_paths();
74 static void eliminate_orphaned_jobmedia_records();
75 static void eliminate_orphaned_file_records();
76 static void eliminate_orphaned_path_records();
77 static void eliminate_orphaned_filename_records();
78 static void eliminate_orphaned_fileset_records();
79 static void eliminate_orphaned_client_records();
80 static void eliminate_orphaned_job_records();
81 static void eliminate_admin_records();
82 static void eliminate_restore_records();
83 static void eliminate_verify_records();
84 static void repair_bad_paths();
85 static void repair_bad_filenames();
86 static void do_interactive_mode();
87 static bool yes_no(const char *prompt);
88 static bool check_idx(const char *col_name);
89 static bool create_tmp_idx(const char *idx_name, const char *table_name,
90                const char *col_name);
91 static bool drop_tmp_idx(const char *idx_name, const char *table_name);
92 static int check_idx_handler(void *ctx, int num_fields, char **row);
93 
usage()94 static void usage()
95 {
96    fprintf(stderr,
97 PROG_COPYRIGHT
98 "\n%sVersion: %s (%s)\n\n"
99 "Usage: dbcheck [-c config ] [-B] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>] [<dbport>] [<dbport>] [<dbsslmode>] [<dbsslkey>] [<dbsslcert>] [<dbsslca>]\n"
100 "       -b              batch mode\n"
101 "       -C              catalog name in the director conf file\n"
102 "       -c              Director conf filename\n"
103 "       -B              print catalog configuration and exit\n"
104 "       -d <nn>         set debug level to <nn>\n"
105 "       -dt             print a timestamp in debug output\n"
106 "       -f              fix inconsistencies\n"
107 "       -v              verbose\n"
108 "       -?              print this message\n"
109 "\n", 2002, "", VERSION, BDATE);
110 
111    exit(1);
112 }
113 
main(int argc,char * argv[])114 int main (int argc, char *argv[])
115 {
116    int ch;
117    const char *user, *password, *db_name, *dbhost;
118    const char *dbsslmode = NULL, *dbsslkey = NULL, *dbsslcert = NULL, *dbsslca = NULL;
119    const char *dbsslcapath = NULL, *dbsslcipher = NULL;
120    int dbport = 0;
121    bool print_catalog=false;
122    char *configfile = NULL;
123    char *catalogname = NULL;
124    char *endptr;
125 
126    setlocale(LC_ALL, "");
127    bindtextdomain("bacula", LOCALEDIR);
128    textdomain("bacula");
129    lmgr_init_thread();
130 
131    my_name_is(argc, argv, "dbcheck");
132    init_msg(NULL, NULL);             /* setup message handler */
133 
134    memset(&id_list, 0, sizeof(id_list));
135    memset(&name_list, 0, sizeof(name_list));
136 
137    while ((ch = getopt(argc, argv, "bc:C:d:fvB?")) != -1) {
138       switch (ch) {
139       case 'B':
140          print_catalog = true;     /* get catalog information from config */
141          break;
142       case 'b':                    /* batch */
143          batch = true;
144          break;
145       case 'C':                    /* CatalogName */
146           catalogname = optarg;
147          break;
148       case 'c':                    /* configfile */
149           configfile = optarg;
150          break;
151       case 'd':                    /* debug level */
152          if (*optarg == 't') {
153             dbg_timestamp = true;
154          } else {
155             debug_level = atoi(optarg);
156             if (debug_level <= 0) {
157                debug_level = 1;
158             }
159          }
160          break;
161       case 'f':                    /* fix inconsistencies */
162          fix = true;
163          break;
164       case 'v':
165          verbose++;
166          break;
167       case '?':
168       default:
169          usage();
170       }
171    }
172    argc -= optind;
173    argv += optind;
174 
175    OSDependentInit();
176 
177    if (configfile) {
178       CAT *catalog = NULL;
179       int found = 0;
180       if (argc > 0) {
181          Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
182       }
183       config = New(CONFIG());
184       parse_dir_config(config, configfile, M_ERROR_TERM);
185       LockRes();
186       foreach_res(catalog, R_CATALOG) {
187          if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
188             ++found;
189             break;
190          } else if (!catalogname) { // stop on first if no catalogname is given
191            ++found;
192            break;
193          }
194       }
195       UnlockRes();
196       if (!found) {
197          if (catalogname) {
198             Pmsg2(0, _("Error can not find the Catalog name[%s] in the given config file [%s]\n"), catalogname, configfile);
199          } else {
200             Pmsg1(0, _("Error there is no Catalog section in the given config file [%s]\n"), configfile);
201          }
202          exit(1);
203       } else {
204          DIRRES *director;
205          LockRes();
206          director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
207          UnlockRes();
208          if (!director) {
209             Pmsg0(0, _("Error no Director resource defined.\n"));
210             exit(1);
211          }
212          set_working_directory(director->working_directory);
213 
214          /* Print catalog information and exit (-B) */
215          if (print_catalog) {
216 
217             POOLMEM *catalog_details = get_pool_memory(PM_MESSAGE);
218             db = db_init_database(NULL, catalog->db_driver, catalog->db_name, catalog->db_user,
219                     catalog->db_password, catalog->db_address,
220                     catalog->db_port, catalog->db_socket,
221                     catalog->db_ssl_mode,
222                     catalog->db_ssl_key, catalog->db_ssl_cert,
223                     catalog->db_ssl_ca,
224                     catalog->db_ssl_capath, catalog->db_ssl_cipher,
225                     catalog->mult_db_connections,
226                     catalog->disable_batch_insert);
227             if (db) {
228                printf("%sdb_type=%s\nworking_dir=%s\n", catalog->display(catalog_details),
229                   db_get_engine_name(db), working_directory);
230                db_close_database(NULL, db);
231             }
232             free_pool_memory(catalog_details);
233             exit(0);
234          }
235 
236          db_name = catalog->db_name;
237          user = catalog->db_user;
238          password = catalog->db_password;
239          dbhost = catalog->db_address;
240          if (dbhost && dbhost[0] == 0) {
241             dbhost = NULL;
242          }
243          dbport = catalog->db_port;
244          dbsslmode = catalog->db_ssl_mode;
245          dbsslkey = catalog->db_ssl_key;
246          dbsslcert = catalog->db_ssl_cert;
247          dbsslca = catalog->db_ssl_ca;
248          dbsslcapath = catalog->db_ssl_capath;
249          dbsslcipher = catalog->db_ssl_cipher;
250       }
251    } else {
252       if (argc > 10) {
253          Pmsg0(0, _("Wrong number of arguments.\n"));
254          usage();
255       }
256 
257       if (argc < 1) {
258          Pmsg0(0, _("Working directory not supplied.\n"));
259          usage();
260       }
261 
262       /* This is needed by SQLite to find the db */
263       working_directory = argv[0];
264       db_name = "bacula";
265       user = db_name;
266       password = "";
267       dbhost = NULL;
268 
269       if (argc >= 2) {
270          db_name = argv[1];
271          user = db_name;
272          if (argc >= 3) {
273             user = argv[2];
274             if (argc >= 4) {
275                password = argv[3];
276                if (argc >= 5) {
277                   dbhost = argv[4];
278                   if (argc >= 6) {
279                      errno = 0;
280                      dbport = strtol(argv[5], &endptr, 10);
281                      if (*endptr != '\0') {
282                         Pmsg0(0, _("Database port must be a numeric value.\n"));
283                         exit(1);
284                      } else if (errno == ERANGE) {
285                         Pmsg0(0, _("Database port must be a int value.\n"));
286                         exit(1);
287                      }
288                      if (argc >= 7) {
289                         dbsslmode = argv[6];
290                         if (argc >= 8) {
291                            dbsslkey = argv[7];
292                            dbsslcert = argv[8];
293                            if (argc == 10) {
294                               dbsslca = argv[9];
295                            } /* if (argc == 10) */
296                         } /* if (argc >= 8) */
297                      } /* if (argc >= 7) */
298                   } /* if (argc >= 6) */
299                } /* if (argc >= 5) */
300             } /* if (argc >= 4) */
301          } /* if (argc >= 3) */
302       } /* if (argc >= 2) */
303    }
304 
305    /* Open database */
306    db = db_init_database(NULL, NULL, db_name, user, password, dbhost,
307           dbport, NULL, dbsslmode, dbsslkey, dbsslcert, dbsslca,
308            dbsslcapath, dbsslcipher, false, false);
309 
310    if (!db || !db_open_database(NULL, db)) {
311       Emsg1(M_FATAL, 0, "%s", db_strerror(db));
312           return 1;
313    }
314 
315    /* Drop temporary index idx_tmp_name if it already exists */
316    drop_tmp_idx("idxPIchk", "File");
317 
318    if (batch) {
319       repair_bad_paths();
320       repair_bad_filenames();
321       eliminate_duplicate_filenames();
322       eliminate_duplicate_paths();
323       eliminate_orphaned_jobmedia_records();
324       eliminate_orphaned_file_records();
325       eliminate_orphaned_path_records();
326       eliminate_orphaned_filename_records();
327       eliminate_orphaned_fileset_records();
328       eliminate_orphaned_client_records();
329       eliminate_orphaned_job_records();
330       eliminate_admin_records();
331       eliminate_restore_records();
332    } else {
333       do_interactive_mode();
334    }
335 
336    /* Drop temporary index idx_tmp_name */
337    drop_tmp_idx("idxPIchk", "File");
338 
339    if (db) db_close_database(NULL, db);
340    close_msg(NULL);
341    term_msg();
342    lmgr_cleanup_main();
343    return 0;
344 }
345 
print_catalog_details(CAT * catalog,const char * working_dir)346 void print_catalog_details(CAT *catalog, const char *working_dir)
347 {
348    POOLMEM *catalog_details = get_pool_memory(PM_MESSAGE);
349 
350    /*
351     * Instantiate a BDB class and see what db_type gets assigned to it.
352     */
353    db = db_init_database(NULL, catalog->db_driver, catalog->db_name, catalog->db_user,
354                          catalog->db_password, catalog->db_address,
355                          catalog->db_port, catalog->db_socket,
356                          catalog->db_ssl_mode, catalog->db_ssl_key,
357                          catalog->db_ssl_cert, catalog->db_ssl_ca,
358                          catalog->db_ssl_capath, catalog->db_ssl_cipher,
359                          catalog->mult_db_connections,
360                          catalog->disable_batch_insert);
361    if (db) {
362       printf("%sdb_type=%s\nworking_dir=%s\n", catalog->display(catalog_details),
363              db_get_engine_name(db), working_directory);
364       db_close_database(NULL, db);
365    }
366    free_pool_memory(catalog_details);
367 }
368 
do_interactive_mode()369 static void do_interactive_mode()
370 {
371    const char *cmd;
372 
373    printf(_("Hello, this is the database check/correct program.\n"));
374    if (fix)
375       printf(_("Modify database is on."));
376    else
377       printf(_("Modify database is off."));
378    if (verbose)
379       printf(_(" Verbose is on.\n"));
380    else
381       printf(_(" Verbose is off.\n"));
382 
383    printf(_("Please select the function you want to perform.\n"));
384 
385    while (!quit) {
386       if (fix) {
387          printf(_("\n"
388 "     1) Toggle modify database flag\n"
389 "     2) Toggle verbose flag\n"
390 "     3) Repair bad Filename records\n"
391 "     4) Repair bad Path records\n"
392 "     5) Eliminate duplicate Filename records\n"
393 "     6) Eliminate duplicate Path records\n"
394 "     7) Eliminate orphaned Jobmedia records\n"
395 "     8) Eliminate orphaned File records\n"
396 "     9) Eliminate orphaned Path records\n"
397 "    10) Eliminate orphaned Filename records\n"
398 "    11) Eliminate orphaned FileSet records\n"
399 "    12) Eliminate orphaned Client records\n"
400 "    13) Eliminate orphaned Job records\n"
401 "    14) Eliminate all Admin records\n"
402 "    15) Eliminate all Restore records\n"
403 "    16) Eliminate all Verify records\n"
404 "    17) All (3-16)\n"
405 "    18) Quit\n"));
406        } else {
407          printf(_("\n"
408 "     1) Toggle modify database flag\n"
409 "     2) Toggle verbose flag\n"
410 "     3) Check for bad Filename records\n"
411 "     4) Check for bad Path records\n"
412 "     5) Check for duplicate Filename records\n"
413 "     6) Check for duplicate Path records\n"
414 "     7) Check for orphaned Jobmedia records\n"
415 "     8) Check for orphaned File records\n"
416 "     9) Check for orphaned Path records\n"
417 "    10) Check for orphaned Filename records\n"
418 "    11) Check for orphaned FileSet records\n"
419 "    12) Check for orphaned Client records\n"
420 "    13) Check for orphaned Job records\n"
421 "    14) Check for all Admin records\n"
422 "    15) Check for all Restore records\n"
423 "    16) Check for all Verify records\n"
424 "    17) All (3-16)\n"
425 "    18) Quit\n"));
426        }
427 
428       cmd = get_cmd(_("Select function number: "));
429       if (cmd) {
430          int item = atoi(cmd);
431          switch (item) {
432          case 1:
433             fix = !fix;
434             if (fix)
435                printf(_("Database will be modified.\n"));
436             else
437                printf(_("Database will NOT be modified.\n"));
438             break;
439          case 2:
440             verbose = verbose?0:1;
441             if (verbose)
442                printf(_(" Verbose is on.\n"));
443             else
444                printf(_(" Verbose is off.\n"));
445             break;
446          case 3:
447             repair_bad_filenames();
448             break;
449          case 4:
450             repair_bad_paths();
451             break;
452          case 5:
453             eliminate_duplicate_filenames();
454             break;
455          case 6:
456             eliminate_duplicate_paths();
457             break;
458          case 7:
459             eliminate_orphaned_jobmedia_records();
460             break;
461          case 8:
462             eliminate_orphaned_file_records();
463             break;
464          case 9:
465             eliminate_orphaned_path_records();
466             break;
467          case 10:
468             eliminate_orphaned_filename_records();
469             break;
470          case 11:
471             eliminate_orphaned_fileset_records();
472             break;
473          case 12:
474             eliminate_orphaned_client_records();
475             break;
476          case 13:
477             eliminate_orphaned_job_records();
478             break;
479          case 14:
480             eliminate_admin_records();
481             break;
482          case 15:
483             eliminate_restore_records();
484             break;
485          case 16:
486             eliminate_verify_records();
487             break;
488          case 17:
489             repair_bad_filenames();
490             repair_bad_paths();
491             eliminate_duplicate_filenames();
492             eliminate_duplicate_paths();
493             eliminate_orphaned_jobmedia_records();
494             eliminate_orphaned_file_records();
495             eliminate_orphaned_path_records();
496             eliminate_orphaned_filename_records();
497             eliminate_orphaned_fileset_records();
498             eliminate_orphaned_client_records();
499             eliminate_orphaned_job_records();
500             eliminate_admin_records();
501             eliminate_restore_records();
502             eliminate_verify_records();
503             break;
504          case 18:
505             quit = true;
506             break;
507          }
508       }
509    }
510 }
511 
print_name_handler(void * ctx,int num_fields,char ** row)512 static int print_name_handler(void *ctx, int num_fields, char **row)
513 {
514    if (row[0]) {
515       printf("%s\n", row[0]);
516    }
517    return 0;
518 }
519 
get_name_handler(void * ctx,int num_fields,char ** row)520 static int get_name_handler(void *ctx, int num_fields, char **row)
521 {
522    POOLMEM *buf = (POOLMEM *)ctx;
523    if (row[0]) {
524       pm_strcpy(&buf, row[0]);
525    }
526    return 0;
527 }
528 
print_job_handler(void * ctx,int num_fields,char ** row)529 static int print_job_handler(void *ctx, int num_fields, char **row)
530 {
531    printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
532               NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
533    return 0;
534 }
535 
print_jobmedia_handler(void * ctx,int num_fields,char ** row)536 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
537 {
538    printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
539               NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
540    return 0;
541 }
542 
print_file_handler(void * ctx,int num_fields,char ** row)543 static int print_file_handler(void *ctx, int num_fields, char **row)
544 {
545    printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
546               NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
547    return 0;
548 }
549 
print_fileset_handler(void * ctx,int num_fields,char ** row)550 static int print_fileset_handler(void *ctx, int num_fields, char **row)
551 {
552    printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
553               NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
554    return 0;
555 }
556 
print_client_handler(void * ctx,int num_fields,char ** row)557 static int print_client_handler(void *ctx, int num_fields, char **row)
558 {
559    printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
560               NPRT(row[0]), NPRT(row[1]));
561    return 0;
562 }
563 
564 /*
565  * Called here with each id to be added to the list
566  */
id_list_handler(void * ctx,int num_fields,char ** row)567 static int id_list_handler(void *ctx, int num_fields, char **row)
568 {
569    ID_LIST *lst = (ID_LIST *)ctx;
570 
571    if (lst->num_ids == MAX_ID_LIST_LEN) {
572       return 1;
573    }
574    if (lst->num_ids == lst->max_ids) {
575       if (lst->max_ids == 0) {
576          lst->max_ids = 10000;
577          lst->Id = (int64_t *)bmalloc(sizeof(int64_t) * lst->max_ids);
578       } else {
579          lst->max_ids = (lst->max_ids * 3) / 2;
580          lst->Id = (int64_t *)brealloc(lst->Id, sizeof(int64_t) * lst->max_ids);
581       }
582    }
583    lst->Id[lst->num_ids++] = str_to_int64(row[0]);
584    return 0;
585 }
586 
587 /*
588  * Construct record id list
589  */
make_id_list(const char * query,ID_LIST * id_list)590 static int make_id_list(const char *query, ID_LIST *id_list)
591 {
592    id_list->num_ids = 0;
593    id_list->num_del = 0;
594    id_list->tot_ids = 0;
595 
596    if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
597       printf("%s", db_strerror(db));
598       return 0;
599    }
600    return 1;
601 }
602 
603 /*
604  * Delete all entries in the list
605  */
delete_id_list(const char * query,ID_LIST * id_list)606 static int delete_id_list(const char *query, ID_LIST *id_list)
607 {
608    char ed1[50];
609    for (int i=0; i < id_list->num_ids; i++) {
610       bsnprintf(buf, sizeof(buf), query, edit_int64(id_list->Id[i], ed1));
611       if (verbose) {
612          printf(_("Deleting: %s\n"), buf);
613       }
614       db_sql_query(db, buf, NULL, NULL);
615    }
616    return 1;
617 }
618 
619 /*
620  * Called here with each name to be added to the list
621  */
name_list_handler(void * ctx,int num_fields,char ** row)622 static int name_list_handler(void *ctx, int num_fields, char **row)
623 {
624    NAME_LIST *name = (NAME_LIST *)ctx;
625 
626    if (name->num_ids == MAX_ID_LIST_LEN) {
627       return 1;
628    }
629    if (name->num_ids == name->max_ids) {
630       if (name->max_ids == 0) {
631          name->max_ids = 10000;
632          name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
633       } else {
634          name->max_ids = (name->max_ids * 3) / 2;
635          name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
636       }
637    }
638    name->name[name->num_ids++] = bstrdup(row[0]);
639    return 0;
640 }
641 
642 /*
643  * Construct name list
644  */
make_name_list(const char * query,NAME_LIST * name_list)645 static int make_name_list(const char *query, NAME_LIST *name_list)
646 {
647    name_list->num_ids = 0;
648    name_list->num_del = 0;
649    name_list->tot_ids = 0;
650 
651    if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
652       printf("%s", db_strerror(db));
653       return 0;
654    }
655    return 1;
656 }
657 
658 /*
659  * Print names in the list
660  */
print_name_list(NAME_LIST * name_list)661 static void print_name_list(NAME_LIST *name_list)
662 {
663    for (int i=0; i < name_list->num_ids; i++) {
664       printf("%s\n", name_list->name[i]);
665    }
666 }
667 
668 /*
669  * Free names in the list
670  */
free_name_list(NAME_LIST * name_list)671 static void free_name_list(NAME_LIST *name_list)
672 {
673    for (int i=0; i < name_list->num_ids; i++) {
674       free(name_list->name[i]);
675    }
676    name_list->num_ids = 0;
677 }
678 
eliminate_duplicate_filenames()679 static void eliminate_duplicate_filenames()
680 {
681    const char *query;
682    char esc_name[5000];
683 
684    printf(_("Checking for duplicate Filename entries.\n"));
685 
686    /* Make list of duplicated names */
687    query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY  Name "
688            "HAVING count(Name) > 1";
689 
690    if (!make_name_list(query, &name_list)) {
691       exit(1);
692    }
693    printf(_("Found %d duplicate Filename records.\n"), name_list.num_ids);
694    if (name_list.num_ids && verbose && yes_no(_("Print the list? (yes/no): "))) {
695       print_name_list(&name_list);
696    }
697    if (quit) {
698       return;
699    }
700    if (fix) {
701       /* Loop through list of duplicate names */
702       for (int i=0; i<name_list.num_ids; i++) {
703          /* Get all the Ids of each name */
704          db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
705          bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
706          if (verbose > 1) {
707             printf("%s\n", buf);
708          }
709          if (!make_id_list(buf, &id_list)) {
710             exit(1);
711          }
712          if (verbose) {
713             printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
714          }
715          /* Force all records to use the first id then delete the other ids */
716          for (int j=1; j<id_list.num_ids; j++) {
717             char ed1[50], ed2[50];
718             bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%s WHERE FilenameId=%s",
719                edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
720             if (verbose > 1) {
721                printf("%s\n", buf);
722             }
723             db_sql_query(db, buf, NULL, NULL);
724             bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%s",
725                ed2);
726             if (verbose > 2) {
727                printf("%s\n", buf);
728             }
729             db_sql_query(db, buf, NULL, NULL);
730          }
731       }
732    }
733    free_name_list(&name_list);
734 }
735 
eliminate_duplicate_paths()736 static void eliminate_duplicate_paths()
737 {
738    const char *query;
739    char esc_name[5000];
740 
741    printf(_("Checking for duplicate Path entries.\n"));
742 
743    /* Make list of duplicated names */
744    query = "SELECT Path, count(Path) as Count FROM Path "
745            "GROUP BY Path HAVING count(Path) > 1";
746 
747    if (!make_name_list(query, &name_list)) {
748       exit(1);
749    }
750    printf(_("Found %d duplicate Path records.\n"), name_list.num_ids);
751    if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
752       print_name_list(&name_list);
753    }
754    if (quit) {
755       return;
756    }
757    if (fix) {
758       /* Loop through list of duplicate names */
759       for (int i=0; i<name_list.num_ids; i++) {
760          /* Get all the Ids of each name */
761          db_escape_string(NULL, db,  esc_name, name_list.name[i], strlen(name_list.name[i]));
762          bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
763          if (verbose > 1) {
764             printf("%s\n", buf);
765          }
766          if (!make_id_list(buf, &id_list)) {
767             exit(1);
768          }
769          if (verbose) {
770             printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
771          }
772          /* Force all records to use the first id then delete the other ids */
773          for (int j=1; j<id_list.num_ids; j++) {
774             char ed1[50], ed2[50];
775             bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%s WHERE PathId=%s",
776                edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
777             if (verbose > 1) {
778                printf("%s\n", buf);
779             }
780             db_sql_query(db, buf, NULL, NULL);
781             bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%s", ed2);
782             if (verbose > 2) {
783                printf("%s\n", buf);
784             }
785             db_sql_query(db, buf, NULL, NULL);
786          }
787       }
788    }
789    free_name_list(&name_list);
790 }
791 
eliminate_orphaned_jobmedia_records()792 static void eliminate_orphaned_jobmedia_records()
793 {
794    const char *query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
795                 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
796                 "WHERE Job.JobId IS NULL LIMIT 300000";
797 
798    printf(_("Checking for orphaned JobMedia entries.\n"));
799    if (!make_id_list(query, &id_list)) {
800       exit(1);
801    }
802    /* Loop doing 300000 at a time */
803    while (id_list.num_ids != 0) {
804       printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
805       if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
806          for (int i=0; i < id_list.num_ids; i++) {
807             char ed1[50];
808             bsnprintf(buf, sizeof(buf),
809 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
810         "WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId",
811                edit_int64(id_list.Id[i], ed1));
812             if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
813                printf("%s\n", db_strerror(db));
814             }
815          }
816       }
817       if (quit) {
818          return;
819       }
820 
821       if (fix && id_list.num_ids > 0) {
822          printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
823          delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%s", &id_list);
824       } else {
825          break;                       /* get out if not updating db */
826       }
827       if (!make_id_list(query, &id_list)) {
828          exit(1);
829       }
830    }
831 }
832 
eliminate_orphaned_file_records()833 static void eliminate_orphaned_file_records()
834 {
835    const char *query = "SELECT File.FileId,Job.JobId FROM File "
836                 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
837                "WHERE Job.JobId IS NULL LIMIT 300000";
838 
839    printf(_("Checking for orphaned File entries. This may take some time!\n"));
840    if (verbose > 1) {
841       printf("%s\n", query);
842    }
843    if (!make_id_list(query, &id_list)) {
844       exit(1);
845    }
846    /* Loop doing 300000 at a time */
847    while (id_list.num_ids != 0) {
848       printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
849       if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
850          for (int i=0; i < id_list.num_ids; i++) {
851             char ed1[50];
852             bsnprintf(buf, sizeof(buf),
853 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
854    "WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
855                edit_int64(id_list.Id[i], ed1));
856             if (!db_sql_query(db, buf, print_file_handler, NULL)) {
857                printf("%s\n", db_strerror(db));
858             }
859          }
860       }
861       if (quit) {
862          return;
863       }
864       if (fix && id_list.num_ids > 0) {
865          printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
866          delete_id_list("DELETE FROM File WHERE FileId=%s", &id_list);
867       } else {
868          break;                       /* get out if not updating db */
869       }
870       if (!make_id_list(query, &id_list)) {
871          exit(1);
872       }
873    }
874 }
875 
eliminate_orphaned_path_records()876 static void eliminate_orphaned_path_records()
877 {
878    db_int64_ctx lctx;
879    lctx.count=0;
880    db_sql_query(db, "SELECT 1 FROM Job WHERE HasCache=1 LIMIT 1",
881                 db_int64_handler, &lctx);
882 
883    /* The BVFS code uses Path records that are not in the File table, for
884     * example if a Job has /home/test/ BVFS will need to create a Path record /
885     * and /home/ to work correctly
886     */
887    if (lctx.count == 1) {
888       printf(_("To prune orphaned Path entries, it is necessary to clear the BVFS Cache first with the bconsole \".bvfs_clear_cache yes\" command.\n"));
889       return;
890    }
891 
892    idx_tmp_name = NULL;
893    /* Check the existence of the required "one column" index */
894    if (!check_idx("PathId"))  {
895       if (yes_no(_("Create temporary index? (yes/no): "))) {
896          /* create temporary index PathId */
897          create_tmp_idx("idxPIchk", "File", "PathId");
898       }
899    }
900 
901    const char *query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
902                "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
903                "WHERE File.PathId IS NULL LIMIT 300000";
904 
905    printf(_("Checking for orphaned Path entries. This may take some time!\n"));
906    if (verbose > 1) {
907       printf("%s\n", query);
908    }
909    if (!make_id_list(query, &id_list)) {
910       exit(1);
911    }
912    /* Loop doing 300000 at a time */
913    while (id_list.num_ids != 0) {
914       printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
915       if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
916          for (int i=0; i < id_list.num_ids; i++) {
917             char ed1[50];
918             bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
919                edit_int64(id_list.Id[i], ed1));
920             db_sql_query(db, buf, print_name_handler, NULL);
921          }
922       }
923       if (quit) {
924          return;
925       }
926       if (fix && id_list.num_ids > 0) {
927          printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
928          delete_id_list("DELETE FROM Path WHERE PathId=%s", &id_list);
929       } else {
930          break;                       /* get out if not updating db */
931       }
932       if (!make_id_list(query, &id_list)) {
933          exit(1);
934       }
935    }
936    /* Drop temporary index idx_tmp_name */
937    drop_tmp_idx("idxPIchk", "File");
938 }
939 
eliminate_orphaned_filename_records()940 static void eliminate_orphaned_filename_records()
941 {
942    idx_tmp_name = NULL;
943    /* Check the existence of the required "one column" index */
944    if (!check_idx("FilenameId") )      {
945       if (yes_no(_("Create temporary index? (yes/no): "))) {
946          /* Create temporary index FilenameId */
947          create_tmp_idx("idxFIchk", "File", "FilenameId");
948       }
949    }
950 
951    const char *query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
952                 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
953                 "WHERE File.FilenameId IS NULL LIMIT 300000";
954 
955    printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
956    if (verbose > 1) {
957       printf("%s\n", query);
958    }
959    if (!make_id_list(query, &id_list)) {
960       exit(1);
961    }
962    /* Loop doing 300000 at a time */
963    while (id_list.num_ids != 0) {
964       printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
965       if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
966          for (int i=0; i < id_list.num_ids; i++) {
967             char ed1[50];
968             bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
969                edit_int64(id_list.Id[i], ed1));
970             db_sql_query(db, buf, print_name_handler, NULL);
971          }
972       }
973       if (quit) {
974          return;
975       }
976       if (fix && id_list.num_ids > 0) {
977          printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
978          delete_id_list("DELETE FROM Filename WHERE FilenameId=%s", &id_list);
979       } else {
980          break;                       /* get out if not updating db */
981       }
982       if (!make_id_list(query, &id_list)) {
983          exit(1);
984       }
985    }
986    /* Drop temporary index idx_tmp_name */
987    drop_tmp_idx("idxFIchk", "File");
988 
989 }
990 
eliminate_orphaned_fileset_records()991 static void eliminate_orphaned_fileset_records()
992 {
993    const char *query;
994 
995    printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
996    query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
997            "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
998            "WHERE Job.FileSetId IS NULL";
999    if (verbose > 1) {
1000       printf("%s\n", query);
1001    }
1002    if (!make_id_list(query, &id_list)) {
1003       exit(1);
1004    }
1005    printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
1006    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1007       for (int i=0; i < id_list.num_ids; i++) {
1008          char ed1[50];
1009          bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
1010                       "WHERE FileSetId=%s", edit_int64(id_list.Id[i], ed1));
1011          if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
1012             printf("%s\n", db_strerror(db));
1013          }
1014       }
1015    }
1016    if (quit) {
1017       return;
1018    }
1019    if (fix && id_list.num_ids > 0) {
1020       printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
1021       delete_id_list("DELETE FROM FileSet WHERE FileSetId=%s", &id_list);
1022    }
1023 }
1024 
eliminate_orphaned_client_records()1025 static void eliminate_orphaned_client_records()
1026 {
1027    const char *query;
1028 
1029    printf(_("Checking for orphaned Client entries.\n"));
1030    /* In English:
1031     *   Wiffle through Client for every Client
1032     *   joining with the Job table including every Client even if
1033     *   there is not a match in Job (left outer join), then
1034     *   filter out only those where no Job points to a Client
1035     *   i.e. Job.Client is NULL
1036     */
1037    query = "SELECT Client.ClientId,Client.Name FROM Client "
1038            "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
1039            "WHERE Job.ClientId IS NULL";
1040    if (verbose > 1) {
1041       printf("%s\n", query);
1042    }
1043    if (!make_id_list(query, &id_list)) {
1044       exit(1);
1045    }
1046    printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
1047    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1048       for (int i=0; i < id_list.num_ids; i++) {
1049          char ed1[50];
1050          bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
1051                       "WHERE ClientId=%s", edit_int64(id_list.Id[i], ed1));
1052          if (!db_sql_query(db, buf, print_client_handler, NULL)) {
1053             printf("%s\n", db_strerror(db));
1054          }
1055       }
1056    }
1057    if (quit) {
1058       return;
1059    }
1060    if (fix && id_list.num_ids > 0) {
1061       printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
1062       delete_id_list("DELETE FROM Client WHERE ClientId=%s", &id_list);
1063    }
1064 }
1065 
eliminate_orphaned_job_records()1066 static void eliminate_orphaned_job_records()
1067 {
1068    const char *query;
1069 
1070    printf(_("Checking for orphaned Job entries.\n"));
1071    /* In English:
1072     *   Wiffle through Job for every Job
1073     *   joining with the Client table including every Job even if
1074     *   there is not a match in Client (left outer join), then
1075     *   filter out only those where no Client exists
1076     *   i.e. Client.Name is NULL
1077     */
1078    query = "SELECT Job.JobId,Job.Name FROM Job "
1079            "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
1080            "WHERE Client.Name IS NULL";
1081    if (verbose > 1) {
1082       printf("%s\n", query);
1083    }
1084    if (!make_id_list(query, &id_list)) {
1085       exit(1);
1086    }
1087    printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
1088    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1089       for (int i=0; i < id_list.num_ids; i++) {
1090          char ed1[50];
1091          bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1092                       "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1093          if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1094             printf("%s\n", db_strerror(db));
1095          }
1096       }
1097    }
1098    if (quit) {
1099       return;
1100    }
1101    if (fix && id_list.num_ids > 0) {
1102       printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
1103       delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1104       printf(_("Deleting JobMedia records of orphaned Job records.\n"));
1105       delete_id_list("DELETE FROM JobMedia WHERE JobId=%s", &id_list);
1106       printf(_("Deleting Log records of orphaned Job records.\n"));
1107       delete_id_list("DELETE FROM Log WHERE JobId=%s", &id_list);
1108    }
1109 }
1110 
eliminate_admin_records()1111 static void eliminate_admin_records()
1112 {
1113    const char *query;
1114 
1115    printf(_("Checking for Admin Job entries.\n"));
1116    query = "SELECT Job.JobId FROM Job "
1117            "WHERE Job.Type='D'";
1118    if (verbose > 1) {
1119       printf("%s\n", query);
1120    }
1121    if (!make_id_list(query, &id_list)) {
1122       exit(1);
1123    }
1124    printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
1125    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1126       for (int i=0; i < id_list.num_ids; i++) {
1127          char ed1[50];
1128          bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1129                       "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1130          if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1131             printf("%s\n", db_strerror(db));
1132          }
1133       }
1134    }
1135    if (quit) {
1136       return;
1137    }
1138    if (fix && id_list.num_ids > 0) {
1139       printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
1140       delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1141    }
1142 }
1143 
eliminate_restore_records()1144 static void eliminate_restore_records()
1145 {
1146    const char *query;
1147 
1148    printf(_("Checking for Restore Job entries.\n"));
1149    query = "SELECT Job.JobId FROM Job "
1150            "WHERE Job.Type='R'";
1151    if (verbose > 1) {
1152       printf("%s\n", query);
1153    }
1154    if (!make_id_list(query, &id_list)) {
1155       exit(1);
1156    }
1157    printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
1158    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1159       for (int i=0; i < id_list.num_ids; i++) {
1160          char ed1[50];
1161          bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1162                       "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1163          if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1164             printf("%s\n", db_strerror(db));
1165          }
1166       }
1167    }
1168    if (quit) {
1169       return;
1170    }
1171    if (fix && id_list.num_ids > 0) {
1172       printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
1173       delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1174    }
1175 }
1176 
eliminate_verify_records()1177 static void eliminate_verify_records()
1178 {
1179    const char *query;
1180 
1181    printf(_("Checking for Verify Job entries.\n"));
1182    query = "SELECT Job.JobId FROM Job "
1183            "WHERE Job.Type='V'";
1184    if (verbose > 1) {
1185       printf("%s\n", query);
1186    }
1187    if (!make_id_list(query, &id_list)) {
1188       exit(1);
1189    }
1190    printf(_("Found %d Verify Job records.\n"), id_list.num_ids);
1191    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1192       for (int i=0; i < id_list.num_ids; i++) {
1193          char ed1[50];
1194          bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1195                       "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1196          if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1197             printf("%s\n", db_strerror(db));
1198          }
1199       }
1200    }
1201    if (quit) {
1202       return;
1203    }
1204    if (fix && id_list.num_ids > 0) {
1205       printf(_("Deleting %d Verify Job records.\n"), id_list.num_ids);
1206       delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1207    }
1208 }
1209 
repair_bad_filenames()1210 static void repair_bad_filenames()
1211 {
1212    const char *query;
1213    int i;
1214 
1215    printf(_("Checking for Filenames with a trailing slash\n"));
1216    query = "SELECT FilenameId,Name from Filename "
1217            "WHERE Name LIKE '%/'";
1218    if (verbose > 1) {
1219       printf("%s\n", query);
1220    }
1221    if (!make_id_list(query, &id_list)) {
1222       exit(1);
1223    }
1224    printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
1225    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1226       for (i=0; i < id_list.num_ids; i++) {
1227          char ed1[50];
1228          bsnprintf(buf, sizeof(buf),
1229             "SELECT Name FROM Filename WHERE FilenameId=%s",
1230                 edit_int64(id_list.Id[i], ed1));
1231          if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1232             printf("%s\n", db_strerror(db));
1233          }
1234       }
1235    }
1236    if (quit) {
1237       return;
1238    }
1239    if (fix && id_list.num_ids > 0) {
1240       POOLMEM *name = get_pool_memory(PM_FNAME);
1241       char esc_name[5000];
1242       printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1243       for (i=0; i < id_list.num_ids; i++) {
1244          int len;
1245          char ed1[50];
1246          bsnprintf(buf, sizeof(buf),
1247             "SELECT Name FROM Filename WHERE FilenameId=%s",
1248                edit_int64(id_list.Id[i], ed1));
1249          if (!db_sql_query(db, buf, get_name_handler, name)) {
1250             printf("%s\n", db_strerror(db));
1251          }
1252          /* Strip trailing slash(es) */
1253          for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
1254             {  }
1255          if (len == 0) {
1256             len = 1;
1257             esc_name[0] = ' ';
1258             esc_name[1] = 0;
1259          } else {
1260             name[len-1] = 0;
1261             db_escape_string(NULL, db, esc_name, name, len);
1262          }
1263          bsnprintf(buf, sizeof(buf),
1264             "UPDATE Filename SET Name='%s' WHERE FilenameId=%s",
1265             esc_name, edit_int64(id_list.Id[i], ed1));
1266          if (verbose > 1) {
1267             printf("%s\n", buf);
1268          }
1269          db_sql_query(db, buf, NULL, NULL);
1270       }
1271       free_pool_memory(name);
1272    }
1273 }
1274 
repair_bad_paths()1275 static void repair_bad_paths()
1276 {
1277    const char *query;
1278    int i;
1279 
1280    printf(_("Checking for Paths without a trailing slash\n"));
1281    query = "SELECT PathId,Path from Path "
1282            "WHERE Path NOT LIKE '%/'";
1283    if (verbose > 1) {
1284       printf("%s\n", query);
1285    }
1286    if (!make_id_list(query, &id_list)) {
1287       exit(1);
1288    }
1289    printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1290    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1291       for (i=0; i < id_list.num_ids; i++) {
1292          char ed1[50];
1293          bsnprintf(buf, sizeof(buf),
1294             "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1295          if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1296             printf("%s\n", db_strerror(db));
1297          }
1298       }
1299    }
1300    if (quit) {
1301       return;
1302    }
1303    if (fix && id_list.num_ids > 0) {
1304       POOLMEM *name = get_pool_memory(PM_FNAME);
1305       char esc_name[5000];
1306       printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1307       for (i=0; i < id_list.num_ids; i++) {
1308          int len;
1309          char ed1[50];
1310          bsnprintf(buf, sizeof(buf),
1311             "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1312          if (!db_sql_query(db, buf, get_name_handler, name)) {
1313             printf("%s\n", db_strerror(db));
1314          }
1315          /* Strip trailing blanks */
1316          for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1317             name[len-1] = 0;
1318          }
1319          /* Add trailing slash */
1320          len = pm_strcat(&name, "/");
1321          db_escape_string(NULL, db,  esc_name, name, len);
1322          bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
1323             esc_name, edit_int64(id_list.Id[i], ed1));
1324          if (verbose > 1) {
1325             printf("%s\n", buf);
1326          }
1327          db_sql_query(db, buf, NULL, NULL);
1328       }
1329       free_pool_memory(name);
1330    }
1331 }
1332 
1333 /*
1334  * Gen next input command from the terminal
1335  */
get_cmd(const char * prompt)1336 static char *get_cmd(const char *prompt)
1337 {
1338    static char cmd[1000];
1339 
1340    printf("%s", prompt);
1341    if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
1342       printf("\n");
1343       quit = true;
1344       return NULL;
1345    }
1346    strip_trailing_junk(cmd);
1347    return cmd;
1348 }
1349 
yes_no(const char * prompt)1350 static bool yes_no(const char *prompt)
1351 {
1352    char *cmd;
1353    cmd = get_cmd(prompt);
1354    if (!cmd) {
1355       quit = true;
1356       return false;
1357    }
1358    return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1359 }
1360 
python_set_prog(JCR *,char const *)1361 bool python_set_prog(JCR*, char const*) { return false; }
1362 
1363 /*
1364  * The code below to add indexes is needed only for MySQL, and
1365  *  that to improve the performance.
1366  */
1367 
1368 #define MAXIDX          100
1369 typedef struct s_idx_list {
1370    char *key_name;
1371    int  count_key; /* how many times the index meets *key_name */
1372    int  count_col; /* how many times meets the desired column name */
1373 } IDX_LIST;
1374 
1375 static IDX_LIST idx_list[MAXIDX];
1376 
1377 /*
1378  * Called here with each table index to be added to the list
1379  */
check_idx_handler(void * ctx,int num_fields,char ** row)1380 static int check_idx_handler(void *ctx, int num_fields, char **row)
1381 {
1382    /*
1383     * Table | Non_unique | Key_name | Seq_in_index | Column_name |...
1384     * File  |          0 | PRIMARY  |            1 | FileId      |...
1385     */
1386    char *name, *key_name, *col_name;
1387    int i, len;
1388    int found = false;
1389 
1390    name = (char *)ctx;
1391    key_name = row[2];
1392    col_name = row[4];
1393    for(i = 0; (idx_list[i].key_name != NULL) && (i < MAXIDX); i++) {
1394       if (strcasecmp(idx_list[i].key_name, key_name) == 0 ) {
1395          idx_list[i].count_key++;
1396          found = true;
1397          if (strcasecmp(col_name, name) == 0) {
1398             idx_list[i].count_col++;
1399          }
1400          break;
1401       }
1402    }
1403    /* If the new Key_name, add it to the list */
1404    if (!found) {
1405       len = strlen(key_name) + 1;
1406       idx_list[i].key_name = (char *)malloc(len);
1407       bstrncpy(idx_list[i].key_name, key_name, len);
1408       idx_list[i].count_key = 1;
1409       if (strcasecmp(col_name, name) == 0) {
1410          idx_list[i].count_col = 1;
1411       } else {
1412          idx_list[i].count_col = 0;
1413       }
1414    }
1415    return 0;
1416 }
1417 
1418 /*
1419  * Return TRUE if "one column" index over *col_name exists
1420  */
check_idx(const char * col_name)1421 static bool check_idx(const char *col_name)
1422 {
1423    int i;
1424    int found = false;
1425    const char *query = "SHOW INDEX FROM File";
1426 
1427    if (db_get_type_index(db) != SQL_TYPE_MYSQL) {
1428       return true;
1429    }
1430    /* Continue for MySQL */
1431    memset(&idx_list, 0, sizeof(idx_list));
1432    if (!db_sql_query(db, query, check_idx_handler, (void *)col_name)) {
1433       printf("%s\n", db_strerror(db));
1434    }
1435    for (i = 0; (idx_list[i].key_name != NULL) && (i < MAXIDX) ; i++) {
1436       /*
1437        * NOTE : if (idx_list[i].count_key > 1) then index idx_list[i].key_name is "multiple-column" index
1438        */
1439       if ((idx_list[i].count_key == 1) && (idx_list[i].count_col == 1)) {
1440          /* "one column" index over *col_name found */
1441          found = true;
1442       }
1443    }
1444    if (found) {
1445       if (verbose) {
1446          printf(_("Ok. Index over the %s column already exists and dbcheck will work faster.\n"), col_name);
1447       }
1448    } else {
1449       printf(_("Note. Index over the %s column not found, that can greatly slow down dbcheck.\n"), col_name);
1450    }
1451    return found;
1452 }
1453 
1454 /*
1455  * Create temporary one-column index
1456  */
create_tmp_idx(const char * idx_name,const char * table_name,const char * col_name)1457 static bool create_tmp_idx(const char *idx_name, const char *table_name,
1458                            const char *col_name)
1459 {
1460    idx_tmp_name = NULL;
1461    printf(_("Create temporary index... This may take some time!\n"));
1462    bsnprintf(buf, sizeof(buf), "CREATE INDEX %s ON %s (%s)", idx_name, table_name, col_name);
1463    if (verbose) {
1464       printf("%s\n", buf);
1465    }
1466    if (db_sql_query(db, buf, NULL, NULL)) {
1467       idx_tmp_name = idx_name;
1468       if (verbose) {
1469          printf(_("Temporary index created.\n"));
1470       }
1471    } else {
1472       printf("%s\n", db_strerror(db));
1473       return false;
1474    }
1475    return true;
1476 }
1477 
1478 /*
1479  * Drop temporary index
1480  */
drop_tmp_idx(const char * idx_name,const char * table_name)1481 static bool drop_tmp_idx(const char *idx_name, const char *table_name)
1482 {
1483    if (idx_tmp_name != NULL) {
1484       printf(_("Drop temporary index.\n"));
1485       bsnprintf(buf, sizeof(buf), "DROP INDEX %s ON %s", idx_name, table_name);
1486       if (verbose) {
1487          printf("%s\n", buf);
1488       }
1489       if (!db_sql_query(db, buf, NULL, NULL)) {
1490          printf("%s\n", db_strerror(db));
1491          return false;
1492       } else {
1493          if (verbose) {
1494             printf(_("Temporary index %s deleted.\n"), idx_tmp_name);
1495          }
1496       }
1497    }
1498    idx_tmp_name = NULL;
1499    return true;
1500 }
1501