2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Director -- User Agent Output Commands
21 * I.e. messages, listing database, showing resources, ...
23 * Kern Sibbald, September MM
29 /* Imported subroutines */
31 /* Imported variables */
33 /* Imported functions */
35 /* Forward referenced functions */
36 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist);
37 static bool list_nextvol(UAContext *ua, int ndays);
40 * Turn auto display of console messages on/off
42 int autodisplay_cmd(UAContext *ua, const char *cmd)
44 static const char *kw[] = {
49 switch (find_arg_keyword(ua, kw)) {
51 ua->auto_display_messages = true;
54 ua->auto_display_messages = false;
57 ua->error_msg(_("ON or OFF keyword missing.\n"));
64 * Turn GUI mode on/off
66 int gui_cmd(UAContext *ua, const char *cmd)
68 static const char *kw[] = {
73 switch (find_arg_keyword(ua, kw)) {
75 ua->jcr->gui = ua->gui = true;
78 ua->jcr->gui = ua->gui = false;
81 ua->error_msg(_("ON or OFF keyword missing.\n"));
88 * Enter with Resources locked
90 static void show_disabled_jobs(UAContext *ua)
94 foreach_res(job, R_JOB) {
95 if (!acl_access_ok(ua, Job_ACL, job->name())) {
98 if (!job->is_enabled()) {
101 ua->send_msg(_("Disabled Jobs:\n"));
103 ua->send_msg(" %s\n", job->name());
107 ua->send_msg(_("No disabled Jobs.\n"));
111 struct showstruct {const char *res_name; int type;};
112 static struct showstruct reses[] = {
113 {NT_("directors"), R_DIRECTOR},
114 {NT_("clients"), R_CLIENT},
115 {NT_("counters"), R_COUNTER},
116 {NT_("devices"), R_DEVICE},
117 {NT_("jobs"), R_JOB},
118 {NT_("storages"), R_STORAGE},
119 {NT_("catalogs"), R_CATALOG},
120 {NT_("schedules"), R_SCHEDULE},
121 {NT_("filesets"), R_FILESET},
122 {NT_("pools"), R_POOL},
123 {NT_("messages"), R_MSGS},
124 // {NT_("consoles"), R_CONSOLE},
125 // {NT_("jobdefs"), R_JOBDEFS},
126 // {NT_{"autochangers"), R_AUTOCHANGER},
137 * show <resource-keyword-name> e.g. show directors
138 * show <resource-keyword-name>=<name> e.g. show director=HeadMan
139 * show disabled shows disabled jobs
142 int show_cmd(UAContext *ua, const char *cmd)
147 RES_HEAD *reshead = NULL;
150 Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
154 for (i=1; i<ua->argc; i++) {
155 if (strcasecmp(ua->argk[i], NT_("disabled")) == 0) {
156 show_disabled_jobs(ua);
164 res_name = ua->argk[i];
165 if (!ua->argv[i]) { /* was a name given? */
166 /* No name, dump all resources of specified type */
168 len = strlen(res_name);
169 for (j=0; reses[j].res_name; j++) {
170 if (strncasecmp(res_name, reses[j].res_name, len) == 0) {
171 type = reses[j].type;
173 reshead = res_head[type-r_first];
182 /* Dump a single resource with specified name */
184 len = strlen(res_name);
185 for (j=0; reses[j].res_name; j++) {
186 if (strncasecmp(res_name, reses[j].res_name, len) == 0) {
187 type = reses[j].type;
188 res = (RES *)GetResWithName(type, ua->argv[i]);
200 for (j=r_first; j<=r_last; j++) {
201 /* Skip R_DEVICE since it is really not used or updated */
203 dump_each_resource(j, bsendmsg, ua);
209 ua->send_msg(_("Keywords for the show command are:\n"));
210 for (j=0; reses[j].res_name; j++) {
211 ua->error_msg("%s\n", reses[j].res_name);
214 /* Resource not found */
216 ua->error_msg(_("%s resource %s not found.\n"), res_name, ua->argv[i]);
218 /* Resource not found */
220 ua->error_msg(_("Resource %s not found\n"), res_name);
222 /* Dump a specific type */
224 if (res) { /* keyword and argument, ie: show job=name */
225 dump_resource(recurse?type:-type, res, bsendmsg, ua);
227 } else if (reshead) { /* keyword only, ie: show job */
228 dump_each_resource(-type, bsendmsg, ua);
239 * Check if the access is permitted for a list of jobids
241 * Not in ua_acl.c because it's using db access, and tools such
242 * as bdirjson are not linked with cats.
244 bool acl_access_jobid_ok(UAContext *ua, const char *jobids)
255 if (!is_a_number_list(jobids)) {
259 /* If no console resource => default console and all is permitted */
260 if (!ua || !ua->cons) {
261 Dmsg0(1400, "Root cons access OK.\n");
262 return true; /* No cons resource -> root console OK for everything */
265 alist *list = ua->cons->ACL_lists[Job_ACL];
266 if (!list) { /* empty list */
267 return false; /* List empty, reject everything */
270 /* Special case *all* gives full access */
271 if (list->size() == 1 && strcasecmp("*all*", (char *)list->get(0)) == 0) {
275 /* If we can't open the database, just say no */
276 if (!open_new_client_db(ua)) {
280 p = tmp = bstrdup(jobids);
282 while (get_next_jobid_from_list(&p, &jid) > 0) {
283 memset(&jr, 0, sizeof(jr));
286 if (db_get_job_record(ua->jcr, ua->db, &jr)) {
287 for (int i=0; i<list->size(); i++) {
288 if (strcasecmp(jr.Name, (char *)list->get(i)) == 0) {
289 Dmsg3(1400, "ACL found %s in %d %s\n", jr.Name,
290 Job_ACL, (char *)list->get(i));
306 * List contents of database
308 * list jobs - lists all jobs run
309 * list jobid=nnn - list job data for jobid
310 * list ujobid=uname - list job data for unique jobid
311 * list job=name - list all jobs with "name"
312 * list jobname=name - same as above
313 * list jobmedia jobid=<nn>
314 * list jobmedia job=name
315 * list joblog jobid=<nn>
316 * list joblog job=name
317 * list files [type=<deleted|all>] jobid=<nn> - list files saved for job nn
318 * list files [type=<deleted|all>] job=name
319 * list pools - list pool records
320 * list jobtotals - list totals for all jobs
321 * list media - list media for given pool (deprecated)
322 * list volumes - list Volumes
323 * list clients - list clients
324 * list nextvol job=xx - list the next vol to be used by job
325 * list nextvolume job=xx - same as above.
326 * list copies jobid=x,y,z
327 * list pluginrestoreconf jobid=x,y,z [id=k]
329 * Note: keyword "long" is before the first command on the command
330 * line results in doing a llist (long listing).
333 /* Do long or full listing */
334 int llist_cmd(UAContext *ua, const char *cmd)
336 return do_list_cmd(ua, cmd, VERT_LIST);
339 /* Do short or summary listing */
340 int list_cmd(UAContext *ua, const char *cmd)
342 if (find_arg(ua, "long") > 0) {
343 return do_list_cmd(ua, cmd, VERT_LIST); /* do a long list */
345 return do_list_cmd(ua, cmd, HORZ_LIST); /* do a short list */
349 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
358 if (!open_new_client_db(ua))
361 memset(&jr, 0, sizeof(jr));
362 memset(&pr, 0, sizeof(pr));
364 Dmsg1(20, "list: %s\n", cmd);
367 ua->error_msg(_("Hey! DB is NULL\n"));
369 /* Apply any limit */
370 for (j = 1; j < ua->argc ; j++) {
371 if (strcasecmp(ua->argk[j], NT_("joberrors")) == 0) {
373 } else if (!ua->argv[j]) {
375 } else if (strcasecmp(ua->argk[j], NT_("order")) == 0) {
376 if ((strcasecmp(ua->argv[j], NT_("desc")) == 0) ||
377 strcasecmp(ua->argv[j], NT_("descending")) == 0) {
379 } else if ((strcasecmp(ua->argv[j], NT_("asc")) == 0) ||
380 strcasecmp(ua->argv[j], NT_("ascending")) == 0) {
383 ua->error_msg(_("Unknown order type %s\n"), ua->argv[j]);
386 } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0) {
387 jr.limit = atoi(ua->argv[j]);
389 } else if (strcasecmp(ua->argk[j], NT_("jobstatus")) == 0) {
390 if (B_ISALPHA(ua->argv[j][0])) {
391 jr.JobStatus = ua->argv[j][0]; /* TODO: Check if the code is correct */
393 } else if (strcasecmp(ua->argk[j], NT_("jobtype")) == 0) {
394 if (B_ISALPHA(ua->argv[j][0])) {
395 jr.JobType = ua->argv[j][0]; /* TODO: Check if the code is correct */
397 } else if (strcasecmp(ua->argk[j], NT_("level")) == 0) {
398 if (strlen(ua->argv[j]) > 1) {
399 jr.JobLevel = get_level_code_from_name(ua->argv[j]);
401 } else if (B_ISALPHA(ua->argv[j][0])) {
402 jr.JobLevel = ua->argv[j][0]; /* TODO: Check if the code is correct */
404 } else if (strcasecmp(ua->argk[j], NT_("level")) == 0) {
407 } else if (strcasecmp(ua->argk[j], NT_("client")) == 0) {
408 if (is_name_valid(ua->argv[j], NULL)) {
410 memset(&cr, 0, sizeof(cr));
411 /* Both Backup & Restore wants to list jobs for this client */
412 if(get_client_dbr(ua, &cr, JT_BACKUP_RESTORE)) {
413 jr.ClientId = cr.ClientId;
419 /* Scan arguments looking for things to do */
420 for (i=1; i<ua->argc; i++) {
422 if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
423 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
426 } else if (strcasecmp(ua->argk[i], NT_("jobtotals")) == 0) {
427 db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
430 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
432 jobid = str_to_int64(ua->argv[i]);
435 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
440 } else if ((strcasecmp(ua->argk[i], NT_("job")) == 0 ||
441 strcasecmp(ua->argk[i], NT_("jobname")) == 0) && ua->argv[i]) {
442 bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
444 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
446 /* List UJOBID=xxx */
447 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
448 bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
450 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
452 /* List Base files */
453 } else if (strcasecmp(ua->argk[i], NT_("basefiles")) == 0) {
454 /* TODO: cleanup this block */
455 for (j=i+1; j<ua->argc; j++) {
456 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
457 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
459 db_get_job_record(ua->jcr, ua->db, &jr);
461 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
462 jobid = str_to_int64(ua->argv[j]);
467 db_list_base_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
472 } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
473 int deleted = 0; /* see only backed up files */
474 for (j=i+1; j<ua->argc; j++) {
475 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
476 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
478 db_get_job_record(ua->jcr, ua->db, &jr);
481 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
482 jobid = str_to_int64(ua->argv[j]);
484 } else if (strcasecmp(ua->argk[j], NT_("type")) == 0 && ua->argv[j]) {
485 if (strcasecmp(ua->argv[j], NT_("deleted")) == 0) {
487 } else if (strcasecmp(ua->argv[j], NT_("all")) == 0) {
490 continue; /* Type should be before the jobid... */
495 db_list_files_for_job(ua->jcr, ua->db, jobid, deleted, prtit, ua);
500 } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
502 for (j=i+1; j<ua->argc; j++) {
503 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
504 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
506 db_get_job_record(ua->jcr, ua->db, &jr);
508 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
509 jobid = str_to_int64(ua->argv[j]);
513 db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
517 /* List for all jobs (jobid=0) */
518 db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
522 } else if (strcasecmp(ua->argk[i], NT_("joblog")) == 0) {
524 for (j=i+1; j<ua->argc; j++) {
525 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
526 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
528 db_get_job_record(ua->jcr, ua->db, &jr);
530 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
531 jobid = str_to_int64(ua->argv[j]);
535 db_list_joblog_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
539 /* List for all jobs (jobid=0) */
540 db_list_joblog_records(ua->jcr, ua->db, 0, prtit, ua, llist);
545 } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
546 strcasecmp(ua->argk[i], NT_("pools")) == 0) {
548 memset(&pr, 0, sizeof(pr));
550 bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
552 db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
554 } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
555 db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
557 } else if (strcasecmp(ua->argk[i], NT_("pluginrestoreconf")) == 0) {
559 memset(&rr, 0, sizeof(rr));
560 rr.FileType = FT_PLUGIN_CONFIG;
562 for (j=i+1; j<ua->argc; j++) {
563 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
564 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
567 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
569 if (acl_access_jobid_ok(ua, ua->argv[j])) {
571 if (is_a_number(ua->argv[j])) {
572 rr.JobId = str_to_uint64(ua->argv[j]);
574 } else if (is_a_number_list(ua->argv[j])) {
575 /* In this case, loop directly to find if all jobids are
577 rr.JobIds = ua->argv[j];
581 ua->error_msg(_("Invalid jobid argument\n"));
585 } else if (((strcasecmp(ua->argk[j], NT_("id")) == 0) ||
586 (strcasecmp(ua->argk[j], NT_("restoreobjectid")) == 0))
589 rr.RestoreObjectId = str_to_uint64(ua->argv[j]);
591 } else if (strcasecmp(ua->argk[j], NT_("objecttype")) == 0 && ua->argv[j]) {
592 if (strcasecmp(ua->argv[j], NT_("PLUGIN_CONFIG")) == 0) {
593 rr.FileType = FT_PLUGIN_CONFIG;
595 } else if (strcasecmp(ua->argv[j], NT_("PLUGIN_CONFIG_FILLED")) == 0) {
596 rr.FileType = FT_PLUGIN_CONFIG_FILLED;
598 } else if (strcasecmp(ua->argv[j], NT_("RESTORE_FIRST")) == 0) {
599 rr.FileType = FT_RESTORE_FIRST;
601 } else if (strcasecmp(ua->argv[j], NT_("ALL")) == 0) {
605 ua->error_msg(_("Unknown ObjectType %s\n"), ua->argv[j]);
614 if (!rr.JobId && !rr.JobIds) {
615 ua->error_msg(_("list pluginrestoreconf requires jobid argument\n"));
619 /* Display the content of the restore object */
620 if (rr.RestoreObjectId > 0) {
621 /* Here, the JobId and the RestoreObjectId are set */
622 if (db_get_restoreobject_record(ua->jcr, ua->db, &rr)) {
623 ua->send_msg("%s\n", NPRTB(rr.object));
625 Dmsg0(200, "Object not found\n");
629 db_list_restore_objects(ua->jcr, ua->db, &rr, prtit, ua, llist);
632 db_free_restoreobject_record(ua->jcr, &rr);
635 /* List MEDIA or VOLUMES */
636 } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
637 strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
638 strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
640 for (j=i+1; j<ua->argc; j++) {
641 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
642 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
644 db_get_job_record(ua->jcr, ua->db, &jr);
646 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
647 jobid = str_to_int64(ua->argv[j]);
651 VolumeName = get_pool_memory(PM_FNAME);
652 n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
653 ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
654 free_pool_memory(VolumeName);
658 /* if no job or jobid keyword found, then we list all media */
662 /* List a specific volume? */
663 if (ua->argv[i] && *ua->argv[i]) {
664 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
665 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
668 /* Is a specific pool wanted? */
669 for (i=1; i<ua->argc; i++) {
670 if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
671 if (!get_pool_dbr(ua, &pr)) {
672 ua->error_msg(_("No Pool specified.\n"));
675 mr.PoolId = pr.PoolId;
676 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
681 /* List Volumes in all pools */
682 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
683 ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"),
684 db_strerror(ua->db));
687 if (num_pools <= 0) {
690 for (i=0; i < num_pools; i++) {
692 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
693 ua->send_msg(_("Pool: %s\n"), pr.Name);
696 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
701 /* List next volume */
702 } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
703 strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
705 j = find_arg_with_value(ua, NT_("days"));
707 n = atoi(ua->argv[j]);
708 if ((n < 0) || (n > 50)) {
709 ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n"));
714 } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
717 for (j=i+1; j<ua->argc; j++) {
718 if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
719 if (is_a_number_list(ua->argv[j])) {
720 jobids = ua->argv[j];
722 } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
723 limit = atoi(ua->argv[j]);
726 db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
727 } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
728 || strcasecmp(ua->argk[i], NT_("days")) == 0
729 || strcasecmp(ua->argk[i], NT_("joberrors")) == 0
730 || strcasecmp(ua->argk[i], NT_("order")) == 0
731 || strcasecmp(ua->argk[i], NT_("jobstatus")) == 0
732 || strcasecmp(ua->argk[i], NT_("client")) == 0
733 || strcasecmp(ua->argk[i], NT_("type")) == 0
734 || strcasecmp(ua->argk[i], NT_("level")) == 0
735 || strcasecmp(ua->argk[i], NT_("jobtype")) == 0
736 || strcasecmp(ua->argk[i], NT_("long")) == 0
739 } else if (strcasecmp(ua->argk[i], NT_("snapshot")) == 0 ||
740 strcasecmp(ua->argk[i], NT_("snapshots")) == 0)
742 snapshot_list(ua, i, prtit, llist);
746 ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
752 static bool list_nextvol(UAContext *ua, int ndays)
763 int i = find_arg_with_value(ua, "job");
765 if ((job = select_job_resource(ua)) == NULL) {
769 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
771 Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
772 if ((job = select_job_resource(ua)) == NULL) {
778 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
779 for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
780 if (!complete_jcr_for_job(jcr, job, run->pool)) {
784 if (!jcr->jr.PoolId) {
785 ua->error_msg(_("Could not find Pool for Job %s\n"), job->name());
788 memset(&pr, 0, sizeof(pr));
789 pr.PoolId = jcr->jr.PoolId;
790 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
791 bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
793 mr.PoolId = jcr->jr.PoolId;
794 get_job_storage(&store, job, run);
795 set_storageid_in_mr(store.store, &mr);
796 /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
797 if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) {
798 ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
799 job->name(), pr.Name, level_to_str(run->level));
802 _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"),
803 job->name(), pr.Name, level_to_str(run->level), mr.VolumeName);
809 if (jcr->db) db_close_database(jcr, jcr->db);
813 ua->error_msg(_("Could not find next Volume for Job %s.\n"),
822 * For a given job, we examine all his run records
823 * to see if it is scheduled today or tomorrow.
825 RUN *find_next_run(RUN *run, JOB *job, utime_t &runtime, int ndays)
827 time_t now, future, endtime;
830 int mday, wday, month, wom, i;
835 sched = job->schedule;
836 if (!sched || !job->is_enabled() || (sched && !sched->is_enabled()) ||
837 (job->client && !job->client->is_enabled())) {
838 return NULL; /* no nothing to report */
841 /* Break down the time into components */
843 endtime = now + (ndays * 60 * 60 * 24);
850 for ( ; run; run=run->next) {
852 * Find runs in next 24 hours. Day 0 is today, so if
853 * ndays=1, look at today and tomorrow.
855 for (day = 0; day <= ndays; day++) {
856 future = now + (day * 60 * 60 * 24);
858 /* Break down the time into components */
859 (void)localtime_r(&future, &tm);
860 mday = tm.tm_mday - 1;
864 woy = tm_woy(future);
865 ldom = tm_ldom(month, tm.tm_year + 1900);
867 is_scheduled = (bit_is_set(mday, run->mday) &&
868 bit_is_set(wday, run->wday) &&
869 bit_is_set(month, run->month) &&
870 bit_is_set(wom, run->wom) &&
871 bit_is_set(woy, run->woy)) ||
872 (bit_is_set(month, run->month) &&
873 bit_is_set(31, run->mday) && mday == ldom);
876 Pmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
877 Pmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
878 Pmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
879 Pmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
880 Pmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
881 Pmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
884 if (is_scheduled) { /* Jobs scheduled on that day */
886 char buf[300], num[10];
887 bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
888 for (i=0; i<24; i++) {
889 if (bit_is_set(i, run->hour)) {
890 bsnprintf(num, sizeof(num), "%d ", i);
891 bstrncat(buf, num, sizeof(buf));
894 bstrncat(buf, "\n", sizeof(buf));
895 Pmsg1(000, "%s", buf);
897 /* find time (time_t) job is to be run */
898 (void)localtime_r(&future, &runtm);
899 for (i= 0; i < 24; i++) {
900 if (bit_is_set(i, run->hour)) {
902 runtm.tm_min = run->minute;
904 runtime = mktime(&runtm);
905 Dmsg2(200, "now=%d runtime=%lld\n", now, runtime);
906 if ((runtime > now) && (runtime < endtime)) {
907 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
908 return run; /* found it, return run resource */
914 } /* end for loop over runs */
920 * Fill in the remaining fields of the jcr as if it
921 * is going to run the job.
923 bool complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
927 memset(&pr, 0, sizeof(POOL_DBR));
928 set_jcr_defaults(jcr, job);
930 jcr->pool = pool; /* override */
933 Dmsg0(100, "complete_jcr close db\n");
934 db_close_database(jcr, jcr->db);
938 Dmsg0(100, "complete_jcr open db\n");
939 jcr->db = db_init_database(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
940 jcr->catalog->db_user,
941 jcr->catalog->db_password, jcr->catalog->db_address,
942 jcr->catalog->db_port, jcr->catalog->db_socket,
943 jcr->catalog->db_ssl_mode, jcr->catalog->db_ssl_key,
944 jcr->catalog->db_ssl_cert, jcr->catalog->db_ssl_ca,
945 jcr->catalog->db_ssl_capath, jcr->catalog->db_ssl_cipher,
946 jcr->catalog->mult_db_connections,
947 jcr->catalog->disable_batch_insert);
948 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
949 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
950 jcr->catalog->db_name);
952 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
953 db_close_database(jcr, jcr->db);
958 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
959 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
960 /* Try to create the pool */
961 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
962 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
963 db_strerror(jcr->db));
965 db_close_database(jcr, jcr->db);
970 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
973 jcr->jr.PoolId = pr.PoolId;
978 static void con_lock_release(void *arg)
983 void do_messages(UAContext *ua, const char *cmd)
987 bool do_truncate = false;
990 dequeue_messages(ua->jcr);
993 pthread_cleanup_push(con_lock_release, (void *)NULL);
995 while (fgets(msg, sizeof(msg), con_fd)) {
997 ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
998 strcpy(ua->UA_sock->msg, msg);
999 ua->UA_sock->msglen = mlen;
1000 ua->UA_sock->send();
1004 (void)ftruncate(fileno(con_fd), 0L);
1006 console_msg_pending = false;
1007 ua->user_notified_msg_pending = false;
1008 pthread_cleanup_pop(0);
1013 int qmessagescmd(UAContext *ua, const char *cmd)
1015 if (console_msg_pending && ua->auto_display_messages) {
1016 do_messages(ua, cmd);
1021 int messagescmd(UAContext *ua, const char *cmd)
1023 if (console_msg_pending) {
1024 do_messages(ua, cmd);
1026 ua->UA_sock->fsend(_("You have no messages.\n"));
1032 * Callback routine for "printing" database file listing
1034 void prtit(void *ctx, const char *msg)
1036 UAContext *ua = (UAContext *)ctx;
1038 if (ua) ua->send_msg("%s", msg);
1042 * Format message and send to other end.
1044 * If the UA_sock is NULL, it means that there is no user
1045 * agent, so we are being called from Bacula core. In
1046 * that case direct the messages to the Job.
1049 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
1051 BSOCK *bs = ua->UA_sock;
1053 POOLMEM *msg = NULL;
1060 msg = get_pool_memory(PM_EMSG);
1064 maxlen = sizeof_pool_memory(msg) - 1;
1065 va_copy(ap, arg_ptr);
1066 len = bvsnprintf(msg, maxlen, fmt, ap);
1068 if (len < 0 || len >= maxlen) {
1069 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
1077 } else { /* No UA, send to Job */
1078 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
1079 free_pool_memory(msg);
1084 #else /* no va_copy() -- brain damaged version of variable arguments */
1086 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
1088 BSOCK *bs = ua->UA_sock;
1090 POOLMEM *msg = NULL;
1096 msg = get_memory(5000);
1099 maxlen = sizeof_pool_memory(msg) - 1;
1100 if (maxlen < 4999) {
1101 msg = realloc_pool_memory(msg, 5000);
1104 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
1105 if (len < 0 || len >= maxlen) {
1106 pm_strcpy(msg, _("Message too long to display.\n"));
1114 } else { /* No UA, send to Job */
1115 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
1116 free_pool_memory(msg);
1122 void bsendmsg(void *ctx, const char *fmt, ...)
1125 va_start(arg_ptr, fmt);
1126 bmsg((UAContext *)ctx, fmt, arg_ptr);
1131 * The following UA methods are mainly intended for GUI
1135 * This is a message that should be displayed on the user's
1138 void UAContext::send_msg(const char *fmt, ...)
1141 va_start(arg_ptr, fmt);
1142 bmsg(this, fmt, arg_ptr);
1148 * This is an error condition with a command. The gui should put
1149 * up an error or critical dialog box. The command is aborted.
1151 void UAContext::error_msg(const char *fmt, ...)
1153 BSOCK *bs = UA_sock;
1156 if (bs && api) bs->signal(BNET_ERROR_MSG);
1157 va_start(arg_ptr, fmt);
1158 bmsg(this, fmt, arg_ptr);
1163 * This is a warning message, that should bring up a warning
1164 * dialog box on the GUI. The command is not aborted, but something
1167 void UAContext::warning_msg(const char *fmt, ...)
1169 BSOCK *bs = UA_sock;
1172 if (bs && api) bs->signal(BNET_WARNING_MSG);
1173 va_start(arg_ptr, fmt);
1174 bmsg(this, fmt, arg_ptr);
1179 * This is an information message that should probably be put
1180 * into the status line of a GUI program.
1182 void UAContext::info_msg(const char *fmt, ...)
1184 BSOCK *bs = UA_sock;
1187 if (bs && api) bs->signal(BNET_INFO_MSG);
1188 va_start(arg_ptr, fmt);
1189 bmsg(this, fmt, arg_ptr);