]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_select.c
This commit was manufactured by cvs2svn to create tag
[bacula/bacula] / bacula / src / dird / ua_select.c
1 /*
2  *
3  *   Bacula Director -- User Agent Prompt and Selection code
4  *
5  *     Kern Sibbald, October MMI
6  *
7  *   Version  $Id$
8  */
9 /*
10    Copyright (C) 2001-2005 Kern Sibbald
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License
14    version 2 as amended with additional clauses defined in the
15    file LICENSE in the main source directory.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
20    the file LICENSE for additional details.
21
22  */
23
24 #include "bacula.h"
25 #include "dird.h"
26
27 /* Imported variables */
28 extern struct s_jl joblevels[];
29
30
31 /*
32  * Confirm a retention period
33  */
34 int confirm_retention(UAContext *ua, utime_t *ret, const char *msg)
35 {
36    char ed1[100];
37
38    for ( ;; ) {
39        bsendmsg(ua, _("The current %s retention period is: %s\n"),
40           msg, edit_utime(*ret, ed1, sizeof(ed1)));
41        if (!get_cmd(ua, _("Continue? (yes/mod/no): "))) {
42           return 0;
43        }
44        if (strcasecmp(ua->cmd, _("mod")) == 0) {
45           if (!get_cmd(ua, _("Enter new retention period: "))) {
46              return 0;
47           }
48           if (!duration_to_utime(ua->cmd, ret)) {
49              bsendmsg(ua, _("Invalid period.\n"));
50              continue;
51           }
52           continue;
53        }
54        if (strcasecmp(ua->cmd, _("yes")) == 0) {
55           return 1;
56        }
57        if (strcasecmp(ua->cmd, _("no")) == 0) {
58           return 0;
59        }
60     }
61     return 1;
62 }
63
64 /*
65  * Given a list of keywords, find the first one
66  *  that is in the argument list.
67  * Returns: -1 if not found
68  *          index into list (base 0) on success
69  */
70 int find_arg_keyword(UAContext *ua, const char **list)
71 {
72    for (int i=1; i<ua->argc; i++) {
73       for(int j=0; list[j]; j++) {
74          if (strcasecmp(_(list[j]), ua->argk[i]) == 0) {
75             return j;
76          }
77       }
78    }
79    return -1;
80 }
81
82 /*
83  * Given one keyword, find the first one that
84  *   is in the argument list.
85  * Returns: argk index (always gt 0)
86  *          -1 if not found
87  */
88 int find_arg(UAContext *ua, const char *keyword)
89 {
90    for (int i=1; i<ua->argc; i++) {
91       if (strcasecmp(keyword, ua->argk[i]) == 0) {
92          return i;
93       }
94    }
95    return -1;
96 }
97
98 /*
99  * Given a single keyword, find it in the argument list, but
100  *   it must have a value
101  * Returns: -1 if not found or no value
102  *           list index (base 0) on success
103  */
104 int find_arg_with_value(UAContext *ua, const char *keyword)
105 {
106    for (int i=1; i<ua->argc; i++) {
107       if (strcasecmp(keyword, ua->argk[i]) == 0) {
108          if (ua->argv[i]) {
109             return i;
110          } else {
111             return -1;
112          }
113       }
114    }
115    return -1;
116 }
117
118 /*
119  * Given a list of keywords, prompt the user
120  * to choose one.
121  *
122  * Returns: -1 on failure
123  *          index into list (base 0) on success
124  */
125 int do_keyword_prompt(UAContext *ua, const char *msg, const char **list)
126 {
127    int i;
128    start_prompt(ua, _("You have the following choices:\n"));
129    for (i=0; list[i]; i++) {
130       add_prompt(ua, list[i]);
131    }
132    return do_prompt(ua, "", msg, NULL, 0);
133 }
134
135
136 /*
137  * Select a Storage resource from prompt list
138  */
139 STORE *select_storage_resource(UAContext *ua)
140 {
141    char name[MAX_NAME_LENGTH];
142    STORE *store;
143
144    start_prompt(ua, _("The defined Storage resources are:\n"));
145    LockRes();
146    foreach_res(store, R_STORAGE) {
147       if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
148          add_prompt(ua, store->hdr.name);
149       }
150    }
151    UnlockRes();
152    if (do_prompt(ua, _("Storage"),  _("Select Storage resource"), name, sizeof(name)) < 0) {
153       return NULL;
154    }
155    store = (STORE *)GetResWithName(R_STORAGE, name);
156    return store;
157 }
158
159 /*
160  * Select a FileSet resource from prompt list
161  */
162 FILESET *select_fileset_resource(UAContext *ua)
163 {
164    char name[MAX_NAME_LENGTH];
165    FILESET *fs;
166
167    start_prompt(ua, _("The defined FileSet resources are:\n"));
168    LockRes();
169    foreach_res(fs, R_FILESET) {
170       if (acl_access_ok(ua, FileSet_ACL, fs->hdr.name)) {
171          add_prompt(ua, fs->hdr.name);
172       }
173    }
174    UnlockRes();
175    if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), name, sizeof(name)) < 0) {
176       return NULL;
177    }
178    fs = (FILESET *)GetResWithName(R_FILESET, name);
179    return fs;
180 }
181
182
183 /*
184  * Get a catalog resource from prompt list
185  */
186 CAT *get_catalog_resource(UAContext *ua)
187 {
188    char name[MAX_NAME_LENGTH];
189    CAT *catalog = NULL;
190    int i;
191
192    for (i=1; i<ua->argc; i++) {
193       if (strcasecmp(ua->argk[i], _("catalog")) == 0 && ua->argv[i]) {
194          if (acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
195             catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
196             break;
197          }
198       }
199    }
200    if (!catalog) {
201       start_prompt(ua, _("The defined Catalog resources are:\n"));
202       LockRes();
203       foreach_res(catalog, R_CATALOG) {
204          if (acl_access_ok(ua, Catalog_ACL, catalog->hdr.name)) {
205             add_prompt(ua, catalog->hdr.name);
206          }
207       }
208       UnlockRes();
209       if (do_prompt(ua, _("Catalog"),  _("Select Catalog resource"), name, sizeof(name)) < 0) {
210          return NULL;
211       }
212       catalog = (CAT *)GetResWithName(R_CATALOG, name);
213    }
214    return catalog;
215 }
216
217
218 /*
219  * Select a Job resource from prompt list
220  */
221 JOB *select_job_resource(UAContext *ua)
222 {
223    char name[MAX_NAME_LENGTH];
224    JOB *job;
225
226    start_prompt(ua, _("The defined Job resources are:\n"));
227    LockRes();
228    foreach_res(job, R_JOB) {
229       if (acl_access_ok(ua, Job_ACL, job->hdr.name)) {
230          add_prompt(ua, job->hdr.name);
231       }
232    }
233    UnlockRes();
234    if (do_prompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) < 0) {
235       return NULL;
236    }
237    job = (JOB *)GetResWithName(R_JOB, name);
238    return job;
239 }
240
241 /*
242  * Select a Restore Job resource from prompt list
243  */
244 JOB *select_restore_job_resource(UAContext *ua)
245 {
246    char name[MAX_NAME_LENGTH];
247    JOB *job;
248
249    start_prompt(ua, _("The defined Restore Job resources are:\n"));
250    LockRes();
251    foreach_res(job, R_JOB) {
252       if (job->JobType == JT_RESTORE && acl_access_ok(ua, Job_ACL, job->hdr.name)) {
253          add_prompt(ua, job->hdr.name);
254       }
255    }
256    UnlockRes();
257    if (do_prompt(ua, _("Job"), _("Select Restore Job"), name, sizeof(name)) < 0) {
258       return NULL;
259    }
260    job = (JOB *)GetResWithName(R_JOB, name);
261    return job;
262 }
263
264
265
266 /*
267  * Select a client resource from prompt list
268  */
269 CLIENT *select_client_resource(UAContext *ua)
270 {
271    char name[MAX_NAME_LENGTH];
272    CLIENT *client;
273
274    start_prompt(ua, _("The defined Client resources are:\n"));
275    LockRes();
276    foreach_res(client, R_CLIENT) {
277       if (acl_access_ok(ua, Client_ACL, client->hdr.name)) {
278          add_prompt(ua, client->hdr.name);
279       }
280    }
281    UnlockRes();
282    if (do_prompt(ua, _("Client"),  _("Select Client (File daemon) resource"), name, sizeof(name)) < 0) {
283       return NULL;
284    }
285    client = (CLIENT *)GetResWithName(R_CLIENT, name);
286    return client;
287 }
288
289 /*
290  *  Get client resource, start by looking for
291  *   client=<client-name>
292  *  if we don't find the keyword, we prompt the user.
293  */
294 CLIENT *get_client_resource(UAContext *ua)
295 {
296    CLIENT *client = NULL;
297    int i;
298
299    for (i=1; i<ua->argc; i++) {
300       if ((strcasecmp(ua->argk[i], N_("client")) == 0 ||
301            strcasecmp(ua->argk[i], N_("fd")) == 0) && ua->argv[i]) {
302          if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
303             break;
304          }
305          client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
306          if (client) {
307             return client;
308          }
309          bsendmsg(ua, _("Error: Client resource %s does not exist.\n"), ua->argv[i]);
310          break;
311       }
312    }
313    return select_client_resource(ua);
314 }
315
316 /* Scan what the user has entered looking for:
317  *
318  *  client=<client-name>
319  *
320  *  if error or not found, put up a list of client DBRs
321  *  to choose from.
322  *
323  *   returns: 0 on error
324  *            1 on success and fills in CLIENT_DBR
325  */
326 int get_client_dbr(UAContext *ua, CLIENT_DBR *cr)
327 {
328    int i;
329
330    if (cr->Name[0]) {                 /* If name already supplied */
331       if (db_get_client_record(ua->jcr, ua->db, cr)) {
332          return 1;
333       }
334       bsendmsg(ua, _("Could not find Client %s: ERR=%s"), cr->Name, db_strerror(ua->db));
335    }
336    for (i=1; i<ua->argc; i++) {
337       if ((strcasecmp(ua->argk[i], _("client")) == 0 ||
338            strcasecmp(ua->argk[i], _("fd")) == 0) && ua->argv[i]) {
339          if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
340             break;
341          }
342          bstrncpy(cr->Name, ua->argv[i], sizeof(cr->Name));
343          if (!db_get_client_record(ua->jcr, ua->db, cr)) {
344             bsendmsg(ua, _("Could not find Client \"%s\": ERR=%s"), ua->argv[i],
345                      db_strerror(ua->db));
346             cr->ClientId = 0;
347             break;
348          }
349          return 1;
350       }
351    }
352    if (!select_client_dbr(ua, cr)) {  /* try once more by proposing a list */
353       return 0;
354    }
355    return 1;
356 }
357
358 /*
359  * Select a Client record from the catalog
360  *  Returns 1 on success
361  *          0 on failure
362  */
363 int select_client_dbr(UAContext *ua, CLIENT_DBR *cr)
364 {
365    CLIENT_DBR ocr;
366    char name[MAX_NAME_LENGTH];
367    int num_clients, i;
368    uint32_t *ids;
369
370
371    cr->ClientId = 0;
372    if (!db_get_client_ids(ua->jcr, ua->db, &num_clients, &ids)) {
373       bsendmsg(ua, _("Error obtaining client ids. ERR=%s\n"), db_strerror(ua->db));
374       return 0;
375    }
376    if (num_clients <= 0) {
377       bsendmsg(ua, _("No clients defined. You must run a job before using this command.\n"));
378       return 0;
379    }
380
381    start_prompt(ua, _("Defined Clients:\n"));
382    for (i=0; i < num_clients; i++) {
383       ocr.ClientId = ids[i];
384       if (!db_get_client_record(ua->jcr, ua->db, &ocr) ||
385           !acl_access_ok(ua, Client_ACL, ocr.Name)) {
386          continue;
387       }
388       add_prompt(ua, ocr.Name);
389    }
390    free(ids);
391    if (do_prompt(ua, _("Client"),  _("Select the Client"), name, sizeof(name)) < 0) {
392       return 0;
393    }
394    memset(&ocr, 0, sizeof(ocr));
395    bstrncpy(ocr.Name, name, sizeof(ocr.Name));
396
397    if (!db_get_client_record(ua->jcr, ua->db, &ocr)) {
398       bsendmsg(ua, _("Could not find Client \"%s\": ERR=%s"), name, db_strerror(ua->db));
399       return 0;
400    }
401    memcpy(cr, &ocr, sizeof(ocr));
402    return 1;
403 }
404
405
406
407 /* Scan what the user has entered looking for:
408  *
409  *  pool=<pool-name>
410  *
411  *  if error or not found, put up a list of pool DBRs
412  *  to choose from.
413  *
414  *   returns: false on error
415  *            true  on success and fills in POOL_DBR
416  */
417 bool get_pool_dbr(UAContext *ua, POOL_DBR *pr)
418 {
419    if (pr->Name[0]) {                 /* If name already supplied */
420       if (db_get_pool_record(ua->jcr, ua->db, pr) &&
421           acl_access_ok(ua, Pool_ACL, pr->Name)) {
422          return true;
423       }
424       bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), pr->Name, db_strerror(ua->db));
425    }
426    if (!select_pool_dbr(ua, pr)) {  /* try once more */
427       return false;
428    }
429    return true;
430 }
431
432 /*
433  * Select a Pool record from the catalog
434  */
435 bool select_pool_dbr(UAContext *ua, POOL_DBR *pr)
436 {
437    POOL_DBR opr;
438    char name[MAX_NAME_LENGTH];
439    int num_pools, i;
440    uint32_t *ids;
441
442    for (i=1; i<ua->argc; i++) {
443       if (strcasecmp(ua->argk[i], N_("pool")) == 0 && ua->argv[i] &&
444           acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
445          bstrncpy(pr->Name, ua->argv[i], sizeof(pr->Name));
446          if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
447             bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), ua->argv[i],
448                      db_strerror(ua->db));
449             pr->PoolId = 0;
450             break;
451          }
452          return true;
453       }
454    }
455
456    pr->PoolId = 0;
457    if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
458       bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db));
459       return 0;
460    }
461    if (num_pools <= 0) {
462       bsendmsg(ua, _("No pools defined. Use the \"create\" command to create one.\n"));
463       return false;
464    }
465
466    start_prompt(ua, _("Defined Pools:\n"));
467    for (i=0; i < num_pools; i++) {
468       opr.PoolId = ids[i];
469       if (!db_get_pool_record(ua->jcr, ua->db, &opr) ||
470           !acl_access_ok(ua, Pool_ACL, opr.Name)) {
471          continue;
472       }
473       add_prompt(ua, opr.Name);
474    }
475    free(ids);
476    if (do_prompt(ua, _("Pool"),  _("Select the Pool"), name, sizeof(name)) < 0) {
477       return false;
478    }
479    memset(&opr, 0, sizeof(opr));
480    bstrncpy(opr.Name, name, sizeof(opr.Name));
481
482    if (!db_get_pool_record(ua->jcr, ua->db, &opr)) {
483       bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), name, db_strerror(ua->db));
484       return false;
485    }
486    memcpy(pr, &opr, sizeof(opr));
487    return true;
488 }
489
490 /*
491  * Select a Pool and a Media (Volume) record from the database
492  */
493 int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr)
494 {
495
496    if (!select_media_dbr(ua, mr)) {
497       return 0;
498    }
499    memset(pr, 0, sizeof(POOL_DBR));
500    pr->PoolId = mr->PoolId;
501    if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
502       bsendmsg(ua, "%s", db_strerror(ua->db));
503       return 0;
504    }
505    if (!acl_access_ok(ua, Pool_ACL, pr->Name)) {
506       bsendmsg(ua, _("No access to Pool \"%s\"\n"), pr->Name);
507       return 0;
508    }
509    return 1;
510 }
511
512 /* Select a Media (Volume) record from the database */
513 int select_media_dbr(UAContext *ua, MEDIA_DBR *mr)
514 {
515    int i;
516
517    memset(mr, 0, sizeof(MEDIA_DBR));
518
519    i = find_arg_with_value(ua, "volume");
520    if (i >= 0) {
521       bstrncpy(mr->VolumeName, ua->argv[i], sizeof(mr->VolumeName));
522    }
523    if (mr->VolumeName[0] == 0) {
524       POOL_DBR pr;
525       memset(&pr, 0, sizeof(pr));
526       /* Get the pool from pool=<pool-name> */
527       if (!get_pool_dbr(ua, &pr)) {
528          return 0;
529       }
530       mr->PoolId = pr.PoolId;
531       db_list_media_records(ua->jcr, ua->db, mr, prtit, ua, HORZ_LIST);
532       if (!get_cmd(ua, _("Enter MediaId or Volume name: "))) {
533          return 0;
534       }
535       if (is_a_number(ua->cmd)) {
536          mr->MediaId = str_to_int64(ua->cmd);
537       } else {
538          bstrncpy(mr->VolumeName, ua->cmd, sizeof(mr->VolumeName));
539       }
540    }
541
542    if (!db_get_media_record(ua->jcr, ua->db, mr)) {
543       bsendmsg(ua, "%s", db_strerror(ua->db));
544       return 0;
545    }
546    return 1;
547 }
548
549
550 /*
551  * Select a pool resource from prompt list
552  */
553 POOL *select_pool_resource(UAContext *ua)
554 {
555    char name[MAX_NAME_LENGTH];
556    POOL *pool;
557
558    start_prompt(ua, _("The defined Pool resources are:\n"));
559    LockRes();
560    foreach_res(pool, R_POOL) {
561       if (acl_access_ok(ua, Pool_ACL, pool->hdr.name)) {
562          add_prompt(ua, pool->hdr.name);
563       }
564    }
565    UnlockRes();
566    if (do_prompt(ua, _("Pool"), _("Select Pool resource"), name, sizeof(name)) < 0) {
567       return NULL;
568    }
569    pool = (POOL *)GetResWithName(R_POOL, name);
570    return pool;
571 }
572
573
574 /*
575  *  If you are thinking about using it, you
576  *  probably want to use select_pool_dbr()
577  *  or get_pool_dbr() above.
578  */
579 POOL *get_pool_resource(UAContext *ua)
580 {
581    POOL *pool = NULL;
582    int i;
583
584    i = find_arg_with_value(ua, "pool");
585    if (i >= 0 && acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
586       pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
587       if (pool) {
588          return pool;
589       }
590       bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
591    }
592    return select_pool_resource(ua);
593 }
594
595 /*
596  * List all jobs and ask user to select one
597  */
598 int select_job_dbr(UAContext *ua, JOB_DBR *jr)
599 {
600    db_list_job_records(ua->jcr, ua->db, jr, prtit, ua, HORZ_LIST);
601    if (!get_pint(ua, _("Enter the JobId to select: "))) {
602       return 0;
603    }
604    jr->JobId = ua->int64_val;
605    if (!db_get_job_record(ua->jcr, ua->db, jr)) {
606       bsendmsg(ua, "%s", db_strerror(ua->db));
607       return 0;
608    }
609    return jr->JobId;
610
611 }
612
613
614 /* Scan what the user has entered looking for:
615  *
616  *  jobid=nn
617  *
618  *  if error or not found, put up a list of Jobs
619  *  to choose from.
620  *
621  *   returns: 0 on error
622  *            JobId on success and fills in JOB_DBR
623  */
624 int get_job_dbr(UAContext *ua, JOB_DBR *jr)
625 {
626    int i;
627
628    for (i=1; i<ua->argc; i++) {
629       if (strcasecmp(ua->argk[i], N_("job")) == 0 && ua->argv[i]) {
630          jr->JobId = 0;
631          bstrncpy(jr->Job, ua->argv[i], sizeof(jr->Job));
632       } else if (strcasecmp(ua->argk[i], N_("jobid")) == 0 && ua->argv[i]) {
633          jr->JobId = str_to_int64(ua->argv[i]);
634       } else {
635          continue;
636       }
637       if (!db_get_job_record(ua->jcr, ua->db, jr)) {
638          bsendmsg(ua, _("Could not find Job \"%s\": ERR=%s"), ua->argv[i],
639                   db_strerror(ua->db));
640          jr->JobId = 0;
641          break;
642       }
643       return jr->JobId;
644    }
645
646    if (!select_job_dbr(ua, jr)) {  /* try once more */
647       return 0;
648    }
649    return jr->JobId;
650 }
651
652 /*
653  * Implement unique set of prompts
654  */
655 void start_prompt(UAContext *ua, const char *msg)
656 {
657   if (ua->max_prompts == 0) {
658      ua->max_prompts = 10;
659      ua->prompt = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
660   }
661   ua->num_prompts = 1;
662   ua->prompt[0] = bstrdup(msg);
663 }
664
665 /*
666  * Add to prompts -- keeping them unique
667  */
668 void add_prompt(UAContext *ua, const char *prompt)
669 {
670    int i;
671    if (ua->num_prompts == ua->max_prompts) {
672       ua->max_prompts *= 2;
673       ua->prompt = (char **)brealloc(ua->prompt, sizeof(char *) *
674          ua->max_prompts);
675     }
676     for (i=1; i < ua->num_prompts; i++) {
677        if (strcmp(ua->prompt[i], prompt) == 0) {
678           return;
679        }
680     }
681     ua->prompt[ua->num_prompts++] = bstrdup(prompt);
682 }
683
684 /*
685  * Display prompts and get user's choice
686  *
687  *  Returns: -1 on error
688  *            index base 0 on success, and choice
689  *               is copied to prompt if not NULL
690  *             prompt is set to the chosen prompt item string
691  */
692 int do_prompt(UAContext *ua, const char *automsg, const char *msg, char *prompt, int max_prompt)
693 {
694    int i, item;
695    char pmsg[MAXSTRING];
696
697    if (prompt) {
698       *prompt = 0;
699    }
700    if (ua->num_prompts == 2) {
701       item = 1;
702       if (prompt) {
703          bstrncpy(prompt, ua->prompt[1], max_prompt);
704       }
705       bsendmsg(ua, _("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
706       goto done;
707    }
708    /* If running non-interactive, bail out */
709    if (ua->batch) {
710       bsendmsg(ua, _("Cannot select %s in batch mode.\n"), automsg);
711       item = -1;
712       goto done;
713    }
714    bsendmsg(ua, ua->prompt[0]);
715    for (i=1; i < ua->num_prompts; i++) {
716       bsendmsg(ua, "%6d: %s\n", i, ua->prompt[i]);
717    }
718
719    for ( ;; ) {
720       /* First item is the prompt string, not the items */
721       if (ua->num_prompts == 1) {
722          bsendmsg(ua, _("Selection is empty!\n"));
723          item = -1;                    /* list is empty ! */
724          break;
725       }
726       if (ua->num_prompts == 2) {
727          item = 1;
728          bsendmsg(ua, _("Item 1 selected automatically.\n"));
729          if (prompt) {
730             bstrncpy(prompt, ua->prompt[1], max_prompt);
731          }
732          break;
733       } else {
734          sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
735       }
736       /* Either a . or an @ will get you out of the loop */
737       if (!get_pint(ua, pmsg)) {
738          item = -1;                   /* error */
739          bsendmsg(ua, _("Selection aborted, nothing done.\n"));
740          break;
741       }
742       item = ua->pint32_val;
743       if (item < 1 || item >= ua->num_prompts) {
744          bsendmsg(ua, _("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
745          continue;
746       }
747       if (prompt) {
748          bstrncpy(prompt, ua->prompt[item], max_prompt);
749       }
750       break;
751    }
752
753 done:
754    for (i=0; i < ua->num_prompts; i++) {
755       free(ua->prompt[i]);
756    }
757    ua->num_prompts = 0;
758    return item>0 ? item-1 : item;
759 }
760
761
762 /*
763  * We scan what the user has entered looking for
764  *    storage=<storage-resource>
765  *    job=<job_name>
766  *    jobid=<jobid>
767  *    ?              (prompt him with storage list)
768  *    <some-error>   (prompt him with storage list)
769  *
770  * If use_default is set, we assume that any keyword without a value
771  *   is the name of the Storage resource wanted.
772  */
773 STORE *get_storage_resource(UAContext *ua, bool use_default)
774 {
775    char *store_name = NULL;
776    STORE *store = NULL;
777    int jobid;
778    JCR *jcr;
779    int i;
780    char ed1[50];
781
782    for (i=1; i<ua->argc; i++) {
783       if (use_default && !ua->argv[i]) {
784          /* Ignore slots, scan and barcode(s) keywords */
785          if (strcasecmp("scan", ua->argk[i]) == 0 ||
786              strcasecmp("barcode", ua->argk[i]) == 0 ||
787              strcasecmp("barcodes", ua->argk[i]) == 0 ||
788              strcasecmp("slots", ua->argk[i]) == 0) {
789             continue;
790          }
791          /* Default argument is storage */
792          if (store_name) {
793             bsendmsg(ua, _("Storage name given twice.\n"));
794             return NULL;
795          }
796          store_name = ua->argk[i];
797          if (*store_name == '?') {
798             *store_name = 0;
799             break;
800          }
801       } else {
802          if (strcasecmp(ua->argk[i], N_("storage")) == 0 ||
803              strcasecmp(ua->argk[i], N_("sd")) == 0) {
804             store_name = ua->argv[i];
805             break;
806
807          } else if (strcasecmp(ua->argk[i], N_("jobid")) == 0) {
808             jobid = str_to_int64(ua->argv[i]);
809             if (jobid <= 0) {
810                bsendmsg(ua, _("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
811                return NULL;
812             }
813             if (!(jcr=get_jcr_by_id(jobid))) {
814                bsendmsg(ua, _("JobId %s is not running.\n"), edit_int64(jobid, ed1));
815                return NULL;
816             }
817             store = jcr->store;
818             free_jcr(jcr);
819             break;
820
821          } else if (strcasecmp(ua->argk[i], N_("job")) == 0) {
822             if (!ua->argv[i]) {
823                bsendmsg(ua, _("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
824                return NULL;
825             }
826             if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
827                bsendmsg(ua, _("Job \"%s\" is not running.\n"), ua->argv[i]);
828                return NULL;
829             }
830             store = jcr->store;
831             free_jcr(jcr);
832             break;
833         }
834       }
835    }
836    if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
837       store = NULL;
838    }
839
840    if (!store && store_name && store_name[0] != 0) {
841       store = (STORE *)GetResWithName(R_STORAGE, store_name);
842       if (!store) {
843          bsendmsg(ua, _("Storage resource \"%s\": not found\n"), store_name);
844       }
845    }
846    if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
847       store = NULL;
848    }
849    /* No keywords found, so present a selection list */
850    if (!store) {
851       store = select_storage_resource(ua);
852    }
853    return store;
854 }
855
856 /* Get drive that we are working with for this storage */
857 int get_storage_drive(UAContext *ua, STORE *store)
858 {
859    int i, drive = -1;
860    /* Get drive for autochanger if possible */
861    i = find_arg_with_value(ua, "drive");
862    if (i >=0) {
863       drive = atoi(ua->argv[i]);
864    } else if (store && store->autochanger) {
865       /* If our structure is not set ask SD for # drives */
866       if (store->drives == 0) {
867          store->drives = get_num_drives_from_SD(ua);
868       }
869       /* If only one drive, default = 0 */
870       if (store->drives == 1) {
871          drive = 0;
872       } else {
873          /* Ask user to enter drive number */
874          ua->cmd[0] = 0;
875          if (!get_cmd(ua, _("Enter autochanger drive[0]: "))) {
876             drive = -1;  /* None */
877          } else {
878             drive = atoi(ua->cmd);
879          }
880      }
881    }
882    return drive;
883 }
884
885
886 /*
887  * Scan looking for mediatype=
888  *
889  *  if not found or error, put up selection list
890  *
891  *  Returns: 0 on error
892  *           1 on success, MediaType is set
893  */
894 int get_media_type(UAContext *ua, char *MediaType, int max_media)
895 {
896    STORE *store;
897    int i;
898
899    i = find_arg_with_value(ua, "mediatype");
900    if (i >= 0) {
901       bstrncpy(MediaType, ua->argv[i], max_media);
902       return 1;
903    }
904
905    start_prompt(ua, _("Media Types defined in conf file:\n"));
906    LockRes();
907    foreach_res(store, R_STORAGE) {
908       add_prompt(ua, store->media_type);
909    }
910    UnlockRes();
911    return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
912 }
913
914 bool get_level_from_name(JCR *jcr, const char *level_name)
915 {
916    /* Look up level name and pull code */
917    bool found = false;
918    for (int i=0; joblevels[i].level_name; i++) {
919       if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
920          jcr->JobLevel = joblevels[i].level;
921          found = true;
922          break;
923       }
924    }
925    return found;
926 }