]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tools/dbcheck.c
Check FileSet & MediaType in restore -- kes12Aug02
[bacula/bacula] / bacula / src / tools / dbcheck.c
1 /*
2  *
3  *  Program to check a Bacula database for consistency and to
4  *   make repairs 
5  *
6  *   Kern E. Sibbald, August 2002
7  *
8  *   Version $Id$
9  *
10  */
11 /*
12    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
13
14    This program is free software; you can redistribute it and/or
15    modify it under the terms of the GNU General Public License as
16    published by the Free Software Foundation; either version 2 of
17    the License, or (at your option) any later version.
18
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22    General Public License for more details.
23
24    You should have received a copy of the GNU General Public
25    License along with this program; if not, write to the Free
26    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27    MA 02111-1307, USA.
28
29  */
30
31 #include "bacula.h"
32 #include "cats/cats.h"
33
34 typedef struct s_id_ctx {
35    uint32_t *Id;                      /* ids to be modified */
36    int num_ids;                       /* ids stored */
37    int max_ids;                       /* size of array */
38    int num_del;                       /* number deleted */
39    int tot_ids;                       /* total to process */
40 } ID_LIST;
41
42 typedef struct s_name_ctx {
43    char **name;                       /* list of names */
44    int num_ids;                       /* ids stored */
45    int max_ids;                       /* size of array */
46    int num_del;                       /* number deleted */
47    int tot_ids;                       /* total to process */
48 } NAME_LIST;
49
50
51
52 /* Global variables */
53 static int fix = FALSE;
54 static int interactive = FALSE;
55 static int verbose = FALSE;
56 static B_DB *db;
57 static ID_LIST id_list;
58 static NAME_LIST name_list;
59 static char buf[2000];
60
61 #define MAX_ID_LIST_LEN 1000000
62
63 /* Forward referenced functions */
64 static int make_id_list(char *query, ID_LIST *id_list);
65 static int delete_id_list(char *query, ID_LIST *id_list);
66 static int make_name_list(char *query, NAME_LIST *name_list);
67 static void print_name_list(NAME_LIST *name_list);
68 static void free_name_list(NAME_LIST *name_list);
69 static void eliminate_duplicate_filenames();
70 static void eliminate_duplicate_paths();
71 static void eliminate_orphaned_jobmedia_records();
72 static void eliminate_orphaned_file_records();
73
74 #ifdef xxxx
75 static void prtit(void *ctx, char *msg)
76 {
77    printf("%s", msg);
78 }
79 #endif
80
81 static void usage()
82 {
83    fprintf(stderr,
84 "Usage: dbcheck [-d debug_level] <working-directory> <bacula-databse> <user> <password>\n"
85 "       -dnn            set debug level to nn\n"
86 "       -f              fix inconsistencies\n"
87 "       -i              interactive mode\n"
88 "       -?              print this message\n\n");
89    exit(1);
90 }
91
92 int main (int argc, char *argv[])
93 {
94    int ch;
95    char *user, *password, *db_name;
96
97    my_name_is(argc, argv, "dbcheck");
98    init_msg(NULL, NULL);              /* setup message handler */
99
100    memset(&id_list, 0, sizeof(id_list));
101    memset(&name_list, 0, sizeof(name_list));
102
103
104    while ((ch = getopt(argc, argv, "d:fi?")) != -1) {
105       switch (ch) {
106          case 'd':                    /* debug level */
107             debug_level = atoi(optarg);
108             if (debug_level <= 0)
109                debug_level = 1; 
110             break;
111
112          case 'f':                    /* fix inconsistencies */
113             fix = TRUE;
114             break;
115
116          case 'i':                    /* interactive */
117             interactive = TRUE;
118             break;
119
120          case '?':
121          default:
122             usage();
123
124       }  
125    }
126    argc -= optind;
127    argv += optind;
128
129    if (argc > 4) {
130       Pmsg0(0, _("Wrong number of arguments.\n"));
131       usage();
132    }
133
134    if (argc < 1) {
135       Pmsg0(0, _("Working directory not supplied.\n"));
136       usage();
137    }
138
139    /* This is needed by SQLite to find the db */
140    working_directory = argv[0];
141    db_name = "bacula";
142    user = db_name;
143    password = "";
144
145    if (argc == 2) {
146       db_name = argv[1];
147       user = db_name;
148    } else if (argc == 3) {
149       db_name = argv[1];
150       user = argv[2];
151    } else if (argc == 4) {
152       db_name = argv[1];
153       user = argv[2];
154       password = argv[3];
155    }
156
157    /* Open database */
158    db = db_init_database(db_name, user, password);
159    if (!db_open_database(db)) {
160       Emsg1(M_FATAL, 0, "%s", db_strerror(db));
161    }
162
163    eliminate_duplicate_filenames();
164
165    eliminate_duplicate_paths();
166
167    eliminate_orphaned_jobmedia_records();
168
169    eliminate_orphaned_file_records();
170
171    db_close_database(db);
172
173    close_msg(NULL);
174    term_msg();
175    return 0;
176 }
177   
178 /*
179  * Called here with each id to be added to the list
180  */
181 static int id_list_handler(void *ctx, int num_fields, char **row)
182 {
183    ID_LIST *lst = (ID_LIST *)ctx;
184
185    if (lst->num_ids == MAX_ID_LIST_LEN) {  
186       return 1;
187    }
188    if (lst->num_ids == lst->max_ids) {
189       if (lst->max_ids == 0) {
190          lst->max_ids = 1000;
191          lst->Id = (uint32_t *)bmalloc(sizeof(uint32_t) * lst->max_ids);
192       } else {
193          lst->max_ids = (lst->max_ids * 3) / 2;
194          lst->Id = (uint32_t *)brealloc(lst->Id, sizeof(uint32_t) * lst->max_ids);
195       }
196    }
197    lst->Id[lst->num_ids++] = (uint32_t)strtod(row[0], NULL);
198    return 0;
199 }
200
201 /*
202  * Construct record id list
203  */
204 static int make_id_list(char *query, ID_LIST *id_list)
205 {
206    id_list->num_ids = 0;
207    id_list->num_del = 0;
208    id_list->tot_ids = 0;
209
210    if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
211       printf("%s", db_strerror(db));
212       return 0;
213    }
214    return 1;
215 }
216
217 /*
218  * Delete all entries in the list 
219  */
220 static int delete_id_list(char *query, ID_LIST *id_list)
221
222    int i;
223
224    for (i=0; i < id_list->num_ids; i++) {
225       sprintf(buf, query, id_list->Id[i]);
226       db_sql_query(db, buf, NULL, NULL);
227    }
228    return 1;
229 }
230
231 /*
232  * Called here with each name to be added to the list
233  */
234 static int name_list_handler(void *ctx, int num_fields, char **row)
235 {
236    NAME_LIST *name = (NAME_LIST *)ctx;
237
238    if (name->num_ids == MAX_ID_LIST_LEN) {  
239       return 1;
240    }
241    if (name->num_ids == name->max_ids) {
242       if (name->max_ids == 0) {
243          name->max_ids = 1000;
244          name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
245       } else {
246          name->max_ids = (name->max_ids * 3) / 2;
247          name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
248       }
249    }
250    name->name[name->num_ids++] = bstrdup(row[0]);
251    return 0;
252 }
253
254
255 /*
256  * Construct name list
257  */
258 static int make_name_list(char *query, NAME_LIST *name_list)
259 {
260    name_list->num_ids = 0;
261    name_list->num_del = 0;
262    name_list->tot_ids = 0;
263
264    if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
265       printf("%s", db_strerror(db));
266       return 0;
267    }
268    return 1;
269 }
270
271
272 /*
273  * Print names in the list
274  */
275 static void print_name_list(NAME_LIST *name_list)
276
277    int i;
278
279    for (i=0; i < name_list->num_ids; i++) {
280       printf("%s\n", name_list->name[i]);
281    }
282 }
283
284
285 /*
286  * Free names in the list
287  */
288 static void free_name_list(NAME_LIST *name_list)
289
290    int i;
291
292    for (i=0; i < name_list->num_ids; i++) {
293       free(name_list->name[i]);
294    }
295    name_list->num_ids = 0;
296 }
297
298 static void eliminate_duplicate_filenames()
299 {
300    char *query;
301
302    printf("Checking for duplicate Filename entries.\n");
303    
304    /* Make list of duplicated names */
305    query = "SELECT Name FROM Filename "
306            "GROUP BY Name HAVING COUNT(FilenameId) > 1";
307    if (!make_name_list(query, &name_list)) {
308       exit(1);
309    }
310    printf("Found %d duplicate Filename records.\n", name_list.num_ids);
311    if (verbose) {
312       print_name_list(&name_list);
313    }
314    if (fix) {
315       /* Loop through list of duplicate names */
316       for (int i=0; i<name_list.num_ids; i++) {
317          /* Get all the Ids of each name */
318          sprintf(buf, "SELECT FilenameId FROM Filename WHERE Name='%s'",
319             name_list.name[i]);
320          if (!make_id_list(buf, &id_list)) {
321             exit(1);
322          }
323          /* Force all records to use the first id then delete the other ids */
324          for (int j=1; j<id_list.num_ids; j++) {
325             sprintf(buf, "UPDATE File SET FilenameId=%u WHERE FilenameId=%u", 
326                id_list.Id[0], id_list.Id[j]);
327             db_sql_query(db, buf, NULL, NULL);
328             sprintf(buf, "DELETE FROM Filename WHERE FilenameId=%u", 
329                id_list.Id[j]);
330             db_sql_query(db, buf, NULL, NULL);
331          }
332       }
333    }
334    free_name_list(&name_list);
335 }
336
337 static void eliminate_duplicate_paths()
338 {
339    char *query;
340
341    printf("Checking for duplicate Path entries.\n");
342    
343    /* Make list of duplicated names */
344    query = "SELECT Path FROM Path "
345            "GROUP BY Path HAVING COUNT(PathId) > 1";
346    if (!make_name_list(query, &name_list)) {
347       exit(1);
348    }
349    printf("Found %d duplicate Path records.\n", name_list.num_ids);
350    if (verbose) {
351       print_name_list(&name_list);
352    }
353    if (fix) {
354       /* Loop through list of duplicate names */
355       for (int i=0; i<name_list.num_ids; i++) {
356          /* Get all the Ids of each name */
357          sprintf(buf, "SELECT PathId FROM Path WHERE Path='%s'",
358             name_list.name[i]);
359          if (!make_id_list(buf, &id_list)) {
360             exit(1);
361          }
362          /* Force all records to use the first id then delete the other ids */
363          for (int j=1; j<id_list.num_ids; j++) {
364             sprintf(buf, "UPDATE File SET PathId=%u WHERE PathId=%u", 
365                id_list.Id[0], id_list.Id[j]);
366             db_sql_query(db, buf, NULL, NULL);
367             sprintf(buf, "DELETE FROM Path WHERE PathId=%u", 
368                id_list.Id[j]);
369             db_sql_query(db, buf, NULL, NULL);
370          }
371       }
372    }
373    free_name_list(&name_list);
374 }
375
376 static void eliminate_orphaned_jobmedia_records()
377 {
378    char *query;
379
380    printf("Checking for orphaned JobMedia entries.\n");
381    query = "SELECT JobMedia.JobId,Job FROM JobMedia LEFT OUTER JOIN Job ON"
382            " (Job.JobId=JobMediaId) GROUP BY MediaId HAVING Job IS NULL";
383    if (!make_id_list(query, &id_list)) {
384       exit(1);
385    }
386    printf("Found %d orphaned JobMedia records.\n", id_list.num_ids);
387    
388    if (fix && id_list.num_ids > 0) {
389       printf("Deleting %d orphaned JobMedia records.\n", id_list.num_ids);
390       delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%u", &id_list);
391    }
392 }
393
394 static void eliminate_orphaned_file_records()
395 {
396    char *query;
397
398    printf("Checking for orphaned File entries.\n");
399    query = "SELECT FileId,Job FROM File LEFT OUTER JOIN Job ON"
400            " (Job.JobId=File.JobId) GROUP BY FileId HAVING Job IS NULL";
401    if (!make_id_list(query, &id_list)) {
402       exit(1);
403    }
404    printf("Found %d orphaned File records.\n", id_list.num_ids);
405    
406    if (fix && id_list.num_ids > 0) {
407       printf("Deleting %d orphaned File records.\n", id_list.num_ids);
408       delete_id_list("DELETE FROM File WHERE FileIdId=%u", &id_list);
409    }
410 }