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 * Bacula Catalog Database interface routines
21 *
22 * Almost generic set of SQL database interface routines
23 * (with a little more work)
24 * SQL engine specific routines are in mysql.c, postgresql.c,
25 * sqlite.c, ...
26 *
27 * Written by Kern Sibbald, March 2000
28 *
29 * Note: at one point, this file was changed to class based by a certain
30 * programmer, and other than "wrapping" in a class, which is a trivial
31 * change for a C++ programmer, nothing substantial was done, yet all the
32 * code was recommitted under this programmer's name. Consequently, we
33 * undo those changes here.
34 */
35
36 #include "bacula.h"
37
38 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
39
40 #include "cats.h"
41
42 /* Forward referenced subroutines */
43 void print_dashes(BDB *mdb);
44 void print_result(BDB *mdb);
45
dbid_list()46 dbid_list::dbid_list()
47 {
48 memset(this, 0, sizeof(dbid_list));
49 max_ids = 1000;
50 DBId = (DBId_t *)malloc(max_ids * sizeof(DBId_t));
51 num_ids = num_seen = tot_ids = 0;
52 PurgedFiles = NULL;
53 }
54
~dbid_list()55 dbid_list::~dbid_list()
56 {
57 free(DBId);
58 }
59
60 /*
61 * Called here to retrieve an string list from the database
62 */
db_string_list_handler(void * ctx,int num_fields,char ** row)63 int db_string_list_handler(void *ctx, int num_fields, char **row)
64 {
65 alist **val = (alist **)ctx;
66
67 if (row[0]) {
68 (*val)->append(bstrdup(row[0]));
69 }
70
71 return 0;
72 }
73
74 /*
75 * Called here to retrieve an integer from the database
76 */
db_int_handler(void * ctx,int num_fields,char ** row)77 int db_int_handler(void *ctx, int num_fields, char **row)
78 {
79 uint32_t *val = (uint32_t *)ctx;
80
81 Dmsg1(800, "int_handler starts with row pointing at %x\n", row);
82
83 if (row[0]) {
84 Dmsg1(800, "int_handler finds '%s'\n", row[0]);
85 *val = str_to_int64(row[0]);
86 } else {
87 Dmsg0(800, "int_handler finds zero\n");
88 *val = 0;
89 }
90 Dmsg0(800, "int_handler finishes\n");
91 return 0;
92 }
93
94 /*
95 * Called here to retrieve a 32/64 bit integer from the database.
96 * The returned integer will be extended to 64 bit.
97 */
db_int64_handler(void * ctx,int num_fields,char ** row)98 int db_int64_handler(void *ctx, int num_fields, char **row)
99 {
100 db_int64_ctx *lctx = (db_int64_ctx *)ctx;
101
102 if (row[0]) {
103 lctx->value = str_to_int64(row[0]);
104 lctx->count++;
105 }
106 return 0;
107 }
108
109 /*
110 * Called here to retrieve a btime from the database.
111 * The returned integer will be extended to 64 bit.
112 */
db_strtime_handler(void * ctx,int num_fields,char ** row)113 int db_strtime_handler(void *ctx, int num_fields, char **row)
114 {
115 db_int64_ctx *lctx = (db_int64_ctx *)ctx;
116
117 if (row[0]) {
118 lctx->value = str_to_utime(row[0]);
119 lctx->count++;
120 }
121 return 0;
122 }
123
124 /*
125 * Use to build a comma separated list of values from a query. "10,20,30"
126 */
db_list_handler(void * ctx,int num_fields,char ** row)127 int db_list_handler(void *ctx, int num_fields, char **row)
128 {
129 db_list_ctx *lctx = (db_list_ctx *)ctx;
130 if (num_fields == 1 && row[0]) {
131 lctx->add(row[0]);
132 }
133 return 0;
134 }
135
136 /*
137 * specific context passed from bdb_check_max_connections to
138 * db_max_connections_handler.
139 */
140 struct max_connections_context {
141 BDB *db;
142 uint32_t nr_connections;
143 };
144
145 /*
146 * Called here to retrieve max_connections from db
147 */
db_max_connections_handler(void * ctx,int num_fields,char ** row)148 static int db_max_connections_handler(void *ctx, int num_fields, char **row)
149 {
150 struct max_connections_context *context;
151 uint32_t index;
152
153 context = (struct max_connections_context *)ctx;
154 switch (context->db->bdb_get_type_index()) {
155 case SQL_TYPE_MYSQL:
156 index = 1;
157 default:
158 index = 0;
159 }
160
161 if (row[index]) {
162 context->nr_connections = str_to_int64(row[index]);
163 } else {
164 Dmsg0(800, "int_handler finds zero\n");
165 context->nr_connections = 0;
166 }
167 return 0;
168 }
169
BDB()170 BDB::BDB()
171 {
172 init_acl();
173 acl_join = get_pool_memory(PM_MESSAGE);
174 acl_where = get_pool_memory(PM_MESSAGE);
175 }
176
~BDB()177 BDB::~BDB()
178 {
179 free_acl();
180 free_pool_memory(acl_join);
181 free_pool_memory(acl_where);
182 }
183
184 /* Get the WHERE section of a query that permits to respect
185 * the console ACLs.
186 *
187 * get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_CLIENT), true)
188 * -> WHERE Job.Name IN ('a', 'b', 'c') AND Client.Name IN ('d', 'e')
189 *
190 * get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_CLIENT), false)
191 * -> AND Job.Name IN ('a', 'b', 'c') AND Client.Name IN ('d', 'e')
192 */
get_acls(int tables,bool where)193 char *BDB::get_acls(int tables, bool where /* use WHERE or AND */)
194 {
195 POOL_MEM tmp;
196 pm_strcpy(acl_where, "");
197
198 for (int i=0 ; i < DB_ACL_LAST; i++) {
199 if (tables & DB_ACL_BIT(i)) {
200 pm_strcat(acl_where, get_acl((DB_ACL_t)i, where));
201 where = acl_where[0] == 0 && where;
202 }
203 }
204 return acl_where;
205 }
206
207 /* Create the JOIN string that will help to filter queries results */
get_acl_join_filter(int tables)208 char *BDB::get_acl_join_filter(int tables)
209 {
210 POOL_MEM tmp;
211 pm_strcpy(acl_join, "");
212
213 if (tables & DB_ACL_BIT(DB_ACL_JOB)) {
214 Mmsg(tmp, " JOIN Job USING (JobId) ");
215 pm_strcat(acl_join, tmp);
216 }
217 if (tables & (DB_ACL_BIT(DB_ACL_CLIENT) | DB_ACL_BIT(DB_ACL_RCLIENT) | DB_ACL_BIT(DB_ACL_BCLIENT))) {
218 Mmsg(tmp, " JOIN Client USING (ClientId) ");
219 pm_strcat(acl_join, tmp);
220 }
221 if (tables & DB_ACL_BIT(DB_ACL_POOL)) {
222 Mmsg(tmp, " JOIN Pool USING (PoolId) ");
223 pm_strcat(acl_join, tmp);
224 }
225 if (tables & DB_ACL_BIT(DB_ACL_PATH)) {
226 Mmsg(tmp, " JOIN Path USING (PathId) ");
227 pm_strcat(acl_join, tmp);
228 }
229 if (tables & DB_ACL_BIT(DB_ACL_LOG)) {
230 Mmsg(tmp, " JOIN Log USING (JobId) ");
231 pm_strcat(acl_join, tmp);
232 }
233 if (tables & DB_ACL_BIT(DB_ACL_FILESET)) {
234 Mmsg(tmp, " LEFT JOIN FileSet USING (FileSetId) ");
235 pm_strcat(acl_join, tmp);
236 }
237 return acl_join;
238 }
239
240 /* Intialize the ACL list */
init_acl()241 void BDB::init_acl()
242 {
243 for(int i=0; i < DB_ACL_LAST; i++) {
244 acls[i] = NULL;
245 }
246 }
247
248 /* Free ACL list */
free_acl()249 void BDB::free_acl()
250 {
251 for(int i=0; i < DB_ACL_LAST; i++) {
252 free_and_null_pool_memory(acls[i]);
253 }
254 }
255
256 /* Get ACL for a given type */
get_acl(DB_ACL_t type,bool where)257 const char *BDB::get_acl(DB_ACL_t type, bool where /* display WHERE or AND */)
258 {
259 if (!acls[type]) {
260 return "";
261 }
262 strcpy(acls[type], where?" WHERE ":" AND ");
263 acls[type][7] = ' ' ; /* replace \0 by ' ' */
264 return acls[type];
265 }
266
267 /* Keep UAContext ACLs in our structure for further SQL queries */
set_acl(JCR * jcr,DB_ACL_t type,alist * list,alist * list2)268 void BDB::set_acl(JCR *jcr, DB_ACL_t type, alist *list, alist *list2)
269 {
270 /* If the list is present, but we authorize everything */
271 if (list && list->size() == 1 && strcasecmp((char*)list->get(0), "*all*") == 0) {
272 return;
273 }
274
275 /* If the list is present, but we authorize everything */
276 if (list2 && list2->size() == 1 && strcasecmp((char*)list2->get(0), "*all*") == 0) {
277 return;
278 }
279
280 POOLMEM *tmp = get_pool_memory(PM_FNAME);
281 POOLMEM *where = get_pool_memory(PM_FNAME);
282
283 *where = 0;
284 *tmp = 0;
285
286 /* For clients, we can have up to 2 lists */
287 escape_acl_list(jcr, &tmp, list);
288 escape_acl_list(jcr, &tmp, list2);
289
290 switch(type) {
291 case DB_ACL_JOB:
292 Mmsg(where, " AND Job.Name IN (%s) ", tmp);
293 break;
294 case DB_ACL_CLIENT:
295 Mmsg(where, " AND Client.Name IN (%s) ", tmp);
296 break;
297 case DB_ACL_BCLIENT:
298 Mmsg(where, " AND Client.Name IN (%s) ", tmp);
299 break;
300 case DB_ACL_RCLIENT:
301 Mmsg(where, " AND Client.Name IN (%s) ", tmp);
302 break;
303 case DB_ACL_FILESET:
304 Mmsg(where, " AND (FileSetId = 0 OR FileSet.FileSet IN (%s)) ", tmp);
305 break;
306 case DB_ACL_POOL:
307 Mmsg(where, " AND (PoolId = 0 OR Pool.Name IN (%s)) ", tmp);
308 break;
309 default:
310 break;
311 }
312 acls[type] = where;
313 free_pool_memory(tmp);
314 }
315
316 /* Convert a ACL list to a SQL IN() list */
escape_acl_list(JCR * jcr,POOLMEM ** escaped_list,alist * lst)317 char *BDB::escape_acl_list(JCR *jcr, POOLMEM **escaped_list, alist *lst)
318 {
319 char *elt;
320 int len;
321 POOL_MEM tmp;
322
323 if (!lst) {
324 return *escaped_list; /* TODO: check how we handle the empty list */
325
326 /* List is empty, reject everything */
327 } else if (lst->size() == 0) {
328 Mmsg(escaped_list, "''");
329 return *escaped_list;
330 }
331
332 foreach_alist(elt, lst) {
333 if (elt && *elt) {
334 len = strlen(elt);
335 /* Escape + ' ' */
336 tmp.check_size(2 * len + 2 + 2);
337
338 pm_strcpy(tmp, "'");
339 bdb_lock();
340 bdb_escape_string(jcr, tmp.c_str() + 1 , elt, len);
341 bdb_unlock();
342 pm_strcat(tmp, "'");
343
344 if (*escaped_list[0]) {
345 pm_strcat(escaped_list, ",");
346 }
347
348 pm_strcat(escaped_list, tmp.c_str());
349 }
350 }
351 return *escaped_list;
352 }
353
354 /*
355 * Check catalog max_connections setting
356 */
bdb_check_max_connections(JCR * jcr,uint32_t max_concurrent_jobs)357 bool BDB::bdb_check_max_connections(JCR *jcr, uint32_t max_concurrent_jobs)
358 {
359 struct max_connections_context context;
360
361 /* Without Batch insert, no need to verify max_connections */
362 if (!batch_insert_available())
363 return true;
364
365 context.db = this;
366 context.nr_connections = 0;
367
368 /* Check max_connections setting */
369 if (!bdb_sql_query(sql_get_max_connections[bdb_get_type_index()],
370 db_max_connections_handler, &context)) {
371 Jmsg(jcr, M_ERROR, 0, "Can't verify max_connections settings %s", errmsg);
372 return false;
373 }
374 if (context.nr_connections && max_concurrent_jobs && max_concurrent_jobs > context.nr_connections) {
375 Mmsg(errmsg,
376 _("Potential performance problem:\n"
377 "max_connections=%d set for %s database \"%s\" should be larger than Director's "
378 "MaxConcurrentJobs=%d\n"),
379 context.nr_connections, bdb_get_engine_name(), get_db_name(), max_concurrent_jobs);
380 Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
381 return false;
382 }
383
384 return true;
385 }
386
387 /* NOTE!!! The following routines expect that the
388 * calling subroutine sets and clears the mutex
389 */
390
391 /* Check that the tables correspond to the version we want */
bdb_check_version(JCR * jcr)392 bool BDB::bdb_check_version(JCR *jcr)
393 {
394 uint32_t bacula_db_version = 0;
395 const char *query = "SELECT VersionId FROM Version";
396
397 bacula_db_version = 0;
398 if (!bdb_sql_query(query, db_int_handler, (void *)&bacula_db_version)) {
399 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
400 return false;
401 }
402 if (bacula_db_version != BDB_VERSION) {
403 Mmsg(errmsg, "Version error for database \"%s\". Wanted %d, got %d\n",
404 get_db_name(), BDB_VERSION, bacula_db_version);
405 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
406 return false;
407 }
408 return true;
409 }
410
411 /*
412 * Utility routine for queries. The database MUST be locked before calling here.
413 * Returns: 0 on failure
414 * 1 on success
415 */
QueryDB(JCR * jcr,char * cmd,const char * file,int line)416 bool BDB::QueryDB(JCR *jcr, char *cmd, const char *file, int line)
417 {
418 sql_free_result();
419 if (!sql_query(cmd, QF_STORE_RESULT)) {
420 m_msg(file, line, &errmsg, _("query %s failed:\n%s\n"), cmd, sql_strerror());
421 if (use_fatal_jmsg()) {
422 j_msg(file, line, jcr, M_FATAL, 0, "%s", errmsg);
423 }
424 if (verbose) {
425 j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
426 }
427 return false;
428 }
429
430 return true;
431 }
432
433 /*
434 * Utility routine to do inserts
435 * Returns: 0 on failure
436 * 1 on success
437 */
InsertDB(JCR * jcr,char * cmd,const char * file,int line)438 bool BDB::InsertDB(JCR *jcr, char *cmd, const char *file, int line)
439 {
440 if (!sql_query(cmd)) {
441 m_msg(file, line, &errmsg, _("insert %s failed:\n%s\n"), cmd, sql_strerror());
442 if (use_fatal_jmsg()) {
443 j_msg(file, line, jcr, M_FATAL, 0, "%s", errmsg);
444 }
445 if (verbose) {
446 j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
447 }
448 return false;
449 }
450 int num_rows = sql_affected_rows();
451 if (num_rows != 1) {
452 char ed1[30];
453 m_msg(file, line, &errmsg, _("Insertion problem: affected_rows=%s\n"),
454 edit_uint64(num_rows, ed1));
455 if (verbose) {
456 j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
457 }
458 return false;
459 }
460 changes++;
461 return true;
462 }
463
464 /* Utility routine for updates.
465 * Returns: false on failure
466 * true on success
467 *
468 * Some UPDATE queries must update record(s), other queries might not update
469 * anything.
470 */
UpdateDB(JCR * jcr,char * cmd,bool can_be_empty,const char * file,int line)471 bool BDB::UpdateDB(JCR *jcr, char *cmd, bool can_be_empty,
472 const char *file, int line)
473 {
474 if (!sql_query(cmd)) {
475 m_msg(file, line, &errmsg, _("update %s failed:\n%s\n"), cmd, sql_strerror());
476 j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg);
477 if (verbose) {
478 j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
479 }
480 return false;
481 }
482 int num_rows = sql_affected_rows();
483 if ((num_rows == 0 && !can_be_empty) || num_rows < 0) {
484 char ed1[30];
485 m_msg(file, line, &errmsg, _("Update failed: affected_rows=%s for %s\n"),
486 edit_uint64(num_rows, ed1), cmd);
487 if (verbose) {
488 // j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
489 }
490 return false;
491 }
492 changes++;
493 return true;
494 }
495
496 /* Utility routine for deletes
497 *
498 * Returns: -1 on error
499 * n number of rows affected
500 */
DeleteDB(JCR * jcr,char * cmd,const char * file,int line)501 int BDB::DeleteDB(JCR *jcr, char *cmd, const char *file, int line)
502 {
503
504 if (!sql_query(cmd)) {
505 m_msg(file, line, &errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror());
506 j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg);
507 if (verbose) {
508 j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
509 }
510 return -1;
511 }
512 changes++;
513 return sql_affected_rows();
514 }
515
516
517 /*
518 * Get record max. Query is already in mdb->cmd
519 * No locking done
520 *
521 * Returns: -1 on failure
522 * count on success
523 */
get_sql_record_max(JCR * jcr,BDB * mdb)524 int get_sql_record_max(JCR *jcr, BDB *mdb)
525 {
526 SQL_ROW row;
527 int stat = 0;
528
529 if (mdb->QueryDB(jcr, mdb->cmd)) {
530 if ((row = mdb->sql_fetch_row()) == NULL) {
531 Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), mdb->sql_strerror());
532 stat = -1;
533 } else {
534 stat = str_to_int64(row[0]);
535 }
536 mdb->sql_free_result();
537 } else {
538 Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), mdb->sql_strerror());
539 stat = -1;
540 }
541 return stat;
542 }
543
544 /*
545 * Given a full filename, split it into its path
546 * and filename parts. They are returned in pool memory
547 * in the mdb structure.
548 */
split_path_and_file(JCR * jcr,BDB * mdb,const char * afname)549 void split_path_and_file(JCR *jcr, BDB *mdb, const char *afname)
550 {
551 const char *p, *f;
552
553 /* Find path without the filename.
554 * I.e. everything after the last / is a "filename".
555 * OK, maybe it is a directory name, but we treat it like
556 * a filename. If we don't find a / then the whole name
557 * must be a path name (e.g. c:).
558 */
559 for (p=f=afname; *p; p++) {
560 if (IsPathSeparator(*p)) {
561 f = p; /* set pos of last slash */
562 }
563 }
564 if (IsPathSeparator(*f)) { /* did we find a slash? */
565 f++; /* yes, point to filename */
566 } else { /* no, whole thing must be path name */
567 f = p;
568 }
569
570 /* If filename doesn't exist (i.e. root directory), we
571 * simply create a blank name consisting of a single
572 * space. This makes handling zero length filenames
573 * easier.
574 */
575 mdb->fnl = p - f;
576 if (mdb->fnl > 0) {
577 mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1);
578 memcpy(mdb->fname, f, mdb->fnl); /* copy filename */
579 mdb->fname[mdb->fnl] = 0;
580 } else {
581 mdb->fname[0] = 0;
582 mdb->fnl = 0;
583 }
584
585 mdb->pnl = f - afname;
586 if (mdb->pnl > 0) {
587 mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1);
588 memcpy(mdb->path, afname, mdb->pnl);
589 mdb->path[mdb->pnl] = 0;
590 } else {
591 Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), afname);
592 Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
593 mdb->path[0] = 0;
594 mdb->pnl = 0;
595 }
596
597 Dmsg3(500, "split fname=%s: path=%s file=%s\n", afname, mdb->path, mdb->fname);
598 }
599
600 /*
601 * Set maximum field length to something reasonable
602 */
max_length(int max_length)603 static int max_length(int max_length)
604 {
605 int max_len = max_length;
606 /* Sanity check */
607 if (max_len < 0) {
608 max_len = 2;
609 } else if (max_len > 100) {
610 max_len = 100;
611 }
612 return max_len;
613 }
614
615 /*
616 * List dashes as part of header for listing SQL results in a table
617 */
618 void
list_dashes(BDB * mdb,DB_LIST_HANDLER * send,void * ctx)619 list_dashes(BDB *mdb, DB_LIST_HANDLER *send, void *ctx)
620 {
621 SQL_FIELD *field;
622 int i, j;
623 int len;
624
625 mdb->sql_field_seek(0);
626 send(ctx, "+");
627 for (i = 0; i < mdb->sql_num_fields(); i++) {
628 field = mdb->sql_fetch_field();
629 if (!field) {
630 break;
631 }
632 len = max_length(field->max_length + 2);
633 for (j = 0; j < len; j++) {
634 send(ctx, "-");
635 }
636 send(ctx, "+");
637 }
638 send(ctx, "\n");
639 }
640
641 /* Small handler to print the last line of a list xxx command */
last_line_handler(void * vctx,const char * str)642 static void last_line_handler(void *vctx, const char *str)
643 {
644 LIST_CTX *ctx = (LIST_CTX *)vctx;
645 bstrncat(ctx->line, str, sizeof(ctx->line));
646 }
647
list_result(void * vctx,int nb_col,char ** row)648 int list_result(void *vctx, int nb_col, char **row)
649 {
650 SQL_FIELD *field;
651 int i, col_len, max_len = 0;
652 char buf[2000], ewc[30];
653
654 LIST_CTX *pctx = (LIST_CTX *)vctx;
655 DB_LIST_HANDLER *send = pctx->send;
656 e_list_type type = pctx->type;
657 BDB *mdb = pctx->mdb;
658 void *ctx = pctx->ctx;
659 JCR *jcr = pctx->jcr;
660
661 if (!pctx->once) {
662 pctx->once = true;
663
664 Dmsg1(800, "list_result starts looking at %d fields\n", mdb->sql_num_fields());
665 /* determine column display widths */
666 mdb->sql_field_seek(0);
667 for (i = 0; i < mdb->sql_num_fields(); i++) {
668 Dmsg1(800, "list_result processing field %d\n", i);
669 field = mdb->sql_fetch_field();
670 if (!field) {
671 break;
672 }
673 col_len = cstrlen(field->name);
674 if (type == VERT_LIST) {
675 if (col_len > max_len) {
676 max_len = col_len;
677 }
678 } else {
679 if (mdb->sql_field_is_numeric(field->type) && (int)field->max_length > 0) { /* fixup for commas */
680 field->max_length += (field->max_length - 1) / 3;
681 }
682 if (col_len < (int)field->max_length) {
683 col_len = field->max_length;
684 }
685 if (col_len < 4 && !mdb->sql_field_is_not_null(field->flags)) {
686 col_len = 4; /* 4 = length of the word "NULL" */
687 }
688 field->max_length = col_len; /* reset column info */
689 }
690 }
691
692 pctx->num_rows++;
693
694 Dmsg0(800, "list_result finished first loop\n");
695 if (type == VERT_LIST) {
696 goto vertical_list;
697 }
698 if (type == ARG_LIST) {
699 goto arg_list;
700 }
701
702 Dmsg1(800, "list_result starts second loop looking at %d fields\n",
703 mdb->sql_num_fields());
704
705 /* Keep the result to display the same line at the end of the table */
706 list_dashes(mdb, last_line_handler, pctx);
707 send(ctx, pctx->line);
708
709 send(ctx, "|");
710 mdb->sql_field_seek(0);
711 for (i = 0; i < mdb->sql_num_fields(); i++) {
712 Dmsg1(800, "list_result looking at field %d\n", i);
713 field = mdb->sql_fetch_field();
714 if (!field) {
715 break;
716 }
717 max_len = max_length(field->max_length);
718 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
719 send(ctx, buf);
720 }
721 send(ctx, "\n");
722 list_dashes(mdb, send, ctx);
723 }
724 Dmsg1(800, "list_result starts third loop looking at %d fields\n",
725 mdb->sql_num_fields());
726 mdb->sql_field_seek(0);
727 send(ctx, "|");
728 for (i = 0; i < mdb->sql_num_fields(); i++) {
729 field = mdb->sql_fetch_field();
730 if (!field) {
731 break;
732 }
733 max_len = max_length(field->max_length);
734 if (row[i] == NULL) {
735 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
736 } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) {
737 bsnprintf(buf, sizeof(buf), " %*s |", max_len,
738 add_commas(row[i], ewc));
739 } else {
740 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
741 }
742 send(ctx, buf);
743 }
744 send(ctx, "\n");
745 return 0;
746
747 vertical_list:
748
749 Dmsg1(800, "list_result starts vertical list at %d fields\n", mdb->sql_num_fields());
750 mdb->sql_field_seek(0);
751 for (i = 0; i < mdb->sql_num_fields(); i++) {
752 field = mdb->sql_fetch_field();
753 if (!field) {
754 break;
755 }
756 if (row[i] == NULL) {
757 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
758 } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) {
759 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
760 add_commas(row[i], ewc));
761 } else {
762 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
763 }
764 send(ctx, buf);
765 }
766 send(ctx, "\n");
767 return 0;
768
769 arg_list:
770 Dmsg1(800, "list_result starts simple list at %d fields\n", mdb->sql_num_fields());
771 mdb->sql_field_seek(0);
772 for (i = 0; i < mdb->sql_num_fields(); i++) {
773 field = mdb->sql_fetch_field();
774 if (!field) {
775 break;
776 }
777 if (row[i] == NULL) {
778 bsnprintf(buf, sizeof(buf), "%s%s=", (i>0?" ":""), field->name);
779 } else {
780 bash_spaces(row[i]);
781 bsnprintf(buf, sizeof(buf), "%s%s=%s ", (i>0?" ":""), field->name, row[i]);
782 }
783 send(ctx, buf);
784 }
785 send(ctx, "\n");
786 return 0;
787
788 }
789
790 /*
791 * If full_list is set, we list vertically, otherwise, we
792 * list on one line horizontally.
793 * Return number of rows
794 */
795 int
list_result(JCR * jcr,BDB * mdb,DB_LIST_HANDLER * send,void * ctx,e_list_type type)796 list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type)
797 {
798 SQL_FIELD *field;
799 SQL_ROW row;
800 int i, col_len, max_len = 0;
801 char buf[2000], ewc[30];
802
803 Dmsg0(800, "list_result starts\n");
804 if (mdb->sql_num_rows() == 0) {
805 send(ctx, _("No results to list.\n"));
806 return mdb->sql_num_rows();
807 }
808
809 Dmsg1(800, "list_result starts looking at %d fields\n", mdb->sql_num_fields());
810 /* determine column display widths */
811 mdb->sql_field_seek(0);
812 for (i = 0; i < mdb->sql_num_fields(); i++) {
813 Dmsg1(800, "list_result processing field %d\n", i);
814 field = mdb->sql_fetch_field();
815 if (!field) {
816 break;
817 }
818 col_len = cstrlen(field->name);
819 if (type == VERT_LIST) {
820 if (col_len > max_len) {
821 max_len = col_len;
822 }
823 } else {
824 if (mdb->sql_field_is_numeric(field->type) && (int)field->max_length > 0) { /* fixup for commas */
825 field->max_length += (field->max_length - 1) / 3;
826 }
827 if (col_len < (int)field->max_length) {
828 col_len = field->max_length;
829 }
830 if (col_len < 4 && !mdb->sql_field_is_not_null(field->flags)) {
831 col_len = 4; /* 4 = length of the word "NULL" */
832 }
833 field->max_length = col_len; /* reset column info */
834 }
835 }
836
837 Dmsg0(800, "list_result finished first loop\n");
838 if (type == VERT_LIST) {
839 goto vertical_list;
840 }
841 if (type == ARG_LIST) {
842 goto arg_list;
843 }
844
845 Dmsg1(800, "list_result starts second loop looking at %d fields\n", mdb->sql_num_fields());
846 list_dashes(mdb, send, ctx);
847 send(ctx, "|");
848 mdb->sql_field_seek(0);
849 for (i = 0; i < mdb->sql_num_fields(); i++) {
850 Dmsg1(800, "list_result looking at field %d\n", i);
851 field = mdb->sql_fetch_field();
852 if (!field) {
853 break;
854 }
855 max_len = max_length(field->max_length);
856 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
857 send(ctx, buf);
858 }
859 send(ctx, "\n");
860 list_dashes(mdb, send, ctx);
861
862 Dmsg1(800, "list_result starts third loop looking at %d fields\n", mdb->sql_num_fields());
863 while ((row = mdb->sql_fetch_row()) != NULL) {
864 mdb->sql_field_seek(0);
865 send(ctx, "|");
866 for (i = 0; i < mdb->sql_num_fields(); i++) {
867 field = mdb->sql_fetch_field();
868 if (!field) {
869 break;
870 }
871 max_len = max_length(field->max_length);
872 if (row[i] == NULL) {
873 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
874 } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) {
875 bsnprintf(buf, sizeof(buf), " %*s |", max_len,
876 add_commas(row[i], ewc));
877 } else {
878 strip_trailing_junk(row[i]);
879 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
880 }
881 send(ctx, buf);
882 }
883 send(ctx, "\n");
884 }
885 list_dashes(mdb, send, ctx);
886 return mdb->sql_num_rows();
887
888 vertical_list:
889
890 Dmsg1(800, "list_result starts vertical list at %d fields\n", mdb->sql_num_fields());
891 while ((row = mdb->sql_fetch_row()) != NULL) {
892 mdb->sql_field_seek(0);
893 for (i = 0; i < mdb->sql_num_fields(); i++) {
894 field = mdb->sql_fetch_field();
895 if (!field) {
896 break;
897 }
898 if (row[i] == NULL) {
899 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
900 } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) {
901 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
902 add_commas(row[i], ewc));
903 } else {
904 strip_trailing_junk(row[i]);
905 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
906 }
907 send(ctx, buf);
908 }
909 send(ctx, "\n");
910 }
911
912 arg_list:
913
914 Dmsg1(800, "list_result starts arg list at %d fields\n", mdb->sql_num_fields());
915 while ((row = mdb->sql_fetch_row()) != NULL) {
916 mdb->sql_field_seek(0);
917 for (i = 0; i < mdb->sql_num_fields(); i++) {
918 field = mdb->sql_fetch_field();
919 if (!field) {
920 break;
921 }
922 if (row[i] == NULL) {
923 bsnprintf(buf, sizeof(buf), "%s%s=", (i>0?" ":""), field->name);
924 } else {
925 bash_spaces(row[i]);
926 bsnprintf(buf, sizeof(buf), "%s%s=%s", (i>0?" ":""), field->name, row[i]);
927 }
928 send(ctx, buf);
929 }
930 send(ctx, "\n");
931 }
932 return mdb->sql_num_rows();
933 }
934
935 /*
936 * Open a new connexion to mdb catalog. This function is used
937 * by batch and accurate mode.
938 */
bdb_open_batch_connexion(JCR * jcr)939 bool BDB::bdb_open_batch_connexion(JCR *jcr)
940 {
941 bool multi_db;
942
943 multi_db = batch_insert_available();
944
945 if (!jcr->db_batch) {
946 jcr->db_batch = bdb_clone_database_connection(jcr, multi_db);
947 if (!jcr->db_batch) {
948 Mmsg0(&errmsg, _("Could not init database batch connection\n"));
949 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
950 return false;
951 }
952
953 if (!jcr->db_batch->bdb_open_database(jcr)) {
954 Mmsg2(&errmsg, _("Could not open database \"%s\": ERR=%s\n"),
955 jcr->db_batch->get_db_name(), jcr->db_batch->bdb_strerror());
956 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
957 return false;
958 }
959 }
960 return true;
961 }
962
963 /*
964 * !!! WARNING !!! Use this function only when bacula is stopped.
965 * ie, after a fatal signal and before exiting the program
966 * Print information about a BDB object.
967 */
bdb_debug_print(JCR * jcr,FILE * fp)968 void bdb_debug_print(JCR *jcr, FILE *fp)
969 {
970 BDB *mdb = jcr->db;
971
972 if (!mdb) {
973 return;
974 }
975
976 fprintf(fp, "BDB=%p db_name=%s db_user=%s connected=%s\n",
977 mdb, NPRTB(mdb->get_db_name()), NPRTB(mdb->get_db_user()), mdb->is_connected() ? "true" : "false");
978 fprintf(fp, "\tcmd=\"%s\" changes=%i\n", NPRTB(mdb->cmd), mdb->changes);
979 mdb->print_lock_info(fp);
980 }
981
bdb_check_settings(JCR * jcr,int64_t * starttime,int val,int64_t val2)982 bool BDB::bdb_check_settings(JCR *jcr, int64_t *starttime, int val, int64_t val2)
983 {
984 return true;
985 }
986
987 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */
988