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.
21 * Bacula Director -- User Agent Prompt and Selection code
23 * Kern Sibbald, October MMI
30 /* Imported variables */
31 extern struct s_jl joblevels[];
33 int confirm_retention_yesno(UAContext *ua, utime_t ret, const char *msg)
38 /* Look for "yes" in command line */
39 if (find_arg(ua, NT_("yes")) != -1) {
44 ua->info_msg(_("The current %s retention period is: %s\n"),
45 msg, edit_utime(ret, ed1, sizeof(ed1)));
46 if (!get_cmd(ua, _("Continue? (yes/no): "))) {
49 if (is_yesno(ua->cmd, &val)) {
50 return val; /* is 1 for yes, 0 for no */
57 * Confirm a retention period
59 int confirm_retention(UAContext *ua, utime_t *ret, const char *msg)
64 /* Look for "yes" in command line */
65 if (find_arg(ua, NT_("yes")) != -1) {
70 ua->info_msg(_("The current %s retention period is: %s\n"),
71 msg, edit_utime(*ret, ed1, sizeof(ed1)));
73 if (!get_cmd(ua, _("Continue? (yes/mod/no): "))) {
76 if (strcasecmp(ua->cmd, _("mod")) == 0) {
77 if (!get_cmd(ua, _("Enter new retention period: "))) {
80 if (!duration_to_utime(ua->cmd, ret)) {
81 ua->error_msg(_("Invalid period.\n"));
86 if (is_yesno(ua->cmd, &val)) {
87 return val; /* is 1 for yes, 0 for no */
94 * Given a list of keywords, find the first one
95 * that is in the argument list.
96 * Returns: -1 if not found
97 * index into list (base 0) on success
99 int find_arg_keyword(UAContext *ua, const char **list)
101 for (int i=1; i<ua->argc; i++) {
102 for(int j=0; list[j]; j++) {
103 if (strcasecmp(list[j], ua->argk[i]) == 0) {
112 * Given one keyword, find the first one that
113 * is in the argument list.
114 * Returns: argk index (always gt 0)
117 int find_arg(UAContext *ua, const char *keyword)
119 for (int i=1; i<ua->argc; i++) {
120 if (strcasecmp(keyword, ua->argk[i]) == 0) {
128 * Given a single keyword, find it in the argument list, but
129 * it must have a value
130 * Returns: -1 if not found or no value
131 * list index (base 0) on success
133 int find_arg_with_value(UAContext *ua, const char *keyword)
135 for (int i=1; i<ua->argc; i++) {
136 if (strcasecmp(keyword, ua->argk[i]) == 0) {
148 * Given a list of keywords, prompt the user
151 * Returns: -1 on failure
152 * index into list (base 0) on success
154 int do_keyword_prompt(UAContext *ua, const char *msg, const char **list)
157 start_prompt(ua, _("You have the following choices:\n"));
158 for (i=0; list[i]; i++) {
159 add_prompt(ua, list[i]);
161 return do_prompt(ua, "", msg, NULL, 0);
166 * Select a Storage resource from prompt list
167 * If unique is set storage resources that have the main address are
168 * combined into one (i.e. they are all part of the same)
169 * storage. Note, not all commands want this.
171 STORE *select_storage_resource(UAContext *ua, bool unique)
174 char name[MAX_NAME_LENGTH];
177 /* Does user want a full selection? */
178 if (unique && find_arg(ua, NT_("select")) > 0) {
181 start_prompt(ua, _("The defined Storage resources are:\n"));
183 foreach_res(store, R_STORAGE) {
184 if (store->is_enabled() && acl_access_ok(ua, Storage_ACL, store->name())) {
186 Mmsg(tmp, "%s:%d", store->address, store->SDport);
187 add_prompt(ua, store->name(), tmp.c_str());
189 add_prompt(ua, store->name());
194 if (do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name)) < 0) {
197 store = (STORE *)GetResWithName(R_STORAGE, name);
202 * Select a FileSet resource from prompt list
204 FILESET *select_fileset_resource(UAContext *ua)
206 char name[MAX_NAME_LENGTH];
209 start_prompt(ua, _("The defined FileSet resources are:\n"));
211 foreach_res(fs, R_FILESET) {
212 if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
213 add_prompt(ua, fs->name());
217 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), name, sizeof(name)) < 0) {
220 fs = (FILESET *)GetResWithName(R_FILESET, name);
226 * Get a catalog resource from prompt list
228 CAT *get_catalog_resource(UAContext *ua)
230 char name[MAX_NAME_LENGTH];
232 CLIENT *client = NULL;
235 for (i=1; i<ua->argc; i++) {
236 if (strcasecmp(ua->argk[i], NT_("catalog")) == 0 && ua->argv[i]) {
237 if (acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
238 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
242 if (strcasecmp(ua->argk[i], NT_("client")) == 0 && ua->argv[i]) {
243 if (acl_access_client_ok(ua, ua->argv[i], JT_BACKUP_RESTORE)) {
244 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
249 if (!catalog && client) { /* Try to take the catalog from the client */
250 catalog = client->catalog;
252 if (ua->gui && !catalog) {
254 catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
257 ua->error_msg(_("Could not find a Catalog resource\n"));
259 } else if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
260 ua->error_msg(_("You must specify a \"use <catalog-name>\" command before continuing.\n"));
266 start_prompt(ua, _("The defined Catalog resources are:\n"));
268 foreach_res(catalog, R_CATALOG) {
269 if (acl_access_ok(ua, Catalog_ACL, catalog->name())) {
270 add_prompt(ua, catalog->name());
274 if (do_prompt(ua, _("Catalog"), _("Select Catalog resource"), name, sizeof(name)) < 0) {
277 catalog = (CAT *)GetResWithName(R_CATALOG, name);
284 * Select a job to enable or disable
286 JOB *select_enable_disable_job_resource(UAContext *ua, bool enable)
288 char name[MAX_NAME_LENGTH];
293 start_prompt(ua, _("The disabled Job resources are:\n"));
295 start_prompt(ua, _("The enabled Job resources are:\n"));
297 foreach_res(job, R_JOB) {
298 if (!acl_access_ok(ua, Job_ACL, job->name())) {
301 if (job->is_enabled() == enable) { /* Already enabled/disabled? */
302 continue; /* yes, skip */
304 add_prompt(ua, job->name());
307 if (do_prompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) < 0) {
310 job = (JOB *)GetResWithName(R_JOB, name);
315 * Select a Job resource from prompt list
317 JOB *select_job_resource(UAContext *ua)
319 char name[MAX_NAME_LENGTH];
322 start_prompt(ua, _("The defined Job resources are:\n"));
324 foreach_res(job, R_JOB) {
325 if (job->is_enabled() && acl_access_ok(ua, Job_ACL, job->name())) {
326 add_prompt(ua, job->name());
330 if (do_prompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) < 0) {
333 job = (JOB *)GetResWithName(R_JOB, name);
338 * Select a Restore Job resource from argument or prompt
340 JOB *get_restore_job(UAContext *ua)
343 int i = find_arg_with_value(ua, "restorejob");
344 if (i >= 0 && acl_access_ok(ua, Job_ACL, ua->argv[i])) {
345 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
346 if (job && job->JobType == JT_RESTORE) {
349 ua->error_msg(_("Error: Restore Job resource \"%s\" does not exist.\n"),
352 return select_restore_job_resource(ua);
356 * Select a Restore Job resource from prompt list
358 JOB *select_restore_job_resource(UAContext *ua)
360 char name[MAX_NAME_LENGTH];
363 start_prompt(ua, _("The defined Restore Job resources are:\n"));
365 foreach_res(job, R_JOB) {
366 if (job->JobType == JT_RESTORE && job->is_enabled() &&
367 acl_access_ok(ua, Job_ACL, job->name())) {
368 add_prompt(ua, job->name());
372 if (do_prompt(ua, _("Job"), _("Select Restore Job"), name, sizeof(name)) < 0) {
375 job = (JOB *)GetResWithName(R_JOB, name);
380 * Select a client to enable or disable
382 CLIENT *select_enable_disable_client_resource(UAContext *ua, bool enable)
384 char name[MAX_NAME_LENGTH];
388 start_prompt(ua, _("The defined Client resources are:\n"));
389 foreach_res(client, R_CLIENT) {
390 if (!acl_access_client_ok(ua, client->name(), JT_BACKUP_RESTORE)) {
393 if (client->is_enabled() == enable) { /* Already enabled/disabled? */
394 continue; /* yes, skip */
396 add_prompt(ua, client->name());
399 if (do_prompt(ua, _("Client"), _("Select Client resource"), name, sizeof(name)) < 0) {
402 client = (CLIENT *)GetResWithName(R_CLIENT, name);
408 * Select a client resource from prompt list
410 CLIENT *select_client_resource(UAContext *ua, int32_t jobtype)
412 char name[MAX_NAME_LENGTH];
415 start_prompt(ua, _("The defined Client resources are:\n"));
417 foreach_res(client, R_CLIENT) {
418 if (client->is_enabled() && acl_access_client_ok(ua, client->name(), jobtype)) {
419 add_prompt(ua, client->name());
423 if (do_prompt(ua, _("Client"), _("Select Client (File daemon) resource"), name, sizeof(name)) < 0) {
426 client = (CLIENT *)GetResWithName(R_CLIENT, name);
431 * Get client resource, start by looking for
432 * client=<client-name>
433 * if we don't find the keyword, we prompt the user.
435 CLIENT *get_client_resource(UAContext *ua, int32_t jobtype)
437 CLIENT *client = NULL;
440 for (i=1; i<ua->argc; i++) {
441 if ((strcasecmp(ua->argk[i], NT_("client")) == 0 ||
442 strcasecmp(ua->argk[i], NT_("fd")) == 0) && ua->argv[i]) {
443 if (!acl_access_client_ok(ua, ua->argv[i], jobtype)) {
446 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
450 ua->error_msg(_("Error: Client resource %s does not exist.\n"), ua->argv[i]);
454 return select_client_resource(ua, jobtype);
458 * Select a schedule to enable or disable
460 SCHED *select_enable_disable_schedule_resource(UAContext *ua, bool enable)
462 char name[MAX_NAME_LENGTH];
466 start_prompt(ua, _("The defined Schedule resources are:\n"));
467 foreach_res(sched, R_SCHEDULE) {
468 if (!acl_access_ok(ua, Schedule_ACL, sched->name())) {
471 if (sched->is_enabled() == enable) { /* Already enabled/disabled? */
472 continue; /* yes, skip */
474 add_prompt(ua, sched->name());
477 if (do_prompt(ua, _("Schedule"), _("Select Schedule resource"), name, sizeof(name)) < 0) {
480 sched = (SCHED *)GetResWithName(R_SCHEDULE, name);
485 /* Scan what the user has entered looking for:
487 * client=<client-name>
489 * if error or not found, put up a list of client DBRs
492 * returns: 0 on error
493 * 1 on success and fills in CLIENT_DBR
495 bool get_client_dbr(UAContext *ua, CLIENT_DBR *cr, int32_t jobtype)
499 if (cr->Name[0]) { /* If name already supplied */
500 if (db_get_client_record(ua->jcr, ua->db, cr)) {
503 ua->error_msg(_("Could not find Client %s: ERR=%s"), cr->Name, db_strerror(ua->db));
505 for (i=1; i<ua->argc; i++) {
506 if ((strcasecmp(ua->argk[i], NT_("client")) == 0 ||
507 strcasecmp(ua->argk[i], NT_("fd")) == 0) && ua->argv[i]) {
508 if (!acl_access_client_ok(ua, ua->argv[i], jobtype)) {
511 bstrncpy(cr->Name, ua->argv[i], sizeof(cr->Name));
512 if (!db_get_client_record(ua->jcr, ua->db, cr)) {
513 ua->error_msg(_("Could not find Client \"%s\": ERR=%s"), ua->argv[i],
514 db_strerror(ua->db));
521 if (!select_client_dbr(ua, cr, jobtype)) { /* try once more by proposing a list */
528 * Select a Client record from the catalog
529 * Returns 1 on success
532 bool select_client_dbr(UAContext *ua, CLIENT_DBR *cr, int32_t jobtype)
535 char name[MAX_NAME_LENGTH];
541 if (!db_get_client_ids(ua->jcr, ua->db, &num_clients, &ids)) {
542 ua->error_msg(_("Error obtaining client ids. ERR=%s\n"), db_strerror(ua->db));
545 if (num_clients <= 0) {
546 ua->error_msg(_("No clients defined. You must run a job before using this command.\n"));
550 start_prompt(ua, _("Defined Clients:\n"));
551 for (i=0; i < num_clients; i++) {
552 ocr.ClientId = ids[i];
553 if (!db_get_client_record(ua->jcr, ua->db, &ocr) ||
554 !acl_access_client_ok(ua, ocr.Name, jobtype)) {
557 add_prompt(ua, ocr.Name);
560 if (do_prompt(ua, _("Client"), _("Select the Client"), name, sizeof(name)) < 0) {
563 memset(&ocr, 0, sizeof(ocr));
564 bstrncpy(ocr.Name, name, sizeof(ocr.Name));
566 if (!db_get_client_record(ua->jcr, ua->db, &ocr)) {
567 ua->error_msg(_("Could not find Client \"%s\": ERR=%s"), name, db_strerror(ua->db));
570 memcpy(cr, &ocr, sizeof(ocr));
574 /* Scan what the user has entered looking for:
578 * where argk can be : pool, recyclepool, scratchpool, nextpool etc..
580 * if error or not found, put up a list of pool DBRs
583 * returns: false on error
584 * true on success and fills in POOL_DBR
586 bool get_pool_dbr(UAContext *ua, POOL_DBR *pr, const char *argk)
588 if (pr->Name[0]) { /* If name already supplied */
589 if (db_get_pool_numvols(ua->jcr, ua->db, pr) &&
590 acl_access_ok(ua, Pool_ACL, pr->Name)) {
593 ua->error_msg(_("Could not find Pool \"%s\": ERR=%s"), pr->Name, db_strerror(ua->db));
595 if (!select_pool_dbr(ua, pr, argk)) { /* try once more */
602 * Select a Pool record from catalog
603 * argk can be pool, recyclepool, scratchpool etc..
605 bool select_pool_dbr(UAContext *ua, POOL_DBR *pr, const char *argk)
608 char name[MAX_NAME_LENGTH];
612 for (i=1; i<ua->argc; i++) {
613 if (strcasecmp(ua->argk[i], argk) == 0 && ua->argv[i] &&
614 acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
615 bstrncpy(pr->Name, ua->argv[i], sizeof(pr->Name));
616 if (!db_get_pool_numvols(ua->jcr, ua->db, pr)) {
617 ua->error_msg(_("Could not find Pool \"%s\": ERR=%s"), ua->argv[i],
618 db_strerror(ua->db));
627 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
628 ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db));
631 if (num_pools <= 0) {
632 ua->error_msg(_("No pools defined. Use the \"create\" command to create one.\n"));
636 start_prompt(ua, _("Defined Pools:\n"));
637 if (bstrcmp(argk, NT_("recyclepool"))) {
638 add_prompt(ua, _("*None*"));
640 for (i=0; i < num_pools; i++) {
642 if (!db_get_pool_numvols(ua->jcr, ua->db, &opr) ||
643 !acl_access_ok(ua, Pool_ACL, opr.Name)) {
646 add_prompt(ua, opr.Name);
649 if (do_prompt(ua, _("Pool"), _("Select the Pool"), name, sizeof(name)) < 0) {
653 memset(&opr, 0, sizeof(opr));
654 /* *None* is only returned when selecting a recyclepool, and in that case
655 * the calling code is only interested in opr.Name, so then we can leave
658 if (!bstrcmp(name, _("*None*"))) {
659 bstrncpy(opr.Name, name, sizeof(opr.Name));
661 if (!db_get_pool_numvols(ua->jcr, ua->db, &opr)) {
662 ua->error_msg(_("Could not find Pool \"%s\": ERR=%s"), name, db_strerror(ua->db));
667 memcpy(pr, &opr, sizeof(opr));
672 * Select a Pool and a Media (Volume) record from the database
674 int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr)
677 if (!select_media_dbr(ua, mr)) {
680 memset(pr, 0, sizeof(POOL_DBR));
681 pr->PoolId = mr->PoolId;
682 if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
683 ua->error_msg("%s", db_strerror(ua->db));
686 if (!acl_access_ok(ua, Pool_ACL, pr->Name)) {
687 ua->error_msg(_("No access to Pool \"%s\"\n"), pr->Name);
693 /* Select a Media (Volume) record from the database */
694 int select_media_dbr(UAContext *ua, MEDIA_DBR *mr)
698 POOLMEM *err = get_pool_memory(PM_FNAME);
702 i = find_arg_with_value(ua, "volume");
704 if (is_name_valid(ua->argv[i], &err)) {
705 bstrncpy(mr->VolumeName, ua->argv[i], sizeof(mr->VolumeName));
710 if (mr->VolumeName[0] == 0) {
712 memset(&pr, 0, sizeof(pr));
713 /* Get the pool from pool=<pool-name> */
714 if (!get_pool_dbr(ua, &pr)) {
717 mr->PoolId = pr.PoolId;
718 db_list_media_records(ua->jcr, ua->db, mr, prtit, ua, HORZ_LIST);
719 if (!get_cmd(ua, _("Enter a Volume name or *MediaId: "))) {
722 if (ua->cmd[0] == '*' && is_a_number(ua->cmd+1)) {
723 mr->MediaId = str_to_int64(ua->cmd+1);
724 } else if (is_name_valid(ua->cmd, &err)) {
725 bstrncpy(mr->VolumeName, ua->cmd, sizeof(mr->VolumeName));
731 if (!db_get_media_record(ua->jcr, ua->db, mr)) {
732 pm_strcpy(err, db_strerror(ua->db));
739 ua->error_msg("%s", err);
741 free_pool_memory(err);
747 * Select a pool resource from prompt list
749 POOL *select_pool_resource(UAContext *ua)
751 char name[MAX_NAME_LENGTH];
754 start_prompt(ua, _("The defined Pool resources are:\n"));
756 foreach_res(pool, R_POOL) {
757 if (acl_access_ok(ua, Pool_ACL, pool->name())) {
758 add_prompt(ua, pool->name());
762 if (do_prompt(ua, _("Pool"), _("Select Pool resource"), name, sizeof(name)) < 0) {
765 pool = (POOL *)GetResWithName(R_POOL, name);
771 * If you are thinking about using it, you
772 * probably want to use select_pool_dbr()
773 * or get_pool_dbr() above.
775 POOL *get_pool_resource(UAContext *ua)
780 i = find_arg_with_value(ua, "pool");
781 if (i >= 0 && acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
782 pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
786 ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
788 return select_pool_resource(ua);
792 * List all jobs and ask user to select one
794 static int select_job_dbr(UAContext *ua, JOB_DBR *jr)
796 db_list_job_records(ua->jcr, ua->db, jr, prtit, ua, HORZ_LIST);
797 if (!get_pint(ua, _("Enter the JobId to select: "))) {
800 jr->JobId = ua->int64_val;
801 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
802 ua->error_msg("%s", db_strerror(ua->db));
810 /* Scan what the user has entered looking for:
814 * if error or not found, put up a list of Jobs
817 * returns: 0 on error
818 * JobId on success and fills in JOB_DBR
820 int get_job_dbr(UAContext *ua, JOB_DBR *jr)
824 for (i=1; i<ua->argc; i++) {
825 if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
827 bstrncpy(jr->Job, ua->argv[i], sizeof(jr->Job));
828 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0 && ua->argv[i]) {
829 jr->JobId = str_to_int64(ua->argv[i]);
834 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
835 ua->error_msg(_("Could not find Job \"%s\": ERR=%s"), ua->argv[i],
836 db_strerror(ua->db));
846 for (i=1; i<ua->argc; i++) {
847 if ((strcasecmp(ua->argk[i], NT_("jobname")) == 0 ||
848 strcasecmp(ua->argk[i], NT_("job")) == 0) && ua->argv[i]) {
850 bstrncpy(jr->Name, ua->argv[i], sizeof(jr->Name));
854 if (!select_job_dbr(ua, jr)) { /* try once more */
861 * Implement unique set of prompts
863 void start_prompt(UAContext *ua, const char *msg)
865 if (ua->max_prompts == 0) {
866 ua->max_prompts = 10;
867 ua->prompt = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
868 ua->unique = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
871 ua->prompt[0] = bstrdup(msg);
872 ua->unique[0] = NULL;
876 * Add to prompts -- keeping them unique by name
878 void add_prompt(UAContext *ua, const char *prompt, char *unique)
881 if (ua->num_prompts == ua->max_prompts) {
882 ua->max_prompts *= 2;
883 ua->prompt = (char **)brealloc(ua->prompt, sizeof(char *) *
885 ua->unique = (char **)brealloc(ua->unique, sizeof(char *) *
888 for (i=1; i < ua->num_prompts; i++) {
889 if (strcmp(ua->prompt[i], prompt) == 0) {
891 } else if (unique && strcmp(ua->unique[i], unique) == 0) {
895 ua->prompt[ua->num_prompts] = bstrdup(prompt);
897 ua->unique[ua->num_prompts++] = bstrdup(unique);
899 ua->unique[ua->num_prompts++] = NULL;
904 * Display prompts and get user's choice
906 * Returns: -1 on error
907 * index base 0 on success, and choice
908 * is copied to prompt if not NULL
909 * prompt is set to the chosen prompt item string
911 int do_prompt(UAContext *ua, const char *automsg, const char *msg,
912 char *prompt, int max_prompt)
915 char pmsg[MAXSTRING];
916 BSOCK *user = ua->UA_sock;
921 if (ua->num_prompts == 2) {
924 bstrncpy(prompt, ua->prompt[1], max_prompt);
926 ua->send_msg(_("Automatically selected %s: %s\n"), NPRTB(automsg), ua->prompt[1]);
929 /* If running non-interactive, bail out */
931 /* First print the choices he wanted to make */
932 ua->send_msg(ua->prompt[0]);
933 for (i=1; i < ua->num_prompts; i++) {
934 ua->send_msg("%6d: %s\n", i, ua->prompt[i]);
936 /* Now print error message */
937 ua->send_msg(_("Your request has multiple choices for \"%s\". Selection is not possible in batch mode.\n"), automsg);
941 if (ua->api) user->signal(BNET_START_SELECT);
942 ua->send_msg(ua->prompt[0]);
943 for (i=1; i < ua->num_prompts; i++) {
945 ua->send_msg("%s", ua->prompt[i]);
947 ua->send_msg("%6d: %s\n", i, ua->prompt[i]);
950 if (ua->api) user->signal(BNET_END_SELECT);
953 /* First item is the prompt string, not the items */
954 if (ua->num_prompts == 1) {
955 ua->error_msg(_("Selection list for \"%s\" is empty!\n"), automsg);
956 item = -1; /* list is empty ! */
959 if (ua->num_prompts == 2) {
961 ua->send_msg(_("Automatically selected: %s\n"), ua->prompt[1]);
963 bstrncpy(prompt, ua->prompt[1], max_prompt);
967 sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
969 /* Either a . or an @ will get you out of the loop */
970 if (ua->api) user->signal(BNET_SELECT_INPUT);
971 if (!get_pint(ua, pmsg)) {
972 item = -1; /* error */
973 ua->info_msg(_("Selection aborted, nothing done.\n"));
976 item = ua->pint32_val;
977 if (item < 1 || item >= ua->num_prompts) {
978 ua->warning_msg(_("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
982 bstrncpy(prompt, ua->prompt[item], max_prompt);
988 for (i=0; i < ua->num_prompts; i++) {
990 if (ua->unique[i]) free(ua->unique[i]);
993 return item>0 ? item-1 : item;
997 * Display prompts and get user's choice
999 * Returns: -1 on error
1000 * number of items selected and the choices are
1001 * copied to selected if not NULL
1002 * selected is an alist of the prompts chosen
1003 * Note! selected must already be initialized.
1005 int do_alist_prompt(UAContext *ua, const char *automsg, const char *msg,
1009 char pmsg[MAXSTRING];
1010 BSOCK *user = ua->UA_sock;
1013 /* First item is the prompt string, not the items */
1014 if (ua->num_prompts == 1) {
1015 ua->error_msg(_("Selection list for \"%s\" is empty!\n"), automsg);
1016 item = -1; /* list is empty ! */
1019 if (ua->num_prompts == 2) {
1021 selected->append(bstrdup(ua->prompt[1]));
1022 ua->send_msg(_("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
1025 /* If running non-interactive, bail out */
1027 /* First print the choices he wanted to make */
1028 ua->send_msg(ua->prompt[0]);
1029 for (i=1; i < ua->num_prompts; i++) {
1030 ua->send_msg("%6d: %s\n", i, ua->prompt[i]);
1032 /* Now print error message */
1033 ua->send_msg(_("Your request has multiple choices for \"%s\". Selection is not possible in batch mode.\n"), automsg);
1037 if (ua->api) user->signal(BNET_START_SELECT);
1038 ua->send_msg(ua->prompt[0]);
1039 for (i=1; i < ua->num_prompts; i++) {
1041 ua->send_msg("%s", ua->prompt[i]);
1043 ua->send_msg("%6d: %s\n", i, ua->prompt[i]);
1046 if (ua->api) user->signal(BNET_END_SELECT);
1048 sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
1052 /* Either a . or an @ will get you out of the loop */
1053 if (ua->api) user->signal(BNET_SELECT_INPUT);
1055 if (!get_selection_list(ua, sl, pmsg, false)) {
1061 for (i=1; i < ua->num_prompts; i++) {
1062 selected->append(bstrdup(ua->prompt[i]));
1065 while ( (item = sl.next()) > 0) {
1066 if (item < 1 || item >= ua->num_prompts) {
1067 ua->warning_msg(_("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
1071 selected->append(bstrdup(ua->prompt[item]));
1075 item = selected->size();
1081 for (i=0; i < ua->num_prompts; i++) {
1082 free(ua->prompt[i]);
1083 if (ua->unique[i]) free(ua->unique[i]);
1085 ua->num_prompts = 0;
1091 * We scan what the user has entered looking for
1092 * storage=<storage-resource>
1095 * ? (prompt him with storage list)
1096 * <some-error> (prompt him with storage list)
1098 * If use_default is set, we assume that any keyword without a value
1099 * is the name of the Storage resource wanted.
1101 STORE *get_storage_resource(UAContext *ua, bool use_default, bool unique)
1103 char store_name[MAX_NAME_LENGTH];
1104 STORE *store = NULL;
1111 for (i=1; i<ua->argc; i++) {
1112 if (use_default && !ua->argv[i]) {
1113 /* Ignore slots, scan and barcode(s) keywords */
1114 if (strcasecmp("scan", ua->argk[i]) == 0 ||
1115 strcasecmp("barcode", ua->argk[i]) == 0 ||
1116 strcasecmp("barcodes", ua->argk[i]) == 0 ||
1117 strcasecmp("slots", ua->argk[i]) == 0) {
1120 /* Default argument is storage (except in enable/disable command) */
1121 if (store_name[0]) {
1122 ua->error_msg(_("Storage name given twice.\n"));
1125 bstrncpy(store_name, ua->argk[i], sizeof(store_name));
1126 if (store_name[0] == '?') {
1131 if (strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1132 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1133 bstrncpy(store_name, NPRTB(ua->argv[i]), sizeof(store_name));
1135 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
1136 jobid = str_to_int64(ua->argv[i]);
1138 ua->error_msg(_("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
1141 if (!(jcr=get_jcr_by_id(jobid))) {
1142 ua->error_msg(_("JobId %s is not running.\n"), edit_int64(jobid, ed1));
1146 bstrncpy(store_name, jcr->wstore->name(), sizeof(store_name));
1150 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0 ||
1151 strcasecmp(ua->argk[i], NT_("jobname")) == 0) {
1153 ua->error_msg(_("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
1156 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
1157 ua->error_msg(_("Job \"%s\" is not running.\n"), ua->argv[i]);
1161 bstrncpy(store_name, jcr->wstore->name(), sizeof(store_name));
1165 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
1167 ua->error_msg(_("Expecting ujobid=xxx, got: %s.\n"), ua->argk[i]);
1170 if ((jcr=get_jcr_by_full_name(ua->argv[i]))) {
1172 bstrncpy(store_name, jcr->wstore->name(), sizeof(store_name));
1177 if (store_name[0]) {
1178 break; /* We can stop the loop if we have something */
1183 if (store_name[0] != 0) {
1184 store = (STORE *)GetResWithName(R_STORAGE, store_name);
1185 if (!store && strcmp(store_name, "storage") != 0) {
1186 /* Looks that the first keyword of the line was not a storage name, make
1187 * sure that it's not "storage=" before we print the following message
1189 ua->error_msg(_("Storage resource \"%s\": not found\n"), store_name);
1192 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1195 /* No keywords found, so present a selection list */
1197 store = select_storage_resource(ua, unique);
1202 /* Get drive that we are working with for this storage */
1203 int get_storage_drive(UAContext *ua, STORE *store)
1206 /* Get drive for autochanger if possible */
1207 i = find_arg_with_value(ua, "drive");
1209 drive = atoi(ua->argv[i]);
1210 } else if (store && store->autochanger) {
1211 /* If our structure is not set ask SD for # drives */
1212 if (store->drives == 0) {
1213 store->drives = get_num_drives_from_SD(ua);
1215 /* If only one drive, default = 0 */
1216 if (store->drives == 1) {
1219 /* Ask user to enter drive number */
1221 if (!get_cmd(ua, _("Enter autochanger drive[0]: "))) {
1222 drive = -1; /* None */
1224 drive = atoi(ua->cmd);
1231 /* Get slot that we are working with for this storage */
1232 int get_storage_slot(UAContext *ua, STORE *store)
1235 /* Get slot for autochanger if possible */
1236 i = find_arg_with_value(ua, "slot");
1238 slot = atoi(ua->argv[i]);
1239 } else if (store && store->autochanger) {
1240 /* Ask user to enter slot number */
1242 if (!get_cmd(ua, _("Enter autochanger slot: "))) {
1243 slot = -1; /* None */
1245 slot = atoi(ua->cmd);
1254 * Scan looking for mediatype=
1256 * if not found or error, put up selection list
1258 * Returns: 0 on error
1259 * 1 on success, MediaType is set
1261 int get_media_type(UAContext *ua, char *MediaType, int max_media)
1266 i = find_arg_with_value(ua, "mediatype");
1268 bstrncpy(MediaType, ua->argv[i], max_media);
1272 start_prompt(ua, _("Media Types defined in conf file:\n"));
1274 foreach_res(store, R_STORAGE) {
1275 if (store->is_enabled()) {
1276 add_prompt(ua, store->media_type);
1280 return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
1283 int get_level_code_from_name(const char *level_name)
1289 for (int i=0; joblevels[i].level_name; i++) {
1290 if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
1291 ret = joblevels[i].level;
1298 bool get_level_from_name(JCR *jcr, const char *level_name)
1300 int level = get_level_code_from_name(level_name);
1302 jcr->setJobLevel(level);
1308 static int count_running_jobs(UAContext *ua)
1310 int tjobs = 0; /* total # number jobs */
1313 /* Count Jobs running */
1315 if (jcr->is_internal_job()) { /* this is us */
1318 tjobs++; /* count of all jobs */
1319 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1320 continue; /* skip not authorized */
1322 njobs++; /* count of authorized jobs */
1326 if (njobs == 0) { /* no authorized */
1328 ua->send_msg(_("No Jobs running.\n"));
1330 ua->send_msg(_("None of your jobs are running.\n"));
1337 /* Get a list of running jobs
1338 * "reason" is used in user messages
1339 * can be: cancel, limit, ...
1340 * Returns: -1 on error
1341 * nb of JCR on success (should be free_jcr() after)
1343 int select_running_jobs(UAContext *ua, alist *jcrs, const char *reason)
1348 char JobName[MAX_NAME_LENGTH];
1350 alist *selected = NULL;
1352 for (i=1; i<ua->argc; i++) {
1353 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
1358 ua->error_msg(_("No value given for \"jobid\".\n"));
1361 if (!sl.set_string(ua->argv[i], true)) {
1362 ua->send_msg("%s", sl.get_errmsg());
1365 foreach_sellist(JobId, &sl) {
1366 jcr = get_jcr_by_id(JobId);
1367 if (jcr && jcr->job && acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1370 ua->error_msg(_("Unauthorized command from this console "
1371 "for JobId=%d.\n"), JobId);
1374 ua->warning_msg(_("Warning Job JobId=%d is not running.\n"), JobId);
1377 if (jcrs->size() == 0) {
1378 goto bail_out; /* If we did not find specified jobid, get out */
1382 /* TODO: might want to implement filters (client, status, etc...) */
1383 } else if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
1385 if (jcr->is_internal_job()) { /* Do not cancel consoles */
1388 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1389 continue; /* skip not authorized */
1391 jcr->inc_use_count();
1396 /* If we have something and no "yes" on command line, get confirmation */
1397 if (jcrs->size() > 0 && find_arg(ua, NT_("yes")) < 0) {
1399 bsnprintf(nbuf, sizeof(nbuf), _("Confirm %s of %d Job%s (yes/no): "),
1400 reason, jcrs->size(), jcrs->size()>1?"s":"");
1401 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
1405 if (jcrs->size() == 0) {
1406 goto bail_out; /* If we did not find specified jobid, get out */
1410 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1412 ua->error_msg(_("No value given for \"job\".\n"));
1415 jcr = get_jcr_by_partial_name(ua->argv[i]);
1417 if (jcr && jcr->job && acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1422 ua->error_msg(_("Unauthorized command from this console "
1423 "for job=%s.\n"), ua->argv[i]);
1428 ua->warning_msg(_("Warning Job %s is not running.\n"), ua->argv[i]);
1430 if (jcrs->size() == 0) {
1431 goto bail_out; /* If we did not find specified jobid, get out */
1435 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
1437 ua->error_msg(_("No value given for \"ujobid\".\n"));
1440 jcr = get_jcr_by_full_name(ua->argv[i]);
1442 if (jcr && jcr->job && acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1447 ua->error_msg(_("Unauthorized command from this console "
1448 "for ujobid=%s.\n"), ua->argv[i]);
1453 ua->warning_msg(_("Warning Job %s is not running.\n"), ua->argv[i]);
1455 if (jcrs->size() == 0) {
1456 goto bail_out; /* If we did not find specified jobid, get out */
1462 if (jcrs->size() == 0) {
1464 * If we still do not have a jcr,
1465 * throw up a list and ask the user to select one.
1469 njobs = count_running_jobs(ua);
1473 start_prompt(ua, _("Select Job(s):\n"));
1476 if (jcr->is_internal_job()) { /* this is us */
1479 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
1480 add_prompt(ua, buf);
1483 bsnprintf(temp, sizeof(temp), _("Choose Job list to %s"), _(reason));
1484 selected = New(alist(5, owned_by_alist));
1485 if (do_alist_prompt(ua, _("Job"), temp, selected) < 0) {
1488 /* Possibly ask for confirmation */
1489 if (selected->size() > 0 && find_arg(ua, NT_("yes")) < 0) {
1491 foreach_alist(item, selected) {
1492 ua->send_msg("%s\n", item);
1494 bsnprintf(nbuf, sizeof(nbuf), _("Confirm %s of %d Job%s (yes/no): "),
1495 reason, selected->size(), selected->size()>1?"s":"");
1496 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
1501 foreach_alist(item, selected) {
1502 if (sscanf(item, "JobId=%d Job=%127s", &njobs, JobName) != 2) {
1503 ua->warning_msg(_("Job \"%s\" not found.\n"), item);
1506 jcr = get_jcr_by_full_name(JobName);
1510 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
1515 if (selected) delete selected;
1516 return jcrs->size();
1519 /* Small helper to scan storage daemon commands and search for volumes */
1520 int scan_storage_cmd(UAContext *ua, const char *cmd,
1521 bool allfrompool, /* Choose to select a specific volume or not */
1522 int *drive, /* Drive number */
1523 MEDIA_DBR *mr, /* Media Record, can have options already filled */
1524 POOL_DBR *pr, /* Pool Record */
1525 const char **action, /* action= argument, can be NULL if not relevant */
1526 char *storage, /* Storage name, must be MAX_NAME_LENGTH long */
1527 int *nb, /* Number of media found */
1528 uint32_t **results) /* List of MediaId */
1530 bool allpools=false, has_vol = false;;
1538 /* Look at arguments */
1539 for (int i=1; i<ua->argc; i++) {
1540 if (strcasecmp(ua->argk[i], NT_("allpools")) == 0) {
1543 } else if (strcasecmp(ua->argk[i], NT_("allfrompool")) == 0) {
1546 } else if (strcasecmp(ua->argk[i], NT_("volume")) == 0
1547 && is_name_valid(ua->argv[i], NULL)) {
1548 bstrncpy(mr->VolumeName, ua->argv[i], sizeof(mr->VolumeName));
1551 } else if (strcasecmp(ua->argk[i], NT_("mediatype")) == 0
1553 bstrncpy(mr->MediaType, ua->argv[i], sizeof(mr->MediaType));
1555 } else if (strcasecmp(ua->argk[i], NT_("drive")) == 0 && ua->argv[i]) {
1556 *drive = atoi(ua->argv[i]);
1558 } else if (strcasecmp(ua->argk[i], NT_("action")) == 0
1559 && is_name_valid(ua->argv[i], NULL)) {
1562 *action = ua->argv[i];
1564 ua->warning_msg(_("Invalid argument \"action\".\n"));
1569 /* Choose storage */
1570 ua->jcr->wstore = store = get_storage_resource(ua, false);
1574 bstrncpy(storage, store->dev_name(), MAX_NAME_LENGTH);
1577 Dmsg0(100, "Can't open db\n");
1582 * Look for all volumes that are enabled
1585 set_storageid_in_mr(store, mr);
1587 if (allfrompool && !has_vol) { /* We need a list of volumes */
1589 /* We don't take all pools and we don't have a volume in argument,
1590 * so we need to choose a pool
1593 /* force pool selection */
1594 POOL *pool = get_pool_resource(ua);
1597 Dmsg0(100, "Can't get pool resource\n");
1600 bstrncpy(pr->Name, pool->name(), sizeof(pr->Name));
1601 if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
1602 Dmsg0(100, "Can't get pool record\n");
1605 mr->PoolId = pr->PoolId;
1608 if (!db_get_media_ids(ua->jcr, ua->db, mr, nb, results)) {
1609 Dmsg0(100, "No results from db_get_media_ids\n");
1613 } else { /* We want a single volume */
1615 if (!select_media_dbr(ua, &mr2)) {
1618 /* The select_media_dbr() doesn't filter on the CacheRetention */
1619 if (mr->CacheRetention) {
1620 if ((mr2.CacheRetention + mr2.LastWritten) > time(NULL)) {
1621 /* The volume cache retention is not exipred */
1626 *results = (uint32_t *) malloc(1 * sizeof(uint32_t));
1627 *results[0] = mr2.MediaId;
1637 ua->send_msg(_("No Volumes found to perform the command.\n"));
1641 ua->jcr->wstore = NULL;