2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Director -- User Agent Commands
22 * Kern Sibbald, September MM
28 /* Imported subroutines */
30 /* Imported variables */
31 extern jobq_t job_queue; /* job queue */
34 /* Imported functions */
35 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
36 extern int gui_cmd(UAContext *ua, const char *cmd);
37 extern int label_cmd(UAContext *ua, const char *cmd);
38 extern int list_cmd(UAContext *ua, const char *cmd);
39 extern int llist_cmd(UAContext *ua, const char *cmd);
40 extern int messagescmd(UAContext *ua, const char *cmd);
41 extern int prunecmd(UAContext *ua, const char *cmd);
42 extern int purge_cmd(UAContext *ua, const char *cmd);
43 extern int truncate_cmd(UAContext *ua, const char *cmd); /* in ua_purge.c */
44 extern int query_cmd(UAContext *ua, const char *cmd);
45 extern int relabel_cmd(UAContext *ua, const char *cmd);
46 extern int restore_cmd(UAContext *ua, const char *cmd);
47 extern int retentioncmd(UAContext *ua, const char *cmd);
48 extern int show_cmd(UAContext *ua, const char *cmd);
49 extern int sqlquery_cmd(UAContext *ua, const char *cmd);
50 extern int status_cmd(UAContext *ua, const char *cmd);
51 extern int update_cmd(UAContext *ua, const char *cmd);
53 /* Forward referenced functions */
54 static int add_cmd(UAContext *ua, const char *cmd);
55 static int automount_cmd(UAContext *ua, const char *cmd);
56 static int cancel_cmd(UAContext *ua, const char *cmd);
57 static int create_cmd(UAContext *ua, const char *cmd);
58 static int delete_cmd(UAContext *ua, const char *cmd);
59 static int disable_cmd(UAContext *ua, const char *cmd);
60 static int enable_cmd(UAContext *ua, const char *cmd);
61 static int estimate_cmd(UAContext *ua, const char *cmd);
62 static int help_cmd(UAContext *ua, const char *cmd);
63 static int memory_cmd(UAContext *ua, const char *cmd);
64 static int mount_cmd(UAContext *ua, const char *cmd);
65 static int release_cmd(UAContext *ua, const char *cmd);
66 static int reload_cmd(UAContext *ua, const char *cmd);
67 static int setdebug_cmd(UAContext *ua, const char *cmd);
68 static int setbwlimit_cmd(UAContext *ua, const char *cmd);
69 static int setip_cmd(UAContext *ua, const char *cmd);
70 static int time_cmd(UAContext *ua, const char *cmd);
71 static int trace_cmd(UAContext *ua, const char *cmd);
72 static int unmount_cmd(UAContext *ua, const char *cmd);
73 static int use_cmd(UAContext *ua, const char *cmd);
74 static int cloud_cmd(UAContext *ua, const char *cmd);
75 static int var_cmd(UAContext *ua, const char *cmd);
76 static int version_cmd(UAContext *ua, const char *cmd);
77 static int wait_cmd(UAContext *ua, const char *cmd);
79 static void do_job_delete(UAContext *ua, JobId_t JobId);
80 static int delete_volume(UAContext *ua);
81 static int delete_pool(UAContext *ua);
82 static void delete_job(UAContext *ua);
83 static void do_storage_cmd(UAContext *ua, const char *command);
85 int qhelp_cmd(UAContext *ua, const char *cmd);
86 int quit_cmd(UAContext *ua, const char *cmd);
88 /* not all in alphabetical order. New commands are added after existing commands with similar letters
89 to prevent breakage of existing user scripts. */
91 const char *key; /* command */
92 int (*func)(UAContext *ua, const char *cmd); /* handler */
93 const char *help; /* main purpose */
94 const char *usage; /* all arguments to build usage */
95 const bool use_in_rs; /* Can use it in Console RunScript */
97 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
98 { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
99 { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
100 { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
101 { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid=<number-list> | job=<job-name> | ujobid=<unique-jobid> | inactive client=<client-name> storage=<storage-name> | all"), false},
102 { NT_("cloud"), cloud_cmd, _("Specific Cloud commands"),
103 NT_("[storage=<storage-name>] [volume=<vol>] [pool=<pool>] [allpools] [allfrompool] [mediatype=<type>] [drive=<number>] [slots=<number] \n"
104 "\tstatus | prune | list | upload | truncate"), true},
105 { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
106 { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume=<vol-name> | pool=<pool-name> | jobid=<id> | snapshot"), true},
107 { NT_("disable"), disable_cmd, _("Disable a job, attributes batch process"), NT_("job=<name> | client=<name> | schedule=<name> | storage=<name> | batch"), true},
108 { NT_("enable"), enable_cmd, _("Enable a job, attributes batch process"), NT_("job=<name> | client=<name> | schedule=<name> | storage=<name> | batch"), true},
109 { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
110 NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
112 { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
113 { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
114 { NT_("help"), help_cmd, _("Print help on specific command"),
115 NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
116 "\n\tmessages memory mount prune purge quit query\n\trestore relabel release reload run status"
117 "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"
118 "\n\tsnapshot"), false},
120 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> drive=<nb> barcodes"), false},
121 { NT_("list"), list_cmd, _("List objects from catalog"),
122 NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [limit=<n>]|\n"
123 "\tjobtotals | pools | volume | media <pool=pool-name> | files [type=<deleted|all>] jobid=<nn> | copies jobid=<nn> |\n"
124 "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot\n"
127 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
128 NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [order=<asc/desc>] [limit=<n>]|\n"
129 "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
130 "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot |\n"
131 "\tfileindex=<mm>\n"), false},
133 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
134 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
135 { NT_("mount"), mount_cmd, _("Mount storage"),
136 NT_("storage=<storage-name> slot=<num> drive=<num> [ device=<device-name> ] [ jobid=<id> | job=<job-name> ]"), false},
138 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
139 NT_("files | jobs | pool=<pool> | snapshot [client=<client-name>] | client=<client-name> | [ expired ] volume=<volume-name> "), true},
141 { NT_("purge"), purge_cmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
142 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
143 { NT_("query"), query_cmd, _("Query catalog"), NT_("[<query-item-number>]"), false},
144 { NT_("restore"), restore_cmd, _("Restore files"),
145 NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
146 "restorejob=<job> restoreclient=<cli>"
147 "\n\tcomment=<text> jobid=<jobid> jobuser=<user> jobgroup=<grp> copies done select all"), false},
149 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
150 NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
152 { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name> [ device=<device-name> ] "), false},
153 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
154 { NT_("run"), run_cmd, _("Run a job"),
155 NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
156 " where=<directory-prefix>\n\twhen=<universal-time-specification> pool=<pool-name>\n\t"
157 " nextpool=<next-pool-name> comment=<text> accurate=<bool> spooldata=<bool> yes"), false},
159 { NT_("restart"), restart_cmd, _("Restart a job"),
160 NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
161 "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
163 { NT_("resume"), restart_cmd, _("Resume a job"),
164 NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
165 "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
167 { NT_("status"), status_cmd, _("Report status"),
168 NT_("all | network [bytes=<nn-b>] | dir=<dir-name> | director | client=<client-name> |\n"
169 "\tstorage=<storage-name> slots |\n"
170 "\tschedule [job=<job-name>] [days=<nn>] [limit=<nn>]\n"
171 "\t\t[time=<universal-time-specification>]"), true},
173 { NT_("stop"), cancel_cmd, _("Stop a job"), NT_("jobid=<number-list> job=<job-name> ujobid=<unique-jobid> all"), false},
174 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
175 NT_("level=<nn> tags=<tags> trace=0/1 options=<0tTc> tags=<tags> | client=<client-name> | dir | storage=<storage-name> | all"), true},
177 { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"),
178 NT_("limit=<speed> client=<client-name> jobid=<number> job=<job-name> ujobid=<unique-jobid>"), true},
180 { NT_("snapshot"), snapshot_cmd, _("Handle snapshots"),
181 NT_("[client=<client-name> | job=<job-name> | jobid=<jobid>] [delete | list | listclient | prune | sync | update]"), true},
183 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
184 { NT_("show"), show_cmd, _("Show resource records"),
185 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> | schedule=<sss> | client=<zzz> | storage=<sss> | disabled | all"), true},
187 { NT_("sqlquery"), sqlquery_cmd, _("Use SQL to query catalog"), NT_(""), false},
188 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
189 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
190 { NT_("truncate"), truncate_cmd, _("Truncate one or more Volumes"), NT_("volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
191 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
192 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
194 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
195 NT_("storage=<storage-name> [ drive=<num> ] [ device=<dev-name> ]| jobid=<id> | job=<job-name>"), false},
197 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
198 NT_("stats\n\tsnapshot\n\tpool=<poolname>\n\tslots storage=<storage> scan"
199 "\n\tvolume=<volname> volstatus=<status> volretention=<time-def> cacheretention=<time-def>"
200 "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
201 "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
202 "\n\t enabled=<yes/no> recyclepool=<pool> actiononpurge=<action>"
203 "\n\t allfrompool=<pool> fromallpools frompool"),true},
204 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_("catalog=<catalog>"), false},
205 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
206 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
207 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
208 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
211 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
213 const char *get_command(int index) {
214 return commands[index].key;
218 * Execute a command from the UA
220 bool do_a_command(UAContext *ua)
227 Dmsg1(900, "Command: %s\n", ua->argk[0]);
232 if (ua->jcr->wstorage) {
233 while (ua->jcr->wstorage->size()) {
234 ua->jcr->wstorage->remove(0);
238 len = strlen(ua->argk[0]);
239 for (i=0; i<comsize; i++) { /* search for command */
240 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
242 /* Check if command permitted, but "quit" is always OK */
243 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
244 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
247 /* Check if this command is authorized in RunScript */
248 if (ua->runscript && !commands[i].use_in_rs) {
249 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
252 if (ua->api) ua->signal(BNET_CMD_BEGIN);
253 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
254 if (ua->api) ua->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
255 found = ua->UA_sock && ua->UA_sock->is_stop() ? false : true;
260 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
267 * This is a common routine used to stuff the Pool DB record defaults
268 * into the Media DB record just before creating a media (Volume)
271 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
273 mr->PoolId = pr->PoolId;
274 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
275 mr->Recycle = pr->Recycle;
276 mr->VolRetention = pr->VolRetention;
277 mr->CacheRetention = pr->CacheRetention;
278 mr->VolUseDuration = pr->VolUseDuration;
279 mr->ActionOnPurge = pr->ActionOnPurge;
280 mr->RecyclePoolId = pr->RecyclePoolId;
281 mr->MaxVolJobs = pr->MaxVolJobs;
282 mr->MaxVolFiles = pr->MaxVolFiles;
283 mr->MaxVolBytes = pr->MaxVolBytes;
284 mr->LabelType = pr->LabelType;
290 * Add Volumes to an existing Pool
292 static int add_cmd(UAContext *ua, const char *cmd)
296 int num, i, max, startnum;
297 char name[MAX_NAME_LENGTH];
299 int Slot = 0, InChanger = 0;
302 "You probably don't want to be using this command since it\n"
303 "creates database records without labeling the Volumes.\n"
304 "You probably want to use the \"label\" command.\n\n"));
306 if (!open_client_db(ua)) {
310 memset(&pr, 0, sizeof(pr));
312 if (!get_pool_dbr(ua, &pr)) {
316 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
317 pr.MaxVols, pr.PoolType);
319 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
320 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
321 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
324 pr.MaxVols = ua->pint32_val;
328 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
329 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
330 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
334 if (pr.MaxVols == 0) {
337 max = pr.MaxVols - pr.NumVols;
341 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
342 if (!get_pint(ua, buf)) {
345 num = ua->pint32_val;
346 if (num < 0 || num > max) {
347 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
355 if (!get_cmd(ua, _("Enter Volume name: "))) {
359 if (!get_cmd(ua, _("Enter base volume name: "))) {
363 /* Don't allow | in Volume name because it is the volume separator character */
364 if (!is_volume_name_legal(ua, ua->cmd)) {
367 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
368 ua->warning_msg(_("Volume name too long.\n"));
371 if (strlen(ua->cmd) == 0) {
372 ua->warning_msg(_("Volume name must be at least one character long.\n"));
378 bstrncpy(name, ua->cmd, sizeof(name));
380 bstrncat(name, "%04d", sizeof(name));
383 if (!get_pint(ua, _("Enter the starting number: "))) {
386 startnum = ua->pint32_val;
388 ua->warning_msg(_("Start number must be greater than zero.\n"));
398 if (store && store->autochanger) {
399 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
402 Slot = ua->pint32_val;
403 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
406 InChanger = ua->pint32_val;
409 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
410 for (i=startnum; i < num+startnum; i++) {
411 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
413 mr.InChanger = InChanger;
415 set_storageid_in_mr(store, &mr);
416 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
417 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
418 ua->error_msg("%s", db_strerror(ua->db));
421 // if (i == startnum) {
422 // first_id = mr.PoolId;
426 Dmsg0(200, "Update pool record.\n");
427 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
428 ua->warning_msg("%s", db_strerror(ua->db));
431 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
437 * Turn auto mount on/off
442 int automount_cmd(UAContext *ua, const char *cmd)
447 if (!get_cmd(ua, _("Turn on or off? "))) {
455 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
460 * Cancel/Stop a job -- Stop marks it as Incomplete
461 * so that it can be restarted.
463 static int cancel_cmd(UAContext *ua, const char *cmd)
468 bool cancel = strcasecmp(commands[ua->cmd_index].key, "cancel") == 0;
469 alist *jcrs = New(alist(5, not_owned_by_alist));
471 /* If the user explicitely ask, we can send the cancel command to
474 if (find_arg(ua, "inactive") > 0) {
475 ret = cancel_inactive_job(ua);
479 nb = select_running_jobs(ua, jcrs, commands[ua->cmd_index].key);
481 foreach_alist(jcr, jcrs) {
482 /* Execute the cancel command only if we don't have an error */
484 ret &= cancel_job(ua, jcr, 60, cancel);
495 * This is a common routine to create or update a
496 * Pool DB base record from a Pool Resource. We handle
497 * the setting of MaxVols and NumVols slightly differently
498 * depending on if we are creating the Pool or we are
499 * simply bringing it into agreement with the resource (updage).
501 * Caution : RecyclePoolId isn't setup in this function.
502 * You can use set_pooldbr_recyclepoolid();
505 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
507 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
508 if (op == POOL_OP_CREATE) {
509 pr->MaxVols = pool->max_volumes;
511 } else { /* update pool */
512 if (pr->MaxVols != pool->max_volumes) {
513 pr->MaxVols = pool->max_volumes;
515 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
516 pr->MaxVols = pr->NumVols;
519 pr->LabelType = pool->LabelType;
520 pr->UseOnce = pool->use_volume_once;
521 pr->UseCatalog = pool->use_catalog;
522 pr->Recycle = pool->Recycle;
523 pr->VolRetention = pool->VolRetention;
524 pr->CacheRetention = pool->CacheRetention;
525 pr->VolUseDuration = pool->VolUseDuration;
526 pr->MaxVolJobs = pool->MaxVolJobs;
527 pr->MaxVolFiles = pool->MaxVolFiles;
528 pr->MaxVolBytes = pool->MaxVolBytes;
529 pr->AutoPrune = pool->AutoPrune;
530 pr->ActionOnPurge = pool->action_on_purge;
531 pr->Recycle = pool->Recycle;
532 if (pool->label_format) {
533 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
535 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
539 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
540 int update_pool_references(JCR *jcr, BDB *db, POOL *pool)
544 if (pool->ScratchPool == pool) {
545 Jmsg(NULL, M_WARNING, 0,
546 _("The ScratchPool directive for Pool \"%s\" is incorrect. Using default Scratch pool instead.\n"),
548 pool->ScratchPool = NULL;
551 if (!pool->RecyclePool && !pool->ScratchPool) {
555 memset(&pr, 0, sizeof(POOL_DBR));
556 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
558 /* Don't compute NumVols here */
559 if (!db_get_pool_record(jcr, db, &pr)) {
560 return -1; /* not exists in database */
563 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
565 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
566 return -1; /* error */
569 /* NumVols is updated here */
570 if (!db_update_pool_record(jcr, db, &pr)) {
571 return -1; /* error */
576 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
577 * works with set_pooldbr_from_poolres
579 bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool)
584 if (pool->RecyclePool) {
585 memset(&rpool, 0, sizeof(POOL_DBR));
587 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
588 if (db_get_pool_record(jcr, db, &rpool)) {
589 pr->RecyclePoolId = rpool.PoolId;
591 Jmsg(jcr, M_WARNING, 0,
592 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
593 "Try to update it with 'update pool=%s'\n"),
594 pool->name(), rpool.Name, rpool.Name,pool->name());
598 } else { /* no RecyclePool used, set it to 0 */
599 pr->RecyclePoolId = 0;
602 if (pool->ScratchPool) {
603 memset(&rpool, 0, sizeof(POOL_DBR));
605 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
606 if (db_get_pool_record(jcr, db, &rpool)) {
607 pr->ScratchPoolId = rpool.PoolId;
609 Jmsg(jcr, M_WARNING, 0,
610 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
611 "Try to update it with 'update pool=%s'\n"),
612 pool->name(), rpool.Name, rpool.Name,pool->name());
615 } else { /* no ScratchPool used, set it to 0 */
616 pr->ScratchPoolId = 0;
624 * Create a pool record from a given Pool resource
625 * Also called from backup.c
626 * Returns: -1 on error
627 * 0 record already exists
631 int create_pool(JCR *jcr, BDB *db, POOL *pool, e_pool_op op)
634 memset(&pr, 0, sizeof(POOL_DBR));
635 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
637 if (db_get_pool_record(jcr, db, &pr)) {
639 if (op == POOL_OP_UPDATE) { /* update request */
640 set_pooldbr_from_poolres(&pr, pool, op);
641 set_pooldbr_references(jcr, db, &pr, pool);
642 db_update_pool_record(jcr, db, &pr);
644 return 0; /* exists */
647 set_pooldbr_from_poolres(&pr, pool, op);
648 set_pooldbr_references(jcr, db, &pr, pool);
650 if (!db_create_pool_record(jcr, db, &pr)) {
651 return -1; /* error */
659 * Create a Pool Record in the database.
660 * It is always created from the Resource record.
662 static int create_cmd(UAContext *ua, const char *cmd)
666 if (!open_client_db(ua)) {
670 pool = get_pool_resource(ua);
675 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
677 ua->error_msg(_("Error: Pool %s already exists.\n"
678 "Use update to change it.\n"), pool->name());
682 ua->error_msg("%s", db_strerror(ua->db));
688 ua->send_msg(_("Pool %s created.\n"), pool->name());
693 extern DIRRES *director;
694 extern char *configfile;
696 static int setbwlimit_client(UAContext *ua, CLIENT *client, char *Job, int64_t limit)
704 /* Connect to File daemon */
705 old_client = ua->jcr->client;
706 ua->jcr->client = client;
707 ua->jcr->max_bandwidth = limit;
709 /* Try to connect for 15 seconds */
710 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
711 client->name(), client->address(), client->FDport);
712 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
713 ua->error_msg(_("Failed to connect to Client.\n"));
716 Dmsg0(120, "Connected to file daemon\n");
718 if (!send_bwlimit(ua->jcr, Job)) {
719 ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
722 /* Note, we add 2000 OK that was sent by FD to us to message */
723 ua->info_msg(_("2000 OK Limiting bandwidth to %sB/s %s\n"),
724 edit_uint64_with_suffix(limit, ed1), *Job?Job:_("on running and future jobs"));
727 ua->jcr->file_bsock->signal(BNET_TERMINATE);
728 free_bsock(ua->jcr->file_bsock);
729 ua->jcr->max_bandwidth = 0;
732 ua->jcr->client = old_client;
736 static int setbwlimit_cmd(UAContext *ua, const char *cmd)
739 CLIENT *client = NULL;
740 char Job[MAX_NAME_LENGTH];
746 const char *lst_all[] = { "job", "jobid", "jobname", "client", NULL };
747 if (find_arg_keyword(ua, lst_all) < 0) {
748 start_prompt(ua, _("Set Bandwidth choice:\n"));
749 add_prompt(ua, _("Running Job")); /* 0 */
750 add_prompt(ua, _("Running and future Jobs for a Client")); /* 1 */
751 action = do_prompt(ua, "item", _("Choose where to limit the bandwidth"),
758 i = find_arg_with_value(ua, "limit");
760 if (!speed_to_uint64(ua->argv[i], strlen(ua->argv[i]), &limit)) {
761 ua->error_msg(_("Invalid value for limit parameter. Expecting speed.\n"));
765 if (!get_cmd(ua, _("Enter new bandwidth limit: "))) {
768 if (!speed_to_uint64(ua->cmd, strlen(ua->cmd), &limit)) {
769 ua->error_msg(_("Invalid value for limit parameter. Expecting speed.\n"));
774 const char *lst[] = { "job", "jobid", "jobname", NULL };
775 if (action == 0 || find_arg_keyword(ua, lst) > 0) {
776 alist *jcrs = New(alist(10, not_owned_by_alist));
777 select_running_jobs(ua, jcrs, "limit");
778 foreach_alist(jcr, jcrs) {
779 jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
780 bstrncpy(Job, jcr->Job, sizeof(Job));
781 client = jcr->client;
782 setbwlimit_client(ua, client, Job, limit);
787 client = get_client_resource(ua, JT_BACKUP_RESTORE);
789 setbwlimit_client(ua, client, Job, limit);
796 * Set a new address in a Client resource. We do this only
797 * if the Console name is the same as the Client name
798 * and the Console can access the client.
800 static int setip_cmd(UAContext *ua, const char *cmd)
804 if (!ua->cons || !acl_access_client_ok(ua, ua->cons->name(), JT_BACKUP_RESTORE)) {
805 ua->error_msg(_("Unauthorized command from this console.\n"));
809 client = GetClientResWithName(ua->cons->name());
812 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
815 /* MA Bug 6 remove ifdef */
816 sockaddr_to_ascii(&(ua->UA_sock->client_addr),
817 sizeof(ua->UA_sock->client_addr), addr, sizeof(addr));
818 client->setAddress(bstrdup(addr));
819 ua->send_msg(_("Client \"%s\" address set to %s\n"),
820 client->name(), addr);
827 * Does all sorts of enable/disable commands: batch, scheduler (not implemented)
828 * job, client, schedule, storage
830 static void do_enable_disable_cmd(UAContext *ua, bool setting)
833 CLIENT *client = NULL;
837 if (find_arg(ua, NT_("batch")) > 0) {
838 ua->send_msg(_("Job Attributes Insertion %sabled\n"), setting?"en":"dis");
839 db_disable_batch_insert(setting);
844 * if (find_arg(ua, NT_("scheduler")) > 0) {
845 * ua->send_msg(_("Job Scheduler %sabled\n"), setting?"en":"dis");
850 i = find_arg(ua, NT_("job"));
854 job = GetJobResWithName(ua->argv[i]);
857 job = select_enable_disable_job_resource(ua, setting);
864 if (!acl_access_ok(ua, Job_ACL, job->name())) {
865 ua->error_msg(_("Unauthorized command from this console.\n"));
868 job->setEnabled(setting);
869 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
872 i = find_arg(ua, NT_("client"));
876 client = GetClientResWithName(ua->argv[i]);
879 client = select_enable_disable_client_resource(ua, setting);
886 if (!acl_access_client_ok(ua, client->name(), JT_BACKUP_RESTORE)) {
887 ua->error_msg(_("Unauthorized command from this console.\n"));
890 client->setEnabled(setting);
891 ua->send_msg(_("Client \"%s\" %sabled\n"), client->name(), setting?"en":"dis");
894 i = find_arg(ua, NT_("schedule"));
898 sched = (SCHED *)GetResWithName(R_SCHEDULE, ua->argv[i]);
901 sched = select_enable_disable_schedule_resource(ua, setting);
908 if (!acl_access_ok(ua, Schedule_ACL, sched->name())) {
909 ua->error_msg(_("Unauthorized command from this console.\n"));
912 sched->setEnabled(setting);
913 ua->send_msg(_("Schedule \"%s\" %sabled\n"), sched->name(), setting?"en":"dis");
916 i = find_arg(ua, NT_("storage"));
918 do_storage_cmd(ua, setting?"enable":"disable");
921 if (i < 0 && !sched && !client && !job) {
922 ua->error_msg(_("You must enter one of the following keywords: job, client, schedule, or storage.\n"));
928 static int enable_cmd(UAContext *ua, const char *cmd)
930 do_enable_disable_cmd(ua, true);
934 static int disable_cmd(UAContext *ua, const char *cmd)
936 do_enable_disable_cmd(ua, false);
940 static void do_dir_setdebug(UAContext *ua, int64_t level, int trace_flag, char *options, int64_t tags)
943 debug_level_tags = tags;
944 set_trace(trace_flag);
945 set_debug_flags(options);
948 static void do_storage_setdebug(UAContext *ua, STORE *store,
949 int64_t level, int trace_flag, int hangup, int blowup,
950 char *options, char *tags)
955 lstore.store = store;
956 pm_strcpy(lstore.store_source, _("unknown source"));
957 set_wstorage(ua->jcr, &lstore);
958 /* Try connecting for up to 15 seconds */
959 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
960 store->name(), store->address, store->SDport);
961 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
962 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
965 Dmsg0(120, _("Connected to storage daemon\n"));
966 sd = ua->jcr->store_bsock;
967 sd->fsend("setdebug=%ld trace=%ld hangup=%ld blowup=%ld options=%s tags=%s\n",
968 (int32_t)level, trace_flag, hangup, blowup, options, NPRTB(tags));
969 if (sd->recv() >= 0) {
970 ua->send_msg("%s", sd->msg);
972 sd->signal(BNET_TERMINATE);
973 free_bsock(ua->jcr->store_bsock);
978 * For the client, we have the following values that can be set
979 * level = debug level
980 * trace = send debug output to a file
981 * options = various options for debug or specific FD behavior
982 * hangup = how many records to send to FD before hanging up
983 * obviously this is most useful for testing restarting
985 * blowup = how many records to send to FD before blowing up the FD.
987 static void do_client_setdebug(UAContext *ua, CLIENT *client,
988 int64_t level, int trace, int hangup, int blowup,
989 char *options, char *tags)
994 /* Connect to File daemon */
996 old_client = ua->jcr->client;
997 ua->jcr->client = client;
998 /* Try to connect for 15 seconds */
999 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1000 client->name(), client->address(), client->FDport);
1001 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
1002 ua->error_msg(_("Failed to connect to Client.\n"));
1003 ua->jcr->client = old_client;
1006 Dmsg0(120, "Connected to file daemon\n");
1008 fd = ua->jcr->file_bsock;
1009 if (ua->jcr->FDVersion <= 10) {
1010 fd->fsend("setdebug=%ld trace=%d hangup=%d\n",
1011 (int32_t)level, trace, hangup);
1013 fd->fsend("setdebug=%ld trace=%d hangup=%d blowup=%d options=%s tags=%s\n",
1014 (int32_t)level, trace, hangup, blowup, options, NPRTB(tags));
1016 if (fd->recv() >= 0) {
1017 ua->send_msg("%s", fd->msg);
1019 fd->signal(BNET_TERMINATE);
1020 free_bsock(ua->jcr->file_bsock);
1021 ua->jcr->client = old_client;
1026 static void do_all_setdebug(UAContext *ua, int64_t level,
1027 int trace_flag, int hangup, int blowup,
1028 char *options, char *tags)
1030 STORE *store, **unique_store;
1031 CLIENT *client, **unique_client;
1036 debug_parse_tags(tags, &t);
1037 do_dir_setdebug(ua, level, trace_flag, options, t);
1039 /* Count Storage items */
1043 foreach_res(store, R_STORAGE) {
1046 unique_store = (STORE **) malloc(i * sizeof(STORE));
1047 /* Find Unique Storage address/port */
1048 store = (STORE *)GetNextRes(R_STORAGE, NULL);
1050 unique_store[i++] = store;
1051 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
1053 for (j=0; j<i; j++) {
1054 if (strcmp(unique_store[j]->address, store->address) == 0 &&
1055 unique_store[j]->SDport == store->SDport) {
1061 unique_store[i++] = store;
1062 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
1067 /* Call each unique Storage daemon */
1068 for (j=0; j<i; j++) {
1069 do_storage_setdebug(ua, unique_store[j], level, trace_flag,
1070 hangup, blowup, options, tags);
1074 /* Count Client items */
1078 foreach_res(client, R_CLIENT) {
1081 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
1082 /* Find Unique Client address/port */
1083 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
1085 unique_client[i++] = client;
1086 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
1088 for (j=0; j<i; j++) {
1089 if (strcmp(unique_client[j]->address(), client->address()) == 0 &&
1090 unique_client[j]->FDport == client->FDport) {
1096 unique_client[i++] = client;
1097 Dmsg2(140, "Stuffing: %s:%d\n", client->address(), client->FDport);
1102 /* Call each unique File daemon */
1103 for (j=0; j<i; j++) {
1104 do_client_setdebug(ua, unique_client[j], level, trace_flag,
1105 hangup, blowup, options, tags);
1107 free(unique_client);
1111 * setdebug level=nn all trace=1/0
1113 static int setdebug_cmd(UAContext *ua, const char *cmd)
1117 int64_t level=0, tags=0;
1118 int trace_flag = -1;
1122 char *tags_str=NULL;
1125 Dmsg1(120, "setdebug:%s:\n", cmd);
1128 i = find_arg_with_value(ua, "options");
1130 bstrncpy(options, ua->argv[i], sizeof(options) - 1);
1133 i = find_arg_with_value(ua, "level");
1135 level = str_to_int64(ua->argv[i]);
1138 if (!get_pint(ua, _("Enter new debug level: "))) {
1141 level = ua->pint32_val;
1144 /* Better to send the tag string instead of tweaking the level
1145 * in case where we extend the tag or change the representation
1147 i = find_arg_with_value(ua, "tags");
1149 tags_str = ua->argv[i];
1150 if (!debug_parse_tags(tags_str, &tags)) {
1151 ua->error_msg(_("Incorrect tags found on command line %s\n"), tags_str);
1156 /* Look for trace flag. -1 => not change */
1157 i = find_arg_with_value(ua, "trace");
1159 trace_flag = atoi(ua->argv[i]);
1160 if (trace_flag > 0) {
1165 /* Look for hangup (debug only) flag. -1 => not change */
1166 i = find_arg_with_value(ua, "hangup");
1168 hangup = atoi(ua->argv[i]);
1171 /* Look for blowup (debug only) flag. -1 => not change */
1172 i = find_arg_with_value(ua, "blowup");
1174 blowup = atoi(ua->argv[i]);
1177 /* General debug? */
1178 for (i=1; i<ua->argc; i++) {
1179 if (strcasecmp(ua->argk[i], "all") == 0) {
1180 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1183 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1184 strcasecmp(ua->argk[i], "director") == 0) {
1185 do_dir_setdebug(ua, level, trace_flag, options, tags);
1188 if (strcasecmp(ua->argk[i], "client") == 0 ||
1189 strcasecmp(ua->argk[i], "fd") == 0) {
1192 client = GetClientResWithName(ua->argv[i]);
1194 do_client_setdebug(ua, client, level, trace_flag,
1195 hangup, blowup, options, tags_str);
1199 client = select_client_resource(ua, JT_BACKUP_RESTORE);
1201 do_client_setdebug(ua, client, level, trace_flag,
1202 hangup, blowup, options, tags_str);
1207 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1208 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1209 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1212 store = GetStoreResWithName(ua->argv[i]);
1214 do_storage_setdebug(ua, store, level, trace_flag,
1215 hangup, blowup, options, tags_str);
1219 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1221 do_storage_setdebug(ua, store, level, trace_flag,
1222 hangup, blowup, options, tags_str);
1228 * We didn't find an appropriate keyword above, so
1231 start_prompt(ua, _("Available daemons are: \n"));
1232 add_prompt(ua, _("Director"));
1233 add_prompt(ua, _("Storage"));
1234 add_prompt(ua, _("Client"));
1235 add_prompt(ua, _("All"));
1236 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1237 case 0: /* Director */
1238 do_dir_setdebug(ua, level, trace_flag, options, tags);
1241 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1243 do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
1248 client = select_client_resource(ua, JT_BACKUP_RESTORE);
1250 do_client_setdebug(ua, client, level, trace_flag, hangup, blowup,
1255 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1264 * Turn debug tracing to file on/off
1266 static int trace_cmd(UAContext *ua, const char *cmd)
1270 if (ua->argc != 2) {
1271 if (!get_cmd(ua, _("Turn on or off? "))) {
1276 onoff = ua->argk[1];
1279 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1283 static int var_cmd(UAContext *ua, const char *cmd)
1285 POOLMEM *val = get_pool_memory(PM_FNAME);
1288 if (!open_client_db(ua)) {
1291 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1294 while (*var == ' ') { /* skip spaces */
1297 Dmsg1(100, "Var=%s:\n", var);
1298 variable_expansion(ua->jcr, var, &val);
1299 ua->send_msg("%s\n", val);
1300 free_pool_memory(val);
1304 static int estimate_cmd(UAContext *ua, const char *cmd)
1307 CLIENT *client = NULL;
1308 FILESET *fileset = NULL;
1310 char since[MAXSTRING];
1314 jcr->setJobType(JT_BACKUP);
1315 jcr->start_time = time(NULL);
1316 jcr->setJobLevel(L_FULL);
1318 for (int i=1; i<ua->argc; i++) {
1319 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1320 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1322 client = GetClientResWithName(ua->argv[i]);
1324 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1327 if (!acl_access_client_ok(ua, client->name(), JT_BACKUP)) {
1328 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1333 ua->error_msg(_("Client name missing.\n"));
1337 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1339 job = GetJobResWithName(ua->argv[i]);
1341 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1344 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1345 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1350 ua->error_msg(_("Job name missing.\n"));
1355 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1357 fileset = GetFileSetResWithName(ua->argv[i]);
1359 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1362 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1363 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1368 ua->error_msg(_("Fileset name missing.\n"));
1372 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1376 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1378 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1379 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1384 ua->error_msg(_("Level value missing.\n"));
1388 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1390 if (!is_yesno(ua->argv[i], &accurate)) {
1391 ua->error_msg(_("Invalid value for accurate. "
1392 "It must be yes or no.\n"));
1397 ua->error_msg(_("Accurate value missing.\n"));
1402 if (!job && !(client && fileset)) {
1403 if (!(job = select_job_resource(ua))) {
1408 job = GetJobResWithName(ua->argk[1]);
1410 ua->error_msg(_("No job specified.\n"));
1413 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1414 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1420 client = job->client;
1423 fileset = job->fileset;
1425 jcr->client = client;
1426 jcr->fileset = fileset;
1428 if (job->pool->catalog) {
1429 ua->catalog = job->pool->catalog;
1431 ua->catalog = client->catalog;
1438 init_jcr_job_record(jcr);
1440 if (!get_or_create_client_record(jcr)) {
1443 if (!get_or_create_fileset_record(jcr)) {
1447 get_level_since_time(ua->jcr, since, sizeof(since));
1449 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1450 jcr->client->name(), jcr->client->address(), jcr->client->FDport);
1451 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1452 ua->error_msg(_("Failed to connect to Client.\n"));
1456 /* The level string change if accurate mode is enabled */
1457 if (accurate >= 0) {
1458 jcr->accurate = accurate;
1460 jcr->accurate = job->accurate;
1463 if (!send_level_command(jcr)) {
1467 if (!send_include_list(jcr)) {
1468 ua->error_msg(_("Error sending include list.\n"));
1472 if (!send_exclude_list(jcr)) {
1473 ua->error_msg(_("Error sending exclude list.\n"));
1478 * If the job is in accurate mode, we send the list of
1481 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1482 if (!send_accurate_current_files(jcr)) {
1486 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1487 while (jcr->file_bsock->recv() >= 0) {
1488 ua->send_msg("%s", jcr->file_bsock->msg);
1492 if (jcr->file_bsock) {
1493 jcr->file_bsock->signal(BNET_TERMINATE);
1494 free_bsock(ua->jcr->file_bsock);
1502 static int time_cmd(UAContext *ua, const char *cmd)
1505 time_t ttime = time(NULL);
1507 (void)localtime_r(&ttime, &tm);
1508 strftime(sdt, sizeof(sdt), "%a %d-%b-%Y %H:%M:%S", &tm);
1509 ua->send_msg("%s\n", sdt);
1514 * reload the conf file
1516 extern "C" void reload_config(int sig);
1518 static int reload_cmd(UAContext *ua, const char *cmd)
1525 * Delete Pool records (should purge Media with it).
1527 * delete pool=<pool-name>
1528 * delete volume pool=<pool-name> volume=<name>
1531 static int delete_cmd(UAContext *ua, const char *cmd)
1533 static const char *keywords[] = {
1540 /* Deleting large jobs can take time! */
1541 if (!open_new_client_db(ua)) {
1545 switch (find_arg_keyword(ua, keywords)) {
1554 while ((i=find_arg(ua, "jobid")) > 0) {
1556 *ua->argk[i] = 0; /* zap keyword already visited */
1560 delete_snapshot(ua);
1567 "In general it is not a good idea to delete either a\n"
1568 "Pool or a Volume since they may contain data.\n\n"));
1570 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1581 delete_snapshot(ua);
1584 ua->warning_msg(_("Nothing done.\n"));
1591 * delete_job has been modified to parse JobID lists like the
1593 * delete JobID=3,4,6,7-11,14
1595 * Thanks to Phil Stracchino for the above addition.
1597 static void delete_job(UAContext *ua)
1599 int JobId; /* not JobId_t because it's unsigned and not compatible with sellist */
1603 int i = find_arg_with_value(ua, NT_("jobid"));
1605 if (!sl.set_string(ua->argv[i], true)) {
1606 ua->warning_msg("%s", sl.get_errmsg());
1610 if (sl.size() > 25 && (find_arg(ua, "yes") < 0)) {
1611 bsnprintf(buf, sizeof(buf),
1612 _("Are you sure you want to delete %d JobIds ? (yes/no): "), sl.size());
1613 if (!get_yesno(ua, buf)) {
1618 foreach_sellist(JobId, &sl) {
1619 do_job_delete(ua, JobId);
1622 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1626 JobId = ua->int64_val;
1627 do_job_delete(ua, JobId);
1632 * do_job_delete now performs the actual delete operation atomically
1634 static void do_job_delete(UAContext *ua, JobId_t JobId)
1638 edit_int64(JobId, ed1);
1639 purge_jobs_from_catalog(ua, ed1);
1640 ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1);
1644 * Delete media records from database -- dangerous
1646 static int delete_volume(UAContext *ua)
1652 if (!select_media_dbr(ua, &mr)) {
1655 ua->warning_msg(_("\nThis command will delete volume %s\n"
1656 "and all Jobs saved on that volume from the Catalog\n"),
1659 if (find_arg(ua, "yes") >= 0) {
1660 ua->pint32_val = 1; /* Have "yes" on command line already" */
1662 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1664 if (!get_yesno(ua, buf)) {
1668 if (!ua->pint32_val) {
1672 /* If not purged, do it */
1673 if (strcmp(mr.VolStatus, "Purged") != 0) {
1674 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1675 ua->error_msg(_("Can't list jobs on this volume\n"));
1679 purge_jobs_from_catalog(ua, lst.list);
1683 db_delete_media_record(ua->jcr, ua->db, &mr);
1688 * Delete a pool record from the database -- dangerous
1690 static int delete_pool(UAContext *ua)
1695 memset(&pr, 0, sizeof(pr));
1697 if (!get_pool_dbr(ua, &pr)) {
1700 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1702 if (!get_yesno(ua, buf)) {
1705 if (ua->pint32_val) {
1706 db_delete_pool_record(ua->jcr, ua->db, &pr);
1711 int memory_cmd(UAContext *ua, const char *cmd)
1713 garbage_collect_memory();
1714 list_dir_status_header(ua);
1715 sm_dump(false, true);
1719 static void do_storage_cmd(UAContext *ua, const char *command)
1724 char dev_name[MAX_NAME_LENGTH];
1728 if (!open_client_db(ua)) {
1731 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1733 store.store = get_storage_resource(ua, true/*arg is storage*/);
1737 pm_strcpy(store.store_source, _("unknown source"));
1738 set_wstorage(jcr, &store);
1739 drive = get_storage_drive(ua, store.store);
1740 /* For the disable/enable command, the slot is not mandatory */
1741 if (strcasecmp(command, "disable") == 0 || strcasecmp(command, "enable") == 0) {
1744 slot = get_storage_slot(ua, store.store);
1749 /* Users may set a device name directly on the command line */
1750 if ((i = find_arg_with_value(ua, "device")) > 0) {
1751 POOLMEM *errmsg = get_pool_memory(PM_NAME);
1752 if (!is_name_valid(ua->argv[i], &errmsg)) {
1753 ua->error_msg(_("Invalid device name. %s"), errmsg);
1754 free_pool_memory(errmsg);
1757 free_pool_memory(errmsg);
1758 bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1760 } else { /* We take the default device name */
1761 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1764 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1765 store.store->media_type, store.store->dev_name(), drive);
1766 Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1768 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1769 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1772 sd = jcr->store_bsock;
1773 bash_spaces(dev_name);
1774 sd->fsend("%s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1775 while (sd->recv() >= 0) {
1776 ua->send_msg("%s", sd->msg);
1778 sd->signal(BNET_TERMINATE);
1779 free_bsock(ua->jcr->store_bsock);
1783 * mount [storage=<name>] [drive=nn] [slot=mm]
1785 static int mount_cmd(UAContext *ua, const char *cmd)
1787 do_storage_cmd(ua, "mount") ; /* mount */
1793 * unmount [storage=<name>] [drive=nn]
1795 static int unmount_cmd(UAContext *ua, const char *cmd)
1797 do_storage_cmd(ua, "unmount"); /* unmount */
1803 * release [storage=<name>] [drive=nn]
1805 static int release_cmd(UAContext *ua, const char *cmd)
1807 do_storage_cmd(ua, "release"); /* release */
1812 * cloud functions, like to upload cached parts to cloud.
1814 int cloud_volumes_cmd(UAContext *ua, const char *cmd, const char *mode)
1818 uint32_t *results = NULL;
1822 char storage[MAX_NAME_LENGTH];
1823 const char *action = mode;
1824 memset(&pr, 0, sizeof(pr));
1827 * Look for all volumes that are enabled and
1828 * have more the 200 bytes.
1831 mr.Recycle = -1; /* All Recycle status */
1832 if (strcmp("prunecache", mode) == 0) {
1833 mr.CacheRetention = 1;
1834 action = "truncate cache";
1837 if (!scan_storage_cmd(ua, cmd, false, /* fromallpool*/
1838 &drive, &mr, &pr, NULL, storage,
1844 if ((sd=open_sd_bsock(ua)) == NULL) {
1845 Dmsg0(100, "Can't open connection to sd\n");
1850 * Loop over the candidate Volumes and upload parts
1852 for (int i=0; i < nb; i++) {
1855 mr.MediaId = results[i];
1856 if (!db_get_media_record(ua->jcr, ua->db, &mr)) {
1860 /* Protect us from spaces */
1861 bash_spaces(mr.VolumeName);
1862 bash_spaces(mr.MediaType);
1863 bash_spaces(pr.Name);
1864 bash_spaces(storage);
1866 sd->fsend("%s Storage=%s Volume=%s PoolName=%s MediaType=%s "
1867 "Slot=%d drive=%d CacheRetention=%lld\n",
1868 action, storage, mr.VolumeName, pr.Name, mr.MediaType,
1869 mr.Slot, drive, mr.CacheRetention);
1871 unbash_spaces(mr.VolumeName);
1872 unbash_spaces(mr.MediaType);
1873 unbash_spaces(pr.Name);
1874 unbash_spaces(storage);
1876 /* Check for valid response */
1877 while (bget_dirmsg(sd) >= 0) {
1878 if (strncmp(sd->msg, "3000 OK truncate cache", 22) == 0) {
1879 ua->send_msg("%s", sd->msg);
1882 } else if (strncmp(sd->msg, "3000 OK", 7) == 0) {
1883 ua->send_msg(_("The volume \"%s\" has been uploaded\n"), mr.VolumeName);
1887 } else if (strncmp(sd->msg, "39", 2) == 0) {
1888 ua->warning_msg("%s", sd->msg);
1891 ua->send_msg("%s", sd->msg);
1895 ua->warning_msg(_("Unable to %s for volume \"%s\"\n"), action, mr.VolumeName);
1902 ua->jcr->wstore = NULL;
1910 /* List volumes in the cloud */
1911 /* TODO: Update the code for .api 2 and llist */
1912 static int cloud_list_cmd(UAContext *ua, const char *cmd)
1915 int64_t size, mtime;
1916 STORE *store = NULL;
1920 char storage[MAX_NAME_LENGTH];
1921 char ed1[50], ed2[50];
1923 uint32_t maxpart=0, part;
1924 uint64_t maxpart_size=0;
1925 memset(&pr, 0, sizeof(pr));
1926 memset(&mr, 0, sizeof(mr));
1928 /* Look at arguments */
1929 for (int i=1; i<ua->argc; i++) {
1930 if (strcasecmp(ua->argk[i], NT_("volume")) == 0
1931 && is_name_valid(ua->argv[i], NULL)) {
1932 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
1934 } else if (strcasecmp(ua->argk[i], NT_("drive")) == 0 && ua->argv[i]) {
1935 drive = atoi(ua->argv[i]);
1939 if (!open_client_db(ua)) {
1943 /* Choose storage */
1944 ua->jcr->wstore = store = get_storage_resource(ua, false);
1948 bstrncpy(storage, store->dev_name(), sizeof(storage));
1949 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
1951 if ((sd=open_sd_bsock(ua)) == NULL) {
1952 Dmsg0(100, "Can't open connection to SD\n");
1956 /* Protect us from spaces */
1957 bash_spaces(mr.MediaType);
1958 bash_spaces(storage);
1959 bash_spaces(mr.VolumeName);
1961 sd->fsend("cloudlist Storage=%s Volume=%s MediaType=%s Slot=%d drive=%d\n",
1962 storage, mr.VolumeName, mr.MediaType, mr.Slot, drive);
1964 if (mr.VolumeName[0]) { /* Want to list parts */
1965 const char *output_hformat="| %8d | %12sB | %20s |\n";
1967 /* Check for valid response */
1968 while (sd->recv() >= 0) {
1969 if (sscanf(sd->msg, "part=%d size=%lld mtime=%lld", &part, &size, &mtime) != 3) {
1970 if (sd->msg[0] == '3') {
1971 ua->send_msg("%s", sd->msg);
1975 /* Print information */
1977 ua->send_msg(_("+----------+---------------+----------------------+\n"));
1978 ua->send_msg(_("| Part | Size | MTime |\n"));
1979 ua->send_msg(_("+----------+---------------+----------------------+\n"));
1982 if (part > maxpart) {
1984 maxpart_size = size;
1987 ua->send_msg(output_hformat, part, edit_uint64_with_suffix(size, ed1), bstrftimes(ed2, sizeof(ed2), mtime));
1990 ua->send_msg(_("+----------+---------------+----------------------+\n"));
1992 /* TODO: See if we fix the catalog record directly */
1993 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1994 POOL_MEM errmsg, tmpmsg;
1995 if (mr.LastPartBytes != maxpart_size) {
1996 Mmsg(tmpmsg, "Error on volume \"%s\". Catalog LastPartBytes mismatch %lld != %lld\n",
1997 mr.VolumeName, mr.LastPartBytes, maxpart_size);
1998 pm_strcpy(errmsg, tmpmsg.c_str());
2000 if (mr.VolCloudParts != maxpart) {
2001 Mmsg(tmpmsg, "Error on volume \"%s\". Catalog VolCloudParts mismatch %ld != %ld\n",
2002 mr.VolumeName, mr.VolCloudParts, maxpart);
2003 pm_strcpy(errmsg, tmpmsg.c_str());
2005 if (strlen(errmsg.c_str()) > 0) {
2006 ua->error_msg("\n%s", errmsg.c_str());
2009 } else { /* TODO: Get the last part if possible? */
2010 const char *output_hformat="| %18s | %9s | %20s | %20s | %12sB |\n";
2012 /* Check for valid response */
2013 while (sd->recv() >= 0) {
2014 if (sscanf(sd->msg, "volume=%127s", mr.VolumeName) != 1) {
2015 if (sd->msg[0] == '3') {
2016 ua->send_msg("%s", sd->msg);
2020 unbash_spaces(mr.VolumeName);
2024 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
2025 memset(&pr, 0, sizeof(POOL_DBR));
2026 pr.PoolId = mr.PoolId;
2027 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
2028 strcpy(pr.Name, "?");
2032 ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2033 ua->send_msg(_("| Volume Name | Status | Media Type | Pool | VolBytes |\n"));
2034 ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2037 /* Print information */
2038 ua->send_msg(output_hformat, mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name,
2039 edit_uint64_with_suffix(mr.VolBytes, ed1));
2043 ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2050 ua->jcr->wstore = NULL;
2054 /* Ask client to create/prune/delete a snapshot via the command line */
2055 static int cloud_cmd(UAContext *ua, const char *cmd)
2057 for (int i=0; i<ua->argc; i++) {
2058 if (strcasecmp(ua->argk[i], NT_("upload")) == 0) {
2059 return cloud_volumes_cmd(ua, cmd, "upload");
2061 } else if (strcasecmp(ua->argk[i], NT_("list")) == 0) {
2062 return cloud_list_cmd(ua, cmd);
2064 } else if (strcasecmp(ua->argk[i], NT_("truncate")) == 0) {
2065 return cloud_volumes_cmd(ua, cmd, "truncate cache");
2067 } else if (strcasecmp(ua->argk[i], NT_("status")) == 0) {
2069 } else if (strcasecmp(ua->argk[i], NT_("prune")) == 0) {
2070 return cloud_volumes_cmd(ua, cmd, "prunecache");
2079 start_prompt(ua, _("Cloud choice: \n"));
2080 add_prompt(ua, _("List Cloud Volumes in the Cloud"));
2081 add_prompt(ua, _("Upload a Volume to the Cloud"));
2082 add_prompt(ua, _("Prune the Cloud Cache"));
2083 add_prompt(ua, _("Truncate a Volume Cache"));
2084 add_prompt(ua, _("Done"));
2086 switch(do_prompt(ua, "", _("Select action to perform on Cloud"), NULL, 0)) {
2087 case 0: /* list cloud */
2088 cloud_list_cmd(ua, cmd);
2090 case 1: /* upload */
2091 cloud_volumes_cmd(ua, cmd, "upload");
2093 case 2: /* Prune cache */
2094 cloud_volumes_cmd(ua, cmd, "prunecache");
2096 case 3: /* Truncate cache */
2097 cloud_volumes_cmd(ua, cmd, "truncate cache");
2100 ua->info_msg(_("Selection terminated.\n"));
2109 * use catalog=<name>
2111 static int use_cmd(UAContext *ua, const char *cmd)
2113 CAT *oldcatalog, *catalog;
2116 close_db(ua); /* close any previously open db */
2117 oldcatalog = ua->catalog;
2119 if (!(catalog = get_catalog_resource(ua))) {
2120 ua->catalog = oldcatalog;
2122 ua->catalog = catalog;
2125 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
2126 ua->catalog->name(), ua->catalog->db_name);
2131 int quit_cmd(UAContext *ua, const char *cmd)
2137 /* Handler to get job status */
2138 static int status_handler(void *ctx, int num_fields, char **row)
2140 char *val = (char *)ctx;
2145 *val = '?'; /* Unknown by default */
2152 * Wait until no job is running
2154 int wait_cmd(UAContext *ua, const char *cmd)
2158 time_t stop_time = 0;
2162 * Wait until no job is running
2164 if (ua->argc == 1) {
2165 bmicrosleep(0, 200000); /* let job actually start */
2166 for (bool running=true; running; ) {
2169 if (!jcr->is_internal_job()) {
2183 i = find_arg_with_value(ua, NT_("timeout"));
2184 if (i > 0 && ua->argv[i]) {
2185 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
2188 /* we have jobid, jobname or ujobid argument */
2190 uint32_t jobid = 0 ;
2192 if (!open_client_db(ua)) {
2193 ua->error_msg(_("ERR: Can't open db\n")) ;
2197 for (int i=1; i<ua->argc; i++) {
2198 if (strcasecmp(ua->argk[i], "jobid") == 0) {
2202 jobid = str_to_int64(ua->argv[i]);
2204 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
2205 strcasecmp(ua->argk[i], "job") == 0) {
2209 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
2211 jobid = jcr->JobId ;
2215 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
2219 jcr=get_jcr_by_full_name(ua->argv[i]) ;
2221 jobid = jcr->JobId ;
2225 /* Wait for a mount request */
2226 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
2227 for (bool waiting=false; !waiting; ) {
2229 if (!jcr->is_internal_job() &&
2230 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount ||
2231 jcr->SDJobStatus == JS_WaitMedia || jcr->SDJobStatus == JS_WaitMount))
2241 if (stop_time && (time(NULL) >= stop_time)) {
2242 ua->warning_msg(_("Wait on mount timed out\n"));
2252 ua->error_msg(_("ERR: Job was not found\n"));
2257 * We wait the end of a specific job
2260 bmicrosleep(0, 200000); /* let job actually start */
2261 for (bool running=true; running; ) {
2264 jcr=get_jcr_by_id(jobid) ;
2277 * We have to get JobStatus
2281 char jobstatus = '?'; /* Unknown by default */
2284 bsnprintf(buf, sizeof(buf),
2285 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
2288 db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
2290 switch (jobstatus) {
2292 status = 1 ; /* Warning */
2297 case JS_ErrorTerminated:
2299 status = 2 ; /* Critical */
2304 status = 0 ; /* Ok */
2308 status = 3 ; /* Unknown */
2312 ua->send_msg("JobId=%i\n", jobid) ;
2313 ua->send_msg("JobStatus=%s (%c)\n",
2314 job_status_to_str(jobstatus, 0),
2317 if (ua->gui || ua->api) {
2318 ua->send_msg("ExitStatus=%i\n", status) ;
2325 static int help_cmd(UAContext *ua, const char *cmd)
2328 ua->send_msg(_(" Command Description\n ======= ===========\n"));
2329 for (i=0; i<comsize; i++) {
2330 if (ua->argc == 2) {
2331 if (!strcasecmp(ua->argk[1], commands[i].key)) {
2332 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2333 commands[i].help, commands[i].usage);
2337 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
2340 if (i == comsize && ua->argc == 2) {
2341 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2343 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2347 int qhelp_cmd(UAContext *ua, const char *cmd)
2350 /* Want to display only commands */
2351 j = find_arg(ua, NT_("all"));
2353 for (i=0; i<comsize; i++) {
2354 ua->send_msg("%s\n", commands[i].key);
2358 /* Want to display a specific help section */
2359 j = find_arg_with_value(ua, NT_("item"));
2360 if (j >= 0 && ua->argk[j]) {
2361 for (i=0; i<comsize; i++) {
2362 if (bstrcmp(commands[i].key, ua->argv[j])) {
2363 ua->send_msg("%s\n", commands[i].usage);
2369 /* Want to display everything */
2370 for (i=0; i<comsize; i++) {
2371 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2377 static int version_cmd(UAContext *ua, const char *cmd)
2379 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2380 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2385 * Test code -- turned on only for debug testing
2387 static int version_cmd(UAContext *ua, const char *cmd)
2390 POOL_MEM query(PM_MESSAGE);
2392 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2393 db_get_query_dbids(ua->jcr, ua->db, query, ids);
2394 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2395 for (int i=0; i < ids.num_ids; i++) {
2396 ua->send_msg("id=%d\n", ids.DBId[i]);
2404 * This call uses open_client_db() and force a
2405 * new dedicated connection to the catalog
2407 bool open_new_client_db(UAContext *ua)
2411 /* Force a new dedicated connection */
2412 ua->force_mult_db_connections = true;
2413 ret = open_client_db(ua);
2414 ua->force_mult_db_connections = false;
2420 * This call explicitly checks for a catalog=xxx and
2421 * if given, opens that catalog. It also checks for
2422 * client=xxx and if found, opens the catalog
2423 * corresponding to that client. If we still don't
2424 * have a catalog, look for a Job keyword and get the
2425 * catalog from its client record.
2427 bool open_client_db(UAContext *ua)
2434 /* Try for catalog keyword */
2435 i = find_arg_with_value(ua, NT_("catalog"));
2437 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2438 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2441 catalog = GetCatalogResWithName(ua->argv[i]);
2443 if (ua->catalog && ua->catalog != catalog) {
2446 ua->catalog = catalog;
2451 /* Try for client keyword */
2452 i = find_arg_with_value(ua, NT_("client"));
2454 if (!acl_access_client_ok(ua, ua->argv[i], JT_BACKUP_RESTORE)) {
2455 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2458 client = GetClientResWithName(ua->argv[i]);
2460 catalog = client->catalog;
2461 if (ua->catalog && ua->catalog != catalog) {
2464 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2465 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2468 ua->catalog = catalog;
2473 /* Try for Job keyword */
2474 i = find_arg_with_value(ua, NT_("job"));
2476 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2477 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2480 job = GetJobResWithName(ua->argv[i]);
2482 catalog = job->client->catalog;
2483 if (ua->catalog && ua->catalog != catalog) {
2486 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2487 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2490 ua->catalog = catalog;
2500 * Open the catalog database.
2502 bool open_db(UAContext *ua)
2506 /* With a restricted console, we can't share a SQL connection */
2508 ua->force_mult_db_connections = true;
2511 /* The force_mult_db_connections is telling us if we modify the
2512 * private or the shared link
2514 if (ua->force_mult_db_connections) {
2515 ua->db = ua->private_db;
2518 ua->db = ua->shared_db;
2526 ua->catalog = get_catalog_resource(ua);
2528 ua->error_msg( _("Could not find a Catalog resource\n"));
2533 /* Some modules like bvfs need their own catalog connection */
2534 mult_db_conn = ua->catalog->mult_db_connections;
2535 if (ua->force_mult_db_connections) {
2536 mult_db_conn = true;
2539 ua->jcr->catalog = ua->catalog;
2541 Dmsg0(100, "UA Open database\n");
2542 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2543 ua->catalog->db_name,
2544 ua->catalog->db_user,
2545 ua->catalog->db_password, ua->catalog->db_address,
2546 ua->catalog->db_port, ua->catalog->db_socket,
2547 ua->catalog->db_ssl_mode, ua->catalog->db_ssl_key,
2548 ua->catalog->db_ssl_cert, ua->catalog->db_ssl_ca,
2549 ua->catalog->db_ssl_capath, ua->catalog->db_ssl_cipher,
2550 mult_db_conn, ua->catalog->disable_batch_insert);
2551 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2552 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2553 ua->catalog->db_name);
2555 ua->error_msg("%s", db_strerror(ua->db));
2560 ua->jcr->db = ua->db;
2562 /* Depending on the type of connection, we set the right variable */
2563 if (ua->force_mult_db_connections) {
2564 ua->private_db = ua->db;
2567 ua->shared_db = ua->db;
2569 /* With a restricted console, the DB backend should know restrictions about
2573 ua->db->set_acl(ua->jcr, DB_ACL_JOB, ua->cons->ACL_lists[Job_ACL]);
2574 ua->db->set_acl(ua->jcr, DB_ACL_CLIENT, ua->cons->ACL_lists[Client_ACL]);
2575 ua->db->set_acl(ua->jcr, DB_ACL_POOL, ua->cons->ACL_lists[Pool_ACL]);
2576 ua->db->set_acl(ua->jcr, DB_ACL_FILESET, ua->cons->ACL_lists[FileSet_ACL]);
2578 /* For RestoreClient and BackupClient, we take also in account the Client list */
2579 ua->db->set_acl(ua->jcr, DB_ACL_RCLIENT,
2580 ua->cons->ACL_lists[Client_ACL],
2581 ua->cons->ACL_lists[RestoreClient_ACL]);
2583 ua->db->set_acl(ua->jcr, DB_ACL_BCLIENT,
2584 ua->cons->ACL_lists[Client_ACL],
2585 ua->cons->ACL_lists[BackupClient_ACL]);
2588 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2590 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2594 void close_db(UAContext *ua)
2600 if (ua->shared_db) {
2601 db_close_database(ua->jcr, ua->shared_db);
2602 ua->shared_db = NULL;
2605 if (ua->private_db) {
2606 db_close_database(ua->jcr, ua->private_db);
2607 ua->private_db = NULL;