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