]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_select.c
Backport from BEE
[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 *MediaId or Volume name: "))) {
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       /* Either a . or an @ will get you out of the loop */
955       if (ua->api) user->signal(BNET_SELECT_INPUT);
956
957       if (!get_selection_list(ua, sl, pmsg, false)) {
958          item = -1;
959          break;
960       }
961
962       if (sl.is_all()) {
963          for (i=1; i < ua->num_prompts; i++) {
964             selected->append(bstrdup(ua->prompt[i]));
965          }
966       } else {
967          while ( (item = sl.next()) > 0) {
968             if (item < 1 || item >= ua->num_prompts) {
969                ua->warning_msg(_("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
970                continue;
971             }
972             selected->append(bstrdup(ua->prompt[item]));
973          }
974       }
975       item = selected->size();
976       break;
977    }
978
979 done:
980    for (i=0; i < ua->num_prompts; i++) {
981       free(ua->prompt[i]);
982       if (ua->unique[i]) free(ua->unique[i]);
983    }
984    ua->num_prompts = 0;
985    return item;
986 }
987
988
989 /*
990  * We scan what the user has entered looking for
991  *    storage=<storage-resource>
992  *    job=<job_name>
993  *    jobid=<jobid>
994  *    ?              (prompt him with storage list)
995  *    <some-error>   (prompt him with storage list)
996  *
997  * If use_default is set, we assume that any keyword without a value
998  *   is the name of the Storage resource wanted.
999  */
1000 STORE *get_storage_resource(UAContext *ua, bool use_default, bool unique)
1001 {
1002    char *store_name = NULL;
1003    STORE *store = NULL;
1004    int jobid;
1005    JCR *jcr;
1006    int i;
1007    char ed1[50];
1008
1009    for (i=1; i<ua->argc; i++) {
1010       if (use_default && !ua->argv[i]) {
1011          /* Ignore slots, scan and barcode(s) keywords */
1012          if (strcasecmp("scan", ua->argk[i]) == 0 ||
1013              strcasecmp("barcode", ua->argk[i]) == 0 ||
1014              strcasecmp("barcodes", ua->argk[i]) == 0 ||
1015              strcasecmp("slots", ua->argk[i]) == 0) {
1016             continue;
1017          }
1018          /* Default argument is storage */
1019          if (store_name) {
1020             ua->error_msg(_("Storage name given twice.\n"));
1021             return NULL;
1022          }
1023          store_name = ua->argk[i];
1024          if (*store_name == '?') {
1025             *store_name = 0;
1026             break;
1027          }
1028       } else {
1029          if (strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1030              strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1031             store_name = ua->argv[i];
1032             break;
1033
1034          } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
1035             jobid = str_to_int64(ua->argv[i]);
1036             if (jobid <= 0) {
1037                ua->error_msg(_("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
1038                return NULL;
1039             }
1040             if (!(jcr=get_jcr_by_id(jobid))) {
1041                ua->error_msg(_("JobId %s is not running.\n"), edit_int64(jobid, ed1));
1042                return NULL;
1043             }
1044             store = jcr->wstore;
1045             free_jcr(jcr);
1046             break;
1047
1048          } else if (strcasecmp(ua->argk[i], NT_("job")) == 0 ||
1049                     strcasecmp(ua->argk[i], NT_("jobname")) == 0) {
1050             if (!ua->argv[i]) {
1051                ua->error_msg(_("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
1052                return NULL;
1053             }
1054             if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
1055                ua->error_msg(_("Job \"%s\" is not running.\n"), ua->argv[i]);
1056                return NULL;
1057             }
1058             store = jcr->wstore;
1059             free_jcr(jcr);
1060             break;
1061          } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
1062             if (!ua->argv[i]) {
1063                ua->error_msg(_("Expecting ujobid=xxx, got: %s.\n"), ua->argk[i]);
1064                return NULL;
1065             }
1066             if ((jcr=get_jcr_by_full_name(ua->argv[i]))) {
1067                store = jcr->wstore;
1068                free_jcr(jcr);
1069                /* The job might not be running, so we try to see other keywords */
1070                if (store) {
1071                   break;
1072                }
1073             }
1074          }
1075       }
1076    }
1077    if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1078       store = NULL;
1079    }
1080
1081    if (!store && store_name && store_name[0] != 0) {
1082       store = (STORE *)GetResWithName(R_STORAGE, store_name);
1083       if (!store) {
1084          ua->error_msg(_("Storage resource \"%s\": not found\n"), store_name);
1085       }
1086    }
1087    if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1088       store = NULL;
1089    }
1090    /* No keywords found, so present a selection list */
1091    if (!store) {
1092       store = select_storage_resource(ua, unique);
1093    }
1094    return store;
1095 }
1096
1097 /* Get drive that we are working with for this storage */
1098 int get_storage_drive(UAContext *ua, STORE *store)
1099 {
1100    int i, drive = -1;
1101    /* Get drive for autochanger if possible */
1102    i = find_arg_with_value(ua, "drive");
1103    if (i >=0) {
1104       drive = atoi(ua->argv[i]);
1105    } else if (store && store->autochanger) {
1106       /* If our structure is not set ask SD for # drives */
1107       if (store->drives == 0) {
1108          store->drives = get_num_drives_from_SD(ua);
1109       }
1110       /* If only one drive, default = 0 */
1111       if (store->drives == 1) {
1112          drive = 0;
1113       } else {
1114          /* Ask user to enter drive number */
1115          ua->cmd[0] = 0;
1116          if (!get_cmd(ua, _("Enter autochanger drive[0]: "))) {
1117             drive = -1;  /* None */
1118          } else {
1119             drive = atoi(ua->cmd);
1120          }
1121      }
1122    }
1123    return drive;
1124 }
1125
1126 /* Get slot that we are working with for this storage */
1127 int get_storage_slot(UAContext *ua, STORE *store)
1128 {
1129    int i, slot = -1;
1130    /* Get slot for autochanger if possible */
1131    i = find_arg_with_value(ua, "slot");
1132    if (i >=0) {
1133       slot = atoi(ua->argv[i]);
1134    } else if (store && store->autochanger) {
1135       /* Ask user to enter slot number */
1136       ua->cmd[0] = 0;
1137       if (!get_cmd(ua, _("Enter autochanger slot: "))) {
1138          slot = -1;  /* None */
1139       } else {
1140          slot = atoi(ua->cmd);
1141       }
1142    }
1143    return slot;
1144 }
1145
1146
1147
1148 /*
1149  * Scan looking for mediatype=
1150  *
1151  *  if not found or error, put up selection list
1152  *
1153  *  Returns: 0 on error
1154  *           1 on success, MediaType is set
1155  */
1156 int get_media_type(UAContext *ua, char *MediaType, int max_media)
1157 {
1158    STORE *store;
1159    int i;
1160
1161    i = find_arg_with_value(ua, "mediatype");
1162    if (i >= 0) {
1163       bstrncpy(MediaType, ua->argv[i], max_media);
1164       return 1;
1165    }
1166
1167    start_prompt(ua, _("Media Types defined in conf file:\n"));
1168    LockRes();
1169    foreach_res(store, R_STORAGE) {
1170       add_prompt(ua, store->media_type);
1171    }
1172    UnlockRes();
1173    return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
1174 }
1175
1176
1177 bool get_level_from_name(JCR *jcr, const char *level_name)
1178 {
1179    /* Look up level name and pull code */
1180    bool found = false;
1181    for (int i=0; joblevels[i].level_name; i++) {
1182       if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
1183          jcr->setJobLevel(joblevels[i].level);
1184          found = true;
1185          break;
1186       }
1187    }
1188    return found;
1189 }
1190
1191 static int count_running_jobs(UAContext *ua)
1192 {
1193    int tjobs = 0;                  /* total # number jobs */
1194    int njobs = 0;
1195    JCR *jcr;
1196    /* Count Jobs running */
1197    foreach_jcr(jcr) {
1198       if (jcr->JobId == 0) {      /* this is us */
1199          continue;
1200       }
1201       tjobs++;                    /* count of all jobs */
1202       if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1203          continue;               /* skip not authorized */
1204       }
1205       njobs++;                   /* count of authorized jobs */
1206    }
1207    endeach_jcr(jcr);
1208
1209    if (njobs == 0) {            /* no authorized */
1210       if (tjobs == 0) {
1211          ua->send_msg(_("No Jobs running.\n"));
1212       } else {
1213          ua->send_msg(_("None of your jobs are running.\n"));
1214       }
1215    }
1216    return njobs;
1217 }
1218
1219
1220 /* Get a list of running jobs
1221  * "reason" is used in user messages
1222  * can be: cancel, limit, ...
1223  *  Returns: -1 on error
1224  *           nb of JCR on success (should be free_jcr() after)
1225  */
1226 int select_running_jobs(UAContext *ua, alist *jcrs, const char *reason)
1227 {
1228    int i;
1229    JCR *jcr = NULL;
1230    int njobs = 0;
1231    char JobName[MAX_NAME_LENGTH];
1232    char temp[256];
1233    alist *selected = NULL;
1234
1235    for (i=1; i<ua->argc; i++) {
1236       if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
1237          sellist sl;
1238          int32_t JobId;
1239
1240          if (sl.set_string(ua->argv[i], true) < 0 && sl.get_errmsg()) {
1241             ua->send_msg("%s", sl.get_errmsg());
1242             break;
1243          }
1244
1245          foreach_sellist(JobId, &sl) {
1246             jcr = get_jcr_by_id(JobId);
1247             if (jcr && jcr->job && acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1248                jcrs->append(jcr);
1249
1250             } else if (jcr) {
1251                ua->error_msg(_("Unauthorized command from this console "
1252                                "for JobId=%d.\n"), JobId);
1253                free_jcr(jcr);
1254
1255             } else {
1256                if (debug_level > 5) {
1257                   ua->warning_msg(_("Warning Job JobId=%d is not running. "
1258                                     "Continuing anyway...\n"), JobId);
1259                }
1260             }
1261          }
1262          break;
1263
1264       /* TODO: might want to implement filters (client, status, etc...) */
1265       } else if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
1266          foreach_jcr(jcr) {
1267             if (jcr->JobId == 0) {      /* this is us */
1268                continue;
1269             }
1270             if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1271                continue;               /* skip not authorized */
1272             }
1273             jcr->inc_use_count();
1274             jcrs->append(jcr);
1275          }
1276          endeach_jcr(jcr);
1277
1278          if (jcrs->size() > 0 && find_arg(ua, NT_("yes")) < 0) {
1279             char nbuf[1000];
1280             bsnprintf(nbuf, sizeof(nbuf),  _("Confirm %s of %d Job%s (yes/no): "),
1281                       reason, jcrs->size(), jcrs->size()>1?"s":"");
1282             if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
1283                return -1;
1284             }
1285          }
1286          break;
1287
1288       } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1289          if (!ua->argv[i]) {
1290             break;
1291          }
1292          if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
1293             ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
1294             jcr = new_jcr(sizeof(JCR), dird_free_jcr);
1295             bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
1296          }
1297          if (jcr && jcr->job && acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1298             jcrs->append(jcr);
1299          }
1300          break;
1301
1302       } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
1303          if (!ua->argv[i]) {
1304             break;
1305          }
1306          if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
1307             ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
1308             jcr = new_jcr(sizeof(JCR), dird_free_jcr);
1309             bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
1310          }
1311          if (jcr && jcr->job && acl_access_ok(ua, Job_ACL, jcr->job->name())) {
1312             jcrs->append(jcr);
1313          }
1314          break;
1315       }
1316    }
1317
1318    if (jcrs->size() == 0) {
1319       /*
1320        * If we still do not have a jcr,
1321        *   throw up a list and ask the user to select one.
1322        */
1323       char *item;
1324       char buf[1000];
1325       njobs = count_running_jobs(ua);
1326       if (njobs == 0) {
1327          goto bail_out;
1328       }
1329       start_prompt(ua, _("Select Job(s):\n"));
1330       foreach_jcr(jcr) {
1331          char ed1[50];
1332          if (jcr->JobId == 0) {      /* this is us */
1333             continue;
1334          }
1335          bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
1336          add_prompt(ua, buf);
1337       }
1338       endeach_jcr(jcr);
1339       bsnprintf(temp, sizeof(temp), _("Choose Job list to %s"), _(reason));
1340       selected = New(alist(5, owned_by_alist));
1341       if (do_alist_prompt(ua, _("Job"), temp, selected) < 0) {
1342          goto bail_out;
1343       }
1344       /* Possibly ask for confirmation */
1345       if (selected->size() > 0 && find_arg(ua, NT_("yes")) < 0) {
1346          char nbuf[1000];
1347          foreach_alist(item, selected) {
1348             ua->send_msg("%s\n", item);
1349          }
1350          bsnprintf(nbuf, sizeof(nbuf),  _("Confirm %s of %d Job%s (yes/no): "),
1351                    reason, selected->size(), selected->size()>1?"s":"");
1352          if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
1353             goto bail_out;
1354          }
1355       }
1356
1357       foreach_alist(item, selected) {
1358          if (sscanf(item, "JobId=%d Job=%127s", &njobs, JobName) != 2) {
1359             ua->warning_msg(_("Job \"%s\" not found.\n"), item);
1360             continue;
1361          }
1362          jcr = get_jcr_by_full_name(JobName);
1363          if (jcr) {
1364             jcrs->append(jcr);
1365          } else {
1366             ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
1367          }
1368       }
1369    }
1370 bail_out:
1371    if (selected) delete selected;
1372    return jcrs->size();
1373 }