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