/*
Bacula® - The Network Backup Solution
- Copyright (C) 2002-2009 Free Software Foundation Europe e.V.
+ Copyright (C) 2002-2011 Free Software Foundation Europe e.V.
The main author of Bacula is Kern Sibbald, with contributions from
many others, a complete list can be found in the file AUTHORS.
This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
+ modify it under the terms of version three of the GNU Affero General Public
License as published by the Free Software Foundation and included
in the file LICENSE.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*
* Kern E. Sibbald, August 2002
*
- * Version $Id$
- *
*/
#include "bacula.h"
#include "cats/cats.h"
+#include "cats/sql_glue.h"
#include "lib/runscript.h"
#include "dird/dird_conf.h"
extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
-/* Dummy functions */
-int generate_daemon_event(JCR *jcr, const char *event)
+/*
+ * Dummy functions
+ */
+int generate_daemon_event(JCR *jcr, const char *event)
{ return 1; }
typedef struct s_id_ctx {
int tot_ids; /* total to process */
} NAME_LIST;
-/* Global variables */
+/*
+ * Global variables
+ */
static bool fix = false;
static bool batch = false;
static B_DB *db;
static char buf[20000];
static bool quit = false;
static CONFIG *config;
+static const char *idx_tmp_name;
#define MAX_ID_LIST_LEN 10000000
-/* Forward referenced functions */
+/*
+ * Forward referenced functions
+ */
+static void print_catalog_details(CAT *catalog, const char *working_dir);
static int make_id_list(const char *query, ID_LIST *id_list);
static int delete_id_list(const char *query, ID_LIST *id_list);
static int make_name_list(const char *query, NAME_LIST *name_list);
static void do_interactive_mode();
static bool yes_no(const char *prompt);
static bool check_idx(const char *col_name);
-static bool create_tmp_idx(const char *idx_name, const char *table_name,
+static bool create_tmp_idx(const char *idx_name, const char *table_name,
const char *col_name);
static bool drop_tmp_idx(const char *idx_name, const char *table_name);
-#ifdef HAVE_MYSQL
static int check_idx_handler(void *ctx, int num_fields, char **row);
-#endif
-
-
-/* Global variables */
-static const char *idx_tmp_name;
static void usage()
{
" -d <nn> set debug level to <nn>\n"
" -dt print a timestamp in debug output\n"
" -f fix inconsistencies\n"
+" -t test if client library is thread-safe\n"
" -v verbose\n"
" -? print this message\n\n");
exit(1);
setlocale(LC_ALL, "");
bindtextdomain("bacula", LOCALEDIR);
textdomain("bacula");
+ lmgr_init_thread();
my_name_is(argc, argv, "dbcheck");
- init_msg(NULL, NULL); /* setup message handler */
+ init_msg(NULL, NULL); /* setup message handler */
memset(&id_list, 0, sizeof(id_list));
memset(&name_list, 0, sizeof(name_list));
case 'B':
print_catalog = true; /* get catalog information from config */
break;
-
case 'b': /* batch */
batch = true;
break;
-
case 'C': /* CatalogName */
catalogname = optarg;
break;
-
case 'c': /* configfile */
configfile = optarg;
break;
-
case 'd': /* debug level */
if (*optarg == 't') {
dbg_timestamp = true;
}
}
break;
-
case 'f': /* fix inconsistencies */
fix = true;
break;
-
case 'v':
verbose++;
break;
-
case '?':
default:
usage();
}
set_working_directory(director->working_directory);
- /* Print catalog information and exit (-B) */
+ /*
+ * Print catalog information and exit (-B)
+ */
if (print_catalog) {
- POOLMEM *buf = get_pool_memory(PM_MESSAGE);
- printf("%sdb_type=%s\nworking_dir=%s\n", catalog->display(buf),
- db_get_type(), working_directory);
- free_pool_memory(buf);
+ print_catalog_details(catalog, director->working_directory);
exit(0);
}
usage();
}
- /* This is needed by SQLite to find the db */
+ /*
+ * This is needed by SQLite to find the db
+ */
working_directory = argv[0];
db_name = "bacula";
user = db_name;
}
}
- /* Open database */
- db = db_init_database(NULL, db_name, user, password, dbhost, dbport, NULL, 0);
+ /*
+ * Open database
+ */
+ db = db_init_database(NULL, NULL, db_name, user, password, dbhost, dbport, NULL, false, false);
if (!db_open_database(NULL, db)) {
Emsg1(M_FATAL, 0, "%s", db_strerror(db));
return 1;
}
+ /*
+ * Drop temporary index idx_tmp_name if it already exists
+ */
+ drop_tmp_idx("idxPIchk", "File");
+
if (batch) {
repair_bad_paths();
repair_bad_filenames();
do_interactive_mode();
}
+ /*
+ * Drop temporary index idx_tmp_name
+ */
+ drop_tmp_idx("idxPIchk", "File");
+
db_close_database(NULL, db);
close_msg(NULL);
term_msg();
+ lmgr_cleanup_main();
return 0;
}
+static void print_catalog_details(CAT *catalog, const char *working_dir)
+{
+ POOLMEM *catalog_details = get_pool_memory(PM_MESSAGE);
+
+ /*
+ * Instantiate a B_DB class and see what db_type gets assigned to it.
+ */
+ db = db_init_database(NULL, catalog->db_driver, catalog->db_name, catalog->db_user,
+ catalog->db_password, catalog->db_address,
+ catalog->db_port, catalog->db_socket,
+ catalog->mult_db_connections,
+ catalog->disable_batch_insert);
+ if (db) {
+ printf("%sdb_type=%s\nworking_dir=%s\n", catalog->display(catalog_details),
+ db->db_get_type(), working_directory);
+ db_close_database(NULL, db);
+ }
+ free_pool_memory(catalog_details);
+}
+
static void do_interactive_mode()
{
const char *cmd;
else
printf(_(" Verbose is off.\n"));
- printf(_("Please select the fuction you want to perform.\n"));
+ printf(_("Please select the function you want to perform.\n"));
while (!quit) {
if (fix) {
static int get_name_handler(void *ctx, int num_fields, char **row)
{
- POOLMEM *buf = (POOLMEM *)ctx;
+ POOLMEM *name = (POOLMEM *)ctx;
+
if (row[0]) {
- pm_strcpy(&buf, row[0]);
+ pm_strcpy(&name, row[0]);
}
return 0;
}
return 0;
}
-
static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
{
printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
return 0;
}
-
/*
* Called here with each id to be added to the list
*/
return 0;
}
-
/*
* Construct name list
*/
}
}
-
/*
* Free names in the list
*/
printf(_("Checking for duplicate Filename entries.\n"));
- /* Make list of duplicated names */
+ /*
+ * Make list of duplicated names
+ */
query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY Name "
"HAVING count(Name) > 1";
return;
}
if (fix) {
- /* Loop through list of duplicate names */
+ /*
+ * Loop through list of duplicate names
+ */
for (int i=0; i<name_list.num_ids; i++) {
- /* Get all the Ids of each name */
+ /*
+ * Get all the Ids of each name
+ */
db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
if (verbose > 1) {
if (verbose) {
printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
}
- /* Force all records to use the first id then delete the other ids */
+ /*
+ * Force all records to use the first id then delete the other ids
+ */
for (int j=1; j<id_list.num_ids; j++) {
char ed1[50], ed2[50];
bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%s WHERE FilenameId=%s",
printf(_("Checking for duplicate Path entries.\n"));
- /* Make list of duplicated names */
-
+ /*
+ * Make list of duplicated names
+ */
query = "SELECT Path, count(Path) as Count FROM Path "
"GROUP BY Path HAVING count(Path) > 1";
return;
}
if (fix) {
- /* Loop through list of duplicate names */
+ /*
+ * Loop through list of duplicate names
+ */
for (int i=0; i<name_list.num_ids; i++) {
- /* Get all the Ids of each name */
+ /*
+ * Get all the Ids of each name
+ */
db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
if (verbose > 1) {
if (verbose) {
printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
}
- /* Force all records to use the first id then delete the other ids */
+ /*
+ * Force all records to use the first id then delete the other ids
+ */
for (int j=1; j<id_list.num_ids; j++) {
char ed1[50], ed2[50];
bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%s WHERE PathId=%s",
if (!make_id_list(query, &id_list)) {
exit(1);
}
- /* Loop doing 300000 at a time */
+ /*
+ * Loop doing 300000 at a time
+ */
while (id_list.num_ids != 0) {
printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
char ed1[50];
bsnprintf(buf, sizeof(buf),
"SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
-"WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId",
+"WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId",
edit_int64(id_list.Id[i], ed1));
if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
printf("%s\n", db_strerror(db));
if (!make_id_list(query, &id_list)) {
exit(1);
}
- /* Loop doing 300000 at a time */
+ /*
+ * Loop doing 300000 at a time
+ */
while (id_list.num_ids != 0) {
printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
char ed1[50];
bsnprintf(buf, sizeof(buf),
"SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
-"WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
+"WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
edit_int64(id_list.Id[i], ed1));
if (!db_sql_query(db, buf, print_file_handler, NULL)) {
printf("%s\n", db_strerror(db));
static void eliminate_orphaned_path_records()
{
+ db_int64_ctx lctx;
+ lctx.count=0;
+ db_sql_query(db, "SELECT 1 FROM Job WHERE HasCache=1 LIMIT 1",
+ db_int64_handler, &lctx);
+
+ if (lctx.count == 1) {
+ printf(_("Pruning orphaned Path entries isn't possible when using BVFS.\n"));
+ return;
+ }
+
idx_tmp_name = NULL;
- /* check the existence of the required "one column" index */
+ /*
+ * Check the existence of the required "one column" index
+ */
if (!check_idx("PathId")) {
if (yes_no(_("Create temporary index? (yes/no): "))) {
- /* create temporary index PathId */
+ /*
+ * create temporary index PathId
+ */
create_tmp_idx("idxPIchk", "File", "PathId");
}
}
if (!make_id_list(query, &id_list)) {
exit(1);
}
- /* Loop doing 300000 at a time */
+ /*
+ * Loop doing 300000 at a time
+ */
while (id_list.num_ids != 0) {
printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
for (int i=0; i < id_list.num_ids; i++) {
char ed1[50];
- bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
+ bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
edit_int64(id_list.Id[i], ed1));
db_sql_query(db, buf, print_name_handler, NULL);
}
if (!make_id_list(query, &id_list)) {
exit(1);
}
- }
- /* drop temporary index idx_tmp_name */
+ }
+ /*
+ * Drop temporary index idx_tmp_name
+ */
drop_tmp_idx("idxPIchk", "File");
}
static void eliminate_orphaned_filename_records()
{
idx_tmp_name = NULL;
- /* check the existence of the required "one column" index */
+ /*
+ * Check the existence of the required "one column" index
+ */
if (!check_idx("FilenameId") ) {
if (yes_no(_("Create temporary index? (yes/no): "))) {
- /* create temporary index FilenameId */
+ /*
+ * Create temporary index FilenameId
+ */
create_tmp_idx("idxFIchk", "File", "FilenameId");
}
}
if (!make_id_list(query, &id_list)) {
exit(1);
}
- /* Loop doing 300000 at a time */
+ /*
+ * Loop doing 300000 at a time
+ */
while (id_list.num_ids != 0) {
printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
for (int i=0; i < id_list.num_ids; i++) {
char ed1[50];
- bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
+ bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
edit_int64(id_list.Id[i], ed1));
db_sql_query(db, buf, print_name_handler, NULL);
}
exit(1);
}
}
- /* drop temporary index idx_tmp_name */
+ /*
+ * Drop temporary index idx_tmp_name
+ */
drop_tmp_idx("idxFIchk", "File");
}
const char *query;
printf(_("Checking for orphaned Client entries.\n"));
- /* In English:
+ /*
+ * In English:
* Wiffle through Client for every Client
* joining with the Job table including every Client even if
* there is not a match in Job (left outer join), then
const char *query;
printf(_("Checking for orphaned Job entries.\n"));
- /* In English:
+ /*
+ * In English:
* Wiffle through Job for every Job
* joining with the Client table including every Job even if
* there is not a match in Client (left outer join), then
}
}
-
static void eliminate_admin_records()
{
const char *query;
}
}
-
-
-
static void repair_bad_filenames()
{
const char *query;
for (i=0; i < id_list.num_ids; i++) {
char ed1[50];
bsnprintf(buf, sizeof(buf),
- "SELECT Name FROM Filename WHERE FilenameId=%s",
+ "SELECT Name FROM Filename WHERE FilenameId=%s",
edit_int64(id_list.Id[i], ed1));
if (!db_sql_query(db, buf, print_name_handler, NULL)) {
printf("%s\n", db_strerror(db));
int len;
char ed1[50];
bsnprintf(buf, sizeof(buf),
- "SELECT Name FROM Filename WHERE FilenameId=%s",
+ "SELECT Name FROM Filename WHERE FilenameId=%s",
edit_int64(id_list.Id[i], ed1));
if (!db_sql_query(db, buf, get_name_handler, name)) {
printf("%s\n", db_strerror(db));
}
- /* Strip trailing slash(es) */
+ /*
+ * Strip trailing slash(es)
+ */
for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
{ }
if (len == 0) {
}
db_sql_query(db, buf, NULL, NULL);
}
+ free_pool_memory(name);
}
}
if (!db_sql_query(db, buf, get_name_handler, name)) {
printf("%s\n", db_strerror(db));
}
- /* Strip trailing blanks */
+ /*
+ * Strip trailing blanks
+ */
for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
name[len-1] = 0;
}
- /* Add trailing slash */
+ /*
+ * Add trailing slash
+ */
len = pm_strcat(&name, "/");
db_escape_string(NULL, db, esc_name, name, len);
bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
}
db_sql_query(db, buf, NULL, NULL);
}
+ free_pool_memory(name);
}
}
-
/*
* Gen next input command from the terminal
*/
bool python_set_prog(JCR*, char const*) { return false; }
-
/*
* The code below to add indexes is needed only for MySQL, and
* that to improve the performance.
*/
-#ifdef HAVE_MYSQL
#define MAXIDX 100
typedef struct s_idx_list {
char *key_name;
static IDX_LIST idx_list[MAXIDX];
-/*
+/*
* Called here with each table index to be added to the list
*/
static int check_idx_handler(void *ctx, int num_fields, char **row)
{
- /* Table | Non_unique | Key_name | Seq_in_index | Column_name |...
- * File | 0 | PRIMARY | 1 | FileId |...
+ /*
+ * Table | Non_unique | Key_name | Seq_in_index | Column_name |...
+ * File | 0 | PRIMARY | 1 | FileId |...
*/
char *name, *key_name, *col_name;
int i, len;
int found = false;
+
name = (char *)ctx;
key_name = row[2];
col_name = row[4];
break;
}
}
- /* if the new Key_name, add it to the list */
+ /*
+ * If the new Key_name, add it to the list
+ */
if (!found) {
len = strlen(key_name) + 1;
idx_list[i].key_name = (char *)malloc(len);
}
return 0;
}
-#endif
/*
* Return TRUE if "one column" index over *col_name exists
*/
static bool check_idx(const char *col_name)
{
-#ifdef HAVE_MYSQL
int i;
int found = false;
-
- memset(&idx_list, 0, sizeof(idx_list));
const char *query = "SHOW INDEX FROM File";
- if (!db_sql_query(db, query, check_idx_handler, (void *)col_name)) {
- printf("%s\n", db_strerror(db));
- }
- for(i = 0; (idx_list[i].key_name != NULL) && (i < MAXIDX) ; i++) {
- /* NOTE : if (idx_list[i].count_key > 1) then index idx_list[i].key_name is "multiple-column" index */
- if ((idx_list[i].count_key == 1) && (idx_list[i].count_col == 1)) {
- /* "one column" index over *col_name found */
- found = true;
+ switch (db_get_type_index(db)) {
+ case SQL_TYPE_MYSQL:
+ memset(&idx_list, 0, sizeof(idx_list));
+ if (!db_sql_query(db, query, check_idx_handler, (void *)col_name)) {
+ printf("%s\n", db_strerror(db));
}
- }
- if (found) {
- if (verbose) {
- printf(_("Ok. Index over the %s column already exists and dbcheck will work faster.\n"), col_name);
+ for (i = 0; (idx_list[i].key_name != NULL) && (i < MAXIDX) ; i++) {
+ /*
+ * NOTE : if (idx_list[i].count_key > 1) then index idx_list[i].key_name is "multiple-column" index
+ */
+ if ((idx_list[i].count_key == 1) && (idx_list[i].count_col == 1)) {
+ /*
+ * "one column" index over *col_name found
+ */
+ found = true;
+ }
}
- } else {
- printf(_("Note. Index over the %s column not found, that can greatly slow down dbcheck.\n"), col_name);
+ if (found) {
+ if (verbose) {
+ printf(_("Ok. Index over the %s column already exists and dbcheck will work faster.\n"), col_name);
+ }
+ } else {
+ printf(_("Note. Index over the %s column not found, that can greatly slow down dbcheck.\n"), col_name);
+ }
+ return found;
+ default:
+ return true;
}
-
- return found;
-#else
- return true;
-#endif
}
/*
* Create temporary one-column index
*/
-static bool create_tmp_idx(const char *idx_name, const char *table_name,
- const char *col_name)
+static bool create_tmp_idx(const char *idx_name, const char *table_name,
+ const char *col_name)
{
idx_tmp_name = NULL;
printf(_("Create temporary index... This may take some time!\n"));
- bsnprintf(buf, sizeof(buf), "CREATE INDEX %s ON %s (%s)", idx_name, table_name, col_name);
+ bsnprintf(buf, sizeof(buf), "CREATE INDEX %s ON %s (%s)", idx_name, table_name, col_name);
if (verbose) {
printf("%s\n", buf);
}