2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 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)
173 char name[MAX_NAME_LENGTH];
176 /* Does user want a full selection? */
177 if (unique && find_arg(ua, NT_("select")) > 0) {
180 start_prompt(ua, _("The defined Storage resources are:\n"));
182 foreach_res(store, R_STORAGE) {
183 if (acl_access_ok(ua, Storage_ACL, store->name())) {
185 add_prompt(ua, store->name(), store->address);
187 add_prompt(ua, store->name());
192 if (do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name)) < 0) {
195 store = (STORE *)GetResWithName(R_STORAGE, name);
200 * Select a FileSet resource from prompt list
202 FILESET *select_fileset_resource(UAContext *ua)
204 char name[MAX_NAME_LENGTH];
207 start_prompt(ua, _("The defined FileSet resources are:\n"));
209 foreach_res(fs, R_FILESET) {
210 if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
211 add_prompt(ua, fs->name());
215 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), name, sizeof(name)) < 0) {
218 fs = (FILESET *)GetResWithName(R_FILESET, name);
224 * Get a catalog resource from prompt list
226 CAT *get_catalog_resource(UAContext *ua)
228 char name[MAX_NAME_LENGTH];
230 CLIENT *client = NULL;
233 for (i=1; i<ua->argc; i++) {
234 if (strcasecmp(ua->argk[i], NT_("catalog")) == 0 && ua->argv[i]) {
235 if (acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
236 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
240 if (strcasecmp(ua->argk[i], NT_("client")) == 0 && ua->argv[i]) {
241 if (acl_access_ok(ua, Client_ACL, ua->argv[i])) {
242 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
247 if (!catalog && client) { /* Try to take the catalog from the client */
248 catalog = client->catalog;
250 if (ua->gui && !catalog) {
252 catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
255 ua->error_msg(_("Could not find a Catalog resource\n"));
257 } else if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
258 ua->error_msg(_("You must specify a \"use <catalog-name>\" command before continuing.\n"));
264 start_prompt(ua, _("The defined Catalog resources are:\n"));
266 foreach_res(catalog, R_CATALOG) {
267 if (acl_access_ok(ua, Catalog_ACL, catalog->name())) {
268 add_prompt(ua, catalog->name());
272 if (do_prompt(ua, _("Catalog"), _("Select Catalog resource"), name, sizeof(name)) < 0) {
275 catalog = (CAT *)GetResWithName(R_CATALOG, name);
282 * Select a job to enable or disable
284 JOB *select_enable_disable_job_resource(UAContext *ua, bool enable)
286 char name[MAX_NAME_LENGTH];
291 start_prompt(ua, _("The disabled Job resources are:\n"));
293 start_prompt(ua, _("The enabled Job resources are:\n"));
295 foreach_res(job, R_JOB) {
296 if (!acl_access_ok(ua, Job_ACL, job->name())) {
299 if (job->enabled == enable) { /* Already enabled/disabled? */
300 continue; /* yes, skip */
302 add_prompt(ua, job->name());
305 if (do_prompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) < 0) {
308 job = (JOB *)GetResWithName(R_JOB, name);
313 * Select a Job resource from prompt list
315 JOB *select_job_resource(UAContext *ua)
317 char name[MAX_NAME_LENGTH];
320 start_prompt(ua, _("The defined Job resources are:\n"));
322 foreach_res(job, R_JOB) {
323 if (acl_access_ok(ua, Job_ACL, job->name())) {
324 add_prompt(ua, job->name());
328 if (do_prompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) < 0) {
331 job = (JOB *)GetResWithName(R_JOB, name);
336 * Select a Restore Job resource from argument or prompt
338 JOB *get_restore_job(UAContext *ua)
341 int i = find_arg_with_value(ua, "restorejob");
342 if (i >= 0 && acl_access_ok(ua, Job_ACL, ua->argv[i])) {
343 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
344 if (job && job->JobType == JT_RESTORE) {
347 ua->error_msg(_("Error: Restore Job resource \"%s\" does not exist.\n"),
350 return select_restore_job_resource(ua);
354 * Select a Restore Job resource from prompt list
356 JOB *select_restore_job_resource(UAContext *ua)
358 char name[MAX_NAME_LENGTH];
361 start_prompt(ua, _("The defined Restore Job resources are:\n"));
363 foreach_res(job, R_JOB) {
364 if (job->JobType == JT_RESTORE && acl_access_ok(ua, Job_ACL, job->name())) {
365 add_prompt(ua, job->name());
369 if (do_prompt(ua, _("Job"), _("Select Restore Job"), name, sizeof(name)) < 0) {
372 job = (JOB *)GetResWithName(R_JOB, name);
377 * Select a client to enable or disable
379 CLIENT *select_enable_disable_client_resource(UAContext *ua, bool enable)
381 char name[MAX_NAME_LENGTH];
385 start_prompt(ua, _("The defined Client resources are:\n"));
386 foreach_res(client, R_CLIENT) {
387 if (!acl_access_ok(ua, Client_ACL, client->name())) {
390 if (client->enabled == enable) { /* Already enabled/disabled? */
391 continue; /* yes, skip */
393 add_prompt(ua, client->name());
396 if (do_prompt(ua, _("Client"), _("Select Client resource"), name, sizeof(name)) < 0) {
399 client = (CLIENT *)GetResWithName(R_CLIENT, name);
405 * Select a client resource from prompt list
407 CLIENT *select_client_resource(UAContext *ua)
409 char name[MAX_NAME_LENGTH];
412 start_prompt(ua, _("The defined Client resources are:\n"));
414 foreach_res(client, R_CLIENT) {
415 if (acl_access_ok(ua, Client_ACL, client->name())) {
416 add_prompt(ua, client->name());
420 if (do_prompt(ua, _("Client"), _("Select Client (File daemon) resource"), name, sizeof(name)) < 0) {
423 client = (CLIENT *)GetResWithName(R_CLIENT, name);
428 * Get client resource, start by looking for
429 * client=<client-name>
430 * if we don't find the keyword, we prompt the user.
432 CLIENT *get_client_resource(UAContext *ua)
434 CLIENT *client = NULL;
437 for (i=1; i<ua->argc; i++) {
438 if ((strcasecmp(ua->argk[i], NT_("client")) == 0 ||
439 strcasecmp(ua->argk[i], NT_("fd")) == 0) && ua->argv[i]) {
440 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
443 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
447 ua->error_msg(_("Error: Client resource %s does not exist.\n"), ua->argv[i]);
451 return select_client_resource(ua);
455 * Select a schedule to enable or disable
457 SCHED *select_enable_disable_schedule_resource(UAContext *ua, bool enable)
459 char name[MAX_NAME_LENGTH];
463 start_prompt(ua, _("The defined Schedule resources are:\n"));
464 foreach_res(sched, R_SCHEDULE) {
465 if (!acl_access_ok(ua, Schedule_ACL, sched->name())) {
468 if (sched->enabled == enable) { /* Already enabled/disabled? */
469 continue; /* yes, skip */
471 add_prompt(ua, sched->name());
474 if (do_prompt(ua, _("Schedule"), _("Select Schedule resource"), name, sizeof(name)) < 0) {
477 sched = (SCHED *)GetResWithName(R_SCHEDULE, name);
482 /* Scan what the user has entered looking for:
484 * client=<client-name>
486 * if error or not found, put up a list of client DBRs
489 * returns: 0 on error
490 * 1 on success and fills in CLIENT_DBR
492 bool get_client_dbr(UAContext *ua, CLIENT_DBR *cr)
496 if (cr->Name[0]) { /* If name already supplied */
497 if (db_get_client_record(ua->jcr, ua->db, cr)) {
500 ua->error_msg(_("Could not find Client %s: ERR=%s"), cr->Name, db_strerror(ua->db));
502 for (i=1; i<ua->argc; i++) {
503 if ((strcasecmp(ua->argk[i], NT_("client")) == 0 ||
504 strcasecmp(ua->argk[i], NT_("fd")) == 0) && ua->argv[i]) {
505 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
508 bstrncpy(cr->Name, ua->argv[i], sizeof(cr->Name));
509 if (!db_get_client_record(ua->jcr, ua->db, cr)) {
510 ua->error_msg(_("Could not find Client \"%s\": ERR=%s"), ua->argv[i],
511 db_strerror(ua->db));
518 if (!select_client_dbr(ua, cr)) { /* try once more by proposing a list */
525 * Select a Client record from the catalog
526 * Returns 1 on success
529 bool select_client_dbr(UAContext *ua, CLIENT_DBR *cr)
532 char name[MAX_NAME_LENGTH];
538 if (!db_get_client_ids(ua->jcr, ua->db, &num_clients, &ids)) {
539 ua->error_msg(_("Error obtaining client ids. ERR=%s\n"), db_strerror(ua->db));
542 if (num_clients <= 0) {
543 ua->error_msg(_("No clients defined. You must run a job before using this command.\n"));
547 start_prompt(ua, _("Defined Clients:\n"));
548 for (i=0; i < num_clients; i++) {
549 ocr.ClientId = ids[i];
550 if (!db_get_client_record(ua->jcr, ua->db, &ocr) ||
551 !acl_access_ok(ua, Client_ACL, ocr.Name)) {
554 add_prompt(ua, ocr.Name);
557 if (do_prompt(ua, _("Client"), _("Select the Client"), name, sizeof(name)) < 0) {
560 memset(&ocr, 0, sizeof(ocr));
561 bstrncpy(ocr.Name, name, sizeof(ocr.Name));
563 if (!db_get_client_record(ua->jcr, ua->db, &ocr)) {
564 ua->error_msg(_("Could not find Client \"%s\": ERR=%s"), name, db_strerror(ua->db));
567 memcpy(cr, &ocr, sizeof(ocr));
571 /* Scan what the user has entered looking for:
575 * where argk can be : pool, recyclepool, scratchpool, nextpool etc..
577 * if error or not found, put up a list of pool DBRs
580 * returns: false on error
581 * true on success and fills in POOL_DBR
583 bool get_pool_dbr(UAContext *ua, POOL_DBR *pr, const char *argk)
585 if (pr->Name[0]) { /* If name already supplied */
586 if (db_get_pool_numvols(ua->jcr, ua->db, pr) &&
587 acl_access_ok(ua, Pool_ACL, pr->Name)) {
590 ua->error_msg(_("Could not find Pool \"%s\": ERR=%s"), pr->Name, db_strerror(ua->db));
592 if (!select_pool_dbr(ua, pr, argk)) { /* try once more */
599 * Select a Pool record from catalog
600 * argk can be pool, recyclepool, scratchpool etc..
602 bool select_pool_dbr(UAContext *ua, POOL_DBR *pr, const char *argk)
605 char name[MAX_NAME_LENGTH];
609 for (i=1; i<ua->argc; i++) {
610 if (strcasecmp(ua->argk[i], argk) == 0 && ua->argv[i] &&
611 acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
612 bstrncpy(pr->Name, ua->argv[i], sizeof(pr->Name));
613 if (!db_get_pool_numvols(ua->jcr, ua->db, pr)) {
614 ua->error_msg(_("Could not find Pool \"%s\": ERR=%s"), ua->argv[i],
615 db_strerror(ua->db));
624 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
625 ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db));
628 if (num_pools <= 0) {
629 ua->error_msg(_("No pools defined. Use the \"create\" command to create one.\n"));
633 start_prompt(ua, _("Defined Pools:\n"));
634 if (bstrcmp(argk, NT_("recyclepool"))) {
635 add_prompt(ua, _("*None*"));
637 for (i=0; i < num_pools; i++) {
639 if (!db_get_pool_numvols(ua->jcr, ua->db, &opr) ||
640 !acl_access_ok(ua, Pool_ACL, opr.Name)) {
643 add_prompt(ua, opr.Name);
646 if (do_prompt(ua, _("Pool"), _("Select the Pool"), name, sizeof(name)) < 0) {
650 memset(&opr, 0, sizeof(opr));
651 /* *None* is only returned when selecting a recyclepool, and in that case
652 * the calling code is only interested in opr.Name, so then we can leave
655 if (!bstrcmp(name, _("*None*"))) {
656 bstrncpy(opr.Name, name, sizeof(opr.Name));
658 if (!db_get_pool_numvols(ua->jcr, ua->db, &opr)) {
659 ua->error_msg(_("Could not find Pool \"%s\": ERR=%s"), name, db_strerror(ua->db));
664 memcpy(pr, &opr, sizeof(opr));
669 * Select a Pool and a Media (Volume) record from the database
671 int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr)
674 if (!select_media_dbr(ua, mr)) {
677 memset(pr, 0, sizeof(POOL_DBR));
678 pr->PoolId = mr->PoolId;
679 if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
680 ua->error_msg("%s", db_strerror(ua->db));
683 if (!acl_access_ok(ua, Pool_ACL, pr->Name)) {
684 ua->error_msg(_("No access to Pool \"%s\"\n"), pr->Name);
690 /* Select a Media (Volume) record from the database */
691 int select_media_dbr(UAContext *ua, MEDIA_DBR *mr)
695 POOLMEM *err = get_pool_memory(PM_FNAME);
699 i = find_arg_with_value(ua, "volume");
701 if (is_name_valid(ua->argv[i], &err)) {
702 bstrncpy(mr->VolumeName, ua->argv[i], sizeof(mr->VolumeName));
707 if (mr->VolumeName[0] == 0) {
709 memset(&pr, 0, sizeof(pr));
710 /* Get the pool from pool=<pool-name> */
711 if (!get_pool_dbr(ua, &pr)) {
714 mr->PoolId = pr.PoolId;
715 db_list_media_records(ua->jcr, ua->db, mr, prtit, ua, HORZ_LIST);
716 if (!get_cmd(ua, _("Enter a Volume name or *MediaId: "))) {
719 if (ua->cmd[0] == '*' && is_a_number(ua->cmd+1)) {
720 mr->MediaId = str_to_int64(ua->cmd+1);
721 } else if (is_name_valid(ua->cmd, &err)) {
722 bstrncpy(mr->VolumeName, ua->cmd, sizeof(mr->VolumeName));
728 if (!db_get_media_record(ua->jcr, ua->db, mr)) {
729 pm_strcpy(err, db_strerror(ua->db));
736 ua->error_msg("%s", err);
738 free_pool_memory(err);
744 * Select a pool resource from prompt list
746 POOL *select_pool_resource(UAContext *ua)
748 char name[MAX_NAME_LENGTH];
751 start_prompt(ua, _("The defined Pool resources are:\n"));
753 foreach_res(pool, R_POOL) {
754 if (acl_access_ok(ua, Pool_ACL, pool->name())) {
755 add_prompt(ua, pool->name());
759 if (do_prompt(ua, _("Pool"), _("Select Pool resource"), name, sizeof(name)) < 0) {
762 pool = (POOL *)GetResWithName(R_POOL, name);
768 * If you are thinking about using it, you
769 * probably want to use select_pool_dbr()
770 * or get_pool_dbr() above.
772 POOL *get_pool_resource(UAContext *ua)
777 i = find_arg_with_value(ua, "pool");
778 if (i >= 0 && acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
779 pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
783 ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
785 return select_pool_resource(ua);
789 * List all jobs and ask user to select one
791 static int select_job_dbr(UAContext *ua, JOB_DBR *jr)
793 db_list_job_records(ua->jcr, ua->db, jr, prtit, ua, HORZ_LIST);
794 if (!get_pint(ua, _("Enter the JobId to select: "))) {
797 jr->JobId = ua->int64_val;
798 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
799 ua->error_msg("%s", db_strerror(ua->db));
807 /* Scan what the user has entered looking for:
811 * if error or not found, put up a list of Jobs
814 * returns: 0 on error
815 * JobId on success and fills in JOB_DBR
817 int get_job_dbr(UAContext *ua, JOB_DBR *jr)
821 for (i=1; i<ua->argc; i++) {
822 if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
824 bstrncpy(jr->Job, ua->argv[i], sizeof(jr->Job));
825 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0 && ua->argv[i]) {
826 jr->JobId = str_to_int64(ua->argv[i]);
831 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
832 ua->error_msg(_("Could not find Job \"%s\": ERR=%s"), ua->argv[i],
833 db_strerror(ua->db));
843 for (i=1; i<ua->argc; i++) {
844 if ((strcasecmp(ua->argk[i], NT_("jobname")) == 0 ||
845 strcasecmp(ua->argk[i], NT_("job")) == 0) && ua->argv[i]) {
847 bstrncpy(jr->Name, ua->argv[i], sizeof(jr->Name));
851 if (!select_job_dbr(ua, jr)) { /* try once more */
858 * Implement unique set of prompts
860 void start_prompt(UAContext *ua, const char *msg)
862 if (ua->max_prompts == 0) {
863 ua->max_prompts = 10;
864 ua->prompt = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
865 ua->unique = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
868 ua->prompt[0] = bstrdup(msg);
869 ua->unique[0] = NULL;
873 * Add to prompts -- keeping them unique by name
875 void add_prompt(UAContext *ua, const char *prompt, char *unique)
878 if (ua->num_prompts == ua->max_prompts) {
879 ua->max_prompts *= 2;
880 ua->prompt = (char **)brealloc(ua->prompt, sizeof(char *) *
882 ua->unique = (char **)brealloc(ua->unique, sizeof(char *) *
885 for (i=1; i < ua->num_prompts; i++) {
886 if (strcmp(ua->prompt[i], prompt) == 0) {
888 } else if (unique && strcmp(ua->unique[i], unique) == 0) {
892 ua->prompt[ua->num_prompts] = bstrdup(prompt);
894 ua->unique[ua->num_prompts++] = bstrdup(unique);
896 ua->unique[ua->num_prompts++] = NULL;
901 * Display prompts and get user's choice
903 * Returns: -1 on error
904 * index base 0 on success, and choice
905 * is copied to prompt if not NULL
906 * prompt is set to the chosen prompt item string
908 int do_prompt(UAContext *ua, const char *automsg, const char *msg,
909 char *prompt, int max_prompt)
912 char pmsg[MAXSTRING];
913 BSOCK *user = ua->UA_sock;
918 if (ua->num_prompts == 2) {
921 bstrncpy(prompt, ua->prompt[1], max_prompt);
923 ua->send_msg(_("Automatically selected %s: %s\n"), NPRTB(automsg), ua->prompt[1]);
926 /* If running non-interactive, bail out */
928 /* First print the choices he wanted to make */
929 ua->send_msg(ua->prompt[0]);
930 for (i=1; i < ua->num_prompts; i++) {
931 ua->send_msg("%6d: %s\n", i, ua->prompt[i]);
933 /* Now print error message */
934 ua->send_msg(_("Your request has multiple choices for \"%s\". Selection is not possible in batch mode.\n"), automsg);
938 if (ua->api) user->signal(BNET_START_SELECT);
939 ua->send_msg(ua->prompt[0]);
940 for (i=1; i < ua->num_prompts; i++) {
942 ua->send_msg("%s", ua->prompt[i]);
944 ua->send_msg("%6d: %s\n", i, ua->prompt[i]);
947 if (ua->api) user->signal(BNET_END_SELECT);
950 /* First item is the prompt string, not the items */
951 if (ua->num_prompts == 1) {
952 ua->error_msg(_("Selection list for \"%s\" is empty!\n"), automsg);
953 item = -1; /* list is empty ! */
956 if (ua->num_prompts == 2) {
958 ua->send_msg(_("Automatically selected: %s\n"), ua->prompt[1]);
960 bstrncpy(prompt, ua->prompt[1], max_prompt);
964 sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
966 /* Either a . or an @ will get you out of the loop */
967 if (ua->api) user->signal(BNET_SELECT_INPUT);
968 if (!get_pint(ua, pmsg)) {
969 item = -1; /* error */
970 ua->info_msg(_("Selection aborted, nothing done.\n"));
973 item = ua->pint32_val;
974 if (item < 1 || item >= ua->num_prompts) {
975 ua->warning_msg(_("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
979 bstrncpy(prompt, ua->prompt[item], max_prompt);
985 for (i=0; i < ua->num_prompts; i++) {
987 if (ua->unique[i]) free(ua->unique[i]);
990 return item>0 ? item-1 : item;
994 * Display prompts and get user's choice
996 * Returns: -1 on error
997 * number of items selected and the choices are
998 * copied to selected if not NULL
999 * selected is an alist of the prompts chosen
1000 * Note! selected must already be initialized.
1002 int do_alist_prompt(UAContext *ua, const char *automsg, const char *msg,
1006 char pmsg[MAXSTRING];
1007 BSOCK *user = ua->UA_sock;
1010 /* First item is the prompt string, not the items */
1011 if (ua->num_prompts == 1) {
1012 ua->error_msg(_("Selection list for \"%s\" is empty!\n"), automsg);
1013 item = -1; /* list is empty ! */
1016 if (ua->num_prompts == 2) {
1018 selected->append(bstrdup(ua->prompt[1]));
1019 ua->send_msg(_("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
1022 /* If running non-interactive, bail out */
1024 /* First print the choices he wanted to make */
1025 ua->send_msg(ua->prompt[0]);
1026 for (i=1; i < ua->num_prompts; i++) {
1027 ua->send_msg("%6d: %s\n", i, ua->prompt[i]);
1029 /* Now print error message */
1030 ua->send_msg(_("Your request has multiple choices for \"%s\". Selection is not possible in batch mode.\n"), automsg);
1034 if (ua->api) user->signal(BNET_START_SELECT);
1035 ua->send_msg(ua->prompt[0]);
1036 for (i=1; i < ua->num_prompts; i++) {
1038 ua->send_msg("%s", ua->prompt[i]);
1040 ua->send_msg("%6d: %s\n", i, ua->prompt[i]);
1043 if (ua->api) user->signal(BNET_END_SELECT);
1045 sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
1049 /* Either a . or an @ will get you out of the loop */
1050 if (ua->api) user->signal(BNET_SELECT_INPUT);
1052 if (!get_selection_list(ua, sl, pmsg, false)) {
1058 for (i=1; i < ua->num_prompts; i++) {
1059 selected->append(bstrdup(ua->prompt[i]));
1062 while ( (item = sl.next()) > 0) {
1063 if (item < 1 || item >= ua->num_prompts) {
1064 ua->warning_msg(_("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
1068 selected->append(bstrdup(ua->prompt[item]));
1072 item = selected->size();
1078 for (i=0; i < ua->num_prompts; i++) {
1079 free(ua->prompt[i]);
1080 if (ua->unique[i]) free(ua->unique[i]);
1082 ua->num_prompts = 0;
1088 * We scan what the user has entered looking for
1089 * storage=<storage-resource>
1092 * ? (prompt him with storage list)
1093 * <some-error> (prompt him with storage list)
1095 * If use_default is set, we assume that any keyword without a value
1096 * is the name of the Storage resource wanted.
1098 STORE *get_storage_resource(UAContext *ua, bool use_default, bool unique)
1100 char *store_name = NULL;
1101 STORE *store = NULL;
1107 for (i=1; i<ua->argc; i++) {
1108 if (use_default && !ua->argv[i]) {
1109 /* Ignore slots, scan and barcode(s) keywords */
1110 if (strcasecmp("scan", ua->argk[i]) == 0 ||
1111 strcasecmp("barcode", ua->argk[i]) == 0 ||
1112 strcasecmp("barcodes", ua->argk[i]) == 0 ||
1113 strcasecmp("slots", ua->argk[i]) == 0) {
1116 /* Default argument is storage */
1118 ua->error_msg(_("Storage name given twice.\n"));
1121 store_name = ua->argk[i];
1122 if (*store_name == '?') {
1127 if (strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1128 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1129 store_name = ua->argv[i];
1132 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
1133 jobid = str_to_int64(ua->argv[i]);
1135 ua->error_msg(_("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
1138 if (!(jcr=get_jcr_by_id(jobid))) {
1139 ua->error_msg(_("JobId %s is not running.\n"), edit_int64(jobid, ed1));
1142 store = jcr->wstore;
1146 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0 ||
1147 strcasecmp(ua->argk[i], NT_("jobname")) == 0) {
1149 ua->error_msg(_("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
1152 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
1153 ua->error_msg(_("Job \"%s\" is not running.\n"), ua->argv[i]);
1156 store = jcr->wstore;
1159 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
1161 ua->error_msg(_("Expecting ujobid=xxx, got: %s.\n"), ua->argk[i]);
1164 if ((jcr=get_jcr_by_full_name(ua->argv[i]))) {
1165 store = jcr->wstore;
1167 /* The job might not be running, so we try to see other keywords */
1175 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1179 if (!store && store_name && store_name[0] != 0) {
1180 store = (STORE *)GetResWithName(R_STORAGE, store_name);
1182 ua->error_msg(_("Storage resource \"%s\": not found\n"), store_name);
1185 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1188 /* No keywords found, so present a selection list */
1190 store = select_storage_resource(ua, unique);
1195 /* Get drive that we are working with for this storage */
1196 int get_storage_drive(UAContext *ua, STORE *store)
1199 /* Get drive for autochanger if possible */
1200 i = find_arg_with_value(ua, "drive");
1202 drive = atoi(ua->argv[i]);
1203 } else if (store && store->autochanger) {
1204 /* If our structure is not set ask SD for # drives */
1205 if (store->drives == 0) {
1206 store->drives = get_num_drives_from_SD(ua);
1208 /* If only one drive, default = 0 */
1209 if (store->drives == 1) {
1212 /* Ask user to enter drive number */
1214 if (!get_cmd(ua, _("Enter autochanger drive[0]: "))) {
1215 drive = -1; /* None */
1217 drive = atoi(ua->cmd);
1224 /* Get slot that we are working with for this storage */
1225 int get_storage_slot(UAContext *ua, STORE *store)
1228 /* Get slot for autochanger if possible */
1229 i = find_arg_with_value(ua, "slot");
1231 slot = atoi(ua->argv[i]);
1232 } else if (store && store->autochanger) {
1233 /* Ask user to enter slot number */
1235 if (!get_cmd(ua, _("Enter autochanger slot: "))) {
1236 slot = -1; /* None */
1238 slot = atoi(ua->cmd);
1247 * Scan looking for mediatype=
1249 * if not found or error, put up selection list
1251 * Returns: 0 on error
1252 * 1 on success, MediaType is set
1254 int get_media_type(UAContext *ua, char *MediaType, int max_media)
1259 i = find_arg_with_value(ua, "mediatype");
1261 bstrncpy(MediaType, ua->argv[i], max_media);
1265 start_prompt(ua, _("Media Types defined in conf file:\n"));
1267 foreach_res(store, R_STORAGE) {
1268 add_prompt(ua, store->media_type);
1271 return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
1275 bool get_level_from_name(JCR *jcr, const char *level_name)
1277 /* Look up level name and pull code */
1279 for (int i=0; joblevels[i].level_name; i++) {
1280 if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
1281 jcr->setJobLevel(joblevels[i].level);
1289 static int count_running_jobs(UAContext *ua)
1291 int tjobs = 0; /* total # number jobs */
1294 /* Count Jobs running */
1296 if (jcr->JobId == 0) { /* this is us */
1299 tjobs++; /* count of all jobs */
1300 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1301 continue; /* skip not authorized */
1303 njobs++; /* count of authorized jobs */
1307 if (njobs == 0) { /* no authorized */
1309 ua->send_msg(_("No Jobs running.\n"));
1311 ua->send_msg(_("None of your jobs are running.\n"));
1318 /* Get a list of running jobs
1319 * "reason" is used in user messages
1320 * can be: cancel, limit, ...
1321 * Returns: -1 on error
1322 * nb of JCR on success (should be free_jcr() after)
1324 int select_running_jobs(UAContext *ua, alist *jcrs, const char *reason)
1329 char JobName[MAX_NAME_LENGTH];
1331 alist *selected = NULL;
1333 for (i=1; i<ua->argc; i++) {
1334 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
1339 ua->error_msg(_("No value given for \"jobid\".\n"));
1342 if (!sl.set_string(ua->argv[i], true)) {
1343 ua->send_msg("%s", sl.get_errmsg());
1346 foreach_sellist(JobId, &sl) {
1347 jcr = get_jcr_by_id(JobId);
1348 if (jcr && jcr->job && acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1351 ua->error_msg(_("Unauthorized command from this console "
1352 "for JobId=%d.\n"), JobId);
1355 ua->warning_msg(_("Warning Job JobId=%d is not running.\n"), JobId);
1358 if (jcrs->size() == 0) {
1359 goto bail_out; /* If we did not find specified jobid, get out */
1363 /* TODO: might want to implement filters (client, status, etc...) */
1364 } else if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
1366 if (jcr->JobId == 0) { /* Do not cancel consoles */
1369 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1370 continue; /* skip not authorized */
1372 jcr->inc_use_count();
1377 /* If we have something and no "yes" on command line, get confirmation */
1378 if (jcrs->size() > 0 && find_arg(ua, NT_("yes")) < 0) {
1380 bsnprintf(nbuf, sizeof(nbuf), _("Confirm %s of %d Job%s (yes/no): "),
1381 reason, jcrs->size(), jcrs->size()>1?"s":"");
1382 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
1386 if (jcrs->size() == 0) {
1387 goto bail_out; /* If we did not find specified jobid, get out */
1391 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1393 ua->error_msg(_("No value given for \"job\".\n"));
1396 jcr = get_jcr_by_partial_name(ua->argv[i]);
1398 if (jcr && jcr->job && acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1403 ua->error_msg(_("Unauthorized command from this console "
1404 "for job=%s.\n"), ua->argv[i]);
1409 ua->warning_msg(_("Warning Job %s is not running.\n"), ua->argv[i]);
1411 if (jcrs->size() == 0) {
1412 goto bail_out; /* If we did not find specified jobid, get out */
1416 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
1418 ua->error_msg(_("No value given for \"ujobid\".\n"));
1421 jcr = get_jcr_by_full_name(ua->argv[i]);
1423 if (jcr && jcr->job && acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1428 ua->error_msg(_("Unauthorized command from this console "
1429 "for ujobid=%s.\n"), ua->argv[i]);
1434 ua->warning_msg(_("Warning Job %s is not running.\n"), ua->argv[i]);
1436 if (jcrs->size() == 0) {
1437 goto bail_out; /* If we did not find specified jobid, get out */
1443 if (jcrs->size() == 0) {
1445 * If we still do not have a jcr,
1446 * throw up a list and ask the user to select one.
1450 njobs = count_running_jobs(ua);
1454 start_prompt(ua, _("Select Job(s):\n"));
1457 if (jcr->JobId == 0) { /* this is us */
1460 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
1461 add_prompt(ua, buf);
1464 bsnprintf(temp, sizeof(temp), _("Choose Job list to %s"), _(reason));
1465 selected = New(alist(5, owned_by_alist));
1466 if (do_alist_prompt(ua, _("Job"), temp, selected) < 0) {
1469 /* Possibly ask for confirmation */
1470 if (selected->size() > 0 && find_arg(ua, NT_("yes")) < 0) {
1472 foreach_alist(item, selected) {
1473 ua->send_msg("%s\n", item);
1475 bsnprintf(nbuf, sizeof(nbuf), _("Confirm %s of %d Job%s (yes/no): "),
1476 reason, selected->size(), selected->size()>1?"s":"");
1477 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
1482 foreach_alist(item, selected) {
1483 if (sscanf(item, "JobId=%d Job=%127s", &njobs, JobName) != 2) {
1484 ua->warning_msg(_("Job \"%s\" not found.\n"), item);
1487 jcr = get_jcr_by_full_name(JobName);
1491 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
1496 if (selected) delete selected;
1497 return jcrs->size();