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