1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2016 Planets Communications B.V.
6 Copyright (C) 2013-2017 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 Get record interface routines
29 *
30 * Note, these routines generally get a record by id or
31 * by name. If more logic is involved, the routine
32 * should be in find.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 "sql.h"
41 #include "lib/edit.h"
42 #include "lib/volume_session_info.h"
43
44 /* -----------------------------------------------------------------------
45 *
46 * Generic Routines (or almost generic)
47 *
48 * -----------------------------------------------------------------------
49 */
50
51 /* Forward referenced functions */
52
53 /**
54 * Given a full filename (with path), look up the File record
55 * (with attributes) in the database.
56 *
57 * Returns: 0 on failure
58 * 1 on success with the File record in FileDbRecord
59 */
GetFileAttributesRecord(JobControlRecord * jcr,char * filename,JobDbRecord * jr,FileDbRecord * fdbr)60 bool BareosDb::GetFileAttributesRecord(JobControlRecord* jcr,
61 char* filename,
62 JobDbRecord* jr,
63 FileDbRecord* fdbr)
64 {
65 bool retval;
66 Dmsg1(100, "db_get_file_attributes_record filename=%s \n", filename);
67
68 DbLock(this);
69
70 SplitPathAndFile(jcr, filename);
71 fdbr->PathId = GetPathRecord(jcr);
72 retval = GetFileRecord(jcr, jr, fdbr);
73
74 DbUnlock(this);
75 return retval;
76 }
77
78 /**
79 * Get a File record
80 * Returns: false on failure
81 * true on success
82 *
83 * DO NOT use Jmsg in this routine.
84 *
85 * Note in this routine, we do not use Jmsg because it may be
86 * called to get attributes of a non-existent file, which is
87 * "normal" if a new file is found during Verify.
88 *
89 * The following is a bit of a kludge: because we always backup a
90 * directory entry, we can end up with two copies of the directory
91 * in the backup. One is when we encounter the directory and find
92 * we cannot recurse into it, and the other is when we find an
93 * explicit mention of the directory. This can also happen if the
94 * use includes the directory twice. In this case, Verify
95 * VolumeToCatalog fails because we have two copies in the catalog,
96 * and only the first one is marked (twice). So, when calling from Verify,
97 * VolumeToCatalog jr is not NULL and we know jr->FileIndex is the fileindex
98 * of the version of the directory/file we actually want and do
99 * a more explicit SQL search.
100 */
GetFileRecord(JobControlRecord * jcr,JobDbRecord * jr,FileDbRecord * fdbr)101 bool BareosDb::GetFileRecord(JobControlRecord* jcr,
102 JobDbRecord* jr,
103 FileDbRecord* fdbr)
104 {
105 bool retval = false;
106 SQL_ROW row;
107 char ed1[50], ed2[50], ed3[50];
108 int num_rows;
109
110 esc_name = CheckPoolMemorySize(esc_name, 2 * fnl + 2);
111 EscapeString(jcr, esc_name, fname, fnl);
112
113 if (jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG) {
114 Mmsg(cmd,
115 "SELECT FileId, LStat, MD5, Fhinfo, Fhnode FROM File,Job WHERE "
116 "File.JobId=Job.JobId AND File.PathId=%s AND "
117 "File.Name='%s' AND Job.Type='B' AND Job.JobStatus IN ('T','W') AND "
118 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
119 edit_int64(fdbr->PathId, ed1), esc_name,
120 edit_int64(jr->ClientId, ed3));
121 } else if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
122 Mmsg(cmd,
123 "SELECT FileId, LStat, MD5, Fhinfo, Fhnode FROM File WHERE "
124 "File.JobId=%s AND File.PathId=%s AND "
125 "File.Name='%s' AND File.FileIndex=%u",
126 edit_int64(fdbr->JobId, ed1), edit_int64(fdbr->PathId, ed2), esc_name,
127 jr->FileIndex);
128 } else {
129 Mmsg(cmd,
130 "SELECT FileId, LStat, MD5, Fhinfo, Fhnode FROM File WHERE "
131 "File.JobId=%s AND File.PathId=%s AND "
132 "File.Name='%s'",
133 edit_int64(fdbr->JobId, ed1), edit_int64(fdbr->PathId, ed2), esc_name);
134 }
135 Dmsg3(450, "Get_file_record JobId=%u Filename=%s PathId=%u\n", fdbr->JobId,
136 esc_name, fdbr->PathId);
137
138 Dmsg1(100, "Query=%s\n", cmd);
139
140 if (QUERY_DB(jcr, cmd)) {
141 num_rows = SqlNumRows();
142 Dmsg1(050, "GetFileRecord num_rows=%d\n", num_rows);
143 if (num_rows >= 1) {
144 if ((row = SqlFetchRow()) == NULL) {
145 Mmsg1(errmsg, _("Error fetching row: %s\n"), sql_strerror());
146 } else {
147 fdbr->FileId = (FileId_t)str_to_int64(row[0]);
148 bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
149 bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
150 retval = true;
151 if (num_rows > 1) {
152 Mmsg3(errmsg,
153 _("GetFileRecord want 1 got rows=%d PathId=%s Filename=%s\n"),
154 num_rows, edit_int64(fdbr->PathId, ed1), esc_name);
155 Dmsg1(000, "=== Problem! %s", errmsg);
156 }
157 }
158 } else {
159 Mmsg2(errmsg, _("File record for PathId=%s Filename=%s not found.\n"),
160 edit_int64(fdbr->PathId, ed1), esc_name);
161 }
162 SqlFreeResult();
163 } else {
164 Mmsg(errmsg, _("File record not found in Catalog.\n"));
165 }
166 return retval;
167 }
168
169
170 /**
171 * Get path record
172 * Returns: 0 on failure
173 * PathId on success
174 *
175 * DO NOT use Jmsg in this routine (see notes for GetFileRecord)
176 */
GetPathRecord(JobControlRecord * jcr)177 int BareosDb::GetPathRecord(JobControlRecord* jcr)
178 {
179 SQL_ROW row;
180 DBId_t PathId = 0;
181 int num_rows;
182
183 esc_name = CheckPoolMemorySize(esc_name, 2 * pnl + 2);
184 EscapeString(jcr, esc_name, path, pnl);
185
186 if (cached_path_id != 0 && cached_path_len == pnl &&
187 bstrcmp(cached_path, path)) {
188 return cached_path_id;
189 }
190
191 Mmsg(cmd, "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
192 if (QUERY_DB(jcr, cmd)) {
193 char ed1[30];
194 num_rows = SqlNumRows();
195 if (num_rows > 1) {
196 Mmsg2(errmsg, _("More than one Path!: %s for path: %s\n"),
197 edit_uint64(num_rows, ed1), path);
198 Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
199 }
200 /* Even if there are multiple paths, take the first one */
201 if (num_rows >= 1) {
202 if ((row = SqlFetchRow()) == NULL) {
203 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
204 } else {
205 PathId = str_to_int64(row[0]);
206 if (PathId <= 0) {
207 Mmsg2(errmsg, _("Get DB path record %s found bad record: %s\n"), cmd,
208 edit_int64(PathId, ed1));
209 PathId = 0;
210 } else {
211 /*
212 * Cache path
213 */
214 if (PathId != cached_path_id) {
215 cached_path_id = PathId;
216 cached_path_len = pnl;
217 PmStrcpy(cached_path, path);
218 }
219 }
220 }
221 } else {
222 Mmsg1(errmsg, _("Path record: %s not found.\n"), path);
223 }
224 SqlFreeResult();
225 } else {
226 Mmsg(errmsg, _("Path record: %s not found in Catalog.\n"), path);
227 }
228 return PathId;
229 }
230
GetPathRecord(JobControlRecord * jcr,const char * new_path)231 int BareosDb::GetPathRecord(JobControlRecord* jcr, const char* new_path)
232 {
233 PmStrcpy(path, new_path);
234 pnl = strlen(path);
235 return GetPathRecord(jcr);
236 }
237
238 /**
239 * Get Job record for given JobId or Job name
240 * Returns: false on failure
241 * true on success
242 */
GetJobRecord(JobControlRecord * jcr,JobDbRecord * jr)243 bool BareosDb::GetJobRecord(JobControlRecord* jcr, JobDbRecord* jr)
244 {
245 bool retval = false;
246 SQL_ROW row;
247 char ed1[50];
248 char esc[MAX_ESCAPE_NAME_LENGTH];
249
250 DbLock(this);
251 if (jr->JobId == 0) {
252 EscapeString(jcr, esc, jr->Job, strlen(jr->Job));
253 Mmsg(cmd,
254 "SELECT VolSessionId,VolSessionTime,"
255 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
256 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
257 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
258 "FROM Job WHERE Job='%s'",
259 esc);
260 } else {
261 Mmsg(cmd,
262 "SELECT VolSessionId,VolSessionTime,"
263 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
264 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
265 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
266 "FROM Job WHERE JobId=%s",
267 edit_int64(jr->JobId, ed1));
268 }
269
270 if (!QUERY_DB(jcr, cmd)) { goto bail_out; }
271 if ((row = SqlFetchRow()) == NULL) {
272 Mmsg1(errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
273 SqlFreeResult();
274 goto bail_out;
275 }
276
277 jr->VolSessionId = str_to_uint64(row[0]);
278 jr->VolSessionTime = str_to_uint64(row[1]);
279 jr->PoolId = str_to_int64(row[2]);
280 bstrncpy(jr->cStartTime, (row[3] != NULL) ? row[3] : "",
281 sizeof(jr->cStartTime));
282 bstrncpy(jr->cEndTime, (row[4] != NULL) ? row[4] : "", sizeof(jr->cEndTime));
283 jr->JobFiles = str_to_int64(row[5]);
284 jr->JobBytes = str_to_int64(row[6]);
285 jr->JobTDate = str_to_int64(row[7]);
286 bstrncpy(jr->Job, (row[8] != NULL) ? row[8] : "", sizeof(jr->Job));
287 jr->JobStatus = (row[9] != NULL) ? (int)*row[9] : JS_FatalError;
288 jr->JobType = (row[10] != NULL) ? (int)*row[10] : JT_BACKUP;
289 jr->JobLevel = (row[11] != NULL) ? (int)*row[11] : L_NONE;
290 jr->ClientId = str_to_uint64((row[12] != NULL) ? row[12] : (char*)"");
291 bstrncpy(jr->Name, (row[13] != NULL) ? row[13] : "", sizeof(jr->Name));
292 jr->PriorJobId = str_to_uint64((row[14] != NULL) ? row[14] : (char*)"");
293 bstrncpy(jr->cRealEndTime, (row[15] != NULL) ? row[15] : "",
294 sizeof(jr->cRealEndTime));
295 if (jr->JobId == 0) { jr->JobId = str_to_int64(row[16]); }
296 jr->FileSetId = str_to_int64(row[17]);
297 bstrncpy(jr->cSchedTime, (row[18] != NULL) ? row[18] : "",
298 sizeof(jr->cSchedTime));
299 bstrncpy(jr->cRealEndTime, (row[19] != NULL) ? row[19] : "",
300 sizeof(jr->cRealEndTime));
301 jr->ReadBytes = str_to_int64(row[20]);
302 jr->StartTime = StrToUtime(jr->cStartTime);
303 jr->SchedTime = StrToUtime(jr->cSchedTime);
304 jr->EndTime = StrToUtime(jr->cEndTime);
305 jr->RealEndTime = StrToUtime(jr->cRealEndTime);
306 jr->HasBase = str_to_int64(row[21]);
307 jr->PurgedFiles = str_to_int64(row[22]);
308
309 SqlFreeResult();
310 retval = true;
311
312 bail_out:
313 DbUnlock(this);
314 return retval;
315 }
316
317 /**
318 * Find VolumeNames for a given JobId
319 * Returns: 0 on error or no Volumes found
320 * number of volumes on success
321 * Volumes are concatenated in VolumeNames
322 * separated by a vertical bar (|) in the order
323 * that they were written.
324 *
325 * Returns: number of volumes on success
326 */
GetJobVolumeNames(JobControlRecord * jcr,JobId_t JobId,POOLMEM * & VolumeNames)327 int BareosDb::GetJobVolumeNames(JobControlRecord* jcr,
328 JobId_t JobId,
329 POOLMEM*& VolumeNames)
330 {
331 SQL_ROW row;
332 char ed1[50];
333 int retval = 0;
334 int i;
335 int num_rows;
336
337 DbLock(this);
338
339 /*
340 * Get one entry per VolumeName, but "sort" by VolIndex
341 */
342 Mmsg(cmd,
343 "SELECT VolumeName,MAX(VolIndex) FROM JobMedia,Media WHERE "
344 "JobMedia.JobId=%s AND JobMedia.MediaId=Media.MediaId "
345 "GROUP BY VolumeName "
346 "ORDER BY 2 ASC",
347 edit_int64(JobId, ed1));
348
349 Dmsg1(130, "VolNam=%s\n", cmd);
350 VolumeNames[0] = '\0';
351 if (QUERY_DB(jcr, cmd)) {
352 num_rows = SqlNumRows();
353 Dmsg1(130, "Num rows=%d\n", num_rows);
354 if (num_rows <= 0) {
355 Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
356 retval = 0;
357 } else {
358 retval = num_rows;
359 for (i = 0; i < retval; i++) {
360 if ((row = SqlFetchRow()) == NULL) {
361 Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i,
362 sql_strerror());
363 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
364 retval = 0;
365 break;
366 } else {
367 if (VolumeNames[0] != '\0') { PmStrcat(VolumeNames, "|"); }
368 PmStrcat(VolumeNames, row[0]);
369 }
370 }
371 }
372 SqlFreeResult();
373 } else {
374 Mmsg(errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
375 }
376 DbUnlock(this);
377
378 return retval;
379 }
380
381 /**
382 * Find Volume parameters for a given JobId
383 * Returns: 0 on error or no Volumes found
384 * number of volumes on success
385 * List of Volumes and start/end file/blocks (malloced structure!)
386 *
387 * Returns: number of volumes on success
388 */
GetJobVolumeParameters(JobControlRecord * jcr,JobId_t JobId,VolumeParameters ** VolParams)389 int BareosDb::GetJobVolumeParameters(JobControlRecord* jcr,
390 JobId_t JobId,
391 VolumeParameters** VolParams)
392 {
393 SQL_ROW row;
394 char ed1[50];
395 int retval = 0;
396 int i;
397 VolumeParameters* Vols = NULL;
398 int num_rows;
399
400 DbLock(this);
401 Mmsg(cmd,
402 "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
403 "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,"
404 "Slot,StorageId,InChanger,"
405 "JobBytes"
406 " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
407 " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
408 edit_int64(JobId, ed1));
409
410 Dmsg1(130, "VolNam=%s\n", cmd);
411 if (QUERY_DB(jcr, cmd)) {
412 num_rows = SqlNumRows();
413 Dmsg1(200, "Num rows=%d\n", num_rows);
414 if (num_rows <= 0) {
415 Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
416 retval = 0;
417 } else {
418 retval = num_rows;
419 DBId_t* SId = NULL;
420 if (retval > 0) {
421 *VolParams = Vols =
422 (VolumeParameters*)malloc(retval * sizeof(VolumeParameters));
423 SId = (DBId_t*)malloc(retval * sizeof(DBId_t));
424 }
425 for (i = 0; i < retval; i++) {
426 if ((row = SqlFetchRow()) == NULL) {
427 Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i,
428 sql_strerror());
429 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
430 retval = 0;
431 break;
432 } else {
433 DBId_t StorageId;
434 uint32_t StartBlock, EndBlock, StartFile, EndFile;
435
436 bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
437 bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
438 Vols[i].FirstIndex = str_to_uint64(row[2]);
439 Vols[i].LastIndex = str_to_uint64(row[3]);
440 StartFile = str_to_uint64(row[4]);
441 EndFile = str_to_uint64(row[5]);
442 StartBlock = str_to_uint64(row[6]);
443 EndBlock = str_to_uint64(row[7]);
444 Vols[i].Slot = str_to_uint64(row[8]);
445 StorageId = str_to_uint64(row[9]);
446 Vols[i].InChanger = str_to_uint64(row[10]);
447 Vols[i].JobBytes = str_to_uint64(row[11]);
448
449 Vols[i].StartAddr = (((uint64_t)StartFile) << 32) | StartBlock;
450 Vols[i].EndAddr = (((uint64_t)EndFile) << 32) | EndBlock;
451 Vols[i].Storage[0] = 0;
452 SId[i] = StorageId;
453 }
454 }
455 for (i = 0; i < retval; i++) {
456 if (SId[i] != 0) {
457 Mmsg(cmd, "SELECT Name from Storage WHERE StorageId=%s",
458 edit_int64(SId[i], ed1));
459 if (QUERY_DB(jcr, cmd)) {
460 if ((row = SqlFetchRow()) && row[0]) {
461 bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
462 }
463 }
464 }
465 }
466 if (SId) { free(SId); }
467 }
468 SqlFreeResult();
469 }
470 DbUnlock(this);
471 return retval;
472 }
473
474 /**
475 * Get the number of pool records
476 *
477 * Returns: -1 on failure
478 * number on success
479 */
GetNumPoolRecords(JobControlRecord * jcr)480 int BareosDb::GetNumPoolRecords(JobControlRecord* jcr)
481 {
482 int retval = 0;
483
484 DbLock(this);
485 Mmsg(cmd, "SELECT count(*) from Pool");
486 retval = GetSqlRecordMax(jcr);
487 DbUnlock(this);
488
489 return retval;
490 }
491
492 /**
493 * This function returns a list of all the Pool record ids.
494 * The caller must free ids if non-NULL.
495 *
496 * Returns 0: on failure
497 * 1: on success
498 */
GetPoolIds(JobControlRecord * jcr,int * num_ids,DBId_t ** ids)499 int BareosDb::GetPoolIds(JobControlRecord* jcr, int* num_ids, DBId_t** ids)
500 {
501 SQL_ROW row;
502 int retval = 0;
503 int i = 0;
504 DBId_t* id;
505
506 DbLock(this);
507 *ids = NULL;
508 Mmsg(cmd, "SELECT PoolId FROM Pool");
509 if (QUERY_DB(jcr, cmd)) {
510 *num_ids = SqlNumRows();
511 if (*num_ids > 0) {
512 id = (DBId_t*)malloc(*num_ids * sizeof(DBId_t));
513 while ((row = SqlFetchRow()) != NULL) { id[i++] = str_to_uint64(row[0]); }
514 *ids = id;
515 }
516 SqlFreeResult();
517 retval = 1;
518 } else {
519 Mmsg(errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror());
520 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
521 retval = 0;
522 }
523
524 DbUnlock(this);
525 return retval;
526 }
527
528 /**
529 * This function returns a list of all the Storage record ids.
530 * The caller must free ids if non-NULL.
531 *
532 * Returns 0: on failure
533 * 1: on success
534 */
GetStorageIds(JobControlRecord * jcr,int * num_ids,DBId_t * ids[])535 int BareosDb::GetStorageIds(JobControlRecord* jcr, int* num_ids, DBId_t* ids[])
536 {
537 SQL_ROW row;
538 int retval = 0;
539 int i = 0;
540 DBId_t* id;
541
542 DbLock(this);
543 *ids = NULL;
544 Mmsg(cmd, "SELECT StorageId FROM Storage");
545 if (QUERY_DB(jcr, cmd)) {
546 *num_ids = SqlNumRows();
547 if (*num_ids > 0) {
548 id = (DBId_t*)malloc(*num_ids * sizeof(DBId_t));
549 while ((row = SqlFetchRow()) != NULL) { id[i++] = str_to_uint64(row[0]); }
550 *ids = id;
551 }
552 SqlFreeResult();
553 retval = 1;
554 } else {
555 Mmsg(errmsg, _("Storage id select failed: ERR=%s\n"), sql_strerror());
556 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
557 retval = 0;
558 }
559
560 DbUnlock(this);
561 return retval;
562 }
563
564 /**
565 * This function returns a list of all the Client record ids.
566 * The caller must free ids if non-NULL.
567 *
568 * Returns false: on failure
569 * true: on success
570 */
GetClientIds(JobControlRecord * jcr,int * num_ids,DBId_t * ids[])571 bool BareosDb::GetClientIds(JobControlRecord* jcr, int* num_ids, DBId_t* ids[])
572 {
573 bool retval = false;
574 SQL_ROW row;
575 int i = 0;
576 DBId_t* id;
577
578 DbLock(this);
579 *ids = NULL;
580 Mmsg(cmd, "SELECT ClientId FROM Client ORDER BY Name");
581 if (QUERY_DB(jcr, cmd)) {
582 *num_ids = SqlNumRows();
583 if (*num_ids > 0) {
584 id = (DBId_t*)malloc(*num_ids * sizeof(DBId_t));
585 while ((row = SqlFetchRow()) != NULL) { id[i++] = str_to_uint64(row[0]); }
586 *ids = id;
587 }
588 SqlFreeResult();
589 retval = true;
590 } else {
591 Mmsg(errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror());
592 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
593 }
594 DbUnlock(this);
595 return retval;
596 }
597
598 /**
599 * Get Pool Record
600 * If the PoolId is non-zero, we get its record,
601 * otherwise, we search on the PoolName
602 *
603 * Returns: false on failure
604 * true on success
605 */
GetPoolRecord(JobControlRecord * jcr,PoolDbRecord * pdbr)606 bool BareosDb::GetPoolRecord(JobControlRecord* jcr, PoolDbRecord* pdbr)
607 {
608 SQL_ROW row;
609 bool ok = false;
610 char ed1[50];
611 int num_rows;
612 char esc[MAX_ESCAPE_NAME_LENGTH];
613
614 DbLock(this);
615 if (pdbr->PoolId != 0) { /* find by id */
616 Mmsg(
617 cmd,
618 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
619 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
620 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,"
621 "ScratchPoolId,"
622 "ActionOnPurge,MinBlocksize,MaxBlocksize FROM Pool WHERE "
623 "Pool.PoolId=%s",
624 edit_int64(pdbr->PoolId, ed1));
625 } else { /* find by name */
626 EscapeString(jcr, esc, pdbr->Name, strlen(pdbr->Name));
627 Mmsg(
628 cmd,
629 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
630 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
631 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,"
632 "ScratchPoolId,"
633 "ActionOnPurge,MinBlocksize,MaxBlocksize FROM Pool WHERE "
634 "Pool.Name='%s'",
635 esc);
636 }
637 if (QUERY_DB(jcr, cmd)) {
638 num_rows = SqlNumRows();
639 if (num_rows > 1) {
640 char ed1[30];
641 Mmsg1(errmsg, _("More than one Pool!: %s\n"), edit_uint64(num_rows, ed1));
642 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
643 } else if (num_rows == 1) {
644 if ((row = SqlFetchRow()) == NULL) {
645 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
646 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
647 } else {
648 pdbr->PoolId = str_to_int64(row[0]);
649 bstrncpy(pdbr->Name, (row[1] != NULL) ? row[1] : "",
650 sizeof(pdbr->Name));
651 pdbr->NumVols = str_to_int64(row[2]);
652 pdbr->MaxVols = str_to_int64(row[3]);
653 pdbr->UseOnce = str_to_int64(row[4]);
654 pdbr->UseCatalog = str_to_int64(row[5]);
655 pdbr->AcceptAnyVolume = str_to_int64(row[6]);
656 pdbr->AutoPrune = str_to_int64(row[7]);
657 pdbr->Recycle = str_to_int64(row[8]);
658 pdbr->VolRetention = str_to_int64(row[9]);
659 pdbr->VolUseDuration = str_to_int64(row[10]);
660 pdbr->MaxVolJobs = str_to_int64(row[11]);
661 pdbr->MaxVolFiles = str_to_int64(row[12]);
662 pdbr->MaxVolBytes = str_to_uint64(row[13]);
663 bstrncpy(pdbr->PoolType, (row[14] != NULL) ? row[14] : "",
664 sizeof(pdbr->PoolType));
665 pdbr->LabelType = str_to_int64(row[15]);
666 bstrncpy(pdbr->LabelFormat, (row[16] != NULL) ? row[16] : "",
667 sizeof(pdbr->LabelFormat));
668 pdbr->RecyclePoolId = str_to_int64(row[17]);
669 pdbr->ScratchPoolId = str_to_int64(row[18]);
670 pdbr->ActionOnPurge = str_to_int32(row[19]);
671 pdbr->MinBlocksize = str_to_int32(row[20]);
672 pdbr->MaxBlocksize = str_to_int32(row[21]);
673 ok = true;
674 }
675 }
676 SqlFreeResult();
677 }
678
679 if (ok) {
680 uint32_t NumVols;
681
682 Mmsg(cmd, "SELECT count(*) from Media WHERE PoolId=%s",
683 edit_int64(pdbr->PoolId, ed1));
684 NumVols = GetSqlRecordMax(jcr);
685 Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
686 if (NumVols != pdbr->NumVols) {
687 pdbr->NumVols = NumVols;
688 ok = UpdatePoolRecord(jcr, pdbr);
689 }
690 } else {
691 Mmsg(errmsg, _("Pool record not found in Catalog.\n"));
692 }
693
694 DbUnlock(this);
695 return ok;
696 }
697
698 /**
699 * Get Storage Record
700 * If the StorageId is non-zero, we get its record, otherwise, we search on the
701 * StorageName
702 *
703 * Returns: false on failure
704 * true on success
705 */
GetStorageRecord(JobControlRecord * jcr,StorageDbRecord * sdbr)706 bool BareosDb::GetStorageRecord(JobControlRecord* jcr, StorageDbRecord* sdbr)
707 {
708 SQL_ROW row;
709 bool ok = false;
710 char ed1[50];
711 int num_rows;
712 char esc[MAX_ESCAPE_NAME_LENGTH];
713
714 DbLock(this);
715 if (sdbr->StorageId != 0) { /* find by id */
716 Mmsg(cmd,
717 "SELECT StorageId,Name,AutoChanger FROM Storage WHERE "
718 "Storage.StorageId=%s",
719 edit_int64(sdbr->StorageId, ed1));
720 } else { /* find by name */
721 EscapeString(jcr, esc, sdbr->Name, strlen(sdbr->Name));
722 Mmsg(cmd,
723 "SELECT StorageId,Name,Autochanger FROM Storage WHERE "
724 "Storage.Name='%s'",
725 esc);
726 }
727 if (QUERY_DB(jcr, cmd)) {
728 num_rows = SqlNumRows();
729 if (num_rows > 1) {
730 char ed1[30];
731
732 Mmsg1(errmsg, _("More than one Storage!: %s\n"),
733 edit_uint64(num_rows, ed1));
734 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
735 } else if (num_rows == 1) {
736 if ((row = SqlFetchRow()) == NULL) {
737 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
738 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
739 } else {
740 sdbr->StorageId = str_to_int64(row[0]);
741 bstrncpy(sdbr->Name, (row[1] != NULL) ? row[1] : "",
742 sizeof(sdbr->Name));
743 sdbr->AutoChanger = str_to_int64(row[2]);
744 ok = true;
745 }
746 }
747 SqlFreeResult();
748 }
749
750 DbUnlock(this);
751 return ok;
752 }
753
754 /**
755 * Get Client Record
756 * If the ClientId is non-zero, we get its record, otherwise, we search on the
757 * Client Name
758 *
759 * Returns: false on failure
760 * true on success
761 */
GetClientRecord(JobControlRecord * jcr,ClientDbRecord * cdbr)762 bool BareosDb::GetClientRecord(JobControlRecord* jcr, ClientDbRecord* cdbr)
763 {
764 bool retval = false;
765 SQL_ROW row;
766 char ed1[50];
767 int num_rows;
768 char esc[MAX_ESCAPE_NAME_LENGTH];
769
770 DbLock(this);
771 if (cdbr->ClientId != 0) { /* find by id */
772 Mmsg(cmd,
773 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
774 "FROM Client WHERE Client.ClientId=%s",
775 edit_int64(cdbr->ClientId, ed1));
776 } else { /* find by name */
777 EscapeString(jcr, esc, cdbr->Name, strlen(cdbr->Name));
778 Mmsg(cmd,
779 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
780 "FROM Client WHERE Client.Name='%s'",
781 esc);
782 }
783
784 if (QUERY_DB(jcr, cmd)) {
785 num_rows = SqlNumRows();
786 if (num_rows > 1) {
787 Mmsg1(errmsg, _("More than one Client!: %s\n"),
788 edit_uint64(num_rows, ed1));
789 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
790 } else if (num_rows == 1) {
791 if ((row = SqlFetchRow()) == NULL) {
792 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
793 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
794 } else {
795 cdbr->ClientId = str_to_int64(row[0]);
796 bstrncpy(cdbr->Name, (row[1] != NULL) ? row[1] : "",
797 sizeof(cdbr->Name));
798 bstrncpy(cdbr->Uname, (row[2] != NULL) ? row[2] : "",
799 sizeof(cdbr->Uname));
800 cdbr->AutoPrune = str_to_int64(row[3]);
801 cdbr->FileRetention = str_to_int64(row[4]);
802 cdbr->JobRetention = str_to_int64(row[5]);
803 retval = true;
804 }
805 } else {
806 Mmsg(errmsg, _("Client record not found in Catalog.\n"));
807 }
808 SqlFreeResult();
809 } else {
810 Mmsg(errmsg, _("Client record not found in Catalog.\n"));
811 }
812
813 DbUnlock(this);
814 return retval;
815 }
816
817 /**
818 * Get Counter Record
819 *
820 * Returns: false on failure
821 * true on success
822 */
GetCounterRecord(JobControlRecord * jcr,CounterDbRecord * cr)823 bool BareosDb::GetCounterRecord(JobControlRecord* jcr, CounterDbRecord* cr)
824 {
825 bool retval = false;
826 SQL_ROW row;
827 int num_rows;
828 char esc[MAX_ESCAPE_NAME_LENGTH];
829
830 DbLock(this);
831 EscapeString(jcr, esc, cr->Counter, strlen(cr->Counter));
832
833 FillQuery(SQL_QUERY_select_counter_values, esc);
834 if (QUERY_DB(jcr, cmd)) {
835 num_rows = SqlNumRows();
836
837 /*
838 * If more than one, report error, but return first row
839 */
840 if (num_rows > 1) {
841 Mmsg1(errmsg, _("More than one Counter!: %d\n"), num_rows);
842 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
843 }
844 if (num_rows >= 1) {
845 if ((row = SqlFetchRow()) == NULL) {
846 Mmsg1(errmsg, _("error fetching Counter row: %s\n"), sql_strerror());
847 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
848 SqlFreeResult();
849 goto bail_out;
850 }
851 cr->MinValue = str_to_int64(row[0]);
852 cr->MaxValue = str_to_int64(row[1]);
853 cr->CurrentValue = str_to_int64(row[2]);
854 if (row[3]) {
855 bstrncpy(cr->WrapCounter, row[3], sizeof(cr->WrapCounter));
856 } else {
857 cr->WrapCounter[0] = 0;
858 }
859 SqlFreeResult();
860 retval = true;
861 goto bail_out;
862 }
863 SqlFreeResult();
864 } else {
865 Mmsg(errmsg, _("Counter record: %s not found in Catalog.\n"), cr->Counter);
866 }
867
868 bail_out:
869 DbUnlock(this);
870 return retval;
871 }
872
873 /**
874 * Get FileSet Record
875 * If the FileSetId is non-zero, we get its record,
876 * otherwise, we search on the name
877 *
878 * Returns: 0 on failure
879 * id on success
880 */
GetFilesetRecord(JobControlRecord * jcr,FileSetDbRecord * fsr)881 int BareosDb::GetFilesetRecord(JobControlRecord* jcr, FileSetDbRecord* fsr)
882 {
883 SQL_ROW row;
884 int retval = 0;
885 char ed1[50];
886 int num_rows;
887 char esc[MAX_ESCAPE_NAME_LENGTH];
888
889 DbLock(this);
890 if (fsr->FileSetId != 0) { /* find by id */
891 Mmsg(cmd,
892 "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
893 "WHERE FileSetId=%s",
894 edit_int64(fsr->FileSetId, ed1));
895 } else { /* find by name */
896 EscapeString(jcr, esc, fsr->FileSet, strlen(fsr->FileSet));
897 Mmsg(cmd,
898 "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
899 "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1",
900 esc);
901 }
902
903 if (QUERY_DB(jcr, cmd)) {
904 num_rows = SqlNumRows();
905 if (num_rows > 1) {
906 char ed1[30];
907 Mmsg1(errmsg, _("Error got %s FileSets but expected only one!\n"),
908 edit_uint64(num_rows, ed1));
909 SqlDataSeek(num_rows - 1);
910 }
911 if ((row = SqlFetchRow()) == NULL) {
912 Mmsg1(errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
913 } else {
914 fsr->FileSetId = str_to_int64(row[0]);
915 bstrncpy(fsr->FileSet, (row[1] != NULL) ? row[1] : "",
916 sizeof(fsr->FileSet));
917 bstrncpy(fsr->MD5, (row[2] != NULL) ? row[2] : "", sizeof(fsr->MD5));
918 bstrncpy(fsr->cCreateTime, (row[3] != NULL) ? row[3] : "",
919 sizeof(fsr->cCreateTime));
920 retval = fsr->FileSetId;
921 }
922 SqlFreeResult();
923 } else {
924 Mmsg(errmsg, _("FileSet record not found in Catalog.\n"));
925 }
926 DbUnlock(this);
927 return retval;
928 }
929
930 /**
931 * Get the number of Media records
932 *
933 * Returns: -1 on failure
934 * number on success
935 */
GetNumMediaRecords(JobControlRecord * jcr)936 int BareosDb::GetNumMediaRecords(JobControlRecord* jcr)
937 {
938 int retval = 0;
939
940 DbLock(this);
941 Mmsg(cmd, "SELECT count(*) from Media");
942 retval = GetSqlRecordMax(jcr);
943 DbUnlock(this);
944 return retval;
945 }
946
PrepareMediaSqlQuery(JobControlRecord * jcr,MediaDbRecord * mr,PoolMem & volumes)947 bool BareosDb::PrepareMediaSqlQuery(JobControlRecord* jcr,
948 MediaDbRecord* mr,
949 PoolMem& volumes)
950 {
951 bool ok = true;
952 char ed1[50];
953 char esc[MAX_NAME_LENGTH * 2 + 1];
954 PoolMem buf(PM_MESSAGE);
955
956 Mmsg(cmd,
957 "SELECT DISTINCT MediaId FROM Media WHERE Recycle=%d AND Enabled=%d ",
958 mr->Recycle, mr->Enabled);
959
960 if (*mr->MediaType) {
961 EscapeString(jcr, esc, mr->MediaType, strlen(mr->MediaType));
962 Mmsg(buf, "AND MediaType='%s' ", esc);
963 PmStrcat(cmd, buf.c_str());
964 }
965
966 if (mr->StorageId) {
967 Mmsg(buf, "AND StorageId=%s ", edit_uint64(mr->StorageId, ed1));
968 PmStrcat(cmd, buf.c_str());
969 }
970
971 if (mr->PoolId) {
972 Mmsg(buf, "AND PoolId=%s ", edit_uint64(mr->PoolId, ed1));
973 PmStrcat(cmd, buf.c_str());
974 }
975
976 if (mr->VolBytes) {
977 Mmsg(buf, "AND VolBytes > %s ", edit_uint64(mr->VolBytes, ed1));
978 PmStrcat(cmd, buf.c_str());
979 }
980
981 if (*mr->VolStatus) {
982 EscapeString(jcr, esc, mr->VolStatus, strlen(mr->VolStatus));
983 Mmsg(buf, "AND VolStatus = '%s' ", esc);
984 PmStrcat(cmd, buf.c_str());
985 }
986
987 if (volumes.strlen() > 0) {
988 /* extra list of volumes given */
989 Mmsg(buf, "AND VolumeName IN (%s) ", volumes.c_str());
990 PmStrcat(cmd, buf.c_str());
991 } else if (*mr->VolumeName) {
992 /* single volume given in media record */
993 EscapeString(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
994 Mmsg(buf, "AND VolumeName = '%s' ", esc);
995 PmStrcat(cmd, buf.c_str());
996 }
997
998 Dmsg1(100, "query=%s\n", cmd);
999
1000 return ok;
1001 }
1002
1003 /**
1004 * This function creates a sql query string at cmd to return a list of all the
1005 * Media records for the current Pool, the correct Media Type, Recyle, Enabled,
1006 * StorageId, VolBytes and volumes or VolumeName if specified. Comma separated
1007 * list of volumes takes precedence over VolumeName. The caller must free ids if
1008 * non-NULL.
1009 */
GetMediaIds(JobControlRecord * jcr,MediaDbRecord * mr,PoolMem & volumes,int * num_ids,DBId_t * ids[])1010 bool BareosDb::GetMediaIds(JobControlRecord* jcr,
1011 MediaDbRecord* mr,
1012 PoolMem& volumes,
1013 int* num_ids,
1014 DBId_t* ids[])
1015 {
1016 bool ok = false;
1017 SQL_ROW row;
1018 int i = 0;
1019 DBId_t* id;
1020
1021 DbLock(this);
1022 *ids = NULL;
1023
1024 if (!PrepareMediaSqlQuery(jcr, mr, volumes)) {
1025 Mmsg(errmsg, _("Media id select failed: invalid parameter"));
1026 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1027 goto bail_out;
1028 }
1029
1030 if (!QUERY_DB(jcr, cmd)) {
1031 Mmsg(errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror());
1032 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1033 goto bail_out;
1034 }
1035
1036 *num_ids = SqlNumRows();
1037 if (*num_ids > 0) {
1038 id = (DBId_t*)malloc(*num_ids * sizeof(DBId_t));
1039 while ((row = SqlFetchRow()) != NULL) { id[i++] = str_to_uint64(row[0]); }
1040 *ids = id;
1041 }
1042 SqlFreeResult();
1043 ok = true;
1044 bail_out:
1045 DbUnlock(this);
1046 return ok;
1047 }
1048
1049
1050 /**
1051 * This function returns a list of all the DBIds that are returned for the
1052 * query.
1053 *
1054 * Returns false: on failure
1055 * true: on success
1056 */
GetQueryDbids(JobControlRecord * jcr,PoolMem & query,dbid_list & ids)1057 bool BareosDb::GetQueryDbids(JobControlRecord* jcr,
1058 PoolMem& query,
1059 dbid_list& ids)
1060 {
1061 SQL_ROW row;
1062 int i = 0;
1063 bool ok = false;
1064
1065 DbLock(this);
1066 ids.num_ids = 0;
1067 if (QUERY_DB(jcr, query.c_str())) {
1068 ids.num_ids = SqlNumRows();
1069 if (ids.num_ids > 0) {
1070 if (ids.max_ids < ids.num_ids) {
1071 free(ids.DBId);
1072 ids.DBId = (DBId_t*)malloc(ids.num_ids * sizeof(DBId_t));
1073 }
1074 while ((row = SqlFetchRow()) != NULL) {
1075 ids.DBId[i++] = str_to_uint64(row[0]);
1076 }
1077 }
1078 SqlFreeResult();
1079 ok = true;
1080 } else {
1081 Mmsg(errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror());
1082 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1083 ok = false;
1084 }
1085 DbUnlock(this);
1086 return ok;
1087 }
1088
1089
1090 /**
1091 * Get Media Record
1092 *
1093 * Returns: false: on failure
1094 * true: on success
1095 */
GetMediaRecord(JobControlRecord * jcr,MediaDbRecord * mr)1096 bool BareosDb::GetMediaRecord(JobControlRecord* jcr, MediaDbRecord* mr)
1097 {
1098 bool retval = false;
1099 SQL_ROW row;
1100 char ed1[50];
1101 int num_rows;
1102 char esc[MAX_ESCAPE_NAME_LENGTH];
1103
1104 DbLock(this);
1105 if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
1106 Mmsg(cmd, "SELECT count(*) from Media");
1107 mr->MediaId = GetSqlRecordMax(jcr);
1108 retval = true;
1109 goto bail_out;
1110 }
1111 if (mr->MediaId != 0) { /* find by id */
1112 Mmsg(cmd,
1113 "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
1114 "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1115 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1116 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1117 "EndFile,EndBlock,LabelType,LabelDate,StorageId,"
1118 "Enabled,LocationId,RecycleCount,InitialWrite,"
1119 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,"
1120 "ActionOnPurge,EncryptionKey,MinBlocksize,MaxBlocksize "
1121 "FROM Media WHERE MediaId=%s",
1122 edit_int64(mr->MediaId, ed1));
1123 } else { /* find by name */
1124 EscapeString(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1125 Mmsg(cmd,
1126 "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
1127 "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1128 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1129 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1130 "EndFile,EndBlock,LabelType,LabelDate,StorageId,"
1131 "Enabled,LocationId,RecycleCount,InitialWrite,"
1132 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,"
1133 "ActionOnPurge,EncryptionKey,MinBlocksize,MaxBlocksize "
1134 "FROM Media WHERE VolumeName='%s'",
1135 esc);
1136 }
1137
1138 if (QUERY_DB(jcr, cmd)) {
1139 char ed1[50];
1140 num_rows = SqlNumRows();
1141 if (num_rows > 1) {
1142 Mmsg1(errmsg, _("More than one Volume!: %s\n"),
1143 edit_uint64(num_rows, ed1));
1144 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1145 } else if (num_rows == 1) {
1146 if ((row = SqlFetchRow()) == NULL) {
1147 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1148 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1149 } else {
1150 /* return values */
1151 mr->MediaId = str_to_int64(row[0]);
1152 bstrncpy(mr->VolumeName, (row[1] != NULL) ? row[1] : "",
1153 sizeof(mr->VolumeName));
1154 mr->VolJobs = str_to_int64(row[2]);
1155 mr->VolFiles = str_to_int64(row[3]);
1156 mr->VolBlocks = str_to_int64(row[4]);
1157 mr->VolBytes = str_to_uint64(row[5]);
1158 mr->VolMounts = str_to_int64(row[6]);
1159 mr->VolErrors = str_to_int64(row[7]);
1160 mr->VolWrites = str_to_int64(row[8]);
1161 mr->MaxVolBytes = str_to_uint64(row[9]);
1162 mr->VolCapacityBytes = str_to_uint64(row[10]);
1163 bstrncpy(mr->MediaType, (row[11] != NULL) ? row[11] : "",
1164 sizeof(mr->MediaType));
1165 bstrncpy(mr->VolStatus, (row[12] != NULL) ? row[12] : "",
1166 sizeof(mr->VolStatus));
1167 mr->PoolId = str_to_int64(row[13]);
1168 mr->VolRetention = str_to_uint64(row[14]);
1169 mr->VolUseDuration = str_to_uint64(row[15]);
1170 mr->MaxVolJobs = str_to_int64(row[16]);
1171 mr->MaxVolFiles = str_to_int64(row[17]);
1172 mr->Recycle = str_to_int64(row[18]);
1173 mr->Slot = str_to_int64(row[19]);
1174 bstrncpy(mr->cFirstWritten, (row[20] != NULL) ? row[20] : "",
1175 sizeof(mr->cFirstWritten));
1176 mr->FirstWritten = (time_t)StrToUtime(mr->cFirstWritten);
1177 bstrncpy(mr->cLastWritten, (row[21] != NULL) ? row[21] : "",
1178 sizeof(mr->cLastWritten));
1179 mr->LastWritten = (time_t)StrToUtime(mr->cLastWritten);
1180 mr->InChanger = str_to_uint64(row[22]);
1181 mr->EndFile = str_to_uint64(row[23]);
1182 mr->EndBlock = str_to_uint64(row[24]);
1183 mr->LabelType = str_to_int64(row[25]);
1184 bstrncpy(mr->cLabelDate, (row[26] != NULL) ? row[26] : "",
1185 sizeof(mr->cLabelDate));
1186 mr->LabelDate = (time_t)StrToUtime(mr->cLabelDate);
1187 mr->StorageId = str_to_int64(row[27]);
1188 mr->Enabled = str_to_int64(row[28]);
1189 mr->LocationId = str_to_int64(row[29]);
1190 mr->RecycleCount = str_to_int64(row[30]);
1191 bstrncpy(mr->cInitialWrite, (row[31] != NULL) ? row[31] : "",
1192 sizeof(mr->cInitialWrite));
1193 mr->InitialWrite = (time_t)StrToUtime(mr->cInitialWrite);
1194 mr->ScratchPoolId = str_to_int64(row[32]);
1195 mr->RecyclePoolId = str_to_int64(row[33]);
1196 mr->VolReadTime = str_to_int64(row[34]);
1197 mr->VolWriteTime = str_to_int64(row[35]);
1198 mr->ActionOnPurge = str_to_int32(row[36]);
1199 bstrncpy(mr->EncrKey, (row[37] != NULL) ? row[37] : "",
1200 sizeof(mr->EncrKey));
1201 mr->MinBlocksize = str_to_int32(row[38]);
1202 mr->MaxBlocksize = str_to_int32(row[39]);
1203 retval = true;
1204 }
1205 } else {
1206 if (mr->MediaId != 0) {
1207 Mmsg1(errmsg, _("Media record MediaId=%s not found.\n"),
1208 edit_int64(mr->MediaId, ed1));
1209 } else {
1210 Mmsg1(errmsg, _("Media record for Volume \"%s\" not found.\n"),
1211 mr->VolumeName);
1212 }
1213 }
1214 SqlFreeResult();
1215 } else {
1216 if (mr->MediaId != 0) {
1217 Mmsg(errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1218 mr->MediaId);
1219 } else {
1220 Mmsg(errmsg, _("Media record for Vol=%s not found in Catalog.\n"),
1221 mr->VolumeName);
1222 }
1223 }
1224
1225 bail_out:
1226 DbUnlock(this);
1227 return retval;
1228 }
1229
1230 /**
1231 * Remove all MD5 from a query (can save lot of memory with many files)
1232 */
strip_md5(char * q)1233 static void strip_md5(char* q)
1234 {
1235 char* p = q;
1236 while ((p = strstr(p, ", MD5"))) { memset(p, ' ', 5 * sizeof(char)); }
1237 }
1238
1239 /**
1240 * Find the last "accurate" backup state (that can take deleted files in
1241 * account)
1242 * 1) Get all files with jobid in list (F subquery)
1243 * Get all files in BaseFiles with jobid in list
1244 * 2) Take only the last version of each file (Temp subquery) => accurate list
1245 * is ok
1246 * 3) Join the result to file table to get fileindex, jobid and lstat
1247 * information
1248 *
1249 * TODO: See if we can do the SORT only if needed (as an argument)
1250 */
GetFileList(JobControlRecord * jcr,char * jobids,bool use_md5,bool use_delta,DB_RESULT_HANDLER * ResultHandler,void * ctx)1251 bool BareosDb::GetFileList(JobControlRecord* jcr,
1252 char* jobids,
1253 bool use_md5,
1254 bool use_delta,
1255 DB_RESULT_HANDLER* ResultHandler,
1256 void* ctx)
1257 {
1258 PoolMem query(PM_MESSAGE);
1259 PoolMem query2(PM_MESSAGE);
1260
1261 if (!*jobids) {
1262 DbLock(this);
1263 Mmsg(errmsg, _("ERR=JobIds are empty\n"));
1264 DbUnlock(this);
1265 return false;
1266 }
1267
1268 if (use_delta) {
1269 FillQuery(query2, SQL_QUERY_select_recent_version_with_basejob_and_delta,
1270 jobids, jobids, jobids, jobids);
1271 } else {
1272 FillQuery(query2, SQL_QUERY_select_recent_version_with_basejob, jobids,
1273 jobids, jobids, jobids);
1274 }
1275
1276 /*
1277 * BootStrapRecord code is optimized for JobId sorted, with Delta, we need to
1278 * get them ordered by date. JobTDate and JobId can be mixed if using Copy or
1279 * Migration
1280 */
1281 Mmsg(query,
1282 "SELECT Path.Path, T1.Name, T1.FileIndex, T1.JobId, LStat, DeltaSeq, "
1283 "MD5, Fhinfo, Fhnode "
1284 "FROM ( %s ) AS T1 "
1285 "JOIN Path ON (Path.PathId = T1.PathId) "
1286 "WHERE FileIndex > 0 "
1287 "ORDER BY T1.JobTDate, FileIndex ASC", /* Return sorted by JobTDate */
1288 /* FileIndex for restore code */
1289 query2.c_str());
1290
1291 if (!use_md5) { strip_md5(query.c_str()); }
1292
1293 Dmsg1(100, "q=%s\n", query.c_str());
1294
1295 return BigSqlQuery(query.c_str(), ResultHandler, ctx);
1296 }
1297
1298 /**
1299 * This procedure gets the base jobid list used by jobids,
1300 */
GetUsedBaseJobids(JobControlRecord * jcr,POOLMEM * jobids,db_list_ctx * result)1301 bool BareosDb::GetUsedBaseJobids(JobControlRecord* jcr,
1302 POOLMEM* jobids,
1303 db_list_ctx* result)
1304 {
1305 PoolMem query(PM_MESSAGE);
1306
1307 Mmsg(query,
1308 "SELECT DISTINCT BaseJobId "
1309 " FROM Job JOIN BaseFiles USING (JobId) "
1310 " WHERE Job.HasBase = 1 "
1311 " AND Job.JobId IN (%s) ",
1312 jobids);
1313 return SqlQueryWithHandler(query.c_str(), DbListHandler, result);
1314 }
1315
1316 /**
1317 * The decision do change an incr/diff was done before
1318 * Full : do nothing
1319 * Differential : get the last full id
1320 * Incremental : get the last full + last diff + last incr(s) ids
1321 *
1322 * If you specify jr->StartTime, it will be used to limit the search
1323 * in the time. (usually now)
1324 *
1325 * If you specify jr->limit, it will be used to limit the list of jobids
1326 * to a that number
1327 *
1328 * TODO: look and merge from ua_restore.c
1329 */
AccurateGetJobids(JobControlRecord * jcr,JobDbRecord * jr,db_list_ctx * jobids)1330 bool BareosDb::AccurateGetJobids(JobControlRecord* jcr,
1331 JobDbRecord* jr,
1332 db_list_ctx* jobids)
1333 {
1334 bool retval = false;
1335 char clientid[50], jobid[50], filesetid[50];
1336 char date[MAX_TIME_LENGTH];
1337 PoolMem query(PM_MESSAGE);
1338
1339 /* Take the current time as upper limit if nothing else specified */
1340 utime_t StartTime = (jr->StartTime) ? jr->StartTime : time(NULL);
1341
1342 bstrutime(date, sizeof(date), StartTime + 1);
1343 jobids->reset();
1344
1345 /*
1346 * First, find the last good Full backup for this job/client/fileset
1347 */
1348 FillQuery(query, SQL_QUERY_create_temp_accurate_jobids,
1349 edit_uint64(jcr->JobId, jobid), edit_uint64(jr->ClientId, clientid),
1350 date, edit_uint64(jr->FileSetId, filesetid));
1351
1352 if (!SqlQuery(query.c_str())) { goto bail_out; }
1353
1354 if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1355 /*
1356 * Now, find the last differential backup after the last full
1357 */
1358 Mmsg(query,
1359 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, "
1360 "PurgedFiles) "
1361 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1362 "FROM Job JOIN FileSet USING (FileSetId) "
1363 "WHERE ClientId = %s "
1364 "AND JobFiles > 0 "
1365 "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' "
1366 "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC "
1367 "LIMIT 1) "
1368 "AND StartTime < '%s' "
1369 "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = "
1370 "%s) "
1371 "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1372 jobid, clientid, jobid, date, filesetid);
1373
1374 if (!SqlQuery(query.c_str())) { goto bail_out; }
1375
1376 /*
1377 * We just have to take all incremental after the last Full/Diff
1378 *
1379 * If we are doing always incremental, we need to limit the search to
1380 * only include incrementals that are older than (now -
1381 * AlwaysIncrementalInterval) and leave AlwaysIncrementalNumber incrementals
1382 */
1383 Mmsg(query,
1384 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, "
1385 "PurgedFiles) "
1386 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1387 "FROM Job JOIN FileSet USING (FileSetId) "
1388 "WHERE ClientId = %s "
1389 "AND JobFiles > 0 "
1390 "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' "
1391 "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC "
1392 "LIMIT 1) "
1393 "AND StartTime < '%s' "
1394 "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = "
1395 "%s) "
1396 "ORDER BY Job.JobTDate DESC ",
1397 jobid, clientid, jobid, date, filesetid);
1398 if (!SqlQuery(query.c_str())) { goto bail_out; }
1399 }
1400
1401 /*
1402 * Build a jobid list ie: 1,2,3,4
1403 */
1404 if (jr->limit) {
1405 Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate LIMIT %d", jobid,
1406 jr->limit);
1407 } else {
1408 Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1409 }
1410 SqlQueryWithHandler(query.c_str(), DbListHandler, jobids);
1411 Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids->list);
1412 retval = true;
1413
1414 bail_out:
1415 Mmsg(query, "DROP TABLE btemp3%s", jobid);
1416 SqlQuery(query.c_str());
1417 return retval;
1418 }
1419
GetBaseFileList(JobControlRecord * jcr,bool use_md5,DB_RESULT_HANDLER * ResultHandler,void * ctx)1420 bool BareosDb::GetBaseFileList(JobControlRecord* jcr,
1421 bool use_md5,
1422 DB_RESULT_HANDLER* ResultHandler,
1423 void* ctx)
1424 {
1425 PoolMem query(PM_MESSAGE);
1426
1427 Mmsg(query,
1428 "SELECT Path, Name, FileIndex, JobId, LStat, 0 As DeltaSeq, MD5, "
1429 "Fhinfo, Fhnode "
1430 "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC",
1431 (uint64_t)jcr->JobId);
1432
1433 if (!use_md5) { strip_md5(query.c_str()); }
1434 return BigSqlQuery(query.c_str(), ResultHandler, ctx);
1435 }
1436
GetBaseJobid(JobControlRecord * jcr,JobDbRecord * jr,JobId_t * jobid)1437 bool BareosDb::GetBaseJobid(JobControlRecord* jcr,
1438 JobDbRecord* jr,
1439 JobId_t* jobid)
1440 {
1441 PoolMem query(PM_MESSAGE);
1442 utime_t StartTime;
1443 db_int64_ctx lctx;
1444 char date[MAX_TIME_LENGTH];
1445 char esc[MAX_ESCAPE_NAME_LENGTH];
1446 bool retval = false;
1447 // char clientid[50], filesetid[50];
1448
1449 *jobid = 0;
1450 lctx.count = 0;
1451 lctx.value = 0;
1452
1453 StartTime = (jr->StartTime) ? jr->StartTime : time(NULL);
1454 bstrutime(date, sizeof(date), StartTime + 1);
1455 EscapeString(jcr, esc, jr->Name, strlen(jr->Name));
1456
1457 /* we can take also client name, fileset, etc... */
1458
1459 Mmsg(query,
1460 "SELECT JobId, Job, StartTime, EndTime, JobTDate, PurgedFiles "
1461 "FROM Job "
1462 // "JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
1463 "WHERE Job.Name = '%s' "
1464 "AND Level='B' AND JobStatus IN ('T','W') AND Type='B' "
1465 // "AND FileSet.FileSet= '%s' "
1466 // "AND Client.Name = '%s' "
1467 "AND StartTime<'%s' "
1468 "ORDER BY Job.JobTDate DESC LIMIT 1",
1469 esc,
1470 // edit_uint64(jr->ClientId, clientid),
1471 // edit_uint64(jr->FileSetId, filesetid));
1472 date);
1473
1474 Dmsg1(10, "GetBaseJobid q=%s\n", query.c_str());
1475 if (!SqlQueryWithHandler(query.c_str(), db_int64_handler, &lctx)) {
1476 goto bail_out;
1477 }
1478 *jobid = (JobId_t)lctx.value;
1479
1480 Dmsg1(10, "GetBaseJobid=%lld\n", *jobid);
1481 retval = true;
1482
1483 bail_out:
1484 return retval;
1485 }
1486
1487 /**
1488 * Get JobIds associated with a volume
1489 */
GetVolumeJobids(JobControlRecord * jcr,MediaDbRecord * mr,db_list_ctx * lst)1490 bool BareosDb::GetVolumeJobids(JobControlRecord* jcr,
1491 MediaDbRecord* mr,
1492 db_list_ctx* lst)
1493 {
1494 char ed1[50];
1495 bool retval;
1496
1497 DbLock(this);
1498 Mmsg(cmd, "SELECT DISTINCT JobId FROM JobMedia WHERE MediaId=%s",
1499 edit_int64(mr->MediaId, ed1));
1500 retval = SqlQueryWithHandler(cmd, DbListHandler, lst);
1501 DbUnlock(this);
1502 return retval;
1503 }
1504
1505 /**
1506 * This function returns the sum of all the Clients JobBytes.
1507 *
1508 * Returns false: on failure
1509 * true: on success
1510 */
get_quota_jobbytes(JobControlRecord * jcr,JobDbRecord * jr,utime_t JobRetention)1511 bool BareosDb::get_quota_jobbytes(JobControlRecord* jcr,
1512 JobDbRecord* jr,
1513 utime_t JobRetention)
1514 {
1515 SQL_ROW row;
1516 int num_rows;
1517 char dt[MAX_TIME_LENGTH];
1518 char ed1[50], ed2[50];
1519 bool retval = false;
1520 time_t now, schedtime;
1521
1522 /*
1523 * Determine the first schedtime we are interested in.
1524 */
1525 now = time(NULL);
1526 schedtime = now - JobRetention;
1527
1528 /*
1529 * Bugfix, theres a small timing bug in the scheduler.
1530 * Add 5 seconds to the schedtime to ensure the
1531 * last job from the job retention gets excluded.
1532 */
1533 schedtime += 5;
1534
1535 bstrutime(dt, sizeof(dt), schedtime);
1536
1537 DbLock(this);
1538
1539 FillQuery(SQL_QUERY_get_quota_jobbytes, edit_uint64(jr->ClientId, ed1),
1540 edit_uint64(jr->JobId, ed2), dt);
1541 if (QUERY_DB(jcr, cmd)) {
1542 num_rows = SqlNumRows();
1543 if (num_rows == 1) {
1544 row = SqlFetchRow();
1545 jr->JobSumTotalBytes = str_to_uint64(row[0]);
1546 } else if (num_rows < 1) {
1547 jr->JobSumTotalBytes = 0;
1548 }
1549 SqlFreeResult();
1550 retval = true;
1551 } else {
1552 Mmsg(errmsg, _("JobBytes sum select failed: ERR=%s\n"), sql_strerror());
1553 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1554 }
1555
1556 DbUnlock(this);
1557 return retval;
1558 }
1559
1560 /**
1561 * This function returns the sum of all the Clients JobBytes of non failed jobs.
1562 *
1563 * Returns false: on failure
1564 * true: on success
1565 */
get_quota_jobbytes_nofailed(JobControlRecord * jcr,JobDbRecord * jr,utime_t JobRetention)1566 bool BareosDb::get_quota_jobbytes_nofailed(JobControlRecord* jcr,
1567 JobDbRecord* jr,
1568 utime_t JobRetention)
1569 {
1570 SQL_ROW row;
1571 char ed1[50], ed2[50];
1572 int num_rows;
1573 char dt[MAX_TIME_LENGTH];
1574 bool retval = false;
1575 time_t now, schedtime;
1576
1577 /*
1578 * Determine the first schedtime we are interested in.
1579 */
1580 now = time(NULL);
1581 schedtime = now - JobRetention;
1582
1583 /*
1584 * Bugfix, theres a small timing bug in the scheduler.
1585 * Add 5 seconds to the schedtime to ensure the
1586 * last job from the job retention gets excluded.
1587 */
1588 schedtime += 5;
1589
1590 bstrutime(dt, sizeof(dt), schedtime);
1591
1592 DbLock(this);
1593
1594 FillQuery(SQL_QUERY_get_quota_jobbytes_nofailed,
1595 edit_uint64(jr->ClientId, ed1), edit_uint64(jr->JobId, ed2), dt);
1596 if (QUERY_DB(jcr, cmd)) {
1597 num_rows = SqlNumRows();
1598 if (num_rows == 1) {
1599 row = SqlFetchRow();
1600 jr->JobSumTotalBytes = str_to_uint64(row[0]);
1601 } else if (num_rows < 1) {
1602 jr->JobSumTotalBytes = 0;
1603 }
1604 SqlFreeResult();
1605 retval = true;
1606 } else {
1607 Mmsg(errmsg, _("JobBytes sum select failed: ERR=%s\n"), sql_strerror());
1608 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1609 }
1610
1611 DbUnlock(this);
1612 return retval;
1613 }
1614
1615 /**
1616 * Fetch the quota value and grace time for a quota.
1617 * Returns false: on failure
1618 * true: on success
1619 */
GetQuotaRecord(JobControlRecord * jcr,ClientDbRecord * cdbr)1620 bool BareosDb::GetQuotaRecord(JobControlRecord* jcr, ClientDbRecord* cdbr)
1621 {
1622 SQL_ROW row;
1623 char ed1[50];
1624 int num_rows;
1625 bool retval = false;
1626
1627 DbLock(this);
1628 Mmsg(cmd,
1629 "SELECT GraceTime, QuotaLimit "
1630 "FROM Quota "
1631 "WHERE ClientId = %s",
1632 edit_int64(cdbr->ClientId, ed1));
1633 if (QUERY_DB(jcr, cmd)) {
1634 num_rows = SqlNumRows();
1635 if (num_rows == 1) {
1636 if ((row = SqlFetchRow()) == NULL) {
1637 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1638 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1639 SqlFreeResult();
1640 } else {
1641 cdbr->GraceTime = str_to_uint64(row[0]);
1642 cdbr->QuotaLimit = str_to_int64(row[1]);
1643 SqlFreeResult();
1644 retval = true;
1645 }
1646 } else {
1647 Mmsg(errmsg, _("Quota record not found in Catalog.\n"));
1648 SqlFreeResult();
1649 }
1650 } else {
1651 Mmsg(errmsg, _("Quota record not found in Catalog.\n"));
1652 }
1653
1654 DbUnlock(this);
1655 return retval;
1656 }
1657
1658 /**
1659 * Fetch the NDMP Dump Level value.
1660 *
1661 * Returns dumplevel on success
1662 * 0: on failure
1663 */
GetNdmpLevelMapping(JobControlRecord * jcr,JobDbRecord * jr,char * filesystem)1664 int BareosDb::GetNdmpLevelMapping(JobControlRecord* jcr,
1665 JobDbRecord* jr,
1666 char* filesystem)
1667 {
1668 SQL_ROW row;
1669 char ed1[50], ed2[50];
1670 int num_rows;
1671 int dumplevel = 0;
1672
1673 DbLock(this);
1674
1675 esc_name = CheckPoolMemorySize(esc_name, strlen(filesystem) * 2 + 1);
1676 EscapeString(jcr, esc_name, filesystem, strlen(filesystem));
1677
1678 Mmsg(cmd,
1679 "SELECT DumpLevel FROM NDMPLevelMap WHERE "
1680 "ClientId='%s' AND FileSetId='%s' AND FileSystem='%s'",
1681 edit_uint64(jr->ClientId, ed1), edit_uint64(jr->FileSetId, ed2),
1682 esc_name);
1683
1684 if (QUERY_DB(jcr, cmd)) {
1685 num_rows = SqlNumRows();
1686 if (num_rows == 1) {
1687 if ((row = SqlFetchRow()) == NULL) {
1688 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1689 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1690 SqlFreeResult();
1691 goto bail_out;
1692 } else {
1693 dumplevel = str_to_uint64(row[0]);
1694 dumplevel++; /* select next dumplevel */
1695 SqlFreeResult();
1696 goto bail_out;
1697 }
1698 } else {
1699 Mmsg(errmsg, _("NDMP Dump Level record not found in Catalog.\n"));
1700 SqlFreeResult();
1701 goto bail_out;
1702 }
1703 } else {
1704 Mmsg(errmsg, _("NDMP Dump Level record not found in Catalog.\n"));
1705 goto bail_out;
1706 }
1707
1708 bail_out:
1709 DbUnlock(this);
1710 return dumplevel;
1711 }
1712
1713 /**
1714 * CountingHandler() with a CountContext* can be used to count the number of
1715 * times that SqlQueryWithHandler() calls the handler.
1716 * This is not neccesarily the number of rows, because the ResultHandler can
1717 * stop processing of further rows by returning non-zero.
1718 */
1719 struct CountContext {
1720 DB_RESULT_HANDLER* handler;
1721 void* ctx;
1722 int count;
1723
CountContextCountContext1724 CountContext(DB_RESULT_HANDLER* t_handler, void* t_ctx)
1725 : handler(t_handler), ctx(t_ctx), count(0)
1726 {
1727 }
1728 };
1729
CountingHandler(void * counting_ctx,int num_fields,char ** rows)1730 static int CountingHandler(void* counting_ctx, int num_fields, char** rows)
1731 {
1732 auto* c = static_cast<struct CountContext*>(counting_ctx);
1733 c->count++;
1734 return c->handler(c->ctx, num_fields, rows);
1735 }
1736
1737 /**
1738 * Fetch NDMP Job Environment based on raw SQL query.
1739 * Returns false: on sql failure or when 0 rows were returned
1740 * true: otherwise
1741 */
GetNdmpEnvironmentString(const std::string & query,DB_RESULT_HANDLER * ResultHandler,void * ctx)1742 bool BareosDb::GetNdmpEnvironmentString(const std::string& query,
1743 DB_RESULT_HANDLER* ResultHandler,
1744 void* ctx)
1745 {
1746 auto myctx = std::unique_ptr<CountContext>(new CountContext(ResultHandler, ctx));
1747 bool status =
1748 SqlQueryWithHandler(query.c_str(), CountingHandler, myctx.get());
1749 Dmsg1(150, "Got %d NDMP environment records\n", myctx->count);
1750 return status && myctx->count > 0; // no rows means no environment was found
1751 }
1752
1753 /**
1754 * Fetch the NDMP Job Environment Strings based on JobId only
1755 *
1756 * Returns false: on failure
1757 * true: on success
1758 */
GetNdmpEnvironmentString(JobId_t JobId,DB_RESULT_HANDLER * ResultHandler,void * ctx)1759 bool BareosDb::GetNdmpEnvironmentString(JobId_t JobId,
1760 DB_RESULT_HANDLER* ResultHandler,
1761 void* ctx)
1762 {
1763 ASSERT(JobId > 0)
1764 std::string query{"SELECT EnvName, EnvValue FROM NDMPJobEnvironment"};
1765 query += " WHERE JobId=" + std::to_string(JobId);
1766
1767 return GetNdmpEnvironmentString(query, ResultHandler, ctx);
1768 }
1769
1770 /**
1771 * Fetch the NDMP Job Environment Strings based on JobId and FileIndex
1772 *
1773 * Returns false: on failure
1774 * true: on success
1775 */
GetNdmpEnvironmentString(JobId_t JobId,int32_t FileIndex,DB_RESULT_HANDLER * ResultHandler,void * ctx)1776 bool BareosDb::GetNdmpEnvironmentString(JobId_t JobId,
1777 int32_t FileIndex,
1778 DB_RESULT_HANDLER* ResultHandler,
1779 void* ctx)
1780 {
1781 ASSERT(JobId > 0)
1782 std::string query{"SELECT EnvName, EnvValue FROM NDMPJobEnvironment"};
1783 query += " WHERE JobId=" + std::to_string(JobId);
1784 query += " AND FileIndex=" + std::to_string(FileIndex);
1785
1786 return GetNdmpEnvironmentString(query, ResultHandler, ctx);
1787 }
1788
1789
1790 /**
1791 * Fetch the NDMP Job Environment Strings for NDMP_BAREOS Backups
1792 *
1793 * Returns false: on failure
1794 * true: on success
1795 */
GetNdmpEnvironmentString(const VolumeSessionInfo & vsi,int32_t FileIndex,DB_RESULT_HANDLER * ResultHandler,void * ctx)1796 bool BareosDb::GetNdmpEnvironmentString(const VolumeSessionInfo& vsi,
1797 int32_t FileIndex,
1798 DB_RESULT_HANDLER* ResultHandler,
1799 void* ctx)
1800 {
1801 db_int64_ctx lctx;
1802 std::string query{"SELECT JobId FROM Job"};
1803 query += " WHERE VolSessionId = " + std::to_string(vsi.id);
1804 query += " AND VolSessionTime = " + std::to_string(vsi.time);
1805
1806 if (SqlQueryWithHandler(query.c_str(), db_int64_handler, &lctx)) {
1807 if (lctx.count == 1) {
1808 /* now lctx.value contains the jobid we restore */
1809 return GetNdmpEnvironmentString(lctx.value, FileIndex, ResultHandler,
1810 ctx);
1811 }
1812 }
1813 Dmsg3(
1814 100,
1815 "Got %d JobIds for VolSessionTime=%lld VolSessionId=%lld instead of 1\n",
1816 lctx.count, vsi.time, vsi.id);
1817 return false;
1818 }
1819
1820 /**
1821 * This function creates a sql query string at cmd to return a list of all the
1822 * Media records for the current Pool, the correct Media Type, Recyle, Enabled,
1823 * StorageId, VolBytes and volumes or VolumeName if specified. Comma separated
1824 * list of volumes takes precedence over VolumeName. The caller must free ids if
1825 * non-NULL.
1826 */
PrepareMediaSqlQuery(JobControlRecord * jcr,MediaDbRecord * mr,PoolMem * querystring,PoolMem & volumes)1827 bool BareosDb::PrepareMediaSqlQuery(JobControlRecord* jcr,
1828 MediaDbRecord* mr,
1829 PoolMem* querystring,
1830 PoolMem& volumes)
1831 {
1832 bool ok = true;
1833 char ed1[50];
1834 char esc[MAX_NAME_LENGTH * 2 + 1];
1835 PoolMem buf(PM_MESSAGE);
1836
1837 /*
1838 * columns we care of.
1839 * Reduced, to be better displayable.
1840 * Important:
1841 * column 2: pool.name, column 3: storage.name,
1842 * as this is used for ACL handling (counting starts at 0).
1843 */
1844 const char* columns =
1845 "Media.MediaId,"
1846 "Media.VolumeName,"
1847 "Pool.Name AS Pool,"
1848 "Storage.Name AS Storage,"
1849 "Media.MediaType,"
1850 /* "Media.DeviceId," */
1851 /* "Media.FirstWritten, "*/
1852 "Media.LastWritten,"
1853 "Media.VolFiles,"
1854 "Media.VolBytes,"
1855 "Media.VolStatus,"
1856 /* "Media.Recycle AS Recycle," */
1857 "Media.ActionOnPurge,"
1858 /* "Media.VolRetention," */
1859 "Media.Comment";
1860
1861 Mmsg(querystring,
1862 "SELECT DISTINCT %s FROM Media "
1863 "LEFT JOIN Pool USING(PoolId) "
1864 "LEFT JOIN Storage USING(StorageId) "
1865 "WHERE Media.Recycle=%d AND Media.Enabled=%d ",
1866 columns, mr->Recycle, mr->Enabled);
1867
1868 if (*mr->MediaType) {
1869 EscapeString(jcr, esc, mr->MediaType, strlen(mr->MediaType));
1870 Mmsg(buf, "AND Media.MediaType='%s' ", esc);
1871 PmStrcat(querystring, buf.c_str());
1872 }
1873
1874 if (mr->StorageId) {
1875 Mmsg(buf, "AND Media.StorageId=%s ", edit_uint64(mr->StorageId, ed1));
1876 PmStrcat(querystring, buf.c_str());
1877 }
1878
1879 if (mr->PoolId) {
1880 Mmsg(buf, "AND Media.PoolId=%s ", edit_uint64(mr->PoolId, ed1));
1881 PmStrcat(querystring, buf.c_str());
1882 }
1883
1884 if (mr->VolBytes) {
1885 Mmsg(buf, "AND Media.VolBytes > %s ", edit_uint64(mr->VolBytes, ed1));
1886 PmStrcat(querystring, buf.c_str());
1887 }
1888
1889 if (*mr->VolStatus) {
1890 EscapeString(jcr, esc, mr->VolStatus, strlen(mr->VolStatus));
1891 Mmsg(buf, "AND Media.VolStatus = '%s' ", esc);
1892 PmStrcat(querystring, buf.c_str());
1893 }
1894
1895 if (volumes.strlen() > 0) {
1896 /* extra list of volumes given */
1897 Mmsg(buf, "AND Media.VolumeName IN (%s) ", volumes.c_str());
1898 PmStrcat(querystring, buf.c_str());
1899 } else if (*mr->VolumeName) {
1900 /* single volume given in media record */
1901 EscapeString(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1902 Mmsg(buf, "AND Media.VolumeName = '%s' ", esc);
1903 PmStrcat(querystring, buf.c_str());
1904 }
1905
1906 Dmsg1(100, "query=%s\n", querystring);
1907
1908 return ok;
1909 }
1910
1911 /**
1912 * verify that all media use the same storage.
1913 */
VerifyMediaIdsFromSingleStorage(JobControlRecord * jcr,dbid_list & mediaIds)1914 bool BareosDb::VerifyMediaIdsFromSingleStorage(JobControlRecord* jcr,
1915 dbid_list& mediaIds)
1916 {
1917 MediaDbRecord mr;
1918 DBId_t storageId = 0;
1919
1920 for (int i = 0; i < mediaIds.size(); i++) {
1921 memset(&mr, 0, sizeof(mr));
1922 mr.MediaId = mediaIds.get(i);
1923 if (!GetMediaRecord(jcr, &mr)) {
1924 Mmsg1(errmsg, _("Failed to find MediaId=%lld\n"), (uint64_t)mr.MediaId);
1925 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1926 return false;
1927 } else if (i == 0) {
1928 storageId = mr.StorageId;
1929 } else if (storageId != mr.StorageId) {
1930 return false;
1931 }
1932 }
1933 return true;
1934 }
1935
1936
1937 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || \
1938 HAVE_DBI */
1939