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