]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_select.c
kes Apply Eric's patch for recycling the Scratch pool.
[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
435
436 /* Scan what the user has entered looking for:
437  *
438  *  pool=<pool-name>
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)
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)) {  /* try once more */
456       return false;
457    }
458    return true;
459 }
460
461 /*
462  * Select a Pool record from the catalog
463  */
464 bool select_pool_dbr(UAContext *ua, POOL_DBR *pr)
465 {
466    POOL_DBR opr;
467    char name[MAX_NAME_LENGTH];
468    int num_pools, i;
469    uint32_t *ids;
470
471    for (i=1; i<ua->argc; i++) {
472       if (strcasecmp(ua->argk[i], NT_("pool")) == 0 && ua->argv[i] &&
473           acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
474          bstrncpy(pr->Name, ua->argv[i], sizeof(pr->Name));
475          if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
476             bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), ua->argv[i],
477                      db_strerror(ua->db));
478             pr->PoolId = 0;
479             break;
480          }
481          return true;
482       }
483    }
484
485    pr->PoolId = 0;
486    if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
487       bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db));
488       return 0;
489    }
490    if (num_pools <= 0) {
491       bsendmsg(ua, _("No pools defined. Use the \"create\" command to create one.\n"));
492       return false;
493    }
494
495    start_prompt(ua, _("Defined Pools:\n"));
496    for (i=0; i < num_pools; i++) {
497       opr.PoolId = ids[i];
498       if (!db_get_pool_record(ua->jcr, ua->db, &opr) ||
499           !acl_access_ok(ua, Pool_ACL, opr.Name)) {
500          continue;
501       }
502       add_prompt(ua, opr.Name);
503    }
504    free(ids);
505    if (do_prompt(ua, _("Pool"),  _("Select the Pool"), name, sizeof(name)) < 0) {
506       return false;
507    }
508    memset(&opr, 0, sizeof(opr));
509    bstrncpy(opr.Name, name, sizeof(opr.Name));
510
511    if (!db_get_pool_record(ua->jcr, ua->db, &opr)) {
512       bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), name, db_strerror(ua->db));
513       return false;
514    }
515    memcpy(pr, &opr, sizeof(opr));
516    return true;
517 }
518
519 /*
520  * Select a Pool and a Media (Volume) record from the database
521  */
522 int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr)
523 {
524
525    if (!select_media_dbr(ua, mr)) {
526       return 0;
527    }
528    memset(pr, 0, sizeof(POOL_DBR));
529    pr->PoolId = mr->PoolId;
530    if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
531       bsendmsg(ua, "%s", db_strerror(ua->db));
532       return 0;
533    }
534    if (!acl_access_ok(ua, Pool_ACL, pr->Name)) {
535       bsendmsg(ua, _("No access to Pool \"%s\"\n"), pr->Name);
536       return 0;
537    }
538    return 1;
539 }
540
541 /* Select a Media (Volume) record from the database */
542 int select_media_dbr(UAContext *ua, MEDIA_DBR *mr)
543 {
544    int i;
545
546    memset(mr, 0, sizeof(MEDIA_DBR));
547
548    i = find_arg_with_value(ua, "volume");
549    if (i >= 0) {
550       bstrncpy(mr->VolumeName, ua->argv[i], sizeof(mr->VolumeName));
551    }
552    if (mr->VolumeName[0] == 0) {
553       POOL_DBR pr;
554       memset(&pr, 0, sizeof(pr));
555       /* Get the pool from pool=<pool-name> */
556       if (!get_pool_dbr(ua, &pr)) {
557          return 0;
558       }
559       mr->PoolId = pr.PoolId;
560       db_list_media_records(ua->jcr, ua->db, mr, prtit, ua, HORZ_LIST);
561       if (!get_cmd(ua, _("Enter MediaId or Volume name: "))) {
562          return 0;
563       }
564       if (is_a_number(ua->cmd)) {
565          mr->MediaId = str_to_int64(ua->cmd);
566       } else {
567          bstrncpy(mr->VolumeName, ua->cmd, sizeof(mr->VolumeName));
568       }
569    }
570
571    if (!db_get_media_record(ua->jcr, ua->db, mr)) {
572       bsendmsg(ua, "%s", db_strerror(ua->db));
573       return 0;
574    }
575    return 1;
576 }
577
578
579 /*
580  * Select a pool resource from prompt list
581  */
582 POOL *select_pool_resource(UAContext *ua)
583 {
584    char name[MAX_NAME_LENGTH];
585    POOL *pool;
586
587    start_prompt(ua, _("The defined Pool resources are:\n"));
588    LockRes();
589    foreach_res(pool, R_POOL) {
590       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
591          add_prompt(ua, pool->name());
592       }
593    }
594    UnlockRes();
595    if (do_prompt(ua, _("Pool"), _("Select Pool resource"), name, sizeof(name)) < 0) {
596       return NULL;
597    }
598    pool = (POOL *)GetResWithName(R_POOL, name);
599    return pool;
600 }
601
602
603 /*
604  *  If you are thinking about using it, you
605  *  probably want to use select_pool_dbr()
606  *  or get_pool_dbr() above.
607  */
608 POOL *get_pool_resource(UAContext *ua)
609 {
610    POOL *pool = NULL;
611    int i;
612
613    i = find_arg_with_value(ua, "pool");
614    if (i >= 0 && acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
615       pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
616       if (pool) {
617          return pool;
618       }
619       bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
620    }
621    return select_pool_resource(ua);
622 }
623
624 /*
625  * List all jobs and ask user to select one
626  */
627 int select_job_dbr(UAContext *ua, JOB_DBR *jr)
628 {
629    db_list_job_records(ua->jcr, ua->db, jr, prtit, ua, HORZ_LIST);
630    if (!get_pint(ua, _("Enter the JobId to select: "))) {
631       return 0;
632    }
633    jr->JobId = ua->int64_val;
634    if (!db_get_job_record(ua->jcr, ua->db, jr)) {
635       bsendmsg(ua, "%s", db_strerror(ua->db));
636       return 0;
637    }
638    return jr->JobId;
639
640 }
641
642
643 /* Scan what the user has entered looking for:
644  *
645  *  jobid=nn
646  *
647  *  if error or not found, put up a list of Jobs
648  *  to choose from.
649  *
650  *   returns: 0 on error
651  *            JobId on success and fills in JOB_DBR
652  */
653 int get_job_dbr(UAContext *ua, JOB_DBR *jr)
654 {
655    int i;
656
657    for (i=1; i<ua->argc; i++) {
658       if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
659          jr->JobId = 0;
660          bstrncpy(jr->Job, ua->argv[i], sizeof(jr->Job));
661       } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0 && ua->argv[i]) {
662          jr->JobId = str_to_int64(ua->argv[i]);
663          jr->Job[0] = 0;
664       } else {
665          continue;
666       }
667       if (!db_get_job_record(ua->jcr, ua->db, jr)) {
668          bsendmsg(ua, _("Could not find Job \"%s\": ERR=%s"), ua->argv[i],
669                   db_strerror(ua->db));
670          jr->JobId = 0;
671          break;
672       }
673       return jr->JobId;
674    }
675
676    jr->JobId = 0;
677    jr->Job[0] = 0;
678
679    for (i=1; i<ua->argc; i++) {
680       if ((strcasecmp(ua->argk[i], NT_("jobname")) == 0 ||
681            strcasecmp(ua->argk[i], NT_("job")) == 0) && ua->argv[i]) {
682          jr->JobId = 0;
683          bstrncpy(jr->Name, ua->argv[i], sizeof(jr->Name));
684          break;
685       }
686    }
687    if (!select_job_dbr(ua, jr)) {  /* try once more */
688       return 0;
689    }
690    return jr->JobId;
691 }
692
693 /*
694  * Implement unique set of prompts
695  */
696 void start_prompt(UAContext *ua, const char *msg)
697 {
698   if (ua->max_prompts == 0) {
699      ua->max_prompts = 10;
700      ua->prompt = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
701   }
702   ua->num_prompts = 1;
703   ua->prompt[0] = bstrdup(msg);
704 }
705
706 /*
707  * Add to prompts -- keeping them unique
708  */
709 void add_prompt(UAContext *ua, const char *prompt)
710 {
711    int i;
712    if (ua->num_prompts == ua->max_prompts) {
713       ua->max_prompts *= 2;
714       ua->prompt = (char **)brealloc(ua->prompt, sizeof(char *) *
715          ua->max_prompts);
716     }
717     for (i=1; i < ua->num_prompts; i++) {
718        if (strcmp(ua->prompt[i], prompt) == 0) {
719           return;
720        }
721     }
722     ua->prompt[ua->num_prompts++] = bstrdup(prompt);
723 }
724
725 /*
726  * Display prompts and get user's choice
727  *
728  *  Returns: -1 on error
729  *            index base 0 on success, and choice
730  *               is copied to prompt if not NULL
731  *             prompt is set to the chosen prompt item string
732  */
733 int do_prompt(UAContext *ua, const char *automsg, const char *msg, char *prompt, int max_prompt)
734 {
735    int i, item;
736    char pmsg[MAXSTRING];
737
738    if (prompt) {
739       *prompt = 0;
740    }
741    if (ua->num_prompts == 2) {
742       item = 1;
743       if (prompt) {
744          bstrncpy(prompt, ua->prompt[1], max_prompt);
745       }
746       bsendmsg(ua, _("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
747       goto done;
748    }
749    /* If running non-interactive, bail out */
750    if (ua->batch) {
751       bsendmsg(ua, _("Cannot select %s in batch mode.\n"), automsg);
752       item = -1;
753       goto done;
754    }
755 // bnet_sig(ua->UA_sock, BNET_START_SELECT);
756    bsendmsg(ua, ua->prompt[0]);
757    for (i=1; i < ua->num_prompts; i++) {
758       bsendmsg(ua, "%6d: %s\n", i, ua->prompt[i]);
759    }
760 // bnet_sig(ua->UA_sock, BNET_END_SELECT);
761
762    for ( ;; ) {
763       /* First item is the prompt string, not the items */
764       if (ua->num_prompts == 1) {
765          bsendmsg(ua, _("Selection list for \"%s\" is empty!\n"), automsg);
766          item = -1;                    /* list is empty ! */
767          break;
768       }
769       if (ua->num_prompts == 2) {
770          item = 1;
771          bsendmsg(ua, _("Item 1 selected automatically.\n"));
772          if (prompt) {
773             bstrncpy(prompt, ua->prompt[1], max_prompt);
774          }
775          break;
776       } else {
777          sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
778       }
779       /* Either a . or an @ will get you out of the loop */
780       if (!get_pint(ua, pmsg)) {
781          item = -1;                   /* error */
782          bsendmsg(ua, _("Selection aborted, nothing done.\n"));
783          break;
784       }
785       item = ua->pint32_val;
786       if (item < 1 || item >= ua->num_prompts) {
787          bsendmsg(ua, _("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
788          continue;
789       }
790       if (prompt) {
791          bstrncpy(prompt, ua->prompt[item], max_prompt);
792       }
793       break;
794    }
795
796 done:
797    for (i=0; i < ua->num_prompts; i++) {
798       free(ua->prompt[i]);
799    }
800    ua->num_prompts = 0;
801    return item>0 ? item-1 : item;
802 }
803
804
805 /*
806  * We scan what the user has entered looking for
807  *    storage=<storage-resource>
808  *    job=<job_name>
809  *    jobid=<jobid>
810  *    ?              (prompt him with storage list)
811  *    <some-error>   (prompt him with storage list)
812  *
813  * If use_default is set, we assume that any keyword without a value
814  *   is the name of the Storage resource wanted.
815  */
816 STORE *get_storage_resource(UAContext *ua, bool use_default)
817 {
818    char *store_name = NULL;
819    STORE *store = NULL;
820    int jobid;
821    JCR *jcr;
822    int i;
823    char ed1[50];
824
825    for (i=1; i<ua->argc; i++) {
826       if (use_default && !ua->argv[i]) {
827          /* Ignore slots, scan and barcode(s) keywords */
828          if (strcasecmp("scan", ua->argk[i]) == 0 ||
829              strcasecmp("barcode", ua->argk[i]) == 0 ||
830              strcasecmp("barcodes", ua->argk[i]) == 0 ||
831              strcasecmp("slots", ua->argk[i]) == 0) {
832             continue;
833          }
834          /* Default argument is storage */
835          if (store_name) {
836             bsendmsg(ua, _("Storage name given twice.\n"));
837             return NULL;
838          }
839          store_name = ua->argk[i];
840          if (*store_name == '?') {
841             *store_name = 0;
842             break;
843          }
844       } else {
845          if (strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
846              strcasecmp(ua->argk[i], NT_("sd")) == 0) {
847             store_name = ua->argv[i];
848             break;
849
850          } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
851             jobid = str_to_int64(ua->argv[i]);
852             if (jobid <= 0) {
853                bsendmsg(ua, _("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
854                return NULL;
855             }
856             if (!(jcr=get_jcr_by_id(jobid))) {
857                bsendmsg(ua, _("JobId %s is not running.\n"), edit_int64(jobid, ed1));
858                return NULL;
859             }
860             store = jcr->wstore;
861             free_jcr(jcr);
862             break;
863
864          } else if (strcasecmp(ua->argk[i], NT_("job")) == 0 ||
865                     strcasecmp(ua->argk[i], NT_("jobname")) == 0) {
866             if (!ua->argv[i]) {
867                bsendmsg(ua, _("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
868                return NULL;
869             }
870             if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
871                bsendmsg(ua, _("Job \"%s\" is not running.\n"), ua->argv[i]);
872                return NULL;
873             }
874             store = jcr->wstore;
875             free_jcr(jcr);
876             break;
877          } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
878             if (!ua->argv[i]) {
879                bsendmsg(ua, _("Expecting ujobid=xxx, got: %s.\n"), ua->argk[i]);
880                return NULL;
881             }
882             if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
883                bsendmsg(ua, _("Job \"%s\" is not running.\n"), ua->argv[i]);
884                return NULL;
885             }
886             store = jcr->wstore;
887             free_jcr(jcr);
888             break;
889         }
890       }
891    }
892    if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
893       store = NULL;
894    }
895
896    if (!store && store_name && store_name[0] != 0) {
897       store = (STORE *)GetResWithName(R_STORAGE, store_name);
898       if (!store) {
899          bsendmsg(ua, _("Storage resource \"%s\": not found\n"), store_name);
900       }
901    }
902    if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
903       store = NULL;
904    }
905    /* No keywords found, so present a selection list */
906    if (!store) {
907       store = select_storage_resource(ua);
908    }
909    return store;
910 }
911
912 /* Get drive that we are working with for this storage */
913 int get_storage_drive(UAContext *ua, STORE *store)
914 {
915    int i, drive = -1;
916    /* Get drive for autochanger if possible */
917    i = find_arg_with_value(ua, "drive");
918    if (i >=0) {
919       drive = atoi(ua->argv[i]);
920    } else if (store && store->autochanger) {
921       /* If our structure is not set ask SD for # drives */
922       if (store->drives == 0) {
923          store->drives = get_num_drives_from_SD(ua);
924       }
925       /* If only one drive, default = 0 */
926       if (store->drives == 1) {
927          drive = 0;
928       } else {
929          /* Ask user to enter drive number */
930          ua->cmd[0] = 0;
931          if (!get_cmd(ua, _("Enter autochanger drive[0]: "))) {
932             drive = -1;  /* None */
933          } else {
934             drive = atoi(ua->cmd);
935          }
936      }
937    }
938    return drive;
939 }
940
941 /* Get slot that we are working with for this storage */
942 int get_storage_slot(UAContext *ua, STORE *store)
943 {
944    int i, slot = -1;
945    /* Get slot for autochanger if possible */
946    i = find_arg_with_value(ua, "slot");
947    if (i >=0) {
948       slot = atoi(ua->argv[i]);
949    } else if (store && store->autochanger) {
950       /* Ask user to enter slot number */
951       ua->cmd[0] = 0;
952       if (!get_cmd(ua, _("Enter autochanger slot: "))) {
953          slot = -1;  /* None */
954       } else {
955          slot = atoi(ua->cmd);
956       }
957    }
958    return slot;
959 }
960
961
962
963 /*
964  * Scan looking for mediatype=
965  *
966  *  if not found or error, put up selection list
967  *
968  *  Returns: 0 on error
969  *           1 on success, MediaType is set
970  */
971 int get_media_type(UAContext *ua, char *MediaType, int max_media)
972 {
973    STORE *store;
974    int i;
975
976    i = find_arg_with_value(ua, "mediatype");
977    if (i >= 0) {
978       bstrncpy(MediaType, ua->argv[i], max_media);
979       return 1;
980    }
981
982    start_prompt(ua, _("Media Types defined in conf file:\n"));
983    LockRes();
984    foreach_res(store, R_STORAGE) {
985       add_prompt(ua, store->media_type);
986    }
987    UnlockRes();
988    return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
989 }
990
991 bool get_level_from_name(JCR *jcr, const char *level_name)
992 {
993    /* Look up level name and pull code */
994    bool found = false;
995    for (int i=0; joblevels[i].level_name; i++) {
996       if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
997          jcr->JobLevel = joblevels[i].level;
998          found = true;
999          break;
1000       }
1001    }
1002    return found;
1003 }