1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2001-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2016 Planets Communications B.V.
6 Copyright (C) 2013-2016 Bareos GmbH & Co. KG
7
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 Affero 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 /*
24 * Kern Sibbald, October MMI
25 */
26 /**
27 * @file
28 * User Agent Prompt and Selection code
29 */
30
31 #include "include/bareos.h"
32 #include "dird.h"
33 #include "dird/dird_globals.h"
34 #include "dird/jcr_private.h"
35 #include "dird/storage.h"
36 #include "dird/ua_input.h"
37 #include "dird/ua_select.h"
38 #include "lib/edit.h"
39 #include "lib/parse_conf.h"
40 #include "lib/util.h"
41
42 #include <algorithm>
43 #include <string>
44 #include <vector>
45
46 namespace directordaemon {
47
48 /* Imported variables */
49 extern struct s_jt jobtypes[];
50 extern struct s_jl joblevels[];
51
52 /**
53 * Confirm a retention period
54 */
ConfirmRetention(UaContext * ua,utime_t * ret,const char * msg)55 bool ConfirmRetention(UaContext* ua, utime_t* ret, const char* msg)
56 {
57 bool retval;
58 char ed1[100];
59 int yes_in_arg;
60
61 yes_in_arg = FindArg(ua, NT_("yes"));
62 for (;;) {
63 ua->InfoMsg(_("The current %s retention period is: %s\n"), msg,
64 edit_utime(*ret, ed1, sizeof(ed1)));
65 if (yes_in_arg != -1) { return true; }
66
67 if (!GetCmd(ua, _("Continue? (yes/mod/no): "))) { return false; }
68
69 if (Bstrcasecmp(ua->cmd, _("mod"))) {
70 if (!GetCmd(ua, _("Enter new retention period: "))) { return false; }
71 if (!DurationToUtime(ua->cmd, ret)) {
72 ua->ErrorMsg(_("Invalid period.\n"));
73 continue;
74 }
75 continue;
76 }
77
78 if (IsYesno(ua->cmd, &retval)) { return retval; }
79 }
80
81 return true;
82 }
83
84 /**
85 * Given a list of keywords, find the first one that is in the argument list.
86 *
87 * Returns: -1 if not found
88 * index into list (base 0) on success
89 */
FindArgKeyword(UaContext * ua,const char ** list)90 int FindArgKeyword(UaContext* ua, const char** list)
91 {
92 for (int i = 1; i < ua->argc; i++) {
93 for (int j = 0; list[j]; j++) {
94 if (Bstrcasecmp(list[j], ua->argk[i])) { return j; }
95 }
96 }
97
98 return -1;
99 }
100
101 /**
102 * Given one keyword, find the first one that is in the argument list.
103 *
104 * Returns: argk index (always gt 0)
105 * -1 if not found
106 */
FindArg(UaContext * ua,const char * keyword)107 int FindArg(UaContext* ua, const char* keyword)
108 {
109 for (int i = 1; i < ua->argc; i++) {
110 if (Bstrcasecmp(keyword, ua->argk[i])) { return i; }
111 }
112
113 return -1;
114 }
115
116 /**
117 * Given a single keyword, find it in the argument list, but it must have a
118 * value
119 *
120 * Returns: -1 if not found or no value
121 * list index (base 0) on success
122 */
FindArgWithValue(UaContext * ua,const char * keyword)123 int FindArgWithValue(UaContext* ua, const char* keyword)
124 {
125 for (int i = 1; i < ua->argc; i++) {
126 if (Bstrcasecmp(keyword, ua->argk[i])) {
127 if (ua->argv[i]) {
128 return i;
129 } else {
130 return -1;
131 }
132 }
133 }
134
135 return -1;
136 }
137
138 /**
139 * Given a list of keywords, prompt the user to choose one.
140 *
141 * Returns: -1 on failure
142 * index into list (base 0) on success
143 */
DoKeywordPrompt(UaContext * ua,const char * msg,const char ** list)144 int DoKeywordPrompt(UaContext* ua, const char* msg, const char** list)
145 {
146 StartPrompt(ua, _("You have the following choices:\n"));
147 for (int i = 0; list[i]; i++) { AddPrompt(ua, list[i]); }
148
149 return DoPrompt(ua, "", msg, NULL, 0);
150 }
151
152 /**
153 * Select a Storage resource from prompt list
154 */
select_storage_resource(UaContext * ua,bool autochanger_only)155 StorageResource* select_storage_resource(UaContext* ua, bool autochanger_only)
156 {
157 StorageResource* store;
158 char name[MAX_NAME_LENGTH];
159 std::vector<std::string> storage_resource_names;
160
161 if (autochanger_only) {
162 StartPrompt(ua, _("The defined Autochanger Storage resources are:\n"));
163 } else {
164 StartPrompt(ua, _("The defined Storage resources are:\n"));
165 }
166
167 LockRes(my_config);
168 foreach_res (store, R_STORAGE) {
169 if (ua->AclAccessOk(Storage_ACL, store->resource_name_)) {
170 if (autochanger_only && !store->autochanger) {
171 continue;
172 } else {
173 storage_resource_names.emplace_back(store->resource_name_);
174 }
175 }
176 }
177 UnlockRes(my_config);
178
179 SortCaseInsensitive(storage_resource_names);
180
181 for (auto& resource_name : storage_resource_names) {
182 AddPrompt(ua, std::move(resource_name));
183 }
184
185 if (DoPrompt(ua, _("Storage"), _("Select Storage resource"), name,
186 sizeof(name)) < 0) {
187 return NULL;
188 }
189 store = ua->GetStoreResWithName(name);
190
191 return store;
192 }
193
194 /**
195 * Select a FileSet resource from prompt list
196 */
select_fileset_resource(UaContext * ua)197 FilesetResource* select_fileset_resource(UaContext* ua)
198 {
199 FilesetResource* fs;
200 char name[MAX_NAME_LENGTH];
201 std::vector<std::string> fileset_resource_names;
202
203 StartPrompt(ua, _("The defined FileSet resources are:\n"));
204
205 LockRes(my_config);
206 foreach_res (fs, R_FILESET) {
207 if (ua->AclAccessOk(FileSet_ACL, fs->resource_name_)) {
208 fileset_resource_names.emplace_back(fs->resource_name_);
209 }
210 }
211 UnlockRes(my_config);
212
213 SortCaseInsensitive(fileset_resource_names);
214
215 for (auto& resource_name : fileset_resource_names) {
216 AddPrompt(ua, std::move(resource_name));
217 }
218
219 if (DoPrompt(ua, _("FileSet"), _("Select FileSet resource"), name,
220 sizeof(name)) < 0) {
221 return NULL;
222 }
223
224 fs = ua->GetFileSetResWithName(name);
225
226 return fs;
227 }
228
229 /**
230 * Get a catalog resource from prompt list
231 */
get_catalog_resource(UaContext * ua)232 CatalogResource* get_catalog_resource(UaContext* ua)
233 {
234 CatalogResource* catalog = NULL;
235 char name[MAX_NAME_LENGTH];
236
237 for (int i = 1; i < ua->argc; i++) {
238 if (Bstrcasecmp(ua->argk[i], NT_("catalog")) && ua->argv[i]) {
239 catalog = ua->GetCatalogResWithName(ua->argv[i]);
240 if (catalog) { break; }
241 }
242 }
243
244 if (ua->gui && !catalog) {
245 LockRes(my_config);
246 catalog = (CatalogResource*)my_config->GetNextRes(R_CATALOG, NULL);
247 UnlockRes(my_config);
248
249 if (!catalog) {
250 ua->ErrorMsg(_("Could not find a Catalog resource\n"));
251 return NULL;
252 } else if (!ua->AclAccessOk(Catalog_ACL, catalog->resource_name_)) {
253 ua->ErrorMsg(
254 _("You must specify a \"use <catalog-name>\" command before "
255 "continuing.\n"));
256 return NULL;
257 }
258
259 return catalog;
260 }
261
262 if (!catalog) {
263 StartPrompt(ua, _("The defined Catalog resources are:\n"));
264
265 LockRes(my_config);
266 foreach_res (catalog, R_CATALOG) {
267 if (ua->AclAccessOk(Catalog_ACL, catalog->resource_name_)) {
268 AddPrompt(ua, catalog->resource_name_);
269 }
270 }
271 UnlockRes(my_config);
272
273 if (DoPrompt(ua, _("Catalog"), _("Select Catalog resource"), name,
274 sizeof(name)) < 0) {
275 return NULL;
276 }
277
278 catalog = ua->GetCatalogResWithName(name);
279 }
280
281 return catalog;
282 }
283
284 /**
285 * Select a job to enable or disable
286 */
select_enable_disable_job_resource(UaContext * ua,bool enable)287 JobResource* select_enable_disable_job_resource(UaContext* ua, bool enable)
288 {
289 JobResource* job;
290 char name[MAX_NAME_LENGTH];
291 std::vector<std::string> job_resource_names;
292
293 StartPrompt(ua, _("The defined Job resources are:\n"));
294
295 LockRes(my_config);
296 foreach_res (job, R_JOB) {
297 if (!ua->AclAccessOk(Job_ACL, job->resource_name_)) { continue; }
298 if (job->enabled == enable) { /* Already enabled/disabled? */
299 continue; /* yes, skip */
300 }
301 job_resource_names.emplace_back(job->resource_name_);
302 }
303 UnlockRes(my_config);
304
305 SortCaseInsensitive(job_resource_names);
306
307 for (auto& resource_name : job_resource_names) {
308 AddPrompt(ua, std::move(resource_name));
309 }
310
311 if (DoPrompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) <
312 0) {
313 return NULL;
314 }
315
316 job = ua->GetJobResWithName(name);
317
318 return job;
319 }
320
321 /**
322 * Select a Job resource from prompt list
323 */
select_job_resource(UaContext * ua)324 JobResource* select_job_resource(UaContext* ua)
325 {
326 JobResource* job;
327 char name[MAX_NAME_LENGTH];
328 std::vector<std::string> job_resource_names;
329
330 StartPrompt(ua, _("The defined Job resources are:\n"));
331
332 LockRes(my_config);
333 foreach_res (job, R_JOB) {
334 if (ua->AclAccessOk(Job_ACL, job->resource_name_)) {
335 job_resource_names.emplace_back(job->resource_name_);
336 }
337 }
338 UnlockRes(my_config);
339
340 SortCaseInsensitive(job_resource_names);
341
342 for (auto& resource_name : job_resource_names) {
343 AddPrompt(ua, std::move(resource_name));
344 }
345
346 if (DoPrompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) <
347 0) {
348 return NULL;
349 }
350
351 job = ua->GetJobResWithName(name);
352
353 return job;
354 }
355
356 /**
357 * Select a Restore Job resource from argument or prompt
358 */
get_restore_job(UaContext * ua)359 JobResource* get_restore_job(UaContext* ua)
360 {
361 int i;
362 JobResource* job;
363
364 i = FindArgWithValue(ua, NT_("restorejob"));
365 if (i >= 0) {
366 job = ua->GetJobResWithName(ua->argv[i]);
367 if (job && job->JobType == JT_RESTORE) { return job; }
368 ua->ErrorMsg(_("Error: Restore Job resource \"%s\" does not exist.\n"),
369 ua->argv[i]);
370 }
371
372 return select_restore_job_resource(ua);
373 }
374
375 /**
376 * Select a Restore Job resource from prompt list
377 */
select_restore_job_resource(UaContext * ua)378 JobResource* select_restore_job_resource(UaContext* ua)
379 {
380 JobResource* job;
381 char name[MAX_NAME_LENGTH];
382 std::vector<std::string> restore_job_names;
383
384 StartPrompt(ua, _("The defined Restore Job resources are:\n"));
385
386 LockRes(my_config);
387 foreach_res (job, R_JOB) {
388 if (job->JobType == JT_RESTORE &&
389 ua->AclAccessOk(Job_ACL, job->resource_name_)) {
390 restore_job_names.emplace_back(job->resource_name_);
391 }
392 }
393 UnlockRes(my_config);
394
395 SortCaseInsensitive(restore_job_names);
396
397 for (auto& resource_name : restore_job_names) {
398 AddPrompt(ua, std::move(resource_name));
399 }
400
401 if (DoPrompt(ua, _("Job"), _("Select Restore Job"), name, sizeof(name)) < 0) {
402 return NULL;
403 }
404
405 job = ua->GetJobResWithName(name);
406
407 return job;
408 }
409
410 /**
411 * Select a client resource from prompt list
412 */
select_client_resource(UaContext * ua)413 ClientResource* select_client_resource(UaContext* ua)
414 {
415 ClientResource* client;
416 char name[MAX_NAME_LENGTH];
417 std::vector<std::string> client_resource_names;
418
419 StartPrompt(ua, _("The defined Client resources are:\n"));
420
421 LockRes(my_config);
422 foreach_res (client, R_CLIENT) {
423 if (ua->AclAccessOk(Client_ACL, client->resource_name_)) {
424 client_resource_names.emplace_back(client->resource_name_);
425 }
426 }
427 UnlockRes(my_config);
428
429 SortCaseInsensitive(client_resource_names);
430
431 for (auto& resource_name : client_resource_names) {
432 AddPrompt(ua, std::move(resource_name));
433 }
434
435 if (DoPrompt(ua, _("Client"), _("Select Client (File daemon) resource"), name,
436 sizeof(name)) < 0) {
437 return NULL;
438 }
439
440 client = ua->GetClientResWithName(name);
441
442 return client;
443 }
444
445 /**
446 * Select a client to enable or disable
447 */
select_enable_disable_client_resource(UaContext * ua,bool enable)448 ClientResource* select_enable_disable_client_resource(UaContext* ua,
449 bool enable)
450 {
451 ClientResource* client;
452 char name[MAX_NAME_LENGTH];
453 std::vector<std::string> client_resource_names;
454
455 StartPrompt(ua, _("The defined Client resources are:\n"));
456
457 LockRes(my_config);
458 foreach_res (client, R_CLIENT) {
459 if (!ua->AclAccessOk(Client_ACL, client->resource_name_)) { continue; }
460 if (client->enabled == enable) { /* Already enabled/disabled? */
461 continue; /* yes, skip */
462 }
463 client_resource_names.emplace_back(client->resource_name_);
464 }
465 UnlockRes(my_config);
466
467 SortCaseInsensitive(client_resource_names);
468
469 for (auto& resource_name : client_resource_names) {
470 AddPrompt(ua, std::move(resource_name));
471 }
472
473 if (DoPrompt(ua, _("Client"), _("Select Client resource"), name,
474 sizeof(name)) < 0) {
475 return NULL;
476 }
477
478 client = ua->GetClientResWithName(name);
479
480 return client;
481 }
482
483 /**
484 * Get client resource, start by looking for
485 * client=<client-name>
486 * if we don't find the keyword, we prompt the user.
487 */
get_client_resource(UaContext * ua)488 ClientResource* get_client_resource(UaContext* ua)
489 {
490 ClientResource* client = NULL;
491
492 for (int i = 1; i < ua->argc; i++) {
493 if ((Bstrcasecmp(ua->argk[i], NT_("client")) ||
494 Bstrcasecmp(ua->argk[i], NT_("fd"))) &&
495 ua->argv[i]) {
496 client = ua->GetClientResWithName(ua->argv[i]);
497 if (client) { return client; }
498
499 ua->ErrorMsg(_("Error: Client resource %s does not exist.\n"),
500 ua->argv[i]);
501
502 break;
503 }
504 }
505
506 return select_client_resource(ua);
507 }
508
509 /**
510 * Select a schedule to enable or disable
511 */
select_enable_disable_schedule_resource(UaContext * ua,bool enable)512 ScheduleResource* select_enable_disable_schedule_resource(UaContext* ua,
513 bool enable)
514 {
515 ScheduleResource* sched;
516 char name[MAX_NAME_LENGTH];
517 std::vector<std::string> schedule_resource_names;
518
519 StartPrompt(ua, _("The defined Schedule resources are:\n"));
520
521 LockRes(my_config);
522 foreach_res (sched, R_SCHEDULE) {
523 if (!ua->AclAccessOk(Schedule_ACL, sched->resource_name_)) { continue; }
524 if (sched->enabled == enable) { /* Already enabled/disabled? */
525 continue; /* yes, skip */
526 }
527 schedule_resource_names.emplace_back(sched->resource_name_);
528 }
529 UnlockRes(my_config);
530
531 SortCaseInsensitive(schedule_resource_names);
532
533 for (auto& resource_name : schedule_resource_names) {
534 AddPrompt(ua, std::move(resource_name));
535 }
536
537 if (DoPrompt(ua, _("Schedule"), _("Select Schedule resource"), name,
538 sizeof(name)) < 0) {
539 return NULL;
540 }
541
542 sched = ua->GetScheduleResWithName(name);
543
544 return sched;
545 }
546
547 /**
548 * Scan what the user has entered looking for:
549 *
550 * client=<client-name>
551 *
552 * If error or not found, put up a list of client DBRs to choose from.
553 *
554 * returns: false on error
555 * true on success and fills in ClientDbRecord
556 */
GetClientDbr(UaContext * ua,ClientDbRecord * cr)557 bool GetClientDbr(UaContext* ua, ClientDbRecord* cr)
558 {
559 if (cr->Name[0]) { /* If name already supplied */
560 if (ua->db->GetClientRecord(ua->jcr, cr)) { return true; }
561 ua->ErrorMsg(_("Could not find Client %s: ERR=%s"), cr->Name,
562 ua->db->strerror());
563 }
564
565 for (int i = 1; i < ua->argc; i++) {
566 if ((Bstrcasecmp(ua->argk[i], NT_("client")) ||
567 Bstrcasecmp(ua->argk[i], NT_("fd"))) &&
568 ua->argv[i]) {
569 if (!ua->AclAccessOk(Client_ACL, ua->argv[i])) { break; }
570 bstrncpy(cr->Name, ua->argv[i], sizeof(cr->Name));
571 if (!ua->db->GetClientRecord(ua->jcr, cr)) {
572 ua->ErrorMsg(_("Could not find Client \"%s\": ERR=%s"), ua->argv[i],
573 ua->db->strerror());
574 cr->ClientId = 0;
575 break;
576 }
577 return true;
578 }
579 }
580
581 if (!SelectClientDbr(ua, cr)) { /* try once more by proposing a list */
582 return false;
583 }
584
585 return true;
586 }
587
588 /**
589 * Select a Client record from the catalog
590 *
591 * Returns true on success
592 * false on failure
593 */
SelectClientDbr(UaContext * ua,ClientDbRecord * cr)594 bool SelectClientDbr(UaContext* ua, ClientDbRecord* cr)
595 {
596 DBId_t* ids = nullptr;
597 ClientDbRecord ocr;
598 int num_clients;
599 char name[MAX_NAME_LENGTH];
600
601 cr->ClientId = 0;
602 if (!ua->db->GetClientIds(ua->jcr, &num_clients, &ids)) {
603 ua->ErrorMsg(_("Error obtaining client ids. ERR=%s\n"), ua->db->strerror());
604 if (ids) { free(ids); }
605 return false;
606 }
607
608 if (num_clients <= 0) {
609 ua->ErrorMsg(_(
610 "No clients defined. You must run a job before using this command.\n"));
611 if (ids) { free(ids); }
612 return false;
613 }
614
615 StartPrompt(ua, _("Defined Clients:\n"));
616 for (int i = 0; i < num_clients; i++) {
617 ocr.ClientId = ids[i];
618 if (!ua->db->GetClientRecord(ua->jcr, &ocr) ||
619 !ua->AclAccessOk(Client_ACL, ocr.Name)) {
620 continue;
621 }
622 AddPrompt(ua, ocr.Name);
623 }
624 if (ids) { free(ids); }
625
626 if (DoPrompt(ua, _("Client"), _("Select the Client"), name, sizeof(name)) <
627 0) {
628 return false;
629 }
630
631 new (&ocr) ClientDbRecord();
632 bstrncpy(ocr.Name, name, sizeof(ocr.Name));
633
634 if (!ua->db->GetClientRecord(ua->jcr, &ocr)) {
635 ua->ErrorMsg(_("Could not find Client \"%s\": ERR=%s"), name,
636 ua->db->strerror());
637 return false;
638 }
639
640 memcpy(cr, &ocr, sizeof(ocr));
641
642 return true;
643 }
644
645 /**
646 * Scan what the user has entered looking for:
647 *
648 * argk=<storage-name>
649 *
650 * where argk can be : storage
651 *
652 * If error or not found, put up a list of storage DBRs to choose from.
653 *
654 * returns: false on error
655 * true on success and fills in StorageDbRecord
656 */
GetStorageDbr(UaContext * ua,StorageDbRecord * sr,const char * argk)657 bool GetStorageDbr(UaContext* ua, StorageDbRecord* sr, const char* argk)
658 {
659 if (sr->Name[0]) { /* If name already supplied */
660 if (ua->db->GetStorageRecord(ua->jcr, sr) &&
661 ua->AclAccessOk(Pool_ACL, sr->Name)) {
662 return true;
663 }
664 ua->ErrorMsg(_("Could not find Storage \"%s\": ERR=%s"), sr->Name,
665 ua->db->strerror());
666 }
667
668 if (!SelectStorageDbr(ua, sr, argk)) { /* try once more */
669 return false;
670 }
671
672 return true;
673 }
674
675 /**
676 * Scan what the user has entered looking for:
677 *
678 * argk=<pool-name>
679 *
680 * where argk can be : pool, recyclepool, scratchpool, nextpool etc..
681 *
682 * If error or not found, put up a list of pool DBRs to choose from.
683 *
684 * returns: false on error
685 * true on success and fills in PoolDbRecord
686 */
GetPoolDbr(UaContext * ua,PoolDbRecord * pr,const char * argk)687 bool GetPoolDbr(UaContext* ua, PoolDbRecord* pr, const char* argk)
688 {
689 if (pr->Name[0]) { /* If name already supplied */
690 if (ua->db->GetPoolRecord(ua->jcr, pr) &&
691 ua->AclAccessOk(Pool_ACL, pr->Name)) {
692 return true;
693 }
694 ua->ErrorMsg(_("Could not find Pool \"%s\": ERR=%s"), pr->Name,
695 ua->db->strerror());
696 }
697
698 if (!SelectPoolDbr(ua, pr, argk)) { /* try once more */
699 return false;
700 }
701
702 return true;
703 }
704
705 /**
706 * Select a Pool record from catalog
707 * argk can be pool, recyclepool, scratchpool etc..
708 */
SelectPoolDbr(UaContext * ua,PoolDbRecord * pr,const char * argk)709 bool SelectPoolDbr(UaContext* ua, PoolDbRecord* pr, const char* argk)
710 {
711 PoolDbRecord opr;
712 DBId_t* ids = nullptr;
713 int num_pools;
714 char name[MAX_NAME_LENGTH];
715
716 for (int i = 1; i < ua->argc; i++) {
717 if (Bstrcasecmp(ua->argk[i], argk) && ua->argv[i] &&
718 ua->AclAccessOk(Pool_ACL, ua->argv[i])) {
719 bstrncpy(pr->Name, ua->argv[i], sizeof(pr->Name));
720 if (!ua->db->GetPoolRecord(ua->jcr, pr)) {
721 ua->ErrorMsg(_("Could not find Pool \"%s\": ERR=%s"), ua->argv[i],
722 ua->db->strerror());
723 pr->PoolId = 0;
724 break;
725 }
726 return true;
727 }
728 }
729
730 pr->PoolId = 0;
731 if (!ua->db->GetPoolIds(ua->jcr, &num_pools, &ids)) {
732 ua->ErrorMsg(_("Error obtaining pool ids. ERR=%s\n"), ua->db->strerror());
733 if (ids) { free(ids); }
734 return 0;
735 }
736
737 if (num_pools <= 0) {
738 ua->ErrorMsg(
739 _("No pools defined. Use the \"create\" command to create one.\n"));
740 if (ids) { free(ids); }
741 return false;
742 }
743
744 StartPrompt(ua, _("Defined Pools:\n"));
745 if (bstrcmp(argk, NT_("recyclepool"))) { AddPrompt(ua, _("*None*")); }
746
747 for (int i = 0; i < num_pools; i++) {
748 opr.PoolId = ids[i];
749 if (!ua->db->GetPoolRecord(ua->jcr, &opr) ||
750 !ua->AclAccessOk(Pool_ACL, opr.Name)) {
751 continue;
752 }
753 AddPrompt(ua, opr.Name);
754 }
755 if (ids) { free(ids); }
756
757 if (DoPrompt(ua, _("Pool"), _("Select the Pool"), name, sizeof(name)) < 0) {
758 return false;
759 }
760
761 new (&opr) PoolDbRecord(); // placement new instead of memset
762
763 /*
764 * *None* is only returned when selecting a recyclepool, and in that case
765 * the calling code is only interested in opr.Name, so then we can leave
766 * pr as all zero.
767 */
768 if (!bstrcmp(name, _("*None*"))) {
769 bstrncpy(opr.Name, name, sizeof(opr.Name));
770
771 if (!ua->db->GetPoolRecord(ua->jcr, &opr)) {
772 ua->ErrorMsg(_("Could not find Pool \"%s\": ERR=%s"), name,
773 ua->db->strerror());
774 return false;
775 }
776 }
777
778 memcpy(pr, &opr, sizeof(opr));
779
780 return true;
781 }
782
783 /**
784 * Select a Pool and a Media (Volume) record from the database
785 */
SelectPoolAndMediaDbr(UaContext * ua,PoolDbRecord * pr,MediaDbRecord * mr)786 bool SelectPoolAndMediaDbr(UaContext* ua, PoolDbRecord* pr, MediaDbRecord* mr)
787 {
788 if (!SelectMediaDbr(ua, mr)) { return false; }
789
790 new (pr) PoolDbRecord(); // placement new instead of memset
791
792 pr->PoolId = mr->PoolId;
793 if (!ua->db->GetPoolRecord(ua->jcr, pr)) {
794 ua->ErrorMsg("%s", ua->db->strerror());
795 return false;
796 }
797
798 if (!ua->AclAccessOk(Pool_ACL, pr->Name, true)) {
799 ua->ErrorMsg(_("No access to Pool \"%s\"\n"), pr->Name);
800 return false;
801 }
802
803 return true;
804 }
805
806 /**
807 * Select a Storage record from catalog
808 * argk can be storage
809 */
SelectStorageDbr(UaContext * ua,StorageDbRecord * sr,const char * argk)810 bool SelectStorageDbr(UaContext* ua, StorageDbRecord* sr, const char* argk)
811 {
812 StorageDbRecord osr;
813 DBId_t* ids = nullptr;
814 int num_storages;
815 char name[MAX_NAME_LENGTH];
816
817 for (int i = 1; i < ua->argc; i++) {
818 if (Bstrcasecmp(ua->argk[i], argk) && ua->argv[i] &&
819 ua->AclAccessOk(Storage_ACL, ua->argv[i])) {
820 bstrncpy(sr->Name, ua->argv[i], sizeof(sr->Name));
821 if (!ua->db->GetStorageRecord(ua->jcr, sr)) {
822 ua->ErrorMsg(_("Could not find Storage \"%s\": ERR=%s"), ua->argv[i],
823 ua->db->strerror());
824 sr->StorageId = 0;
825 break;
826 }
827 return true;
828 }
829 }
830
831 sr->StorageId = 0;
832 if (!ua->db->GetStorageIds(ua->jcr, &num_storages, &ids)) {
833 ua->ErrorMsg(_("Error obtaining storage ids. ERR=%s\n"),
834 ua->db->strerror());
835 if (ids) { free(ids); }
836 return 0;
837 }
838
839 if (num_storages <= 0) {
840 ua->ErrorMsg(_("No storages defined.\n"));
841 if (ids) { free(ids); }
842 return false;
843 }
844
845 StartPrompt(ua, _("Defined Storages:\n"));
846 if (bstrcmp(argk, NT_("recyclestorage"))) { AddPrompt(ua, _("*None*")); }
847
848 for (int i = 0; i < num_storages; i++) {
849 osr.StorageId = ids[i];
850 if (!ua->db->GetStorageRecord(ua->jcr, &osr) ||
851 !ua->AclAccessOk(Storage_ACL, osr.Name)) {
852 continue;
853 }
854 AddPrompt(ua, osr.Name);
855 }
856 if (ids) { free(ids); }
857
858 if (DoPrompt(ua, _("Storage"), _("Select the Storage"), name, sizeof(name)) <
859 0) {
860 return false;
861 }
862
863 new (&osr) StorageDbRecord(); // placement new instead of memset
864
865 /*
866 * *None* is only returned when selecting a recyclestorage, and in that case
867 * the calling code is only interested in osr.Name, so then we can leave
868 * sr as all zero.
869 */
870 if (!bstrcmp(name, _("*None*"))) {
871 bstrncpy(osr.Name, name, sizeof(osr.Name));
872
873 if (!ua->db->GetStorageRecord(ua->jcr, &osr)) {
874 ua->ErrorMsg(_("Could not find Storage \"%s\": ERR=%s"), name,
875 ua->db->strerror());
876 return false;
877 }
878 }
879
880 memcpy(sr, &osr, sizeof(osr));
881
882 return true;
883 }
884
885 /**
886 * Select a Media (Volume) record from the database
887 */
SelectMediaDbr(UaContext * ua,MediaDbRecord * mr)888 bool SelectMediaDbr(UaContext* ua, MediaDbRecord* mr)
889 {
890 int i;
891 int retval = false;
892 POOLMEM* err = GetPoolMemory(PM_FNAME);
893
894 *err = 0;
895 new (mr) MediaDbRecord(); // placement new instead of memset
896 i = FindArgWithValue(ua, NT_("volume"));
897 if (i >= 0) {
898 if (IsNameValid(ua->argv[i], err)) {
899 bstrncpy(mr->VolumeName, ua->argv[i], sizeof(mr->VolumeName));
900 } else {
901 goto bail_out;
902 }
903 }
904
905 if (mr->VolumeName[0] == 0) {
906 PoolDbRecord pr;
907 /*
908 * Get the pool from pool=<pool-name>
909 */
910 if (!GetPoolDbr(ua, &pr)) { goto bail_out; }
911
912 mr->PoolId = pr.PoolId;
913 ua->db->ListMediaRecords(ua->jcr, mr, NULL, false, ua->send, HORZ_LIST);
914
915 ua->SendMsg(_("Enter the volume name or MediaId of the volume prefixed with an asterisk (*).\n"));
916 if (!GetCmd(ua, _("E.g. \"full-0001\" or \"*42\": "))) { goto bail_out; }
917
918 if (ua->cmd[0] == '*' && Is_a_number(ua->cmd + 1)) {
919 mr->MediaId = str_to_int64(ua->cmd + 1);
920 } else if (IsNameValid(ua->cmd, err)) {
921 bstrncpy(mr->VolumeName, ua->cmd, sizeof(mr->VolumeName));
922 } else {
923 goto bail_out;
924 }
925 }
926
927 if (!ua->db->GetMediaRecord(ua->jcr, mr)) {
928 PmStrcpy(err, ua->db->strerror());
929 goto bail_out;
930 }
931 retval = true;
932
933 bail_out:
934 if (!retval && *err) { ua->ErrorMsg("%s", err); }
935 FreePoolMemory(err);
936
937 return retval;
938 }
939
940 /**
941 * Select a pool resource from prompt list
942 */
select_pool_resource(UaContext * ua)943 PoolResource* select_pool_resource(UaContext* ua)
944 {
945 PoolResource* pool;
946 char name[MAX_NAME_LENGTH];
947 std::vector<std::string> pool_resource_names;
948
949 StartPrompt(ua, _("The defined Pool resources are:\n"));
950 LockRes(my_config);
951 foreach_res (pool, R_POOL) {
952 if (ua->AclAccessOk(Pool_ACL, pool->resource_name_)) {
953 pool_resource_names.emplace_back(pool->resource_name_);
954 }
955 }
956 UnlockRes(my_config);
957
958 SortCaseInsensitive(pool_resource_names);
959
960 for (auto& resource_name : pool_resource_names) {
961 AddPrompt(ua, std::move(resource_name));
962 }
963
964 if (DoPrompt(ua, _("Pool"), _("Select Pool resource"), name, sizeof(name)) <
965 0) {
966 return NULL;
967 }
968
969 pool = ua->GetPoolResWithName(name);
970
971 return pool;
972 }
973
974 /**
975 * If you are thinking about using it, you
976 * probably want to use SelectPoolDbr()
977 * or GetPoolDbr() above.
978 */
get_pool_resource(UaContext * ua)979 PoolResource* get_pool_resource(UaContext* ua)
980 {
981 int i;
982 PoolResource* pool = NULL;
983
984 i = FindArgWithValue(ua, NT_("pool"));
985 if (i >= 0 && ua->AclAccessOk(Pool_ACL, ua->argv[i])) {
986 pool = ua->GetPoolResWithName(ua->argv[i]);
987 if (pool) { return pool; }
988 ua->ErrorMsg(_("Error: Pool resource \"%s\" does not exist.\n"),
989 ua->argv[i]);
990 }
991
992 return select_pool_resource(ua);
993 }
994
995 /**
996 * List all jobs and ask user to select one
997 */
SelectJobDbr(UaContext * ua,JobDbRecord * jr)998 int SelectJobDbr(UaContext* ua, JobDbRecord* jr)
999 {
1000 ua->db->ListJobRecords(ua->jcr, jr, "", NULL, 0, 0, NULL, NULL, 0, 0, 0,
1001 ua->send, HORZ_LIST);
1002 if (!GetPint(ua, _("Enter the JobId to select: "))) { return 0; }
1003
1004 jr->JobId = ua->int64_val;
1005 if (!ua->db->GetJobRecord(ua->jcr, jr)) {
1006 ua->ErrorMsg("%s", ua->db->strerror());
1007 return 0;
1008 }
1009
1010 return jr->JobId;
1011 }
1012
1013 /**
1014 * Scan what the user has entered looking for:
1015 *
1016 * jobid=nn
1017 *
1018 * if error or not found, put up a list of Jobs
1019 * to choose from.
1020 *
1021 * returns: 0 on error
1022 * JobId on success and fills in JobDbRecord
1023 */
GetJobDbr(UaContext * ua,JobDbRecord * jr)1024 int GetJobDbr(UaContext* ua, JobDbRecord* jr)
1025 {
1026 int i;
1027
1028 for (i = 1; i < ua->argc; i++) {
1029 if (Bstrcasecmp(ua->argk[i], NT_("ujobid")) && ua->argv[i]) {
1030 jr->JobId = 0;
1031 bstrncpy(jr->Job, ua->argv[i], sizeof(jr->Job));
1032 } else if (Bstrcasecmp(ua->argk[i], NT_("jobid")) && ua->argv[i]) {
1033 jr->JobId = str_to_int64(ua->argv[i]);
1034 jr->Job[0] = 0;
1035 } else {
1036 continue;
1037 }
1038 if (!ua->db->GetJobRecord(ua->jcr, jr)) {
1039 ua->ErrorMsg(_("Could not find Job \"%s\": ERR=%s"), ua->argv[i],
1040 ua->db->strerror());
1041 jr->JobId = 0;
1042 break;
1043 }
1044 return jr->JobId;
1045 }
1046
1047 jr->JobId = 0;
1048 jr->Job[0] = 0;
1049
1050 for (i = 1; i < ua->argc; i++) {
1051 if ((Bstrcasecmp(ua->argk[i], NT_("jobname")) ||
1052 Bstrcasecmp(ua->argk[i], NT_("job"))) &&
1053 ua->argv[i]) {
1054 jr->JobId = 0;
1055 bstrncpy(jr->Name, ua->argv[i], sizeof(jr->Name));
1056 break;
1057 }
1058 }
1059
1060 if (!SelectJobDbr(ua, jr)) { /* try once more */
1061 return 0;
1062 }
1063
1064 return jr->JobId;
1065 }
1066
1067 /**
1068 * Implement unique set of prompts
1069 */
StartPrompt(UaContext * ua,const char * msg)1070 void StartPrompt(UaContext* ua, const char* msg)
1071 {
1072 if (ua->max_prompts == 0) {
1073 ua->max_prompts = 10;
1074 ua->prompt = (char**)malloc(sizeof(char*) * ua->max_prompts);
1075 }
1076 ua->num_prompts = 1;
1077 ua->prompt[0] = strdup(msg);
1078 }
1079
1080 /**
1081 * Add to prompts -- keeping them unique
1082 */
AddPrompt(UaContext * ua,const char * prompt)1083 void AddPrompt(UaContext* ua, const char* prompt)
1084 {
1085 if (ua->num_prompts == ua->max_prompts) {
1086 ua->max_prompts *= 2;
1087 ua->prompt = (char**)realloc(ua->prompt, sizeof(char*) * ua->max_prompts);
1088 }
1089
1090 for (int i = 1; i < ua->num_prompts; i++) {
1091 if (bstrcmp(ua->prompt[i], prompt)) { return; }
1092 }
1093
1094 ua->prompt[ua->num_prompts++] = strdup(prompt);
1095 }
1096
AddPrompt(UaContext * ua,std::string && prompt)1097 void AddPrompt(UaContext* ua, std::string&& prompt)
1098 {
1099 std::string p{prompt};
1100 AddPrompt(ua, p.c_str());
1101 }
1102
1103 /**
1104 * Display prompts and get user's choice
1105 *
1106 * Returns: -1 on error
1107 * index base 0 on success, and choice is copied to prompt if not NULL
1108 * prompt is set to the chosen prompt item string
1109 */
DoPrompt(UaContext * ua,const char * automsg,const char * msg,char * prompt,int max_prompt)1110 int DoPrompt(UaContext* ua,
1111 const char* automsg,
1112 const char* msg,
1113 char* prompt,
1114 int max_prompt)
1115 {
1116 int item;
1117 PoolMem pmsg(PM_MESSAGE);
1118 BareosSocket* user = ua->UA_sock;
1119
1120 if (prompt) { *prompt = 0; }
1121 if (ua->num_prompts == 2) {
1122 item = 1;
1123 if (prompt) { bstrncpy(prompt, ua->prompt[1], max_prompt); }
1124 if (!ua->api && !ua->runscript) {
1125 ua->SendMsg(_("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
1126 }
1127 goto done;
1128 }
1129
1130 /*
1131 * If running non-interactive, bail out
1132 */
1133 if (ua->batch) {
1134 /*
1135 * First print the choices he wanted to make
1136 */
1137 ua->SendMsg(ua->prompt[0]);
1138 for (int i = 1; i < ua->num_prompts; i++) {
1139 ua->SendMsg("%6d: %s\n", i, ua->prompt[i]);
1140 }
1141
1142 /*
1143 * Now print error message
1144 */
1145 ua->SendMsg(_("Your request has multiple choices for \"%s\". Selection is "
1146 "not possible in batch mode.\n"),
1147 automsg);
1148 item = -1;
1149 goto done;
1150 }
1151
1152 if (ua->api) { user->signal(BNET_START_SELECT); }
1153
1154 ua->SendMsg(ua->prompt[0]);
1155 for (int i = 1; i < ua->num_prompts; i++) {
1156 if (ua->api) {
1157 ua->SendMsg("%s", ua->prompt[i]);
1158 } else {
1159 ua->SendMsg("%6d: %s\n", i, ua->prompt[i]);
1160 }
1161 }
1162
1163 if (ua->api) { user->signal(BNET_END_SELECT); }
1164
1165 while (1) {
1166 /*
1167 * First item is the prompt string, not the items
1168 */
1169 if (ua->num_prompts == 1) {
1170 ua->ErrorMsg(_("Selection list for \"%s\" is empty!\n"), automsg);
1171 item = -1; /* list is empty ! */
1172 break;
1173 }
1174 if (ua->num_prompts == 2) {
1175 item = 1;
1176 ua->SendMsg(_("Automatically selected: %s\n"), ua->prompt[1]);
1177 if (prompt) { bstrncpy(prompt, ua->prompt[1], max_prompt); }
1178 break;
1179 } else {
1180 Mmsg(pmsg, "%s (1-%d): ", msg, ua->num_prompts - 1);
1181 }
1182
1183 /*
1184 * Either a . or an @ will get you out of the loop
1185 */
1186 if (ua->api) { user->signal(BNET_SELECT_INPUT); }
1187
1188 if (!GetPint(ua, pmsg.c_str())) {
1189 item = -1; /* error */
1190 ua->InfoMsg(_("Selection aborted, nothing done.\n"));
1191 break;
1192 }
1193 item = ua->pint32_val;
1194 if (item < 1 || item >= ua->num_prompts) {
1195 ua->WarningMsg(_("Please enter a number between 1 and %d\n"),
1196 ua->num_prompts - 1);
1197 continue;
1198 }
1199 if (prompt) { bstrncpy(prompt, ua->prompt[item], max_prompt); }
1200 break;
1201 }
1202
1203 done:
1204 for (int i = 0; i < ua->num_prompts; i++) { free(ua->prompt[i]); }
1205 ua->num_prompts = 0;
1206
1207 return (item > 0) ? (item - 1) : item;
1208 }
1209
1210 /**
1211 * We scan what the user has entered looking for :
1212 * - storage=<storage-resource>
1213 * - job=<job_name>
1214 * - jobid=<jobid>
1215 * - ? (prompt him with storage list)
1216 * - <some-error> (prompt him with storage list)
1217 *
1218 * If use_default is set, we assume that any keyword without a value
1219 * is the name of the Storage resource wanted.
1220 *
1221 * If autochangers_only is given, we limit the output to autochangers only.
1222 */
get_storage_resource(UaContext * ua,bool use_default,bool autochangers_only)1223 StorageResource* get_storage_resource(UaContext* ua,
1224 bool use_default,
1225 bool autochangers_only)
1226 {
1227 int i;
1228 JobControlRecord* jcr;
1229 int jobid;
1230 char ed1[50];
1231 char* StoreName = NULL;
1232 StorageResource* store = NULL;
1233
1234 Dmsg1(100, "get_storage_resource: autochangers_only is %d\n",
1235 autochangers_only);
1236
1237 for (i = 1; i < ua->argc; i++) {
1238 /*
1239 * Ignore any zapped keyword.
1240 */
1241 if (*ua->argk[i] == 0) { continue; }
1242 if (use_default && !ua->argv[i]) {
1243 /*
1244 * Ignore barcode, barcodes, encrypt, scan and slots keywords.
1245 */
1246 if (Bstrcasecmp("barcode", ua->argk[i]) ||
1247 Bstrcasecmp("barcodes", ua->argk[i]) ||
1248 Bstrcasecmp("encrypt", ua->argk[i]) ||
1249 Bstrcasecmp("scan", ua->argk[i]) ||
1250 Bstrcasecmp("slots", ua->argk[i])) {
1251 continue;
1252 }
1253 /*
1254 * Default argument is storage
1255 */
1256 if (StoreName) {
1257 ua->ErrorMsg(_("Storage name given twice.\n"));
1258 return NULL;
1259 }
1260 StoreName = ua->argk[i];
1261 if (*StoreName == '?') {
1262 *StoreName = 0;
1263 break;
1264 }
1265 } else {
1266 if (Bstrcasecmp(ua->argk[i], NT_("storage")) ||
1267 Bstrcasecmp(ua->argk[i], NT_("sd"))) {
1268 StoreName = ua->argv[i];
1269 break;
1270 } else if (Bstrcasecmp(ua->argk[i], NT_("jobid"))) {
1271 jobid = str_to_int64(ua->argv[i]);
1272 if (jobid <= 0) {
1273 ua->ErrorMsg(_("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
1274 return NULL;
1275 }
1276 if (!(jcr = get_jcr_by_id(jobid))) {
1277 ua->ErrorMsg(_("JobId %s is not running.\n"), edit_int64(jobid, ed1));
1278 return NULL;
1279 }
1280 store = jcr->impl->res.write_storage;
1281 FreeJcr(jcr);
1282 break;
1283 } else if (Bstrcasecmp(ua->argk[i], NT_("job")) ||
1284 Bstrcasecmp(ua->argk[i], NT_("jobname"))) {
1285 if (!ua->argv[i]) {
1286 ua->ErrorMsg(_("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
1287 return NULL;
1288 }
1289 if (!(jcr = get_jcr_by_partial_name(ua->argv[i]))) {
1290 ua->ErrorMsg(_("Job \"%s\" is not running.\n"), ua->argv[i]);
1291 return NULL;
1292 }
1293 store = jcr->impl->res.write_storage;
1294 FreeJcr(jcr);
1295 break;
1296 } else if (Bstrcasecmp(ua->argk[i], NT_("ujobid"))) {
1297 if (!ua->argv[i]) {
1298 ua->ErrorMsg(_("Expecting ujobid=xxx, got: %s.\n"), ua->argk[i]);
1299 return NULL;
1300 }
1301 if (!(jcr = get_jcr_by_full_name(ua->argv[i]))) {
1302 ua->ErrorMsg(_("Job \"%s\" is not running.\n"), ua->argv[i]);
1303 return NULL;
1304 }
1305 store = jcr->impl->res.write_storage;
1306 FreeJcr(jcr);
1307 break;
1308 }
1309 }
1310 }
1311
1312 if (store && !ua->AclAccessOk(Storage_ACL, store->resource_name_)) {
1313 store = NULL;
1314 }
1315
1316 if (!store && StoreName && StoreName[0] != 0) {
1317 store = ua->GetStoreResWithName(StoreName);
1318
1319 if (!store) {
1320 ua->ErrorMsg(_("Storage resource \"%s\": not found\n"), StoreName);
1321 }
1322 }
1323
1324 if (store && !ua->AclAccessOk(Storage_ACL, store->resource_name_)) {
1325 store = NULL;
1326 }
1327
1328 /*
1329 * No keywords found, so present a selection list
1330 */
1331 if (!store) { store = select_storage_resource(ua, autochangers_only); }
1332
1333 return store;
1334 }
1335
1336 /**
1337 * Get drive that we are working with for this storage
1338 */
GetStorageDrive(UaContext * ua,StorageResource * store)1339 drive_number_t GetStorageDrive(UaContext* ua, StorageResource* store)
1340 {
1341 int i;
1342 char drivename[10];
1343 drive_number_t drive = kInvalidDriveNumber;
1344
1345 /*
1346 * Get drive for autochanger if possible
1347 */
1348 i = FindArgWithValue(ua, NT_("drive"));
1349 if (i >= 0) {
1350 drive = static_cast<drive_number_t>(atoi(ua->argv[i]));
1351 } else if (store && store->autochanger) {
1352 drive_number_t drives;
1353
1354 drives = GetNumDrives(ua, store);
1355
1356 /*
1357 * If only one drive, default = 0
1358 */
1359 if (drives == 1) {
1360 drive = 0;
1361 } else {
1362 /*
1363 * Ask user to enter drive number
1364 */
1365 StartPrompt(ua, _("Select Drive:\n"));
1366 for (drive_number_t cnt = 0; cnt < drives; cnt++) {
1367 Bsnprintf(drivename, sizeof(drivename), "Drive %hd", cnt);
1368 AddPrompt(ua, drivename);
1369 }
1370 if (DoPrompt(ua, _("Drive"), _("Select drive"), drivename,
1371 sizeof(drivename)) < 0) {
1372 drive = kInvalidDriveNumber; /* None */
1373 } else {
1374 sscanf(drivename, "Drive %hd", &drive);
1375 }
1376 }
1377 } else {
1378 /*
1379 * If only one drive, default = 0
1380 */
1381 drive = 0;
1382 }
1383
1384 return drive;
1385 }
1386
1387 /**
1388 * Get slot that we are working with for this storage
1389 */
GetStorageSlot(UaContext * ua,StorageResource * store)1390 slot_number_t GetStorageSlot(UaContext* ua, StorageResource* store)
1391 {
1392 int i;
1393 slot_number_t slot = -1;
1394
1395 /*
1396 * Get slot for autochanger if possible
1397 */
1398 i = FindArgWithValue(ua, NT_("slot"));
1399 if (i >= 0) {
1400 slot = atoi(ua->argv[i]);
1401 } else if (store && store->autochanger) {
1402 /*
1403 * Ask user to enter slot number
1404 */
1405 ua->cmd[0] = 0;
1406 if (!GetCmd(ua, _("Enter autochanger slot: "))) {
1407 slot = -1; /* None */
1408 } else {
1409 slot = atoi(ua->cmd);
1410 }
1411 }
1412
1413 return slot;
1414 }
1415
1416 /**
1417 * Scan looking for mediatype=
1418 *
1419 * if not found or error, put up selection list
1420 *
1421 * Returns: 0 on error
1422 * 1 on success, MediaType is set
1423 */
GetMediaType(UaContext * ua,char * MediaType,int max_media)1424 int GetMediaType(UaContext* ua, char* MediaType, int max_media)
1425 {
1426 StorageResource* store;
1427 int i;
1428
1429 i = FindArgWithValue(ua, NT_("mediatype"));
1430 if (i >= 0) {
1431 bstrncpy(MediaType, ua->argv[i], max_media);
1432 return 1;
1433 }
1434
1435 StartPrompt(ua, _("Media Types defined in conf file:\n"));
1436
1437 LockRes(my_config);
1438 foreach_res (store, R_STORAGE) {
1439 if (ua->AclAccessOk(Storage_ACL, store->resource_name_)) {
1440 AddPrompt(ua, store->media_type);
1441 }
1442 }
1443 UnlockRes(my_config);
1444
1445 return (DoPrompt(ua, _("Media Type"), _("Select the Media Type"), MediaType,
1446 max_media) < 0)
1447 ? 0
1448 : 1;
1449 }
1450
GetLevelFromName(JobControlRecord * jcr,const char * level_name)1451 bool GetLevelFromName(JobControlRecord* jcr, const char* level_name)
1452 {
1453 bool found = false;
1454
1455 /*
1456 * Look up level name and pull code
1457 */
1458 for (int i = 0; joblevels[i].level_name; i++) {
1459 if (Bstrcasecmp(level_name, joblevels[i].level_name)) {
1460 jcr->setJobLevel(joblevels[i].level);
1461 found = true;
1462 break;
1463 }
1464 }
1465
1466 return found;
1467 }
1468
1469 /**
1470 * Insert an JobId into the list of selected JobIds when its a unique new id.
1471 */
InsertSelectedJobid(alist * selected_jobids,JobId_t JobId)1472 static inline bool InsertSelectedJobid(alist* selected_jobids, JobId_t JobId)
1473 {
1474 bool found;
1475 JobId_t* selected_jobid = nullptr;
1476
1477 found = false;
1478 foreach_alist (selected_jobid, selected_jobids) {
1479 if (*selected_jobid == JobId) {
1480 found = true;
1481 break;
1482 }
1483 }
1484
1485 if (!found) {
1486 selected_jobid = (JobId_t*)malloc(sizeof(JobId_t));
1487 *selected_jobid = JobId;
1488 selected_jobids->append(selected_jobid);
1489 return true;
1490 }
1491
1492 return false;
1493 }
1494
1495 /**
1496 * Get a job selection, "reason" is used in user messages and can be: cancel,
1497 * limit, ...
1498 *
1499 * Returns: NULL on error
1500 * alist on success with the selected jobids.
1501 */
select_jobs(UaContext * ua,const char * reason)1502 alist* select_jobs(UaContext* ua, const char* reason)
1503 {
1504 int i;
1505 int cnt = 0;
1506 int njobs = 0;
1507 JobControlRecord* jcr = NULL;
1508 bool select_all = false;
1509 bool select_by_state = false;
1510 alist* selected_jobids;
1511 const char* lst[] = {"job", "jobid", "ujobid", NULL};
1512 enum
1513 {
1514 none,
1515 all_jobs,
1516 created_jobs,
1517 blocked_jobs,
1518 waiting_jobs,
1519 running_jobs
1520 } selection_criterium;
1521
1522 /*
1523 * Allocate a list for holding the selected JobIds.
1524 */
1525 selected_jobids = new alist(10, owned_by_alist);
1526
1527 /*
1528 * See if "all" is given.
1529 */
1530 if (FindArg(ua, NT_("all")) > 0) { select_all = true; }
1531
1532 /*
1533 * See if "state=" is given.
1534 */
1535 if (FindArgWithValue(ua, NT_("state")) > 0) { select_by_state = true; }
1536
1537 /*
1538 * See if there are any jobid, job or ujobid keywords.
1539 */
1540 if (FindArgKeyword(ua, lst) > 0) {
1541 for (i = 1; i < ua->argc; i++) {
1542 if (Bstrcasecmp(ua->argk[i], NT_("jobid"))) {
1543 JobId_t JobId = str_to_int64(ua->argv[i]);
1544 if (!JobId) { continue; }
1545 if (!(jcr = get_jcr_by_id(JobId))) {
1546 ua->ErrorMsg(
1547 _("JobId %s is not running. Use Job name to %s inactive jobs.\n"),
1548 ua->argv[i], _(reason));
1549 continue;
1550 }
1551 } else if (Bstrcasecmp(ua->argk[i], NT_("job"))) {
1552 if (!ua->argv[i]) { continue; }
1553 if (!(jcr = get_jcr_by_partial_name(ua->argv[i]))) {
1554 ua->WarningMsg(
1555 _("Warning Job %s is not running. Continuing anyway ...\n"),
1556 ua->argv[i]);
1557 continue;
1558 }
1559 } else if (Bstrcasecmp(ua->argk[i], NT_("ujobid"))) {
1560 if (!ua->argv[i]) { continue; }
1561 if (!(jcr = get_jcr_by_full_name(ua->argv[i]))) {
1562 ua->WarningMsg(
1563 _("Warning Job %s is not running. Continuing anyway ...\n"),
1564 ua->argv[i]);
1565 continue;
1566 }
1567 }
1568
1569 if (jcr) {
1570 if (jcr->impl->res.job &&
1571 !ua->AclAccessOk(Job_ACL, jcr->impl->res.job->resource_name_,
1572 true)) {
1573 ua->ErrorMsg(_("Unauthorized command from this console.\n"));
1574 goto bail_out;
1575 }
1576
1577 if (InsertSelectedJobid(selected_jobids, jcr->JobId)) { cnt++; }
1578
1579 FreeJcr(jcr);
1580 jcr = NULL;
1581 }
1582 }
1583 }
1584
1585 /*
1586 * If we didn't select any Jobs using jobid, job or ujobid keywords try other
1587 * selections.
1588 */
1589 if (cnt == 0) {
1590 char buf[1000];
1591 int tjobs = 0; /* Total # number jobs */
1592
1593 /*
1594 * Count Jobs running
1595 */
1596 foreach_jcr (jcr) {
1597 if (jcr->JobId == 0) { /* This is us */
1598 continue;
1599 }
1600 tjobs++; /* Count of all jobs */
1601 if (!ua->AclAccessOk(Job_ACL, jcr->impl->res.job->resource_name_)) {
1602 continue; /* Skip not authorized */
1603 }
1604 njobs++; /* Count of authorized jobs */
1605 }
1606 endeach_jcr(jcr);
1607
1608 if (njobs == 0) { /* No authorized */
1609 if (tjobs == 0) {
1610 ua->SendMsg(_("No Jobs running.\n"));
1611 } else {
1612 ua->SendMsg(_("None of your jobs are running.\n"));
1613 }
1614 goto bail_out;
1615 }
1616
1617 if (select_all || select_by_state) {
1618 /*
1619 * Set selection criterium.
1620 */
1621 selection_criterium = none;
1622 if (select_all) {
1623 selection_criterium = all_jobs;
1624 } else {
1625 i = FindArgWithValue(ua, NT_("state"));
1626 if (i > 0) {
1627 if (Bstrcasecmp(ua->argv[i], NT_("created"))) {
1628 selection_criterium = created_jobs;
1629 }
1630
1631 if (Bstrcasecmp(ua->argv[i], NT_("blocked"))) {
1632 selection_criterium = blocked_jobs;
1633 }
1634
1635 if (Bstrcasecmp(ua->argv[i], NT_("waiting"))) {
1636 selection_criterium = waiting_jobs;
1637 }
1638
1639 if (Bstrcasecmp(ua->argv[i], NT_("running"))) {
1640 selection_criterium = running_jobs;
1641 }
1642
1643 if (selection_criterium == none) {
1644 ua->ErrorMsg(_(
1645 "Illegal state either created, blocked, waiting or running\n"));
1646 goto bail_out;
1647 }
1648 }
1649 }
1650
1651 /*
1652 * Select from all available Jobs the Jobs matching the selection
1653 * criterium.
1654 */
1655 foreach_jcr (jcr) {
1656 if (jcr->JobId == 0) { /* This is us */
1657 continue;
1658 }
1659
1660 if (!ua->AclAccessOk(Job_ACL, jcr->impl->res.job->resource_name_)) {
1661 continue; /* Skip not authorized */
1662 }
1663
1664 /*
1665 * See if we need to select this JobId.
1666 */
1667 switch (selection_criterium) {
1668 case all_jobs:
1669 break;
1670 case created_jobs:
1671 if (jcr->JobStatus != JS_Created) { continue; }
1672 break;
1673 case blocked_jobs:
1674 if (!jcr->job_started || jcr->JobStatus != JS_Blocked) { continue; }
1675 break;
1676 case waiting_jobs:
1677 if (!JobWaiting(jcr)) { continue; }
1678 break;
1679 case running_jobs:
1680 if (!jcr->job_started || jcr->JobStatus != JS_Running) { continue; }
1681 break;
1682 default:
1683 break;
1684 }
1685
1686 InsertSelectedJobid(selected_jobids, jcr->JobId);
1687 ua->SendMsg(_("Selected Job %d for cancelling\n"), jcr->JobId);
1688 }
1689
1690 if (selected_jobids->empty()) {
1691 ua->SendMsg(_("No Jobs selected.\n"));
1692 goto bail_out;
1693 }
1694
1695 /*
1696 * Only ask for confirmation when not in batch mode and there is no yes on
1697 * the cmdline.
1698 */
1699 if (!ua->batch && FindArg(ua, NT_("yes")) == -1) {
1700 if (!GetYesno(ua, _("Confirm cancel (yes/no): ")) || !ua->pint32_val) {
1701 goto bail_out;
1702 }
1703 }
1704 } else {
1705 char temp[256];
1706 char JobName[MAX_NAME_LENGTH];
1707
1708 /*
1709 * Interactivly select a Job.
1710 */
1711 StartPrompt(ua, _("Select Job:\n"));
1712 foreach_jcr (jcr) {
1713 char ed1[50];
1714 if (jcr->JobId == 0) { /* This is us */
1715 continue;
1716 }
1717 if (!ua->AclAccessOk(Job_ACL, jcr->impl->res.job->resource_name_)) {
1718 continue; /* Skip not authorized */
1719 }
1720 Bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"),
1721 edit_int64(jcr->JobId, ed1), jcr->Job);
1722 AddPrompt(ua, buf);
1723 }
1724 endeach_jcr(jcr);
1725
1726 Bsnprintf(temp, sizeof(temp), _("Choose Job to %s"), _(reason));
1727 if (DoPrompt(ua, _("Job"), temp, buf, sizeof(buf)) < 0) { goto bail_out; }
1728
1729 if (bstrcmp(reason, "cancel")) {
1730 if (ua->api && njobs == 1) {
1731 char nbuf[1000];
1732
1733 Bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
1734 _("Confirm cancel?"));
1735 if (!GetYesno(ua, nbuf) || !ua->pint32_val) { goto bail_out; }
1736 } else {
1737 if (njobs == 1) {
1738 if (!GetYesno(ua, _("Confirm cancel (yes/no): ")) ||
1739 !ua->pint32_val) {
1740 goto bail_out;
1741 }
1742 }
1743 }
1744 }
1745
1746 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
1747 jcr = get_jcr_by_full_name(JobName);
1748 if (!jcr) {
1749 ua->WarningMsg(_("Job \"%s\" not found.\n"), JobName);
1750 goto bail_out;
1751 }
1752
1753 InsertSelectedJobid(selected_jobids, jcr->JobId);
1754 FreeJcr(jcr);
1755 }
1756 }
1757
1758 return selected_jobids;
1759
1760 bail_out:
1761 delete selected_jobids;
1762
1763 return NULL;
1764 }
1765
1766 /**
1767 * Get a slot selection.
1768 *
1769 * Returns: false on error
1770 * true on success with the selected slots set in the slot_list.
1771 */
GetUserSlotList(UaContext * ua,char * slot_list,const char * argument,int num_slots)1772 bool GetUserSlotList(UaContext* ua,
1773 char* slot_list,
1774 const char* argument,
1775 int num_slots)
1776 {
1777 int i, len, beg, end;
1778 const char* msg;
1779 char search_argument[20];
1780 char *p, *e, *h;
1781
1782 /*
1783 * See if the argument given is found on the cmdline.
1784 */
1785 bstrncpy(search_argument, argument, sizeof(search_argument));
1786 i = FindArgWithValue(ua, search_argument);
1787 if (i == -1) { /* not found */
1788 /*
1789 * See if the last letter of search_argument is a 's'
1790 * When it is strip it and try if that argument is given.
1791 */
1792 len = strlen(search_argument);
1793 if (len > 0 && search_argument[len - 1] == 's') {
1794 search_argument[len - 1] = '\0';
1795 i = FindArgWithValue(ua, search_argument);
1796 }
1797 }
1798
1799 if (i > 0) {
1800 /*
1801 * Scan slot list in ua->argv[i]
1802 */
1803 StripTrailingJunk(ua->argv[i]);
1804 for (p = ua->argv[i]; p && *p; p = e) {
1805 /*
1806 * Check for list
1807 */
1808 e = strchr(p, ',');
1809 if (e) { *e++ = 0; }
1810 /*
1811 * Check for range
1812 */
1813 h = strchr(p, '-'); /* range? */
1814 if (h == p) {
1815 msg = _("Negative numbers not permitted\n");
1816 goto bail_out;
1817 }
1818 if (h) {
1819 *h++ = 0;
1820 if (!IsAnInteger(h)) {
1821 msg = _("Range end is not integer.\n");
1822 goto bail_out;
1823 }
1824 SkipSpaces(&p);
1825 if (!IsAnInteger(p)) {
1826 msg = _("Range start is not an integer.\n");
1827 goto bail_out;
1828 }
1829 beg = atoi(p);
1830 end = atoi(h);
1831 if (end < beg) {
1832 msg = _("Range end not bigger than start.\n");
1833 goto bail_out;
1834 }
1835 } else {
1836 SkipSpaces(&p);
1837 if (!IsAnInteger(p)) {
1838 msg = _("Input value is not an integer.\n");
1839 goto bail_out;
1840 }
1841 beg = end = atoi(p);
1842 }
1843 if (beg <= 0 || end <= 0) {
1844 msg = _("Values must be be greater than zero.\n");
1845 goto bail_out;
1846 }
1847 if (end > num_slots) {
1848 msg = _("Slot too large.\n");
1849 goto bail_out;
1850 }
1851
1852 /*
1853 * Turn on specified range
1854 */
1855 for (i = beg; i <= end; i++) { SetBit(i - 1, slot_list); }
1856 }
1857 } else {
1858 /*
1859 * Turn everything on
1860 */
1861 for (i = 1; i <= num_slots; i++) { SetBit(i - 1, slot_list); }
1862 }
1863
1864 if (debug_level >= 100) {
1865 Dmsg0(100, "Slots turned on:\n");
1866 for (i = 1; i <= num_slots; i++) {
1867 if (BitIsSet(i - 1, slot_list)) { Dmsg1(100, "%d\n", i); }
1868 }
1869 }
1870
1871 return true;
1872
1873 bail_out:
1874 Dmsg1(100, "Problem with user selection ERR=%s\n", msg);
1875
1876 return false;
1877 }
1878
GetUserJobTypeSelection(UaContext * ua,int * jobtype)1879 bool GetUserJobTypeSelection(UaContext* ua, int* jobtype)
1880 {
1881 int i;
1882 char job_type[MAX_NAME_LENGTH];
1883
1884 /* set returning jobtype to invalid */
1885 *jobtype = -1;
1886
1887 if ((i = FindArgWithValue(ua, NT_("jobtype"))) >= 0) {
1888 bstrncpy(job_type, ua->argv[i], sizeof(job_type));
1889 } else {
1890 StartPrompt(ua, _("Jobtype to prune:\n"));
1891 for (i = 0; jobtypes[i].type_name; i++) {
1892 AddPrompt(ua, jobtypes[i].type_name);
1893 }
1894
1895 if (DoPrompt(ua, _("JobType"), _("Select Job Type"), job_type,
1896 sizeof(job_type)) < 0) {
1897 return false;
1898 }
1899 }
1900
1901 for (i = 0; jobtypes[i].type_name; i++) {
1902 if (Bstrcasecmp(jobtypes[i].type_name, job_type)) { break; }
1903 }
1904
1905 if (!jobtypes[i].type_name) {
1906 ua->WarningMsg(_("Illegal jobtype %s.\n"), job_type);
1907 return false;
1908 }
1909
1910 *jobtype = jobtypes[i].job_type;
1911
1912 return true;
1913 }
1914
GetUserJobStatusSelection(UaContext * ua,int * jobstatus)1915 bool GetUserJobStatusSelection(UaContext* ua, int* jobstatus)
1916 {
1917 int i;
1918
1919 if ((i = FindArgWithValue(ua, NT_("jobstatus"))) >= 0) {
1920 if (strlen(ua->argv[i]) == 1 && ua->argv[i][0] >= 'A' &&
1921 ua->argv[i][0] <= 'z') {
1922 *jobstatus = ua->argv[i][0];
1923 } else if (Bstrcasecmp(ua->argv[i], "terminated")) {
1924 *jobstatus = JS_Terminated;
1925 } else if (Bstrcasecmp(ua->argv[i], "warnings")) {
1926 *jobstatus = JS_Warnings;
1927 } else if (Bstrcasecmp(ua->argv[i], "canceled")) {
1928 *jobstatus = JS_Canceled;
1929 } else if (Bstrcasecmp(ua->argv[i], "running")) {
1930 *jobstatus = JS_Running;
1931 } else if (Bstrcasecmp(ua->argv[i], "error")) {
1932 *jobstatus = JS_ErrorTerminated;
1933 } else if (Bstrcasecmp(ua->argv[i], "fatal")) {
1934 *jobstatus = JS_FatalError;
1935 } else {
1936 /* invalid jobstatus */
1937 return false;
1938 }
1939 }
1940 return true;
1941 }
1942
GetUserJobLevelSelection(UaContext * ua,int * joblevel)1943 bool GetUserJobLevelSelection(UaContext* ua, int* joblevel)
1944 {
1945 int i;
1946
1947 if ((i = FindArgWithValue(ua, NT_("joblevel"))) >= 0) {
1948 if (strlen(ua->argv[i]) == 1 && ua->argv[i][0] >= 'A' &&
1949 ua->argv[i][0] <= 'z') {
1950 *joblevel = ua->argv[i][0];
1951 } else {
1952 /* invalid joblevel */
1953 return false;
1954 }
1955 }
1956 return true;
1957 }
1958 } /* namespace directordaemon */
1959