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