]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_select.c
- Add Rudolf Cejka's new rc-chio-changer.
[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-2006 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          jr->Job[0] = 0;
635       } else {
636          continue;
637       }
638       if (!db_get_job_record(ua->jcr, ua->db, jr)) {
639          bsendmsg(ua, _("Could not find Job \"%s\": ERR=%s"), ua->argv[i],
640                   db_strerror(ua->db));
641          jr->JobId = 0;
642          break;
643       }
644       return jr->JobId;
645    }
646
647    jr->JobId = 0;
648    jr->Job[0] = 0;
649
650    for (i=1; i<ua->argc; i++) {
651       if (strcasecmp(ua->argk[i], N_("jobname")) == 0 && ua->argv[i]) {
652          jr->JobId = 0;
653          bstrncpy(jr->Name, ua->argv[i], sizeof(jr->Name));
654          break;
655       }
656    }
657    if (!select_job_dbr(ua, jr)) {  /* try once more */
658       return 0;
659    }
660    return jr->JobId;
661 }
662
663 /*
664  * Implement unique set of prompts
665  */
666 void start_prompt(UAContext *ua, const char *msg)
667 {
668   if (ua->max_prompts == 0) {
669      ua->max_prompts = 10;
670      ua->prompt = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
671   }
672   ua->num_prompts = 1;
673   ua->prompt[0] = bstrdup(msg);
674 }
675
676 /*
677  * Add to prompts -- keeping them unique
678  */
679 void add_prompt(UAContext *ua, const char *prompt)
680 {
681    int i;
682    if (ua->num_prompts == ua->max_prompts) {
683       ua->max_prompts *= 2;
684       ua->prompt = (char **)brealloc(ua->prompt, sizeof(char *) *
685          ua->max_prompts);
686     }
687     for (i=1; i < ua->num_prompts; i++) {
688        if (strcmp(ua->prompt[i], prompt) == 0) {
689           return;
690        }
691     }
692     ua->prompt[ua->num_prompts++] = bstrdup(prompt);
693 }
694
695 /*
696  * Display prompts and get user's choice
697  *
698  *  Returns: -1 on error
699  *            index base 0 on success, and choice
700  *               is copied to prompt if not NULL
701  *             prompt is set to the chosen prompt item string
702  */
703 int do_prompt(UAContext *ua, const char *automsg, const char *msg, char *prompt, int max_prompt)
704 {
705    int i, item;
706    char pmsg[MAXSTRING];
707
708    if (prompt) {
709       *prompt = 0;
710    }
711    if (ua->num_prompts == 2) {
712       item = 1;
713       if (prompt) {
714          bstrncpy(prompt, ua->prompt[1], max_prompt);
715       }
716       bsendmsg(ua, _("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
717       goto done;
718    }
719    /* If running non-interactive, bail out */
720    if (ua->batch) {
721       bsendmsg(ua, _("Cannot select %s in batch mode.\n"), automsg);
722       item = -1;
723       goto done;
724    }
725 // bnet_sig(ua->UA_sock, BNET_START_SELECT);
726    bsendmsg(ua, ua->prompt[0]);
727    for (i=1; i < ua->num_prompts; i++) {
728       bsendmsg(ua, "%6d: %s\n", i, ua->prompt[i]);
729    }
730 // bnet_sig(ua->UA_sock, BNET_END_SELECT);
731
732    for ( ;; ) {
733       /* First item is the prompt string, not the items */
734       if (ua->num_prompts == 1) {
735          bsendmsg(ua, _("Selection is empty!\n"));
736          item = -1;                    /* list is empty ! */
737          break;
738       }
739       if (ua->num_prompts == 2) {
740          item = 1;
741          bsendmsg(ua, _("Item 1 selected automatically.\n"));
742          if (prompt) {
743             bstrncpy(prompt, ua->prompt[1], max_prompt);
744          }
745          break;
746       } else {
747          sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
748       }
749       /* Either a . or an @ will get you out of the loop */
750       if (!get_pint(ua, pmsg)) {
751          item = -1;                   /* error */
752          bsendmsg(ua, _("Selection aborted, nothing done.\n"));
753          break;
754       }
755       item = ua->pint32_val;
756       if (item < 1 || item >= ua->num_prompts) {
757          bsendmsg(ua, _("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
758          continue;
759       }
760       if (prompt) {
761          bstrncpy(prompt, ua->prompt[item], max_prompt);
762       }
763       break;
764    }
765
766 done:
767    for (i=0; i < ua->num_prompts; i++) {
768       free(ua->prompt[i]);
769    }
770    ua->num_prompts = 0;
771    return item>0 ? item-1 : item;
772 }
773
774
775 /*
776  * We scan what the user has entered looking for
777  *    storage=<storage-resource>
778  *    job=<job_name>
779  *    jobid=<jobid>
780  *    ?              (prompt him with storage list)
781  *    <some-error>   (prompt him with storage list)
782  *
783  * If use_default is set, we assume that any keyword without a value
784  *   is the name of the Storage resource wanted.
785  */
786 STORE *get_storage_resource(UAContext *ua, bool use_default)
787 {
788    char *store_name = NULL;
789    STORE *store = NULL;
790    int jobid;
791    JCR *jcr;
792    int i;
793    char ed1[50];
794
795    for (i=1; i<ua->argc; i++) {
796       if (use_default && !ua->argv[i]) {
797          /* Ignore slots, scan and barcode(s) keywords */
798          if (strcasecmp("scan", ua->argk[i]) == 0 ||
799              strcasecmp("barcode", ua->argk[i]) == 0 ||
800              strcasecmp("barcodes", ua->argk[i]) == 0 ||
801              strcasecmp("slots", ua->argk[i]) == 0) {
802             continue;
803          }
804          /* Default argument is storage */
805          if (store_name) {
806             bsendmsg(ua, _("Storage name given twice.\n"));
807             return NULL;
808          }
809          store_name = ua->argk[i];
810          if (*store_name == '?') {
811             *store_name = 0;
812             break;
813          }
814       } else {
815          if (strcasecmp(ua->argk[i], N_("storage")) == 0 ||
816              strcasecmp(ua->argk[i], N_("sd")) == 0) {
817             store_name = ua->argv[i];
818             break;
819
820          } else if (strcasecmp(ua->argk[i], N_("jobid")) == 0) {
821             jobid = str_to_int64(ua->argv[i]);
822             if (jobid <= 0) {
823                bsendmsg(ua, _("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
824                return NULL;
825             }
826             if (!(jcr=get_jcr_by_id(jobid))) {
827                bsendmsg(ua, _("JobId %s is not running.\n"), edit_int64(jobid, ed1));
828                return NULL;
829             }
830             store = jcr->store;
831             free_jcr(jcr);
832             break;
833
834          } else if (strcasecmp(ua->argk[i], N_("job")) == 0) {
835             if (!ua->argv[i]) {
836                bsendmsg(ua, _("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
837                return NULL;
838             }
839             if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
840                bsendmsg(ua, _("Job \"%s\" is not running.\n"), ua->argv[i]);
841                return NULL;
842             }
843             store = jcr->store;
844             free_jcr(jcr);
845             break;
846         }
847       }
848    }
849    if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
850       store = NULL;
851    }
852
853    if (!store && store_name && store_name[0] != 0) {
854       store = (STORE *)GetResWithName(R_STORAGE, store_name);
855       if (!store) {
856          bsendmsg(ua, _("Storage resource \"%s\": not found\n"), store_name);
857       }
858    }
859    if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
860       store = NULL;
861    }
862    /* No keywords found, so present a selection list */
863    if (!store) {
864       store = select_storage_resource(ua);
865    }
866    return store;
867 }
868
869 /* Get drive that we are working with for this storage */
870 int get_storage_drive(UAContext *ua, STORE *store)
871 {
872    int i, drive = -1;
873    /* Get drive for autochanger if possible */
874    i = find_arg_with_value(ua, "drive");
875    if (i >=0) {
876       drive = atoi(ua->argv[i]);
877    } else if (store && store->autochanger) {
878       /* If our structure is not set ask SD for # drives */
879       if (store->drives == 0) {
880          store->drives = get_num_drives_from_SD(ua);
881       }
882       /* If only one drive, default = 0 */
883       if (store->drives == 1) {
884          drive = 0;
885       } else {
886          /* Ask user to enter drive number */
887          ua->cmd[0] = 0;
888          if (!get_cmd(ua, _("Enter autochanger drive[0]: "))) {
889             drive = -1;  /* None */
890          } else {
891             drive = atoi(ua->cmd);
892          }
893      }
894    }
895    return drive;
896 }
897
898
899 /*
900  * Scan looking for mediatype=
901  *
902  *  if not found or error, put up selection list
903  *
904  *  Returns: 0 on error
905  *           1 on success, MediaType is set
906  */
907 int get_media_type(UAContext *ua, char *MediaType, int max_media)
908 {
909    STORE *store;
910    int i;
911
912    i = find_arg_with_value(ua, "mediatype");
913    if (i >= 0) {
914       bstrncpy(MediaType, ua->argv[i], max_media);
915       return 1;
916    }
917
918    start_prompt(ua, _("Media Types defined in conf file:\n"));
919    LockRes();
920    foreach_res(store, R_STORAGE) {
921       add_prompt(ua, store->media_type);
922    }
923    UnlockRes();
924    return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
925 }
926
927 bool get_level_from_name(JCR *jcr, const char *level_name)
928 {
929    /* Look up level name and pull code */
930    bool found = false;
931    for (int i=0; joblevels[i].level_name; i++) {
932       if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
933          jcr->JobLevel = joblevels[i].level;
934          found = true;
935          break;
936       }
937    }
938    return found;
939 }