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