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