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> noautoparent"
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);
1746 /* Users may set a device name directly on the command line */
1747 if ((i = find_arg_with_value(ua, "device")) > 0) {
1748 POOLMEM *errmsg = get_pool_memory(PM_NAME);
1749 if (!is_name_valid(ua->argv[i], &errmsg)) {
1750 ua->error_msg(_("Invalid device name. %s"), errmsg);
1751 free_pool_memory(errmsg);
1754 free_pool_memory(errmsg);
1755 bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1757 } else { /* We take the default device name */
1758 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1761 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1762 store.store->media_type, store.store->dev_name(), drive);
1763 Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1765 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1766 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1769 sd = jcr->store_bsock;
1770 bash_spaces(dev_name);
1771 sd->fsend("%s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1772 while (sd->recv() >= 0) {
1773 ua->send_msg("%s", sd->msg);
1775 sd->signal(BNET_TERMINATE);
1776 free_bsock(ua->jcr->store_bsock);
1780 * mount [storage=<name>] [drive=nn] [slot=mm]
1782 static int mount_cmd(UAContext *ua, const char *cmd)
1784 do_storage_cmd(ua, "mount") ; /* mount */
1790 * unmount [storage=<name>] [drive=nn]
1792 static int unmount_cmd(UAContext *ua, const char *cmd)
1794 do_storage_cmd(ua, "unmount"); /* unmount */
1800 * release [storage=<name>] [drive=nn]
1802 static int release_cmd(UAContext *ua, const char *cmd)
1804 do_storage_cmd(ua, "release"); /* release */
1809 * cloud functions, like to upload cached parts to cloud.
1811 int cloud_volumes_cmd(UAContext *ua, const char *cmd, const char *mode)
1815 uint32_t *results = NULL;
1819 char storage[MAX_NAME_LENGTH];
1820 const char *action = mode;
1821 memset(&pr, 0, sizeof(pr));
1824 * Look for all volumes that are enabled and
1825 * have more the 200 bytes.
1828 mr.Recycle = -1; /* All Recycle status */
1829 if (strcmp("prunecache", mode) == 0) {
1830 mr.CacheRetention = 1;
1831 action = "truncate cache";
1834 if (!scan_storage_cmd(ua, cmd, false, /* fromallpool*/
1835 &drive, &mr, &pr, NULL, storage,
1841 if ((sd=open_sd_bsock(ua)) == NULL) {
1842 Dmsg0(100, "Can't open connection to sd\n");
1847 * Loop over the candidate Volumes and upload parts
1849 for (int i=0; i < nb; i++) {
1852 mr.MediaId = results[i];
1853 if (!db_get_media_record(ua->jcr, ua->db, &mr)) {
1857 /* Protect us from spaces */
1858 bash_spaces(mr.VolumeName);
1859 bash_spaces(mr.MediaType);
1860 bash_spaces(pr.Name);
1861 bash_spaces(storage);
1863 sd->fsend("%s Storage=%s Volume=%s PoolName=%s MediaType=%s "
1864 "Slot=%d drive=%d CacheRetention=%lld\n",
1865 action, storage, mr.VolumeName, pr.Name, mr.MediaType,
1866 mr.Slot, drive, mr.CacheRetention);
1868 unbash_spaces(mr.VolumeName);
1869 unbash_spaces(mr.MediaType);
1870 unbash_spaces(pr.Name);
1871 unbash_spaces(storage);
1873 /* Check for valid response */
1874 while (bget_dirmsg(sd) >= 0) {
1875 if (strncmp(sd->msg, "3000 OK truncate cache", 22) == 0) {
1876 ua->send_msg("%s", sd->msg);
1879 } else if (strncmp(sd->msg, "3000 OK", 7) == 0) {
1880 ua->send_msg(_("The volume \"%s\" has been uploaded\n"), mr.VolumeName);
1884 } else if (strncmp(sd->msg, "39", 2) == 0) {
1885 ua->warning_msg("%s", sd->msg);
1888 ua->send_msg("%s", sd->msg);
1892 ua->warning_msg(_("Unable to %s for volume \"%s\"\n"), action, mr.VolumeName);
1899 ua->jcr->wstore = NULL;
1907 /* List volumes in the cloud */
1908 /* TODO: Update the code for .api 2 and llist */
1909 static int cloud_list_cmd(UAContext *ua, const char *cmd)
1912 int64_t size, mtime;
1913 STORE *store = NULL;
1917 char storage[MAX_NAME_LENGTH];
1918 char ed1[50], ed2[50];
1920 uint32_t maxpart=0, part;
1921 uint64_t maxpart_size=0;
1922 memset(&pr, 0, sizeof(pr));
1923 memset(&mr, 0, sizeof(mr));
1925 /* Look at arguments */
1926 for (int i=1; i<ua->argc; i++) {
1927 if (strcasecmp(ua->argk[i], NT_("volume")) == 0
1928 && is_name_valid(ua->argv[i], NULL)) {
1929 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
1931 } else if (strcasecmp(ua->argk[i], NT_("drive")) == 0 && ua->argv[i]) {
1932 drive = atoi(ua->argv[i]);
1936 if (!open_client_db(ua)) {
1940 /* Choose storage */
1941 ua->jcr->wstore = store = get_storage_resource(ua, false);
1945 bstrncpy(storage, store->dev_name(), sizeof(storage));
1946 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
1948 if ((sd=open_sd_bsock(ua)) == NULL) {
1949 Dmsg0(100, "Can't open connection to SD\n");
1953 /* Protect us from spaces */
1954 bash_spaces(mr.MediaType);
1955 bash_spaces(storage);
1956 bash_spaces(mr.VolumeName);
1958 sd->fsend("cloudlist Storage=%s Volume=%s MediaType=%s Slot=%d drive=%d\n",
1959 storage, mr.VolumeName, mr.MediaType, mr.Slot, drive);
1961 if (mr.VolumeName[0]) { /* Want to list parts */
1962 const char *output_hformat="| %8d | %12sB | %20s |\n";
1964 /* Check for valid response */
1965 while (sd->recv() >= 0) {
1966 if (sscanf(sd->msg, "part=%d size=%lld mtime=%lld", &part, &size, &mtime) != 3) {
1967 if (sd->msg[0] == '3') {
1968 ua->send_msg("%s", sd->msg);
1972 /* Print information */
1974 ua->send_msg(_("+----------+---------------+----------------------+\n"));
1975 ua->send_msg(_("| Part | Size | MTime |\n"));
1976 ua->send_msg(_("+----------+---------------+----------------------+\n"));
1979 if (part > maxpart) {
1981 maxpart_size = size;
1984 ua->send_msg(output_hformat, part, edit_uint64_with_suffix(size, ed1), bstrftimes(ed2, sizeof(ed2), mtime));
1987 ua->send_msg(_("+----------+---------------+----------------------+\n"));
1989 /* TODO: See if we fix the catalog record directly */
1990 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1991 POOL_MEM errmsg, tmpmsg;
1992 if (mr.LastPartBytes != maxpart_size) {
1993 Mmsg(tmpmsg, "Error on volume \"%s\". Catalog LastPartBytes mismatch %lld != %lld\n",
1994 mr.VolumeName, mr.LastPartBytes, maxpart_size);
1995 pm_strcpy(errmsg, tmpmsg.c_str());
1997 if (mr.VolCloudParts != maxpart) {
1998 Mmsg(tmpmsg, "Error on volume \"%s\". Catalog VolCloudParts mismatch %ld != %ld\n",
1999 mr.VolumeName, mr.VolCloudParts, maxpart);
2000 pm_strcpy(errmsg, tmpmsg.c_str());
2002 if (strlen(errmsg.c_str()) > 0) {
2003 ua->error_msg("\n%s", errmsg.c_str());
2006 } else { /* TODO: Get the last part if possible? */
2007 const char *output_hformat="| %18s | %9s | %20s | %20s | %12sB |\n";
2009 /* Check for valid response */
2010 while (sd->recv() >= 0) {
2011 if (sscanf(sd->msg, "volume=%127s", mr.VolumeName) != 1) {
2012 if (sd->msg[0] == '3') {
2013 ua->send_msg("%s", sd->msg);
2017 unbash_spaces(mr.VolumeName);
2021 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
2022 memset(&pr, 0, sizeof(POOL_DBR));
2023 pr.PoolId = mr.PoolId;
2024 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
2025 strcpy(pr.Name, "?");
2029 ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2030 ua->send_msg(_("| Volume Name | Status | Media Type | Pool | VolBytes |\n"));
2031 ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2034 /* Print information */
2035 ua->send_msg(output_hformat, mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name,
2036 edit_uint64_with_suffix(mr.VolBytes, ed1));
2040 ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2047 ua->jcr->wstore = NULL;
2051 /* Ask client to create/prune/delete a snapshot via the command line */
2052 static int cloud_cmd(UAContext *ua, const char *cmd)
2054 for (int i=0; i<ua->argc; i++) {
2055 if (strcasecmp(ua->argk[i], NT_("upload")) == 0) {
2056 return cloud_volumes_cmd(ua, cmd, "upload");
2058 } else if (strcasecmp(ua->argk[i], NT_("list")) == 0) {
2059 return cloud_list_cmd(ua, cmd);
2061 } else if (strcasecmp(ua->argk[i], NT_("truncate")) == 0) {
2062 return cloud_volumes_cmd(ua, cmd, "truncate cache");
2064 } else if (strcasecmp(ua->argk[i], NT_("status")) == 0) {
2066 } else if (strcasecmp(ua->argk[i], NT_("prune")) == 0) {
2067 return cloud_volumes_cmd(ua, cmd, "prunecache");
2076 start_prompt(ua, _("Cloud choice: \n"));
2077 add_prompt(ua, _("List Cloud Volumes in the Cloud"));
2078 add_prompt(ua, _("Upload a Volume to the Cloud"));
2079 add_prompt(ua, _("Prune the Cloud Cache"));
2080 add_prompt(ua, _("Truncate a Volume Cache"));
2081 add_prompt(ua, _("Done"));
2083 switch(do_prompt(ua, "", _("Select action to perform on Cloud"), NULL, 0)) {
2084 case 0: /* list cloud */
2085 cloud_list_cmd(ua, cmd);
2087 case 1: /* upload */
2088 cloud_volumes_cmd(ua, cmd, "upload");
2090 case 2: /* Prune cache */
2091 cloud_volumes_cmd(ua, cmd, "prunecache");
2093 case 3: /* Truncate cache */
2094 cloud_volumes_cmd(ua, cmd, "truncate cache");
2097 ua->info_msg(_("Selection terminated.\n"));
2106 * use catalog=<name>
2108 static int use_cmd(UAContext *ua, const char *cmd)
2110 CAT *oldcatalog, *catalog;
2113 close_db(ua); /* close any previously open db */
2114 oldcatalog = ua->catalog;
2116 if (!(catalog = get_catalog_resource(ua))) {
2117 ua->catalog = oldcatalog;
2119 ua->catalog = catalog;
2122 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
2123 ua->catalog->name(), ua->catalog->db_name);
2128 int quit_cmd(UAContext *ua, const char *cmd)
2134 /* Handler to get job status */
2135 static int status_handler(void *ctx, int num_fields, char **row)
2137 char *val = (char *)ctx;
2142 *val = '?'; /* Unknown by default */
2149 * Wait until no job is running
2151 int wait_cmd(UAContext *ua, const char *cmd)
2155 time_t stop_time = 0;
2159 * Wait until no job is running
2161 if (ua->argc == 1) {
2162 bmicrosleep(0, 200000); /* let job actually start */
2163 for (bool running=true; running; ) {
2166 if (!jcr->is_internal_job()) {
2180 i = find_arg_with_value(ua, NT_("timeout"));
2181 if (i > 0 && ua->argv[i]) {
2182 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
2185 /* we have jobid, jobname or ujobid argument */
2187 uint32_t jobid = 0 ;
2189 if (!open_client_db(ua)) {
2190 ua->error_msg(_("ERR: Can't open db\n")) ;
2194 for (int i=1; i<ua->argc; i++) {
2195 if (strcasecmp(ua->argk[i], "jobid") == 0) {
2199 jobid = str_to_int64(ua->argv[i]);
2201 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
2202 strcasecmp(ua->argk[i], "job") == 0) {
2206 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
2208 jobid = jcr->JobId ;
2212 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
2216 jcr=get_jcr_by_full_name(ua->argv[i]) ;
2218 jobid = jcr->JobId ;
2222 /* Wait for a mount request */
2223 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
2224 for (bool waiting=false; !waiting; ) {
2226 if (!jcr->is_internal_job() &&
2227 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount ||
2228 jcr->SDJobStatus == JS_WaitMedia || jcr->SDJobStatus == JS_WaitMount))
2238 if (stop_time && (time(NULL) >= stop_time)) {
2239 ua->warning_msg(_("Wait on mount timed out\n"));
2249 ua->error_msg(_("ERR: Job was not found\n"));
2254 * We wait the end of a specific job
2257 bmicrosleep(0, 200000); /* let job actually start */
2258 for (bool running=true; running; ) {
2261 jcr=get_jcr_by_id(jobid) ;
2274 * We have to get JobStatus
2278 char jobstatus = '?'; /* Unknown by default */
2281 bsnprintf(buf, sizeof(buf),
2282 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
2285 db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
2287 switch (jobstatus) {
2289 status = 1 ; /* Warning */
2294 case JS_ErrorTerminated:
2296 status = 2 ; /* Critical */
2301 status = 0 ; /* Ok */
2305 status = 3 ; /* Unknown */
2309 ua->send_msg("JobId=%i\n", jobid) ;
2310 ua->send_msg("JobStatus=%s (%c)\n",
2311 job_status_to_str(jobstatus, 0),
2314 if (ua->gui || ua->api) {
2315 ua->send_msg("ExitStatus=%i\n", status) ;
2322 static int help_cmd(UAContext *ua, const char *cmd)
2325 ua->send_msg(_(" Command Description\n ======= ===========\n"));
2326 for (i=0; i<comsize; i++) {
2327 if (ua->argc == 2) {
2328 if (!strcasecmp(ua->argk[1], commands[i].key)) {
2329 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2330 commands[i].help, commands[i].usage);
2334 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
2337 if (i == comsize && ua->argc == 2) {
2338 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2340 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2344 int qhelp_cmd(UAContext *ua, const char *cmd)
2347 /* Want to display only commands */
2348 j = find_arg(ua, NT_("all"));
2350 for (i=0; i<comsize; i++) {
2351 ua->send_msg("%s\n", commands[i].key);
2355 /* Want to display a specific help section */
2356 j = find_arg_with_value(ua, NT_("item"));
2357 if (j >= 0 && ua->argk[j]) {
2358 for (i=0; i<comsize; i++) {
2359 if (bstrcmp(commands[i].key, ua->argv[j])) {
2360 ua->send_msg("%s\n", commands[i].usage);
2366 /* Want to display everything */
2367 for (i=0; i<comsize; i++) {
2368 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2374 static int version_cmd(UAContext *ua, const char *cmd)
2376 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2377 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2382 * Test code -- turned on only for debug testing
2384 static int version_cmd(UAContext *ua, const char *cmd)
2387 POOL_MEM query(PM_MESSAGE);
2389 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2390 db_get_query_dbids(ua->jcr, ua->db, query, ids);
2391 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2392 for (int i=0; i < ids.num_ids; i++) {
2393 ua->send_msg("id=%d\n", ids.DBId[i]);
2401 * This call uses open_client_db() and force a
2402 * new dedicated connection to the catalog
2404 bool open_new_client_db(UAContext *ua)
2408 /* Force a new dedicated connection */
2409 ua->force_mult_db_connections = true;
2410 ret = open_client_db(ua);
2411 ua->force_mult_db_connections = false;
2417 * This call explicitly checks for a catalog=xxx and
2418 * if given, opens that catalog. It also checks for
2419 * client=xxx and if found, opens the catalog
2420 * corresponding to that client. If we still don't
2421 * have a catalog, look for a Job keyword and get the
2422 * catalog from its client record.
2424 bool open_client_db(UAContext *ua)
2431 /* Try for catalog keyword */
2432 i = find_arg_with_value(ua, NT_("catalog"));
2434 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2435 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2438 catalog = GetCatalogResWithName(ua->argv[i]);
2440 if (ua->catalog && ua->catalog != catalog) {
2443 ua->catalog = catalog;
2448 /* Try for client keyword */
2449 i = find_arg_with_value(ua, NT_("client"));
2451 if (!acl_access_client_ok(ua, ua->argv[i], JT_BACKUP_RESTORE)) {
2452 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2455 client = GetClientResWithName(ua->argv[i]);
2457 catalog = client->catalog;
2458 if (ua->catalog && ua->catalog != catalog) {
2461 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2462 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2465 ua->catalog = catalog;
2470 /* Try for Job keyword */
2471 i = find_arg_with_value(ua, NT_("job"));
2473 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2474 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2477 job = GetJobResWithName(ua->argv[i]);
2479 catalog = job->client->catalog;
2480 if (ua->catalog && ua->catalog != catalog) {
2483 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2484 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2487 ua->catalog = catalog;
2497 * Open the catalog database.
2499 bool open_db(UAContext *ua)
2503 /* With a restricted console, we can't share a SQL connection */
2505 ua->force_mult_db_connections = true;
2508 /* The force_mult_db_connections is telling us if we modify the
2509 * private or the shared link
2511 if (ua->force_mult_db_connections) {
2512 ua->db = ua->private_db;
2515 ua->db = ua->shared_db;
2523 ua->catalog = get_catalog_resource(ua);
2525 ua->error_msg( _("Could not find a Catalog resource\n"));
2530 /* Some modules like bvfs need their own catalog connection */
2531 mult_db_conn = ua->catalog->mult_db_connections;
2532 if (ua->force_mult_db_connections) {
2533 mult_db_conn = true;
2536 ua->jcr->catalog = ua->catalog;
2538 Dmsg0(100, "UA Open database\n");
2539 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2540 ua->catalog->db_name,
2541 ua->catalog->db_user,
2542 ua->catalog->db_password, ua->catalog->db_address,
2543 ua->catalog->db_port, ua->catalog->db_socket,
2544 ua->catalog->db_ssl_mode, ua->catalog->db_ssl_key,
2545 ua->catalog->db_ssl_cert, ua->catalog->db_ssl_ca,
2546 ua->catalog->db_ssl_capath, ua->catalog->db_ssl_cipher,
2547 mult_db_conn, ua->catalog->disable_batch_insert);
2548 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2549 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2550 ua->catalog->db_name);
2552 ua->error_msg("%s", db_strerror(ua->db));
2557 ua->jcr->db = ua->db;
2559 /* Depending on the type of connection, we set the right variable */
2560 if (ua->force_mult_db_connections) {
2561 ua->private_db = ua->db;
2564 ua->shared_db = ua->db;
2566 /* With a restricted console, the DB backend should know restrictions about
2570 ua->db->set_acl(ua->jcr, DB_ACL_JOB, ua->cons->ACL_lists[Job_ACL]);
2571 ua->db->set_acl(ua->jcr, DB_ACL_CLIENT, ua->cons->ACL_lists[Client_ACL]);
2572 ua->db->set_acl(ua->jcr, DB_ACL_POOL, ua->cons->ACL_lists[Pool_ACL]);
2573 ua->db->set_acl(ua->jcr, DB_ACL_FILESET, ua->cons->ACL_lists[FileSet_ACL]);
2575 /* For RestoreClient and BackupClient, we take also in account the Client list */
2576 ua->db->set_acl(ua->jcr, DB_ACL_RCLIENT,
2577 ua->cons->ACL_lists[Client_ACL],
2578 ua->cons->ACL_lists[RestoreClient_ACL]);
2580 ua->db->set_acl(ua->jcr, DB_ACL_BCLIENT,
2581 ua->cons->ACL_lists[Client_ACL],
2582 ua->cons->ACL_lists[BackupClient_ACL]);
2585 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2587 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2591 void close_db(UAContext *ua)
2597 if (ua->shared_db) {
2598 db_close_database(ua->jcr, ua->shared_db);
2599 ua->shared_db = NULL;
2602 if (ua->private_db) {
2603 db_close_database(ua->jcr, ua->private_db);
2604 ua->private_db = NULL;