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-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, September MM
25 */
26 /**
27 * @file
28 * User Agent Output Commands
29 *
30 * I.e. messages, listing database, showing resources, ...
31 */
32
33 #include "include/bareos.h"
34 #include "dird.h"
35 #include "dird/dird_globals.h"
36 #include "dird/get_database_connection.h"
37 #include "dird/jcr_private.h"
38 #include "dird/job.h"
39 #include "dird/ua_cmdstruct.h"
40 #include "cats/sql_pooling.h"
41 #include "dird/next_vol.h"
42 #include "dird/ua_db.h"
43 #include "dird/ua_output.h"
44 #include "dird/ua_select.h"
45 #include "dird/show_cmd_available_resources.h"
46 #include "lib/edit.h"
47 #include "lib/parse_conf.h"
48
49 namespace directordaemon {
50
51 /* Imported subroutines */
52
53 /* Imported variables */
54 extern struct s_jl joblevels[];
55
56 /* Imported functions */
57
58 /* Forward referenced functions */
59 static bool DoListCmd(UaContext* ua, const char* cmd, e_list_type llist);
60 static bool ListNextvol(UaContext* ua, int ndays);
61 static bool ParseListBackupsCmd(UaContext* ua,
62 const char* range,
63 e_list_type llist);
64
65 /**
66 * Some defaults.
67 */
68 #define DEFAULT_LOG_LINES 5
69 #define DEFAULT_NR_DAYS 50
70
71 /**
72 * Turn auto display of console messages on/off
73 */
AutodisplayCmd(UaContext * ua,const char * cmd)74 bool AutodisplayCmd(UaContext* ua, const char* cmd)
75 {
76 static const char* kw[] = {NT_("on"), NT_("off"), NULL};
77
78 switch (FindArgKeyword(ua, kw)) {
79 case 0:
80 ua->auto_display_messages = true;
81 break;
82 case 1:
83 ua->auto_display_messages = false;
84 break;
85 default:
86 ua->ErrorMsg(_("ON or OFF keyword missing.\n"));
87 break;
88 }
89 return true;
90 }
91
92 /**
93 * Turn GUI mode on/off
94 */
gui_cmd(UaContext * ua,const char * cmd)95 bool gui_cmd(UaContext* ua, const char* cmd)
96 {
97 static const char* kw[] = {NT_("on"), NT_("off"), NULL};
98
99 switch (FindArgKeyword(ua, kw)) {
100 case 0:
101 ua->jcr->gui = ua->gui = true;
102 break;
103 case 1:
104 ua->jcr->gui = ua->gui = false;
105 break;
106 default:
107 ua->ErrorMsg(_("ON or OFF keyword missing.\n"));
108 break;
109 }
110 return true;
111 }
112
113 /**
114 * Enter with Resources locked
115 */
ShowDisabledJobs(UaContext * ua)116 static void ShowDisabledJobs(UaContext* ua)
117 {
118 JobResource* job;
119 bool first = true;
120
121 foreach_res (job, R_JOB) {
122 if (!ua->AclAccessOk(Job_ACL, job->resource_name_, false)) { continue; }
123
124 if (!job->enabled) {
125 if (first) {
126 first = false;
127 ua->SendMsg(_("Disabled Jobs:\n"));
128 }
129 ua->SendMsg(" %s\n", job->resource_name_);
130 }
131 }
132
133 if (first) { ua->SendMsg(_("No disabled Jobs.\n")); }
134 }
135
136 /**
137 * Enter with Resources locked
138 */
ShowDisabledClients(UaContext * ua)139 static void ShowDisabledClients(UaContext* ua)
140 {
141 ClientResource* client;
142 bool first = true;
143
144 foreach_res (client, R_CLIENT) {
145 if (!ua->AclAccessOk(Client_ACL, client->resource_name_, false)) {
146 continue;
147 }
148
149 if (!client->enabled) {
150 if (first) {
151 first = false;
152 ua->SendMsg(_("Disabled Clients:\n"));
153 }
154 ua->SendMsg(" %s\n", client->resource_name_);
155 }
156 }
157
158 if (first) { ua->SendMsg(_("No disabled Clients.\n")); }
159 }
160
161 /**
162 * Enter with Resources locked
163 */
ShowDisabledSchedules(UaContext * ua)164 static void ShowDisabledSchedules(UaContext* ua)
165 {
166 ScheduleResource* sched;
167 bool first = true;
168
169 foreach_res (sched, R_SCHEDULE) {
170 if (!ua->AclAccessOk(Schedule_ACL, sched->resource_name_, false)) {
171 continue;
172 }
173
174 if (!sched->enabled) {
175 if (first) {
176 first = false;
177 ua->SendMsg(_("Disabled Schedules:\n"));
178 }
179 ua->SendMsg(" %s\n", sched->resource_name_);
180 }
181 }
182
183 if (first) { ua->SendMsg(_("No disabled Schedules.\n")); }
184 }
185
186 /**
187 * Enter with Resources locked
188 */
ShowAll(UaContext * ua,bool hide_sensitive_data,bool verbose)189 static void ShowAll(UaContext* ua, bool hide_sensitive_data, bool verbose)
190 {
191 for (int j = my_config->r_first_; j <= my_config->r_last_; j++) {
192 switch (j) {
193 case R_DEVICE:
194 /*
195 * Skip R_DEVICE since it is really not used or updated
196 */
197 continue;
198 default:
199 if (my_config->res_head_[j - my_config->r_first_]) {
200 my_config->DumpResourceCb_(
201 j, my_config->res_head_[j - my_config->r_first_], bsendmsg, ua,
202 hide_sensitive_data, verbose);
203 }
204 break;
205 }
206 }
207 }
208
209 /**
210 * Displays Resources
211 *
212 * show
213 * show all
214 * show <resource-keyword-name> - e.g. show directors
215 * show <resource-keyword-name>=<name> - e.g. show director=HeadMan
216 * show disabled - shows disabled jobs, clients and schedules
217 * show disabled jobs - shows disabled jobs
218 * show disabled clients - shows disabled clients
219 * show disabled schedules - shows disabled schedules
220 */
show_cmd(UaContext * ua,const char * cmd)221 bool show_cmd(UaContext* ua, const char* cmd)
222 {
223 int i, j, type, len;
224 int recurse;
225 char* res_name;
226 BareosResource* res = NULL;
227 bool verbose = false;
228 bool hide_sensitive_data;
229
230 Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
231
232 /*
233 * When the console has no access to the configure cmd then any show cmd
234 * will suppress all sensitive information like for instance passwords.
235 */
236 hide_sensitive_data = !ua->AclAccessOk(Command_ACL, "configure", false);
237
238 if (FindArg(ua, NT_("verbose")) > 0) { verbose = true; }
239
240 LockRes(my_config);
241
242 /*
243 * Without parameter, show all ressources.
244 */
245 if (ua->argc == 1) { ShowAll(ua, hide_sensitive_data, verbose); }
246
247 for (i = 1; i < ua->argc; i++) {
248 /*
249 * skip verbose keyword, already handled earlier.
250 */
251 if (Bstrcasecmp(ua->argk[i], NT_("verbose"))) { continue; }
252
253 if (Bstrcasecmp(ua->argk[i], NT_("disabled"))) {
254 if (((i + 1) < ua->argc) && Bstrcasecmp(ua->argk[i + 1], NT_("jobs"))) {
255 ShowDisabledJobs(ua);
256 } else if (((i + 1) < ua->argc) &&
257 Bstrcasecmp(ua->argk[i + 1], NT_("clients"))) {
258 ShowDisabledClients(ua);
259 } else if (((i + 1) < ua->argc) &&
260 Bstrcasecmp(ua->argk[i + 1], NT_("schedules"))) {
261 ShowDisabledSchedules(ua);
262 } else {
263 ShowDisabledJobs(ua);
264 ShowDisabledClients(ua);
265 ShowDisabledSchedules(ua);
266 }
267
268 goto bail_out;
269 }
270
271 type = 0;
272 res_name = ua->argk[i];
273 if (!ua->argv[i]) { /* was a name given? */
274 /*
275 * No name, dump all resources of specified type
276 */
277 recurse = 1;
278 len = strlen(res_name);
279 for (j = 0; show_cmd_available_resources[j].res_name; j++) {
280 if (bstrncasecmp(res_name, _(show_cmd_available_resources[j].res_name),
281 len)) {
282 type = show_cmd_available_resources[j].type;
283 if (type > 0) {
284 res = my_config->res_head_[type - my_config->r_first_];
285 } else {
286 res = NULL;
287 }
288 break;
289 }
290 }
291 } else {
292 /*
293 * Dump a single resource with specified name
294 */
295 recurse = 0;
296 len = strlen(res_name);
297 for (j = 0; show_cmd_available_resources[j].res_name; j++) {
298 if (bstrncasecmp(res_name, _(show_cmd_available_resources[j].res_name),
299 len)) {
300 type = show_cmd_available_resources[j].type;
301 res = (BareosResource*)ua->GetResWithName(type, ua->argv[i], true);
302 if (!res) { type = -3; }
303 break;
304 }
305 }
306 }
307
308 switch (type) {
309 case -1: /* all */
310 ShowAll(ua, hide_sensitive_data, verbose);
311 break;
312 case -2:
313 ua->SendMsg(_("Keywords for the show command are:\n"));
314 for (j = 0; show_cmd_available_resources[j].res_name; j++) {
315 ua->ErrorMsg("%s\n", _(show_cmd_available_resources[j].res_name));
316 }
317 goto bail_out;
318 case -3:
319 ua->ErrorMsg(_("%s resource %s not found.\n"), res_name, ua->argv[i]);
320 goto bail_out;
321 case 0:
322 ua->ErrorMsg(_("Resource %s not found\n"), res_name);
323 goto bail_out;
324 default:
325 my_config->DumpResourceCb_(recurse ? type : -type, res, bsendmsg, ua,
326 hide_sensitive_data, verbose);
327 break;
328 }
329 }
330
331 bail_out:
332 UnlockRes(my_config);
333 return true;
334 }
335
336 /**
337 * List contents of database
338 *
339 * list jobs - lists all jobs run
340 * list jobid=nnn - list job data for jobid
341 * list ujobid=uname - list job data for unique jobid
342 * list job=name - list all jobs with "name"
343 * list jobname=name - same as above
344 * list jobmedia jobid=nnn
345 * list jobmedia ujobid=uname
346 * list joblog jobid=<nn>
347 * list joblog job=name
348 * list log [ limit=<number> [ offset=<number> ] ]
349 * list basefiles jobid=nnn - list files saved for job nn
350 * list basefiles ujobid=uname
351 * list files jobid=<nn> - list files saved for job nn
352 * list files ujobid=name
353 * list pools - list pool records
354 * list jobtotals - list totals for all jobs
355 * list media - list media for given pool (deprecated)
356 * list volumes - list Volumes
357 * list clients - list clients
358 * list nextvol job=xx - list the next vol to be used by job
359 * list nextvolume job=xx - same as above.
360 * list copies jobid=x,y,z
361 */
362
363 /* Do long or full listing */
LlistCmd(UaContext * ua,const char * cmd)364 bool LlistCmd(UaContext* ua, const char* cmd)
365 {
366 return DoListCmd(ua, cmd, VERT_LIST);
367 }
368
369 /* Do short or summary listing */
list_cmd(UaContext * ua,const char * cmd)370 bool list_cmd(UaContext* ua, const char* cmd)
371 {
372 return DoListCmd(ua, cmd, HORZ_LIST);
373 }
374
GetJobidFromCmdline(UaContext * ua)375 static int GetJobidFromCmdline(UaContext* ua)
376 {
377 int i, jobid;
378 JobDbRecord jr;
379 ClientDbRecord cr;
380
381 i = FindArgWithValue(ua, NT_("ujobid"));
382 if (i >= 0) {
383 bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
384 jr.JobId = 0;
385 if (ua->db->GetJobRecord(ua->jcr, &jr)) {
386 jobid = jr.JobId;
387 } else {
388 return -1;
389 }
390 } else {
391 i = FindArgWithValue(ua, NT_("jobid"));
392 if (i >= 0) {
393 jr.JobId = str_to_int64(ua->argv[i]);
394 } else {
395 jobid = 0;
396 goto bail_out;
397 }
398 }
399
400 if (ua->db->GetJobRecord(ua->jcr, &jr)) {
401 jobid = jr.JobId;
402 } else {
403 Dmsg1(200, "GetJobidFromCmdline: Failed to get job record for JobId %d\n",
404 jr.JobId);
405 jobid = -1;
406 goto bail_out;
407 }
408
409 if (!ua->AclAccessOk(Job_ACL, jr.Name, true)) {
410 Dmsg1(200, "GetJobidFromCmdline: No access to Job %s\n", jr.Name);
411 jobid = -1;
412 goto bail_out;
413 }
414
415 if (jr.ClientId) {
416 cr.ClientId = jr.ClientId;
417 if (ua->db->GetClientRecord(ua->jcr, &cr)) {
418 if (!ua->AclAccessOk(Client_ACL, cr.Name, true)) {
419 Dmsg1(200, "GetJobidFromCmdline: No access to Client %s\n", cr.Name);
420 jobid = -1;
421 goto bail_out;
422 }
423 } else {
424 Dmsg1(
425 200,
426 "GetJobidFromCmdline: Failed to get client record for ClientId %d\n",
427 jr.ClientId);
428 jobid = -1;
429 goto bail_out;
430 }
431 }
432
433 bail_out:
434 return jobid;
435 }
436
437 /**
438 * Filter convenience functions that abstract the actions needed to
439 * perform a certain type of acl or resource filtering.
440 */
SetAclFilter(UaContext * ua,int column,int acltype)441 static inline void SetAclFilter(UaContext* ua, int column, int acltype)
442 {
443 if (ua->AclHasRestrictions(acltype)) {
444 ua->send->AddAclFilterTuple(column, acltype);
445 }
446 }
447
SetResFilter(UaContext * ua,int column,int restype)448 static inline void SetResFilter(UaContext* ua, int column, int restype)
449 {
450 ua->send->AddResFilterTuple(column, restype);
451 }
452
SetEnabledFilter(UaContext * ua,int column,int restype)453 static inline void SetEnabledFilter(UaContext* ua, int column, int restype)
454 {
455 ua->send->AddEnabledFilterTuple(column, restype);
456 }
457
SetDisabledFilter(UaContext * ua,int column,int restype)458 static inline void SetDisabledFilter(UaContext* ua, int column, int restype)
459 {
460 ua->send->AddDisabledFilterTuple(column, restype);
461 }
462
SetHiddenColumnAclFilter(UaContext * ua,int column,int acltype)463 static inline void SetHiddenColumnAclFilter(UaContext* ua,
464 int column,
465 int acltype)
466 {
467 ua->send->AddHiddenColumn(column);
468 if (ua->AclHasRestrictions(acltype)) {
469 ua->send->AddAclFilterTuple(column, acltype);
470 }
471 }
472
SetHiddenColumn(UaContext * ua,int column)473 static inline void SetHiddenColumn(UaContext* ua, int column)
474 {
475 ua->send->AddHiddenColumn(column);
476 }
477
SetQueryRange(PoolMem & query_range,UaContext * ua,JobDbRecord * jr)478 static void SetQueryRange(PoolMem& query_range, UaContext* ua, JobDbRecord* jr)
479 {
480 int i;
481
482 /*
483 * Ensure query range is an empty string instead of NULL
484 * to avoid any issues.
485 */
486 if (query_range.c_str() == NULL) { PmStrcpy(query_range, ""); }
487
488 /*
489 * See if this is a second call to SetQueryRange() if so and any acl
490 * filters have been set we setup a new query_range filter including a
491 * limit filter.
492 */
493 if (query_range.strlen()) {
494 if (!ua->send->has_acl_filters()) { return; }
495 PmStrcpy(query_range, "");
496 }
497
498 /*
499 * Apply any limit
500 */
501 i = FindArgWithValue(ua, NT_("limit"));
502 if (i >= 0) {
503 PoolMem temp(PM_MESSAGE);
504
505 jr->limit = atoi(ua->argv[i]);
506 ua->send->AddLimitFilterTuple(jr->limit);
507
508 temp.bsprintf(" LIMIT %d", jr->limit);
509 PmStrcat(query_range, temp.c_str());
510
511 /*
512 * offset is only valid, if limit is given
513 */
514 i = FindArgWithValue(ua, NT_("offset"));
515 if (i >= 0) {
516 jr->offset = atoi(ua->argv[i]);
517 ua->send->AddOffsetFilterTuple(jr->offset);
518 temp.bsprintf(" OFFSET %d", atoi(ua->argv[i]));
519 PmStrcat(query_range, temp.c_str());
520 }
521 }
522 }
523
DoListCmd(UaContext * ua,const char * cmd,e_list_type llist)524 static bool DoListCmd(UaContext* ua, const char* cmd, e_list_type llist)
525 {
526 JobDbRecord jr;
527 PoolDbRecord pr;
528 utime_t now;
529 MediaDbRecord mr;
530 int days = 0, hours = 0, jobstatus = 0, joblevel = 0;
531 bool count, last, current, enabled, disabled;
532 int i, d, h, jobid;
533 time_t schedtime = 0;
534 char* clientname = NULL;
535 char* volumename = NULL;
536 char* poolname = NULL;
537 const int secs_in_day = 86400;
538 const int secs_in_hour = 3600;
539 PoolMem query_range(PM_MESSAGE);
540
541 if (!OpenClientDb(ua, true)) { return true; }
542
543 Dmsg1(20, "list: %s\n", cmd);
544
545 if (ua->argc <= 1) {
546 ua->ErrorMsg(_("%s command requires a keyword\n"), NPRT(ua->argk[0]));
547 return false;
548 }
549
550 /*
551 * days or hours given?
552 */
553 d = FindArgWithValue(ua, NT_("days"));
554 h = FindArgWithValue(ua, NT_("hours"));
555
556 now = (utime_t)time(NULL);
557 if (d > 0) {
558 days = str_to_int64(ua->argv[d]);
559 schedtime = now - secs_in_day * days; /* Days in the past */
560 }
561
562 if (h > 0) {
563 hours = str_to_int64(ua->argv[h]);
564 schedtime = now - secs_in_hour * hours; /* Hours in the past */
565 }
566
567 current = FindArg(ua, NT_("current")) >= 0;
568 enabled = FindArg(ua, NT_("enabled")) >= 0;
569 disabled = FindArg(ua, NT_("disabled")) >= 0;
570 count = FindArg(ua, NT_("count")) >= 0;
571 last = FindArg(ua, NT_("last")) >= 0;
572
573 PmStrcpy(query_range, "");
574 SetQueryRange(query_range, ua, &jr);
575
576 i = FindArgWithValue(ua, NT_("client"));
577 if (i >= 0) {
578 if (ua->GetClientResWithName(ua->argv[i])) {
579 clientname = ua->argv[i];
580 } else {
581 ua->ErrorMsg(_("invalid client parameter\n"));
582 return false;
583 }
584 }
585
586 /*
587 * jobstatus=X
588 */
589 if (!GetUserJobStatusSelection(ua, &jobstatus)) {
590 ua->ErrorMsg(_("invalid jobstatus parameter\n"));
591 return false;
592 }
593
594 /*
595 * joblevel=X
596 */
597 if (!GetUserJobLevelSelection(ua, &joblevel)) {
598 ua->ErrorMsg(_("invalid joblevel parameter\n"));
599 return false;
600 }
601
602 /*
603 * Select what to do based on the first argument.
604 */
605 if ((Bstrcasecmp(ua->argk[1], NT_("jobs")) && (ua->argv[1] == NULL)) ||
606 ((Bstrcasecmp(ua->argk[1], NT_("job")) ||
607 Bstrcasecmp(ua->argk[1], NT_("jobname"))) &&
608 ua->argv[1])) {
609 /*
610 * List jobs or List job=xxx
611 */
612 i = FindArgWithValue(ua, NT_("jobname"));
613 if (i < 0) { i = FindArgWithValue(ua, NT_("job")); }
614 if (i >= 0) {
615 jr.JobId = 0;
616 bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
617 }
618
619 i = FindArgWithValue(ua, NT_("volume"));
620 if (i >= 0) { volumename = ua->argv[i]; }
621
622 i = FindArgWithValue(ua, NT_("pool"));
623 if (i >= 0) { poolname = ua->argv[i]; }
624
625 switch (llist) {
626 case VERT_LIST:
627 if (!count) {
628 SetAclFilter(ua, 2, Job_ACL); /* JobName */
629 SetAclFilter(ua, 7, Client_ACL); /* ClientName */
630 SetAclFilter(ua, 21, Pool_ACL); /* PoolName */
631 SetAclFilter(ua, 24, FileSet_ACL); /* FilesetName */
632 }
633 if (current) {
634 SetResFilter(ua, 2, R_JOB); /* JobName */
635 SetResFilter(ua, 7, R_CLIENT); /* ClientName */
636 SetResFilter(ua, 21, R_POOL); /* PoolName */
637 SetResFilter(ua, 24, R_FILESET); /* FilesetName */
638 }
639 if (enabled) { SetEnabledFilter(ua, 2, R_JOB); /* JobName */ }
640 if (disabled) { SetDisabledFilter(ua, 2, R_JOB); /* JobName */ }
641 break;
642 default:
643 if (!count) {
644 SetAclFilter(ua, 1, Job_ACL); /* JobName */
645 SetAclFilter(ua, 2, Client_ACL); /* ClientName */
646 }
647 if (current) {
648 SetResFilter(ua, 1, R_JOB); /* JobName */
649 SetResFilter(ua, 2, R_CLIENT); /* ClientName */
650 }
651 if (enabled) { SetEnabledFilter(ua, 1, R_JOB); /* JobName */ }
652 if (disabled) { SetDisabledFilter(ua, 1, R_JOB); /* JobName */ }
653 break;
654 }
655
656 SetQueryRange(query_range, ua, &jr);
657
658 ua->db->ListJobRecords(ua->jcr, &jr, query_range.c_str(), clientname,
659 jobstatus, joblevel, volumename, poolname, schedtime,
660 last, count, ua->send, llist);
661 } else if (Bstrcasecmp(ua->argk[1], NT_("jobtotals"))) {
662 /*
663 * List JOBTOTALS
664 */
665 ua->db->ListJobTotals(ua->jcr, &jr, ua->send);
666 } else if ((Bstrcasecmp(ua->argk[1], NT_("jobid")) ||
667 Bstrcasecmp(ua->argk[1], NT_("ujobid"))) &&
668 ua->argv[1]) {
669 /*
670 * List JOBID=nn
671 * List UJOBID=xxx
672 */
673 if (ua->argv[1]) {
674 jobid = GetJobidFromCmdline(ua);
675 if (jobid > 0) {
676 jr.JobId = jobid;
677
678 i = FindArgWithValue(ua, NT_("pool"));
679 if (i >= 0) { poolname = ua->argv[i]; }
680
681 switch (llist) {
682 case VERT_LIST:
683 SetAclFilter(ua, 2, Job_ACL); /* JobName */
684 SetAclFilter(ua, 7, Client_ACL); /* ClientName */
685 SetAclFilter(ua, 21, Pool_ACL); /* PoolName */
686 SetAclFilter(ua, 24, FileSet_ACL); /* FilesetName */
687 if (current) {
688 SetResFilter(ua, 2, R_JOB); /* JobName */
689 SetResFilter(ua, 7, R_CLIENT); /* ClientName */
690 SetResFilter(ua, 21, R_POOL); /* PoolName */
691 SetResFilter(ua, 24, R_FILESET); /* FilesetName */
692 }
693 if (enabled) { SetEnabledFilter(ua, 2, R_JOB); /* JobName */ }
694 if (disabled) { SetDisabledFilter(ua, 2, R_JOB); /* JobName */ }
695 break;
696 default:
697 SetAclFilter(ua, 1, Job_ACL); /* JobName */
698 SetAclFilter(ua, 2, Client_ACL); /* ClientName */
699 if (current) {
700 SetResFilter(ua, 1, R_JOB); /* JobName */
701 SetResFilter(ua, 2, R_CLIENT); /* ClientName */
702 }
703 if (enabled) { SetEnabledFilter(ua, 1, R_JOB); /* JobName */ }
704 if (disabled) { SetDisabledFilter(ua, 1, R_JOB); /* JobName */ }
705 break;
706 }
707
708 SetQueryRange(query_range, ua, &jr);
709
710 ua->db->ListJobRecords(ua->jcr, &jr, query_range.c_str(), clientname,
711 jobstatus, joblevel, volumename, poolname,
712 schedtime, last, count, ua->send, llist);
713 }
714 }
715 } else if (Bstrcasecmp(ua->argk[1], NT_("basefiles"))) {
716 /*
717 * List BASEFILES
718 */
719 jobid = GetJobidFromCmdline(ua);
720 if (jobid > 0) {
721 ua->db->ListBaseFilesForJob(ua->jcr, jobid, ua->send);
722 } else {
723 ua->ErrorMsg(
724 _("jobid not found in db, access to job or client denied by ACL, or "
725 "client not found in db\n"));
726 }
727 } else if (Bstrcasecmp(ua->argk[1], NT_("files"))) {
728 /*
729 * List FILES
730 */
731 jobid = GetJobidFromCmdline(ua);
732 if (jobid > 0) {
733 ua->db->ListFilesForJob(ua->jcr, jobid, ua->send);
734 } else {
735 ua->ErrorMsg(
736 _("jobid not found in db, access to job or client denied by ACL, or "
737 "client not found in db\n"));
738 }
739 } else if (Bstrcasecmp(ua->argk[1], NT_("fileset"))) {
740 int filesetid = 0;
741
742 /*
743 * List FileSet
744 */
745 i = FindArgWithValue(ua, NT_("filesetid"));
746 if (i > 0) { filesetid = str_to_int64(ua->argv[i]); }
747
748 jobid = GetJobidFromCmdline(ua);
749 if (jobid > 0 || filesetid > 0) {
750 jr.JobId = jobid;
751 jr.FileSetId = filesetid;
752
753 SetAclFilter(ua, 1, FileSet_ACL); /* FilesetName */
754 if (current) { SetResFilter(ua, 1, R_FILESET); /* FilesetName */ }
755
756 SetQueryRange(query_range, ua, &jr);
757
758 ua->db->ListFilesets(ua->jcr, &jr, query_range.c_str(), ua->send, llist);
759 } else {
760 ua->ErrorMsg(
761 _("jobid not found in db, access to job or client denied by ACL, or "
762 "client not found in db or missing filesetid\n"));
763 }
764 } else if (Bstrcasecmp(ua->argk[1], NT_("filesets"))) {
765 /*
766 * List FILESETs
767 */
768 SetAclFilter(ua, 1, FileSet_ACL); /* FilesetName */
769 if (current) { SetResFilter(ua, 1, R_FILESET); /* FilesetName */ }
770
771 SetQueryRange(query_range, ua, &jr);
772
773 ua->db->ListFilesets(ua->jcr, &jr, query_range.c_str(), ua->send, llist);
774 } else if (Bstrcasecmp(ua->argk[1], NT_("jobmedia"))) {
775 /*
776 * List JOBMEDIA
777 */
778 jobid = GetJobidFromCmdline(ua);
779 if (jobid >= 0) {
780 ua->db->ListJobmediaRecords(ua->jcr, jobid, ua->send, llist);
781 } else {
782 ua->ErrorMsg(
783 _("jobid not found in db, access to job or client denied by ACL, or "
784 "client not found in db\n"));
785 }
786 } else if (Bstrcasecmp(ua->argk[1], NT_("joblog"))) {
787 /*
788 * List JOBLOG
789 */
790 jobid = GetJobidFromCmdline(ua);
791 if (jobid >= 0) {
792 ua->db->ListJoblogRecords(ua->jcr, jobid, query_range.c_str(), count,
793 ua->send, llist);
794 } else {
795 ua->ErrorMsg(
796 _("jobid not found in db, access to job or client denied by ACL, or "
797 "client not found in db\n"));
798 }
799 } else if (Bstrcasecmp(ua->argk[1], NT_("log"))) {
800 bool reverse;
801
802 /*
803 * List last <limit> LOG entries
804 * default is DEFAULT_LOG_LINES entries
805 */
806 reverse = FindArg(ua, NT_("reverse")) >= 0;
807
808 if (strlen(query_range.c_str()) == 0) {
809 Mmsg(query_range, " LIMIT %d", DEFAULT_LOG_LINES);
810 }
811
812 if (ua->api != API_MODE_JSON) {
813 SetHiddenColumn(ua, 0); /* LogId */
814 SetHiddenColumnAclFilter(ua, 1, Job_ACL); /* JobName */
815 SetHiddenColumnAclFilter(ua, 2, Client_ACL); /* ClientName */
816 SetHiddenColumn(ua, 3); /* LogTime */
817 } else {
818 SetAclFilter(ua, 1, Job_ACL); /* JobName */
819 SetAclFilter(ua, 2, Client_ACL); /* ClientName */
820 }
821
822 SetQueryRange(query_range, ua, &jr);
823
824 ua->db->ListLogRecords(ua->jcr, clientname, query_range.c_str(), reverse,
825 ua->send, llist);
826 } else if (Bstrcasecmp(ua->argk[1], NT_("pool")) ||
827 Bstrcasecmp(ua->argk[1], NT_("pools"))) {
828 PoolDbRecord pr;
829
830 /*
831 * List POOLS
832 */
833 if (ua->argv[1]) { bstrncpy(pr.Name, ua->argv[1], sizeof(pr.Name)); }
834
835 SetAclFilter(ua, 1, Pool_ACL); /* PoolName */
836 if (current) { SetResFilter(ua, 1, R_POOL); /* PoolName */ }
837
838 SetQueryRange(query_range, ua, &jr);
839
840 ua->db->ListPoolRecords(ua->jcr, &pr, ua->send, llist);
841 } else if (Bstrcasecmp(ua->argk[1], NT_("clients"))) {
842 /*
843 * List CLIENTS
844 */
845 SetAclFilter(ua, 1, Client_ACL); /* ClientName */
846 if (current) { SetResFilter(ua, 1, R_CLIENT); /* ClientName */ }
847 if (enabled) { SetEnabledFilter(ua, 1, R_CLIENT); /* ClientName */ }
848 if (disabled) { SetDisabledFilter(ua, 1, R_CLIENT); /* ClientName */ }
849
850 SetQueryRange(query_range, ua, &jr);
851
852 ua->db->ListClientRecords(ua->jcr, NULL, ua->send, llist);
853 } else if (Bstrcasecmp(ua->argk[1], NT_("client")) && ua->argv[1]) {
854 /*
855 * List CLIENT=xxx
856 */
857 SetAclFilter(ua, 1, Client_ACL); /* ClientName */
858 if (current) { SetResFilter(ua, 1, R_CLIENT); /* ClientName */ }
859 if (enabled) { SetEnabledFilter(ua, 1, R_CLIENT); /* ClientName */ }
860 if (disabled) { SetDisabledFilter(ua, 1, R_CLIENT); /* ClientName */ }
861
862 SetQueryRange(query_range, ua, &jr);
863
864 ua->db->ListClientRecords(ua->jcr, ua->argv[1], ua->send, llist);
865 } else if (Bstrcasecmp(ua->argk[1], NT_("storages"))) {
866 /*
867 * List STORAGES
868 */
869 SetAclFilter(ua, 1, Storage_ACL); /* StorageName */
870 if (current) { SetResFilter(ua, 1, R_STORAGE); /* StorageName */ }
871 if (enabled) { SetEnabledFilter(ua, 1, R_STORAGE); /* StorageName */ }
872 if (disabled) { SetDisabledFilter(ua, 1, R_STORAGE); /* StorageName */ }
873
874 SetQueryRange(query_range, ua, &jr);
875
876 ua->db->ListSqlQuery(ua->jcr, "SELECT * FROM Storage", ua->send, llist,
877 "storages");
878 } else if (Bstrcasecmp(ua->argk[1], NT_("media")) ||
879 Bstrcasecmp(ua->argk[1], NT_("volume")) ||
880 Bstrcasecmp(ua->argk[1], NT_("volumes"))) {
881 /*
882 * List MEDIA or VOLUMES
883 */
884 jobid = GetJobidFromCmdline(ua);
885 if (jobid > 0) {
886 ua->db->ListVolumesOfJobid(ua->jcr, jobid, ua->send, llist);
887 } else if (jobid == 0) {
888 /*
889 * List a specific volume?
890 */
891 if (ua->argv[1]) {
892 bstrncpy(mr.VolumeName, ua->argv[1], sizeof(mr.VolumeName));
893 ua->send->ObjectStart("volume");
894 ua->db->ListMediaRecords(ua->jcr, &mr, query_range.c_str(), count,
895 ua->send, llist);
896 ua->send->ObjectEnd("volume");
897 } else {
898 /*
899 * If no job or jobid keyword found, then we list all media
900 * Is a specific pool wanted?
901 */
902 i = FindArgWithValue(ua, NT_("pool"));
903 if (i >= 0) {
904 bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
905
906 if (!GetPoolDbr(ua, &pr)) {
907 ua->ErrorMsg(_("Pool %s doesn't exist.\n"), ua->argv[i]);
908 return true;
909 }
910
911 SetQueryRange(query_range, ua, &jr);
912
913 mr.PoolId = pr.PoolId;
914 ua->send->ArrayStart("volumes");
915 ua->db->ListMediaRecords(ua->jcr, &mr, query_range.c_str(), count,
916 ua->send, llist);
917 ua->send->ArrayEnd("volumes");
918 return true;
919 } else {
920 int num_pools;
921 uint32_t* ids = nullptr;
922
923 /*
924 * List all volumes, flat
925 */
926 if (FindArg(ua, NT_("all")) > 0) {
927 /*
928 * The result of "list media all"
929 * does not contain the Pool information,
930 * therefore checking the Pool_ACL is not possible.
931 * For this reason, we prevent this command.
932 */
933 if (ua->AclHasRestrictions(Pool_ACL) && (llist != VERT_LIST)) {
934 ua->ErrorMsg(
935 _("Restricted permission. Use the commands 'list media' or "
936 "'llist media all' instead\n"));
937 return false;
938 }
939 ua->send->ArrayStart("volumes");
940 SetAclFilter(ua, 4, Pool_ACL); /* PoolName */
941 if (current) { SetResFilter(ua, 4, R_POOL); /* PoolName */ }
942 ua->db->ListMediaRecords(ua->jcr, &mr, query_range.c_str(), count,
943 ua->send, llist);
944 ua->send->ArrayEnd("volumes");
945 } else {
946 /*
947 * List Volumes in all pools
948 */
949 if (!ua->db->GetPoolIds(ua->jcr, &num_pools, &ids)) {
950 ua->ErrorMsg(_("Error obtaining pool ids. ERR=%s\n"),
951 ua->db->strerror());
952 return true;
953 }
954
955 if (num_pools <= 0) {
956 if (ids) { free(ids); }
957 return true;
958 }
959
960 ua->send->ObjectStart("volumes");
961 for (i = 0; i < num_pools; i++) {
962 pr.PoolId = ids[i];
963 if (ua->db->GetPoolRecord(ua->jcr, &pr)) {
964 if (ua->AclAccessOk(Pool_ACL, pr.Name, false)) {
965 ua->send->Decoration("Pool: %s\n", pr.Name);
966 ua->send->ArrayStart(pr.Name);
967 mr.PoolId = ids[i];
968 ua->db->ListMediaRecords(ua->jcr, &mr, query_range.c_str(),
969 count, ua->send, llist);
970 ua->send->ArrayEnd(pr.Name);
971 }
972 }
973 }
974 ua->send->ObjectEnd("volumes");
975 free(ids);
976 }
977 }
978 }
979 }
980
981 return true;
982 } else if (Bstrcasecmp(ua->argk[1], NT_("nextvol")) ||
983 Bstrcasecmp(ua->argk[1], NT_("nextvolume"))) {
984 int days;
985
986 /*
987 * List next volume
988 */
989 days = 1;
990
991 i = FindArgWithValue(ua, NT_("days"));
992 if (i >= 0) {
993 days = atoi(ua->argv[i]);
994 if ((days < 0) || (days > DEFAULT_NR_DAYS)) {
995 ua->WarningMsg(_("Ignoring invalid value for days. Max is %d.\n"),
996 DEFAULT_NR_DAYS);
997 days = 1;
998 }
999 }
1000 ListNextvol(ua, days);
1001 } else if (Bstrcasecmp(ua->argk[1], NT_("copies"))) {
1002 /*
1003 * List copies
1004 */
1005 i = FindArgWithValue(ua, NT_("jobid"));
1006 if (i >= 0) {
1007 if (Is_a_number_list(ua->argv[i])) {
1008 ua->db->ListCopiesRecords(ua->jcr, query_range.c_str(), ua->argv[i],
1009 ua->send, llist);
1010 }
1011 } else {
1012 ua->db->ListCopiesRecords(ua->jcr, query_range.c_str(), NULL, ua->send,
1013 llist);
1014 }
1015 } else if (Bstrcasecmp(ua->argk[1], NT_("backups"))) {
1016 if (ParseListBackupsCmd(ua, query_range.c_str(), llist)) {
1017 switch (llist) {
1018 case VERT_LIST:
1019 SetAclFilter(ua, 2, Job_ACL); /* JobName */
1020 SetAclFilter(ua, 21, Pool_ACL); /* PoolName */
1021 if (current) {
1022 SetResFilter(ua, 2, R_JOB); /* JobName */
1023 SetResFilter(ua, 21, R_POOL); /* PoolName */
1024 }
1025 if (enabled) { SetEnabledFilter(ua, 2, R_JOB); /* JobName */ }
1026 if (disabled) { SetDisabledFilter(ua, 2, R_JOB); /* JobName */ }
1027 break;
1028 default:
1029 SetAclFilter(ua, 1, Job_ACL); /* JobName */
1030 if (current) { SetResFilter(ua, 1, R_JOB); /* JobName */ }
1031 if (enabled) { SetEnabledFilter(ua, 1, R_JOB); /* JobName */ }
1032 if (disabled) { SetDisabledFilter(ua, 1, R_JOB); /* JobName */ }
1033 break;
1034 }
1035
1036 ua->db->ListSqlQuery(ua->jcr, ua->cmd, ua->send, llist, "backups");
1037 }
1038 } else if (Bstrcasecmp(ua->argk[1], NT_("jobstatistics")) ||
1039 Bstrcasecmp(ua->argk[1], NT_("jobstats"))) {
1040 jobid = GetJobidFromCmdline(ua);
1041 if (jobid > 0) {
1042 ua->db->ListJobstatisticsRecords(ua->jcr, jobid, ua->send, llist);
1043 } else {
1044 ua->ErrorMsg(_("no jobid given\n"));
1045 return false;
1046 }
1047 } else {
1048 ua->ErrorMsg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[1]));
1049 return false;
1050 }
1051
1052 return true;
1053 }
1054
parse_jobstatus_selection_param(PoolMem & selection,UaContext * ua,const char * default_selection)1055 static inline bool parse_jobstatus_selection_param(
1056 PoolMem& selection,
1057 UaContext* ua,
1058 const char* default_selection)
1059 {
1060 int pos;
1061
1062 selection.strcpy("");
1063 if ((pos = FindArgWithValue(ua, "jobstatus")) >= 0) {
1064 int cnt = 0;
1065 int jobstatus;
1066 PoolMem temp;
1067 char *cur_stat, *bp;
1068
1069 cur_stat = ua->argv[pos];
1070 while (cur_stat) {
1071 bp = strchr(cur_stat, ',');
1072 if (bp) { *bp++ = '\0'; }
1073
1074 /*
1075 * Try matching the status to an internal Job Termination code.
1076 */
1077 if (strlen(cur_stat) == 1 && cur_stat[0] >= 'A' && cur_stat[0] <= 'z') {
1078 jobstatus = cur_stat[0];
1079 } else if (Bstrcasecmp(cur_stat, "terminated")) {
1080 jobstatus = JS_Terminated;
1081 } else if (Bstrcasecmp(cur_stat, "warnings")) {
1082 jobstatus = JS_Warnings;
1083 } else if (Bstrcasecmp(cur_stat, "canceled")) {
1084 jobstatus = JS_Canceled;
1085 } else if (Bstrcasecmp(cur_stat, "running")) {
1086 jobstatus = JS_Running;
1087 } else if (Bstrcasecmp(cur_stat, "error")) {
1088 jobstatus = JS_Error;
1089 } else if (Bstrcasecmp(cur_stat, "fatal")) {
1090 jobstatus = JS_FatalError;
1091 } else {
1092 cur_stat = bp;
1093 continue;
1094 }
1095
1096 if (cnt == 0) {
1097 Mmsg(temp, " AND JobStatus IN ('%c'", jobstatus);
1098 PmStrcat(selection, temp.c_str());
1099 } else {
1100 Mmsg(temp, ",'%c'", jobstatus);
1101 PmStrcat(selection, temp.c_str());
1102 }
1103 cur_stat = bp;
1104 cnt++;
1105 }
1106
1107 /*
1108 * Close set if we opened one.
1109 */
1110 if (cnt > 0) { PmStrcat(selection, ")"); }
1111 }
1112
1113 if (selection.strlen() == 0) {
1114 /*
1115 * When no explicit Job Termination code specified use default
1116 */
1117 selection.strcpy(default_selection);
1118 }
1119
1120 return true;
1121 }
1122
parse_level_selection_param(PoolMem & selection,UaContext * ua,const char * default_selection)1123 static inline bool parse_level_selection_param(PoolMem& selection,
1124 UaContext* ua,
1125 const char* default_selection)
1126 {
1127 int pos;
1128
1129 selection.strcpy("");
1130 if ((pos = FindArgWithValue(ua, "level")) >= 0) {
1131 int cnt = 0;
1132 PoolMem temp;
1133 char *cur_level, *bp;
1134
1135 cur_level = ua->argv[pos];
1136 while (cur_level) {
1137 bp = strchr(cur_level, ',');
1138 if (bp) { *bp++ = '\0'; }
1139
1140 /*
1141 * Try mapping from text level to internal level.
1142 */
1143 for (int i = 0; joblevels[i].level_name; i++) {
1144 if (joblevels[i].job_type == JT_BACKUP &&
1145 bstrncasecmp(joblevels[i].level_name, cur_level,
1146 strlen(cur_level))) {
1147 if (cnt == 0) {
1148 Mmsg(temp, " AND Level IN ('%c'", joblevels[i].level);
1149 PmStrcat(selection, temp.c_str());
1150 } else {
1151 Mmsg(temp, ",'%c'", joblevels[i].level);
1152 PmStrcat(selection, temp.c_str());
1153 }
1154 }
1155 }
1156 cur_level = bp;
1157 cnt++;
1158 }
1159
1160 /*
1161 * Close set if we opened one.
1162 */
1163 if (cnt > 0) { PmStrcat(selection, ")"); }
1164 }
1165 if (selection.strlen() == 0) { selection.strcpy(default_selection); }
1166
1167 return true;
1168 }
1169
parse_fileset_selection_param(PoolMem & selection,UaContext * ua,bool listall)1170 static inline bool parse_fileset_selection_param(PoolMem& selection,
1171 UaContext* ua,
1172 bool listall)
1173 {
1174 int fileset;
1175
1176 PmStrcpy(selection, "");
1177 fileset = FindArgWithValue(ua, "fileset");
1178 if ((fileset >= 0 && Bstrcasecmp(ua->argv[fileset], "any")) ||
1179 (listall && fileset < 0)) {
1180 FilesetResource* fs;
1181 PoolMem temp(PM_MESSAGE);
1182
1183 LockRes(my_config);
1184 foreach_res (fs, R_FILESET) {
1185 if (!ua->AclAccessOk(FileSet_ACL, fs->resource_name_, false)) {
1186 continue;
1187 }
1188 if (selection.strlen() == 0) {
1189 temp.bsprintf("AND (FileSet='%s'", fs->resource_name_);
1190 } else {
1191 temp.bsprintf(" OR FileSet='%s'", fs->resource_name_);
1192 }
1193 PmStrcat(selection, temp.c_str());
1194 }
1195 PmStrcat(selection, ") ");
1196 UnlockRes(my_config);
1197 } else if (fileset >= 0) {
1198 if (!ua->AclAccessOk(FileSet_ACL, ua->argv[fileset], true)) {
1199 ua->ErrorMsg(_("Access to specified FileSet not allowed.\n"));
1200 return false;
1201 } else {
1202 selection.bsprintf("AND FileSet='%s' ", ua->argv[fileset]);
1203 }
1204 }
1205
1206 return true;
1207 }
1208
ParseListBackupsCmd(UaContext * ua,const char * range,e_list_type llist)1209 static bool ParseListBackupsCmd(UaContext* ua,
1210 const char* range,
1211 e_list_type llist)
1212 {
1213 int pos, client;
1214 PoolMem temp(PM_MESSAGE), selection(PM_MESSAGE), criteria(PM_MESSAGE);
1215
1216 client = FindArgWithValue(ua, "client");
1217 if (client < 0) {
1218 ua->ErrorMsg(_("missing parameter: client\n"));
1219 return false;
1220 }
1221
1222 if (!ua->AclAccessOk(Client_ACL, ua->argv[client], true)) {
1223 ua->ErrorMsg(_("Access to specified Client not allowed.\n"));
1224 return false;
1225 }
1226
1227 selection.bsprintf("AND Job.Type='B' AND Client.Name='%s' ",
1228 ua->argv[client]);
1229
1230 /*
1231 * Build a selection pattern based on the jobstatus and level arguments.
1232 */
1233 parse_jobstatus_selection_param(temp, ua, "AND JobStatus IN ('T','W') ");
1234 PmStrcat(selection, temp.c_str());
1235
1236 parse_level_selection_param(temp, ua, "");
1237 PmStrcat(selection, temp.c_str());
1238
1239 if (!parse_fileset_selection_param(temp, ua, true)) { return false; }
1240 PmStrcat(selection, temp.c_str());
1241
1242 /*
1243 * Build a criteria pattern if the order and/or limit argument are given.
1244 */
1245 PmStrcpy(criteria, "");
1246 if ((pos = FindArgWithValue(ua, "order")) >= 0) {
1247 if (bstrncasecmp(ua->argv[pos], "ascending", strlen(ua->argv[pos]))) {
1248 PmStrcat(criteria, " ASC");
1249 } else if (bstrncasecmp(ua->argv[pos], "descending",
1250 strlen(ua->argv[pos]))) {
1251 PmStrcat(criteria, " DESC");
1252 } else {
1253 return false;
1254 }
1255 }
1256
1257 /*
1258 * add range settings
1259 */
1260 PmStrcat(criteria, range);
1261
1262 if (llist == VERT_LIST) {
1263 ua->db->FillQuery(ua->cmd, BareosDb::SQL_QUERY::list_jobs_long,
1264 selection.c_str(), criteria.c_str());
1265 } else {
1266 ua->db->FillQuery(ua->cmd, BareosDb::SQL_QUERY::list_jobs,
1267 selection.c_str(), criteria.c_str());
1268 }
1269
1270 return true;
1271 }
1272
ListNextvol(UaContext * ua,int ndays)1273 static bool ListNextvol(UaContext* ua, int ndays)
1274 {
1275 int i;
1276 JobResource* job;
1277 JobControlRecord* jcr;
1278 UnifiedStorageResource store;
1279 RunResource* run;
1280 utime_t runtime;
1281 bool found = false;
1282
1283 i = FindArgWithValue(ua, "job");
1284 if (i <= 0) {
1285 if ((job = select_job_resource(ua)) == NULL) { return false; }
1286 } else {
1287 job = ua->GetJobResWithName(ua->argv[i]);
1288 if (!job) {
1289 Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
1290 if ((job = select_job_resource(ua)) == NULL) { return false; }
1291 }
1292 }
1293
1294 jcr = NewDirectorJcr();
1295 for (run = NULL; (run = find_next_run(run, job, runtime, ndays));) {
1296 if (!CompleteJcrForJob(jcr, job, run->pool)) {
1297 found = false;
1298 goto get_out;
1299 }
1300 if (!jcr->impl->jr.PoolId) {
1301 ua->ErrorMsg(_("Could not find Pool for Job %s\n"), job->resource_name_);
1302 continue;
1303 }
1304 PoolDbRecord pr;
1305 pr.PoolId = jcr->impl->jr.PoolId;
1306 if (!ua->db->GetPoolRecord(jcr, &pr)) {
1307 bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
1308 }
1309 MediaDbRecord mr;
1310 mr.PoolId = jcr->impl->jr.PoolId;
1311 GetJobStorage(&store, job, run);
1312 SetStorageidInMr(store.store, &mr);
1313 /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
1314 if (!FindNextVolumeForAppend(jcr, &mr, 1, NULL, fnv_no_create_vol,
1315 fnv_prune)) {
1316 ua->ErrorMsg(
1317 _("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
1318 job->resource_name_, pr.Name, JobLevelToString(run->level));
1319 } else {
1320 ua->SendMsg(_("The next Volume to be used by Job \"%s\" (Pool=%s, "
1321 "Level=%s) will be %s\n"),
1322 job->resource_name_, pr.Name, JobLevelToString(run->level),
1323 mr.VolumeName);
1324 found = true;
1325 }
1326 }
1327
1328 get_out:
1329 if (jcr->db) {
1330 DbSqlClosePooledConnection(jcr, jcr->db);
1331 jcr->db = NULL;
1332 }
1333 FreeJcr(jcr);
1334 if (!found) {
1335 ua->ErrorMsg(_("Could not find next Volume for Job %s.\n"),
1336 job->resource_name_);
1337 return false;
1338 }
1339 return true;
1340 }
1341
1342 /**
1343 * For a given job, we examine all his run records
1344 * to see if it is scheduled today or tomorrow.
1345 */
find_next_run(RunResource * run,JobResource * job,utime_t & runtime,int ndays)1346 RunResource* find_next_run(RunResource* run,
1347 JobResource* job,
1348 utime_t& runtime,
1349 int ndays)
1350 {
1351 time_t now, future, endtime;
1352 ScheduleResource* sched;
1353 struct tm tm, runtm;
1354 int mday, wday, month, wom, i;
1355 int woy;
1356 int day;
1357
1358 sched = job->schedule;
1359 if (sched == NULL) { /* scheduled? */
1360 return NULL; /* no nothing to report */
1361 }
1362
1363 /* Break down the time into components */
1364 now = time(NULL);
1365 endtime = now + (ndays * 60 * 60 * 24);
1366
1367 if (run == NULL) {
1368 run = sched->run;
1369 } else {
1370 run = run->next;
1371 }
1372 for (; run; run = run->next) {
1373 /*
1374 * Find runs in next 24 hours. Day 0 is today, so if
1375 * ndays=1, look at today and tomorrow.
1376 */
1377 for (day = 0; day <= ndays; day++) {
1378 future = now + (day * 60 * 60 * 24);
1379
1380 /* Break down the time into components */
1381 Blocaltime(&future, &tm);
1382 mday = tm.tm_mday - 1;
1383 wday = tm.tm_wday;
1384 month = tm.tm_mon;
1385 wom = mday / 7;
1386 woy = TmWoy(future);
1387
1388 bool is_scheduled = BitIsSet(mday, run->date_time_bitfield.mday) &&
1389 BitIsSet(wday, run->date_time_bitfield.wday) &&
1390 BitIsSet(month, run->date_time_bitfield.month) &&
1391 BitIsSet(wom, run->date_time_bitfield.wom) &&
1392 BitIsSet(woy, run->date_time_bitfield.woy);
1393
1394 if (is_scheduled) { /* Jobs scheduled on that day */
1395 /* find time (time_t) job is to be run */
1396 Blocaltime(&future, &runtm);
1397 for (i = 0; i < 24; i++) {
1398 if (BitIsSet(i, run->date_time_bitfield.hour)) {
1399 runtm.tm_hour = i;
1400 runtm.tm_min = run->minute;
1401 runtm.tm_sec = 0;
1402 runtime = mktime(&runtm);
1403 Dmsg2(200, "now=%d runtime=%lld\n", now, runtime);
1404 if ((runtime > now) && (runtime < endtime)) {
1405 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
1406 return run; /* found it, return run resource */
1407 }
1408 }
1409 }
1410 }
1411 }
1412 } /* end for loop over runs */
1413 /* Nothing found */
1414 return NULL;
1415 }
1416
1417 /**
1418 * Fill in the remaining fields of the jcr as if it is going to run the job.
1419 */
CompleteJcrForJob(JobControlRecord * jcr,JobResource * job,PoolResource * pool)1420 bool CompleteJcrForJob(JobControlRecord* jcr,
1421 JobResource* job,
1422 PoolResource* pool)
1423 {
1424 SetJcrDefaults(jcr, job);
1425 if (pool) { jcr->impl->res.pool = pool; /* override */ }
1426 if (jcr->db) {
1427 Dmsg0(100, "complete_jcr close db\n");
1428 DbSqlClosePooledConnection(jcr, jcr->db);
1429 jcr->db = NULL;
1430 }
1431
1432 Dmsg0(100, "complete_jcr open db\n");
1433 jcr->db = GetDatabaseConnection(jcr);
1434 if (jcr->db == NULL) {
1435 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
1436 jcr->impl->res.catalog->db_name);
1437 return false;
1438 }
1439 PoolDbRecord pr;
1440 bstrncpy(pr.Name, jcr->impl->res.pool->resource_name_, sizeof(pr.Name));
1441 while (!jcr->db->GetPoolRecord(jcr, &pr)) { /* get by Name */
1442 /* Try to create the pool */
1443 if (CreatePool(jcr, jcr->db, jcr->impl->res.pool, POOL_OP_CREATE) < 0) {
1444 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
1445 jcr->db->strerror());
1446 if (jcr->db) {
1447 DbSqlClosePooledConnection(jcr, jcr->db);
1448 jcr->db = NULL;
1449 }
1450 return false;
1451 } else {
1452 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
1453 }
1454 }
1455 jcr->impl->jr.PoolId = pr.PoolId;
1456 return true;
1457 }
1458
ConLockRelease(void * arg)1459 static void ConLockRelease(void* arg) { Vw(con_lock); }
1460
DoMessages(UaContext * ua,const char * cmd)1461 void DoMessages(UaContext* ua, const char* cmd)
1462 {
1463 char msg[2000];
1464 int mlen;
1465 bool DoTruncate = false;
1466
1467 /*
1468 * Flush any queued messages.
1469 */
1470 if (ua->jcr) { DequeueMessages(ua->jcr); }
1471
1472 Pw(con_lock);
1473 pthread_cleanup_push(ConLockRelease, (void*)NULL);
1474 rewind(con_fd);
1475 while (fgets(msg, sizeof(msg), con_fd)) {
1476 mlen = strlen(msg);
1477 ua->UA_sock->msg = CheckPoolMemorySize(ua->UA_sock->msg, mlen + 1);
1478 strcpy(ua->UA_sock->msg, msg);
1479 ua->UA_sock->message_length = mlen;
1480 ua->UA_sock->send();
1481 DoTruncate = true;
1482 }
1483 if (DoTruncate) { (void)ftruncate(fileno(con_fd), 0L); }
1484 console_msg_pending = FALSE;
1485 ua->user_notified_msg_pending = FALSE;
1486 pthread_cleanup_pop(0);
1487 Vw(con_lock);
1488 }
1489
DotMessagesCmd(UaContext * ua,const char * cmd)1490 bool DotMessagesCmd(UaContext* ua, const char* cmd)
1491 {
1492 if (console_msg_pending && ua->AclNoRestrictions(Command_ACL) &&
1493 ua->auto_display_messages) {
1494 DoMessages(ua, cmd);
1495 }
1496 return true;
1497 }
1498
MessagesCmd(UaContext * ua,const char * cmd)1499 bool MessagesCmd(UaContext* ua, const char* cmd)
1500 {
1501 if (console_msg_pending && ua->AclNoRestrictions(Command_ACL)) {
1502 DoMessages(ua, cmd);
1503 } else {
1504 ua->send->Decoration(_("You have no messages.\n"));
1505 }
1506 return true;
1507 }
1508
1509 /**
1510 * Callback routine for "filtering" database listing.
1511 */
filterit(void * ctx,void * data,of_filter_tuple * tuple)1512 of_filter_state filterit(void* ctx, void* data, of_filter_tuple* tuple)
1513 {
1514 char** row = (char**)data;
1515 UaContext* ua = (UaContext*)ctx;
1516 of_filter_state retval = OF_FILTER_STATE_SHOW;
1517
1518 switch (tuple->type) {
1519 case OF_FILTER_LIMIT:
1520 break;
1521 case OF_FILTER_OFFSET:
1522 break;
1523 case OF_FILTER_ACL:
1524 if (!row[tuple->u.acl_filter.column] ||
1525 strlen(row[tuple->u.acl_filter.column]) == 0) {
1526 retval = OF_FILTER_STATE_UNKNOWN;
1527 } else {
1528 if (!ua->AclAccessOk(tuple->u.acl_filter.acltype,
1529 row[tuple->u.acl_filter.column], false)) {
1530 Dmsg2(200,
1531 "filterit: Filter on acl_type %d value %s, suppress output\n",
1532 tuple->u.acl_filter.acltype, row[tuple->u.acl_filter.column]);
1533 retval = OF_FILTER_STATE_SUPPRESS;
1534 }
1535 }
1536 goto bail_out;
1537 case OF_FILTER_RESOURCE:
1538 if (!row[tuple->u.res_filter.column] ||
1539 strlen(row[tuple->u.res_filter.column]) == 0) {
1540 retval = OF_FILTER_STATE_UNKNOWN;
1541 } else {
1542 if (!my_config->GetResWithName(tuple->u.res_filter.restype,
1543 row[tuple->u.res_filter.column],
1544 false)) {
1545 Dmsg2(200,
1546 "filterit: Filter on resource_type %d value %s, suppress "
1547 "output\n",
1548 tuple->u.res_filter.restype, row[tuple->u.res_filter.column]);
1549 retval = OF_FILTER_STATE_SUPPRESS;
1550 }
1551 }
1552 goto bail_out;
1553 case OF_FILTER_ENABLED:
1554 case OF_FILTER_DISABLED: {
1555 bool enabled = true;
1556
1557 if (!row[tuple->u.res_filter.column] ||
1558 strlen(row[tuple->u.res_filter.column]) == 0) {
1559 retval = OF_FILTER_STATE_UNKNOWN;
1560 goto bail_out;
1561 }
1562
1563 if (tuple->type == OF_FILTER_DISABLED) { enabled = false; }
1564
1565 switch (tuple->u.res_filter.restype) {
1566 case R_CLIENT: {
1567 ClientResource* client;
1568
1569 client = ua->GetClientResWithName(row[tuple->u.res_filter.column],
1570 false, false);
1571 if (!client || client->enabled != enabled) {
1572 Dmsg2(200, "filterit: Filter on Client, %s is not %sabled\n",
1573 row[tuple->u.res_filter.column], (enabled) ? "En" : "Dis");
1574 retval = OF_FILTER_STATE_SUPPRESS;
1575 }
1576 goto bail_out;
1577 }
1578 case R_JOB: {
1579 JobResource* job;
1580
1581 job = ua->GetJobResWithName(row[tuple->u.res_filter.column], false,
1582 false);
1583 if (!job || job->enabled != enabled) {
1584 Dmsg2(200, "filterit: Filter on Job, %s is not %sabled\n",
1585 row[tuple->u.res_filter.column], (enabled) ? "En" : "Dis");
1586 retval = OF_FILTER_STATE_SUPPRESS;
1587 }
1588 goto bail_out;
1589 }
1590 case R_STORAGE: {
1591 StorageResource* store;
1592
1593 store = ua->GetStoreResWithName(row[tuple->u.res_filter.column],
1594 false, false);
1595 if (!store || store->enabled != enabled) {
1596 Dmsg2(200, "filterit: Filter on Storage, %s is not %sabled\n",
1597 row[tuple->u.res_filter.column], (enabled) ? "En" : "Dis");
1598 retval = OF_FILTER_STATE_SUPPRESS;
1599 }
1600 goto bail_out;
1601 }
1602 case R_SCHEDULE: {
1603 ScheduleResource* schedule;
1604
1605 schedule = ua->GetScheduleResWithName(row[tuple->u.res_filter.column],
1606 false, false);
1607 if (!schedule || schedule->enabled != enabled) {
1608 Dmsg2(200, "filterit: Filter on Schedule, %s is not %sabled\n",
1609 row[tuple->u.res_filter.column], (enabled) ? "En" : "Dis");
1610 retval = OF_FILTER_STATE_SUPPRESS;
1611 }
1612 goto bail_out;
1613 }
1614 default:
1615 goto bail_out;
1616 }
1617 break;
1618 }
1619 default:
1620 retval = OF_FILTER_STATE_SUPPRESS;
1621 }
1622
1623 bail_out:
1624 return retval;
1625 }
1626
1627 /**
1628 * Callback routine for "printing" database listing
1629 */
printit(void * ctx,const char * msg)1630 bool printit(void* ctx, const char* msg)
1631 {
1632 bool retval = false;
1633 UaContext* ua = (UaContext*)ctx;
1634
1635 if (ua->UA_sock) {
1636 retval = ua->UA_sock->fsend("%s", msg);
1637 } else { /* No UA, send to Job */
1638 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
1639 retval = true;
1640 }
1641
1642 return retval;
1643 }
1644
1645 /**
1646 * Format message and send to other end.
1647
1648 * If the UA_sock is NULL, it means that there is no user
1649 * agent, so we are being called from BAREOS core. In
1650 * that case direct the messages to the Job.
1651 */
1652 #ifdef HAVE_VA_COPY
bmsg(UaContext * ua,const char * fmt,va_list arg_ptr)1653 void bmsg(UaContext* ua, const char* fmt, va_list arg_ptr)
1654 {
1655 BareosSocket* bs = ua->UA_sock;
1656 int maxlen, len;
1657 POOLMEM* msg = NULL;
1658 va_list ap;
1659
1660 if (bs) { msg = bs->msg; }
1661 if (!msg) { msg = GetPoolMemory(PM_EMSG); }
1662
1663 again:
1664 maxlen = SizeofPoolMemory(msg) - 1;
1665 va_copy(ap, arg_ptr);
1666 len = Bvsnprintf(msg, maxlen, fmt, ap);
1667 va_end(ap);
1668 if (len < 0 || len >= maxlen) {
1669 msg = ReallocPoolMemory(msg, maxlen + maxlen / 2);
1670 goto again;
1671 }
1672
1673 if (bs) {
1674 bs->msg = msg;
1675 bs->message_length = len;
1676 bs->send();
1677 } else { /* No UA, send to Job */
1678 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
1679 FreePoolMemory(msg);
1680 }
1681 }
1682
1683 #else /* no va_copy() -- brain damaged version of variable arguments */
1684
bmsg(UaContext * ua,const char * fmt,va_list arg_ptr)1685 void bmsg(UaContext* ua, const char* fmt, va_list arg_ptr)
1686 {
1687 BareosSocket* bs = ua->UA_sock;
1688 int maxlen, len;
1689 POOLMEM* msg = NULL;
1690
1691 if (bs) { msg = bs->msg; }
1692 if (!msg) { msg = GetMemory(5000); }
1693
1694 maxlen = SizeofPoolMemory(msg) - 1;
1695 if (maxlen < 4999) {
1696 msg = ReallocPoolMemory(msg, 5000);
1697 maxlen = 4999;
1698 }
1699 len = Bvsnprintf(msg, maxlen, fmt, arg_ptr);
1700 if (len < 0 || len >= maxlen) {
1701 PmStrcpy(msg, _("Message too long to display.\n"));
1702 len = strlen(msg);
1703 }
1704
1705 if (bs) {
1706 bs->msg = msg;
1707 bs->message_length = len;
1708 bs->send();
1709 } else { /* No UA, send to Job */
1710 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
1711 FreePoolMemory(msg);
1712 }
1713 }
1714 #endif
1715
bsendmsg(void * ctx,const char * fmt,...)1716 void bsendmsg(void* ctx, const char* fmt, ...)
1717 {
1718 va_list arg_ptr;
1719 va_start(arg_ptr, fmt);
1720 bmsg((UaContext*)ctx, fmt, arg_ptr);
1721 va_end(arg_ptr);
1722 }
1723
1724 /*
1725 * The following UA methods are mainly intended for GUI
1726 * programs
1727 */
1728 /**
1729 * This is a message that should be displayed on the user's
1730 * console.
1731 */
SendMsg(const char * fmt,...)1732 void UaContext::SendMsg(const char* fmt, ...)
1733 {
1734 va_list arg_ptr;
1735 PoolMem message;
1736
1737 /* send current buffer */
1738 send->SendBuffer();
1739
1740 va_start(arg_ptr, fmt);
1741 message.Bvsprintf(fmt, arg_ptr);
1742 va_end(arg_ptr);
1743 send->message(NULL, message);
1744 }
1745
SendRawMsg(const char * msg)1746 void UaContext::SendRawMsg(const char* msg) { SendMsg(msg); }
1747
1748
1749 /**
1750 * This is an error condition with a command. The gui should put
1751 * up an error or critical dialog box. The command is aborted.
1752 */
ErrorMsg(const char * fmt,...)1753 void UaContext::ErrorMsg(const char* fmt, ...)
1754 {
1755 va_list arg_ptr;
1756 BareosSocket* bs = UA_sock;
1757 PoolMem message;
1758
1759 /* send current buffer */
1760 send->SendBuffer();
1761
1762 if (bs && api) bs->signal(BNET_ERROR_MSG);
1763 va_start(arg_ptr, fmt);
1764 message.Bvsprintf(fmt, arg_ptr);
1765 va_end(arg_ptr);
1766 send->message(MSG_TYPE_ERROR, message);
1767 }
1768
1769 /**
1770 * This is a warning message, that should bring up a warning
1771 * dialog box on the GUI. The command is not aborted, but something
1772 * went wrong.
1773 */
WarningMsg(const char * fmt,...)1774 void UaContext::WarningMsg(const char* fmt, ...)
1775 {
1776 va_list arg_ptr;
1777 BareosSocket* bs = UA_sock;
1778 PoolMem message;
1779
1780 /* send current buffer */
1781 send->SendBuffer();
1782
1783 if (bs && api) bs->signal(BNET_WARNING_MSG);
1784 va_start(arg_ptr, fmt);
1785 message.Bvsprintf(fmt, arg_ptr);
1786 va_end(arg_ptr);
1787 send->message(MSG_TYPE_WARNING, message);
1788 }
1789
1790 /**
1791 * This is an information message that should probably be put
1792 * into the status line of a GUI program.
1793 */
InfoMsg(const char * fmt,...)1794 void UaContext::InfoMsg(const char* fmt, ...)
1795 {
1796 va_list arg_ptr;
1797 BareosSocket* bs = UA_sock;
1798 PoolMem message;
1799
1800 /* send current buffer */
1801 send->SendBuffer();
1802
1803 if (bs && api) bs->signal(BNET_INFO_MSG);
1804 va_start(arg_ptr, fmt);
1805 message.Bvsprintf(fmt, arg_ptr);
1806 va_end(arg_ptr);
1807 send->message(MSG_TYPE_INFO, message);
1808 }
1809
1810
SendCmdUsage(const char * fmt,...)1811 void UaContext::SendCmdUsage(const char* fmt, ...)
1812 {
1813 va_list arg_ptr;
1814 PoolMem message;
1815 PoolMem usage;
1816
1817 /* send current buffer */
1818 send->SendBuffer();
1819
1820 va_start(arg_ptr, fmt);
1821 message.Bvsprintf(fmt, arg_ptr);
1822 va_end(arg_ptr);
1823
1824 if (cmddef) {
1825 if (cmddef->key && cmddef->usage) {
1826 usage.bsprintf("\nUSAGE: %s %s\n", cmddef->key, cmddef->usage);
1827 message.strcat(usage);
1828 }
1829 }
1830
1831 send->message(NULL, message);
1832 }
1833 } /* namespace directordaemon */
1834