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