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