1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2016 Planets Communications B.V.
6    Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * Kern Sibbald, March 2000
25  */
26 /**
27  * @file
28  * BAREOS Catalog Database interface routines
29  *
30  * Almost generic set of SQL database interface routines
31  * (with a little more work) SQL engine specific routines are in
32  * mysql.c, postgresql.c, sqlite.c, ...
33  */
34 
35 #include "include/bareos.h"
36 
37 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
38 
39 #include "cats.h"
40 #include "lib/edit.h"
41 
42 /* Forward referenced subroutines */
43 
dbid_list()44 dbid_list::dbid_list()
45 {
46   max_ids = 1000;
47   DBId = (DBId_t*)malloc(max_ids * sizeof(DBId_t));
48   num_ids = num_seen = tot_ids = 0;
49   PurgedFiles = nullptr;
50 }
51 
~dbid_list()52 dbid_list::~dbid_list() { free(DBId); }
53 
get(int i) const54 DBId_t dbid_list::get(int i) const
55 {
56   if (i >= size()) {
57     Emsg2(
58         M_ERROR_TERM, 0,
59         _("Unable to access dbid_list entry %d. Only %d entries available.\n"),
60         i, size());
61     return (DBId_t)0;
62   }
63   return DBId[i];
64 }
65 
66 
67 /**
68  * Called here to retrieve an integer from the database
69  */
DbIntHandler(void * ctx,int num_fields,char ** row)70 int DbIntHandler(void* ctx, int num_fields, char** row)
71 {
72   uint32_t* val = (uint32_t*)ctx;
73 
74   Dmsg1(800, "int_handler starts with row pointing at %x\n", row);
75 
76   if (row[0]) {
77     Dmsg1(800, "int_handler finds '%s'\n", row[0]);
78     *val = str_to_int64(row[0]);
79   } else {
80     Dmsg0(800, "int_handler finds zero\n");
81     *val = 0;
82   }
83   Dmsg0(800, "int_handler finishes\n");
84   return 0;
85 }
86 
87 /**
88  * Called here to retrieve a 32/64 bit integer from the database.
89  *   The returned integer will be extended to 64 bit.
90  */
db_int64_handler(void * ctx,int num_fields,char ** row)91 int db_int64_handler(void* ctx, int num_fields, char** row)
92 {
93   db_int64_ctx* lctx = (db_int64_ctx*)ctx;
94 
95   if (row[0]) {
96     lctx->value = str_to_int64(row[0]);
97     lctx->count++;
98   }
99   return 0;
100 }
101 
102 /**
103  * Called here to retrieve a btime from the database.
104  *   The returned integer will be extended to 64 bit.
105  */
DbStrtimeHandler(void * ctx,int num_fields,char ** row)106 int DbStrtimeHandler(void* ctx, int num_fields, char** row)
107 {
108   db_int64_ctx* lctx = (db_int64_ctx*)ctx;
109 
110   if (row[0]) {
111     lctx->value = StrToUtime(row[0]);
112     lctx->count++;
113   }
114   return 0;
115 }
116 
117 /**
118  * Use to build a comma separated list of values from a query. "10,20,30"
119  */
DbListHandler(void * ctx,int num_fields,char ** row)120 int DbListHandler(void* ctx, int num_fields, char** row)
121 {
122   db_list_ctx* lctx = (db_list_ctx*)ctx;
123   if (num_fields == 1 && row[0]) { lctx->add(row[0]); }
124   return 0;
125 }
126 
127 /**
128  * specific context passed from db_check_max_connections to
129  * DbMaxConnectionsHandler.
130  */
131 struct max_connections_context {
132   BareosDb* db;
133   uint32_t nr_connections;
134 };
135 
136 /**
137  * Called here to retrieve an integer from the database
138  */
DbMaxConnectionsHandler(void * ctx,int num_fields,char ** row)139 static inline int DbMaxConnectionsHandler(void* ctx, int num_fields, char** row)
140 {
141   struct max_connections_context* context;
142   uint32_t index;
143 
144   context = (struct max_connections_context*)ctx;
145   switch (context->db->GetTypeIndex()) {
146     case SQL_TYPE_MYSQL:
147       index = 1;
148       break;
149     default:
150       index = 0;
151       break;
152   }
153 
154   if (row[index]) {
155     context->nr_connections = str_to_int64(row[index]);
156   } else {
157     Dmsg0(800, "int_handler finds zero\n");
158     context->nr_connections = 0;
159   }
160   return 0;
161 }
162 
163 /**
164  * Check catalog max_connections setting
165  */
CheckMaxConnections(JobControlRecord * jcr,uint32_t max_concurrent_jobs)166 bool BareosDb::CheckMaxConnections(JobControlRecord* jcr,
167                                    uint32_t max_concurrent_jobs)
168 {
169   PoolMem query(PM_MESSAGE);
170   struct max_connections_context context;
171 
172   /*
173    * Without Batch insert, no need to verify max_connections
174    */
175   if (!BatchInsertAvailable()) return true;
176 
177   context.db = this;
178   context.nr_connections = 0;
179 
180   /*
181    * Check max_connections setting
182    */
183   FillQuery(query, SQL_QUERY::sql_get_max_connections);
184   if (!SqlQueryWithHandler(query.c_str(), DbMaxConnectionsHandler, &context)) {
185     Jmsg(jcr, M_ERROR, 0, "Can't verify max_connections settings %s", errmsg);
186     return false;
187   }
188 
189   if (context.nr_connections && max_concurrent_jobs &&
190       max_concurrent_jobs > context.nr_connections) {
191     Mmsg(errmsg,
192          _("Potential performance problem:\n"
193            "max_connections=%d set for %s database \"%s\" should be larger "
194            "than Director's "
195            "MaxConcurrentJobs=%d\n"),
196          context.nr_connections, GetType(), get_db_name(), max_concurrent_jobs);
197     Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
198     return false;
199   }
200 
201   return true;
202 }
203 
204 /* NOTE!!! The following routines expect that the
205  *  calling subroutine sets and clears the mutex
206  */
207 
208 /*
209  * Check that the tables correspond to the version we want
210  */
CheckTablesVersion(JobControlRecord * jcr)211 bool BareosDb::CheckTablesVersion(JobControlRecord* jcr)
212 {
213   uint32_t bareos_db_version = 0;
214   const char* query = "SELECT VersionId FROM Version";
215 
216   if (!SqlQueryWithHandler(query, DbIntHandler, (void*)&bareos_db_version)) {
217     Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
218     return false;
219   }
220 
221   if (bareos_db_version != BDB_VERSION) {
222     Mmsg(errmsg, "Version error for database \"%s\". Wanted %d, got %d\n",
223          get_db_name(), BDB_VERSION, bareos_db_version);
224     Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
225     return false;
226   }
227 
228   return true;
229 }
230 
231 /**
232  * Utility routine for queries. The database MUST be locked before calling here.
233  * Returns: false on failure
234  *          true on success
235  */
QueryDB(const char * file,int line,JobControlRecord * jcr,const char * select_cmd)236 bool BareosDb::QueryDB(const char* file,
237                        int line,
238                        JobControlRecord* jcr,
239                        const char* select_cmd)
240 {
241   SqlFreeResult();
242   Dmsg1(1000, "query: %s\n", select_cmd);
243   if (!SqlQuery(select_cmd, QF_STORE_RESULT)) {
244     msg_(file, line, errmsg, _("query %s failed:\n%s\n"), select_cmd,
245          sql_strerror());
246     j_msg(file, line, jcr, M_FATAL, 0, "%s", errmsg);
247     if (verbose) { j_msg(file, line, jcr, M_INFO, 0, "%s\n", select_cmd); }
248     return false;
249   }
250 
251   return true;
252 }
253 
254 /**
255  * Utility routine to do inserts
256  * Returns: false on failure
257  *          true on success
258  */
InsertDB(const char * file,int line,JobControlRecord * jcr,const char * select_cmd)259 bool BareosDb::InsertDB(const char* file,
260                         int line,
261                         JobControlRecord* jcr,
262                         const char* select_cmd)
263 {
264   int num_rows;
265 
266   if (!SqlQuery(select_cmd)) {
267     msg_(file, line, errmsg, _("insert %s failed:\n%s\n"), select_cmd,
268          sql_strerror());
269     j_msg(file, line, jcr, M_FATAL, 0, "%s", errmsg);
270     if (verbose) { j_msg(file, line, jcr, M_INFO, 0, "%s\n", select_cmd); }
271     return false;
272   }
273   num_rows = SqlAffectedRows();
274   if (num_rows != 1) {
275     char ed1[30];
276     msg_(file, line, errmsg, _("Insertion problem: affected_rows=%s\n"),
277          edit_uint64(num_rows, ed1));
278     if (verbose) { j_msg(file, line, jcr, M_INFO, 0, "%s\n", select_cmd); }
279     return false;
280   }
281   changes++;
282   return true;
283 }
284 
285 /**
286  * Utility routine for updates.
287  * Returns: false on failure
288  *          true on success
289  */
UpdateDB(const char * file,int line,JobControlRecord * jcr,const char * UpdateCmd,int nr_afr)290 bool BareosDb::UpdateDB(const char* file,
291                         int line,
292                         JobControlRecord* jcr,
293                         const char* UpdateCmd,
294                         int nr_afr)
295 {
296   int num_rows;
297 
298   if (!SqlQuery(UpdateCmd)) {
299     msg_(file, line, errmsg, _("update %s failed:\n%s\n"), UpdateCmd,
300          sql_strerror());
301     j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg);
302     if (verbose) { j_msg(file, line, jcr, M_INFO, 0, "%s\n", UpdateCmd); }
303     return false;
304   }
305 
306   if (nr_afr > 0) {
307     num_rows = SqlAffectedRows();
308     if (num_rows < nr_afr) {
309       char ed1[30];
310       msg_(file, line, errmsg, _("Update failed: affected_rows=%s for %s\n"),
311            edit_uint64(num_rows, ed1), UpdateCmd);
312       if (verbose) {
313         //          j_msg(file, line, jcr, M_INFO, 0, "%s\n", UpdateCmd);
314       }
315       return false;
316     }
317   }
318 
319   changes++;
320   return true;
321 }
322 
323 /**
324  * Utility routine for deletes
325  *
326  * Returns: -1 on error
327  *           n number of rows affected
328  */
DeleteDB(const char * file,int line,JobControlRecord * jcr,const char * DeleteCmd)329 int BareosDb::DeleteDB(const char* file,
330                        int line,
331                        JobControlRecord* jcr,
332                        const char* DeleteCmd)
333 {
334   if (!SqlQuery(DeleteCmd)) {
335     msg_(file, line, errmsg, _("delete %s failed:\n%s\n"), DeleteCmd,
336          sql_strerror());
337     j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg);
338     if (verbose) { j_msg(file, line, jcr, M_INFO, 0, "%s\n", DeleteCmd); }
339     return -1;
340   }
341   changes++;
342   return SqlAffectedRows();
343 }
344 
345 /**
346  * Get record max. Query is already in mdb->cmd
347  *  No locking done
348  *
349  * Returns: -1 on failure
350  *          count on success
351  */
GetSqlRecordMax(JobControlRecord * jcr)352 int BareosDb::GetSqlRecordMax(JobControlRecord* jcr)
353 {
354   SQL_ROW row;
355   int retval = 0;
356 
357   if (QUERY_DB(jcr, cmd)) {
358     if ((row = SqlFetchRow()) == NULL) {
359       Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
360       retval = -1;
361     } else {
362       retval = str_to_int64(row[0]);
363     }
364     SqlFreeResult();
365   } else {
366     Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
367     retval = -1;
368   }
369   return retval;
370 }
371 
372 /**
373  * Return pre-edited error message
374  */
strerror()375 char* BareosDb::strerror() { return errmsg; }
376 
377 /**
378  * Given a full filename, split it into its path
379  *  and filename parts. They are returned in pool memory
380  *  in the mdb structure.
381  */
SplitPathAndFile(JobControlRecord * jcr,const char * filename)382 void BareosDb::SplitPathAndFile(JobControlRecord* jcr, const char* filename)
383 {
384   const char *p, *f;
385 
386   /* Find path without the filename.
387    * I.e. everything after the last / is a "filename".
388    * OK, maybe it is a directory name, but we treat it like
389    * a filename. If we don't find a / then the whole name
390    * must be a path name (e.g. c:).
391    */
392   for (p = f = filename; *p; p++) {
393     if (IsPathSeparator(*p)) { f = p; /* set pos of last slash */ }
394   }
395   if (IsPathSeparator(*f)) { /* did we find a slash? */
396     f++;                     /* yes, point to filename */
397   } else {
398     f = p; /* no, whole thing must be path name */
399   }
400 
401   /* If filename doesn't exist (i.e. root directory), we
402    * simply create a blank name consisting of a single
403    * space. This makes handling zero length filenames
404    * easier.
405    */
406   fnl = p - f;
407   if (fnl > 0) {
408     fname = CheckPoolMemorySize(fname, fnl + 1);
409     memcpy(fname, f, fnl); /* copy filename */
410     fname[fnl] = 0;
411   } else {
412     fname[0] = 0;
413     fnl = 0;
414   }
415 
416   pnl = f - filename;
417   if (pnl > 0) {
418     path = CheckPoolMemorySize(path, pnl + 1);
419     memcpy(path, filename, pnl);
420     path[pnl] = 0;
421   } else {
422     Mmsg1(errmsg, _("Path length is zero. File=%s\n"), fname);
423     Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
424     path[0] = 0;
425     pnl = 0;
426   }
427 
428   Dmsg2(500, "split path=%s file=%s\n", path, fname);
429 }
430 
431 /**
432  * Set maximum field length to something reasonable
433  */
MaxLength(int MaxLength)434 static int MaxLength(int MaxLength)
435 {
436   int max_len = MaxLength;
437   /* Sanity check */
438   if (max_len < 0) {
439     max_len = 2;
440   } else if (max_len > 100) {
441     max_len = 100;
442   }
443   return max_len;
444 }
445 
446 /**
447  * List dashes as part of header for listing SQL results in a table
448  */
ListDashes(OutputFormatter * send)449 void BareosDb::ListDashes(OutputFormatter* send)
450 {
451   int len;
452   int num_fields;
453   SQL_FIELD* field;
454 
455   SqlFieldSeek(0);
456   send->Decoration("+");
457   num_fields = SqlNumFields();
458   for (int i = 0; i < num_fields; i++) {
459     field = SqlFetchField();
460     if (!field) { break; }
461     len = MaxLength(field->max_length + 2);
462     for (int j = 0; j < len; j++) { send->Decoration("-"); }
463     send->Decoration("+");
464   }
465   send->Decoration("\n");
466 }
467 
468 /**
469  * List result handler used by queries done with db_big_sql_query()
470  */
ListResult(void * vctx,int nb_col,char ** row)471 int BareosDb::ListResult(void* vctx, int nb_col, char** row)
472 {
473   JobControlRecord* jcr;
474   char ewc[30];
475   PoolMem key;
476   PoolMem value;
477   int num_fields;
478   SQL_FIELD* field;
479   e_list_type type;
480   OutputFormatter* send;
481   int col_len, max_len = 0;
482   ListContext* pctx = (ListContext*)vctx;
483 
484   /*
485    * Get pointers from context.
486    */
487   type = pctx->type;
488   send = pctx->send;
489   jcr = pctx->jcr;
490 
491   /*
492    * See if this row must be filtered.
493    */
494   if (send->HasFilters() && !send->FilterData(row)) { return 0; }
495 
496   send->ObjectStart();
497 
498   num_fields = SqlNumFields();
499   switch (type) {
500     case NF_LIST:
501     case RAW_LIST:
502       /*
503        * No need to calculate things like maximum field lenght for
504        * unformated or raw output.
505        */
506       break;
507     case HORZ_LIST:
508     case VERT_LIST:
509       if (!pctx->once) {
510         pctx->once = true;
511 
512         Dmsg1(800, "ListResult starts looking at %d fields\n", num_fields);
513         /*
514          * Determine column display widths
515          */
516         SqlFieldSeek(0);
517         for (int i = 0; i < num_fields; i++) {
518           Dmsg1(800, "ListResult processing field %d\n", i);
519           field = SqlFetchField();
520           if (!field) { break; }
521 
522           /*
523            * See if this is a hidden column.
524            */
525           if (send->IsHiddenColumn(i)) {
526             Dmsg1(800, "ListResult field %d is hidden\n", i);
527             continue;
528           }
529 
530           col_len = cstrlen(field->name);
531           if (type == VERT_LIST) {
532             if (col_len > max_len) { max_len = col_len; }
533           } else {
534             if (SqlFieldIsNumeric(field->type) &&
535                 (int)field->max_length > 0) { /* fixup for commas */
536               field->max_length += (field->max_length - 1) / 3;
537             }
538             if (col_len < (int)field->max_length) {
539               col_len = field->max_length;
540             }
541             if (col_len < 4 && !SqlFieldIsNotNull(field->flags)) {
542               col_len = 4; /* 4 = length of the word "NULL" */
543             }
544             field->max_length = col_len; /* reset column info */
545           }
546         }
547 
548         pctx->num_rows++;
549 
550         Dmsg0(800, "ListResult finished first loop\n");
551         if (type == VERT_LIST) { break; }
552 
553         Dmsg1(800, "ListResult starts second loop looking at %d fields\n",
554               num_fields);
555 
556         /*
557          * Keep the result to display the same line at the end of the table
558          */
559         ListDashes(send);
560 
561         send->Decoration("|");
562         SqlFieldSeek(0);
563         for (int i = 0; i < num_fields; i++) {
564           Dmsg1(800, "ListResult looking at field %d\n", i);
565 
566           field = SqlFetchField();
567           if (!field) { break; }
568 
569           /*
570            * See if this is a hidden column.
571            */
572           if (send->IsHiddenColumn(i)) {
573             Dmsg1(800, "ListResult field %d is hidden\n", i);
574             continue;
575           }
576 
577           max_len = MaxLength(field->max_length);
578           send->Decoration(" %-*s |", max_len, field->name);
579         }
580         send->Decoration("\n");
581         ListDashes(send);
582       }
583       break;
584     default:
585       break;
586   }
587 
588   switch (type) {
589     case NF_LIST:
590     case RAW_LIST:
591       Dmsg1(800, "ListResult starts third loop looking at %d fields\n",
592             num_fields);
593       SqlFieldSeek(0);
594       for (int i = 0; i < num_fields; i++) {
595         field = SqlFetchField();
596         if (!field) { break; }
597 
598         /*
599          * See if this is a hidden column.
600          */
601         if (send->IsHiddenColumn(i)) {
602           Dmsg1(800, "ListResult field %d is hidden\n", i);
603           continue;
604         }
605 
606         if (row[i] == NULL) {
607           value.bsprintf("%s", "NULL");
608         } else {
609           value.bsprintf("%s", row[i]);
610         }
611         send->ObjectKeyValue(field->name, value.c_str(), " %s");
612       }
613       if (type != RAW_LIST) { send->Decoration("\n"); }
614       break;
615     case HORZ_LIST:
616       Dmsg1(800, "ListResult starts third loop looking at %d fields\n",
617             num_fields);
618       SqlFieldSeek(0);
619       send->Decoration("|");
620       for (int i = 0; i < num_fields; i++) {
621         field = SqlFetchField();
622         if (!field) { break; }
623 
624         /*
625          * See if this is a hidden column.
626          */
627         if (send->IsHiddenColumn(i)) {
628           Dmsg1(800, "ListResult field %d is hidden\n", i);
629           continue;
630         }
631 
632         max_len = MaxLength(field->max_length);
633         if (row[i] == NULL) {
634           value.bsprintf(" %-*s |", max_len, "NULL");
635         } else if (SqlFieldIsNumeric(field->type) && !jcr->gui &&
636                    IsAnInteger(row[i])) {
637           value.bsprintf(" %*s |", max_len, add_commas(row[i], ewc));
638         } else {
639           value.bsprintf(" %-*s |", max_len, row[i]);
640         }
641 
642         /*
643          * Use value format string to send preformated value.
644          */
645         send->ObjectKeyValue(field->name, row[i], value.c_str());
646       }
647       send->Decoration("\n");
648       break;
649     case VERT_LIST:
650       Dmsg1(800, "ListResult starts vertical list at %d fields\n", num_fields);
651       SqlFieldSeek(0);
652       for (int i = 0; i < num_fields; i++) {
653         field = SqlFetchField();
654         if (!field) { break; }
655 
656         /*
657          * See if this is a hidden column.
658          */
659         if (send->IsHiddenColumn(i)) {
660           Dmsg1(800, "ListResult field %d is hidden\n", i);
661           continue;
662         }
663 
664         if (row[i] == NULL) {
665           key.bsprintf(" %*s: ", max_len, field->name);
666           value.bsprintf("%s\n", "NULL");
667         } else if (SqlFieldIsNumeric(field->type) && !jcr->gui &&
668                    IsAnInteger(row[i])) {
669           key.bsprintf(" %*s: ", max_len, field->name);
670           value.bsprintf("%s\n", add_commas(row[i], ewc));
671         } else {
672           key.bsprintf(" %*s: ", max_len, field->name);
673           value.bsprintf("%s\n", row[i]);
674         }
675 
676         /*
677          * Use value format string to send preformated value.
678          */
679         send->ObjectKeyValue(field->name, key.c_str(), row[i], value.c_str());
680       }
681       send->Decoration("\n");
682       break;
683     default:
684       break;
685   }
686   send->ObjectEnd();
687 
688   return 0;
689 }
690 
ListResult(void * vctx,int nb_col,char ** row)691 int ListResult(void* vctx, int nb_col, char** row)
692 {
693   ListContext* pctx = (ListContext*)vctx;
694   BareosDb* mdb = pctx->mdb;
695 
696   return mdb->ListResult(vctx, nb_col, row);
697 }
698 
699 /**
700  * If full_list is set, we list vertically, otherwise, we list on one line
701  * horizontally.
702  *
703  * Return number of rows
704  */
ListResult(JobControlRecord * jcr,OutputFormatter * send,e_list_type type)705 int BareosDb::ListResult(JobControlRecord* jcr,
706                          OutputFormatter* send,
707                          e_list_type type)
708 {
709   SQL_ROW row;
710   char ewc[30];
711   PoolMem key;
712   PoolMem value;
713   int num_fields;
714   SQL_FIELD* field;
715   bool filters_enabled;
716   int col_len, max_len = 0;
717 
718   Dmsg0(800, "ListResult starts\n");
719   if (SqlNumRows() == 0) {
720     send->Decoration(_("No results to list.\n"));
721     return 0;
722   }
723 
724   num_fields = SqlNumFields();
725   switch (type) {
726     case E_LIST_INIT:
727     case NF_LIST:
728     case RAW_LIST:
729       /*
730        * No need to calculate things like column widths for unformatted or raw
731        * output.
732        */
733       break;
734     case HORZ_LIST:
735     case VERT_LIST:
736       Dmsg1(800, "ListResult starts looking at %d fields\n", num_fields);
737       /*
738        * Determine column display widths
739        */
740       SqlFieldSeek(0);
741       for (int i = 0; i < num_fields; i++) {
742         Dmsg1(800, "ListResult processing field %d\n", i);
743 
744         field = SqlFetchField();
745         if (!field) { break; }
746 
747         /*
748          * See if this is a hidden column.
749          */
750         if (send->IsHiddenColumn(i)) {
751           Dmsg1(800, "ListResult field %d is hidden\n", i);
752           continue;
753         }
754 
755         col_len = cstrlen(field->name);
756         if (type == VERT_LIST) {
757           if (col_len > max_len) { max_len = col_len; }
758         } else {
759           if (SqlFieldIsNumeric(field->type) &&
760               (int)field->max_length > 0) { /* fixup for commas */
761             field->max_length += (field->max_length - 1) / 3;
762           }
763           if (col_len < (int)field->max_length) { col_len = field->max_length; }
764           if (col_len < 4 && !SqlFieldIsNotNull(field->flags)) {
765             col_len = 4; /* 4 = length of the word "NULL" */
766           }
767           field->max_length = col_len; /* reset column info */
768         }
769       }
770       break;
771   }
772 
773   Dmsg0(800, "ListResult finished first loop\n");
774 
775   /*
776    * See if filters are enabled for this list function.
777    * We use this to shortcut for calling the FilterData() method in the
778    * OutputFormatter class.
779    */
780   filters_enabled = send->HasFilters();
781 
782   switch (type) {
783     case E_LIST_INIT:
784     case NF_LIST:
785     case RAW_LIST:
786       Dmsg1(800, "ListResult starts second loop looking at %d fields\n",
787             num_fields);
788       while ((row = SqlFetchRow()) != NULL) {
789         /*
790          * See if we should allow this under the current filtering.
791          */
792         if (filters_enabled && !send->FilterData(row)) { continue; }
793 
794         send->ObjectStart();
795         SqlFieldSeek(0);
796         for (int i = 0; i < num_fields; i++) {
797           field = SqlFetchField();
798           if (!field) { break; }
799 
800           /*
801            * See if this is a hidden column.
802            */
803           if (send->IsHiddenColumn(i)) {
804             Dmsg1(800, "ListResult field %d is hidden\n", i);
805             continue;
806           }
807 
808           if (row[i] == NULL) {
809             value.bsprintf("%s", "NULL");
810           } else {
811             value.bsprintf("%s", row[i]);
812           }
813           send->ObjectKeyValue(field->name, value.c_str(), " %s");
814         }
815         if (type != RAW_LIST) { send->Decoration("\n"); }
816         send->ObjectEnd();
817       }
818       break;
819     case HORZ_LIST:
820       Dmsg1(800, "ListResult starts second loop looking at %d fields\n",
821             num_fields);
822       ListDashes(send);
823       send->Decoration("|");
824       SqlFieldSeek(0);
825       for (int i = 0; i < num_fields; i++) {
826         Dmsg1(800, "ListResult looking at field %d\n", i);
827 
828         field = SqlFetchField();
829         if (!field) { break; }
830 
831         /*
832          * See if this is a hidden column.
833          */
834         if (send->IsHiddenColumn(i)) {
835           Dmsg1(800, "ListResult field %d is hidden\n", i);
836           continue;
837         }
838 
839         max_len = MaxLength(field->max_length);
840         send->Decoration(" %-*s |", max_len, field->name);
841       }
842       send->Decoration("\n");
843       ListDashes(send);
844 
845       Dmsg1(800, "ListResult starts third loop looking at %d fields\n",
846             num_fields);
847       while ((row = SqlFetchRow()) != NULL) {
848         /*
849          * See if we should allow this under the current filtering.
850          */
851         if (filters_enabled && !send->FilterData(row)) { continue; }
852 
853         send->ObjectStart();
854         SqlFieldSeek(0);
855         send->Decoration("|");
856         for (int i = 0; i < num_fields; i++) {
857           field = SqlFetchField();
858           if (!field) { break; }
859 
860           /*
861            * See if this is a hidden column.
862            */
863           if (send->IsHiddenColumn(i)) {
864             Dmsg1(800, "ListResult field %d is hidden\n", i);
865             continue;
866           }
867 
868           max_len = MaxLength(field->max_length);
869           if (row[i] == NULL) {
870             value.bsprintf(" %-*s |", max_len, "NULL");
871           } else if (SqlFieldIsNumeric(field->type) && !jcr->gui &&
872                      IsAnInteger(row[i])) {
873             value.bsprintf(" %*s |", max_len, add_commas(row[i], ewc));
874           } else {
875             value.bsprintf(" %-*s |", max_len, row[i]);
876           }
877           if (i == num_fields - 1) { value.strcat("\n"); }
878 
879           /*
880            * Use value format string to send preformated value
881            */
882           send->ObjectKeyValue(field->name, row[i], value.c_str());
883         }
884         send->ObjectEnd();
885       }
886       ListDashes(send);
887       break;
888     case VERT_LIST:
889       Dmsg1(800, "ListResult starts vertical list at %d fields\n", num_fields);
890       while ((row = SqlFetchRow()) != NULL) {
891         /*
892          * See if we should allow this under the current filtering.
893          */
894         if (filters_enabled && !send->FilterData(row)) { continue; }
895 
896         send->ObjectStart();
897         SqlFieldSeek(0);
898         for (int i = 0; i < num_fields; i++) {
899           field = SqlFetchField();
900           if (!field) { break; }
901 
902           /*
903            * See if this is a hidden column.
904            */
905           if (send->IsHiddenColumn(i)) {
906             Dmsg1(800, "ListResult field %d is hidden\n", i);
907             continue;
908           }
909 
910           if (row[i] == NULL) {
911             key.bsprintf(" %*s: ", max_len, field->name);
912             value.bsprintf("%s\n", "NULL");
913           } else if (SqlFieldIsNumeric(field->type) && !jcr->gui &&
914                      IsAnInteger(row[i])) {
915             key.bsprintf(" %*s: ", max_len, field->name);
916             value.bsprintf("%s\n", add_commas(row[i], ewc));
917           } else {
918             key.bsprintf(" %*s: ", max_len, field->name);
919             value.bsprintf("%s\n", row[i]);
920           }
921 
922           /*
923            * Use value format string to send preformated value
924            */
925           send->ObjectKeyValue(field->name, key.c_str(), row[i], value.c_str());
926         }
927         send->Decoration("\n");
928         send->ObjectEnd();
929       }
930       break;
931   }
932 
933   return SqlNumRows();
934 }
935 
936 /*
937  * If full_list is set, we list vertically, otherwise, we list on one line
938  * horizontally.
939  *
940  * Return number of rows
941  */
ListResult(JobControlRecord * jcr,BareosDb * mdb,OutputFormatter * send,e_list_type type)942 int ListResult(JobControlRecord* jcr,
943                BareosDb* mdb,
944                OutputFormatter* send,
945                e_list_type type)
946 {
947   return mdb->ListResult(jcr, send, type);
948 }
949 
950 /**
951  * Open a new connexion to mdb catalog. This function is used by batch and
952  * accurate mode.
953  */
OpenBatchConnection(JobControlRecord * jcr)954 bool BareosDb::OpenBatchConnection(JobControlRecord* jcr)
955 {
956   bool multi_db;
957 
958   multi_db = BatchInsertAvailable();
959   if (!jcr->db_batch) {
960     jcr->db_batch = CloneDatabaseConnection(jcr, multi_db, multi_db);
961     if (!jcr->db_batch) {
962       Mmsg0(errmsg, _("Could not init database batch connection\n"));
963       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
964       return false;
965     }
966   }
967   return true;
968 }
969 
DbDebugPrint(FILE * fp)970 void BareosDb::DbDebugPrint(FILE* fp)
971 {
972   fprintf(fp, "BareosDb=%p db_name=%s db_user=%s connected=%s\n", this,
973           NPRTB(get_db_name()), NPRTB(get_db_user()),
974           IsConnected() ? "true" : "false");
975   fprintf(fp, "\tcmd=\"%s\" changes=%i\n", NPRTB(cmd), changes);
976 
977   PrintLockInfo(fp);
978 }
979 
980 /**
981  * !!! WARNING !!! Use this function only when bareos is stopped.
982  * ie, after a fatal signal and before exiting the program
983  * Print information about a BareosDb object.
984  */
DbDebugPrint(JobControlRecord * jcr,FILE * fp)985 void DbDebugPrint(JobControlRecord* jcr, FILE* fp)
986 {
987   BareosDb* mdb = jcr->db;
988 
989   if (!mdb) { return; }
990 
991   mdb->DbDebugPrint(fp);
992 }
993 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || \
994           HAVE_DBI */
995