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