2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Bacula Director -- User Agent Commands
24 * Kern Sibbald, September MM
31 /* Imported subroutines */
33 /* Imported variables */
34 extern jobq_t job_queue; /* job queue */
37 /* Imported functions */
38 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
39 extern int gui_cmd(UAContext *ua, const char *cmd);
40 extern int label_cmd(UAContext *ua, const char *cmd);
41 extern int list_cmd(UAContext *ua, const char *cmd);
42 extern int llist_cmd(UAContext *ua, const char *cmd);
43 extern int messagescmd(UAContext *ua, const char *cmd);
44 extern int prunecmd(UAContext *ua, const char *cmd);
45 extern int purge_cmd(UAContext *ua, const char *cmd);
46 extern int truncate_cmd(UAContext *ua, const char *cmd); /* in ua_purge.c */
47 extern int querycmd(UAContext *ua, const char *cmd);
48 extern int relabel_cmd(UAContext *ua, const char *cmd);
49 extern int restore_cmd(UAContext *ua, const char *cmd);
50 extern int retentioncmd(UAContext *ua, const char *cmd);
51 extern int show_cmd(UAContext *ua, const char *cmd);
52 extern int sqlquerycmd(UAContext *ua, const char *cmd);
53 extern int status_cmd(UAContext *ua, const char *cmd);
54 extern int update_cmd(UAContext *ua, const char *cmd);
56 /* Forward referenced functions */
57 static int add_cmd(UAContext *ua, const char *cmd);
58 static int automount_cmd(UAContext *ua, const char *cmd);
59 static int cancel_cmd(UAContext *ua, const char *cmd);
60 static int create_cmd(UAContext *ua, const char *cmd);
61 static int delete_cmd(UAContext *ua, const char *cmd);
62 static int disable_cmd(UAContext *ua, const char *cmd);
63 static int enable_cmd(UAContext *ua, const char *cmd);
64 static int estimate_cmd(UAContext *ua, const char *cmd);
65 static int help_cmd(UAContext *ua, const char *cmd);
66 static int memory_cmd(UAContext *ua, const char *cmd);
67 static int mount_cmd(UAContext *ua, const char *cmd);
68 static int release_cmd(UAContext *ua, const char *cmd);
69 static int reload_cmd(UAContext *ua, const char *cmd);
70 static int setdebug_cmd(UAContext *ua, const char *cmd);
71 static int setbwlimit_cmd(UAContext *ua, const char *cmd);
72 static int setip_cmd(UAContext *ua, const char *cmd);
73 static int time_cmd(UAContext *ua, const char *cmd);
74 static int trace_cmd(UAContext *ua, const char *cmd);
75 static int unmount_cmd(UAContext *ua, const char *cmd);
76 static int use_cmd(UAContext *ua, const char *cmd);
77 static int var_cmd(UAContext *ua, const char *cmd);
78 static int version_cmd(UAContext *ua, const char *cmd);
79 static int wait_cmd(UAContext *ua, const char *cmd);
81 static void do_job_delete(UAContext *ua, JobId_t JobId);
82 static int delete_volume(UAContext *ua);
83 static int delete_pool(UAContext *ua);
84 static void delete_job(UAContext *ua);
85 static void do_storage_cmd(UAContext *ua, const char *command);
87 int qhelp_cmd(UAContext *ua, const char *cmd);
88 int quit_cmd(UAContext *ua, const char *cmd);
90 /* not all in alphabetical order. New commands are added after existing commands with similar letters
91 to prevent breakage of existing user scripts. */
93 const char *key; /* command */
94 int (*func)(UAContext *ua, const char *cmd); /* handler */
95 const char *help; /* main purpose */
96 const char *usage; /* all arguments to build usage */
97 const bool use_in_rs; /* Can use it in Console RunScript */
99 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
100 { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
101 { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
102 { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
103 { 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},
104 { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
105 { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume=<vol-name> | pool=<pool-name> | jobid=<id> | snapshot"), true},
106 { NT_("disable"), disable_cmd, _("Disable a job, attributes batch process"), NT_("job=<name> | batch"), true},
107 { NT_("enable"), enable_cmd, _("Enable a job, attributes batch process"), NT_("job=<name> | batch"), true},
108 { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
109 NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
111 { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
112 { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
113 { NT_("help"), help_cmd, _("Print help on specific command"),
114 NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
115 "\n\tmessages memory mount prune purge quit query\n\trestore relabel release reload run status"
116 "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"
117 "\n\tsnapshot"), false},
119 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> barcodes"), false},
120 { NT_("list"), list_cmd, _("List objects from catalog"),
121 NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [limit=<n>]|\n"
122 "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
123 "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot"), false},
125 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
126 NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [limit=<n>]|\n"
127 "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
128 "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot"), false},
130 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
131 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
132 { NT_("mount"), mount_cmd, _("Mount storage"),
133 NT_("storage=<storage-name> slot=<num> drive=<num> [ device=<device-name> ] [ jobid=<id> | job=<job-name> ]"), false},
135 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
136 NT_("files | jobs | pool=<pool> | snapshot [client=<client-name>] | client=<client-name> | [ expired ] volume=<volume-name> "), true},
138 { NT_("purge"), purge_cmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
139 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
140 { NT_("query"), querycmd, _("Query catalog"), NT_(""), false},
141 { NT_("restore"), restore_cmd, _("Restore files"),
142 NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
144 "\n\tcomment=<text> jobid=<jobid> copies done select all"), false},
146 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
147 NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
149 { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name> [ device=<device-name> ] "), false},
150 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
151 { NT_("run"), run_cmd, _("Run a job"),
152 NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
153 " where=<directory-prefix>\n\twhen=<universal-time-specification> pool=<pool-name>\n\t"
154 " nextpool=<next-pool-name> comment=<text> accurate=<bool> spooldata=<bool> yes"), false},
156 { NT_("restart"), restart_cmd, _("Restart a job"),
157 NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
158 "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
160 { NT_("resume"), restart_cmd, _("Resume a job"),
161 NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
162 "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
164 { NT_("status"), status_cmd, _("Report status"),
165 NT_("all | dir=<dir-name> | director | client=<client-name> |\n"
166 "\tstorage=<storage-name> slots |\n"
167 "\tschedule [job=<job-name>] [days=<nn>] [limit=<nn>]\n"
168 "\t\t[time=<universal-time-specification>]"), true},
170 { NT_("stop"), cancel_cmd, _("Stop a job"), NT_("jobid=<number-list> job=<job-name> ujobid=<unique-jobid> all"), false},
171 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
172 NT_("level=<nn> tags=<tags> trace=0/1 options=<0tTc> tags=<tags> | client=<client-name> | dir | storage=<storage-name> | all"), true},
174 { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"),
175 NT_("limit=<nn-kbs> client=<client-name> jobid=<number> job=<job-name> ujobid=<unique-jobid>"), true},
177 { NT_("snapshot"), snapshot_cmd, _("Handle snapshots"),
178 NT_("[client=<client-name> | job=<job-name> | jobid=<jobid>] [delete | list | listclient | prune | sync | update]"), true},
180 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
181 { NT_("show"), show_cmd, _("Show resource records"),
182 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> | schedule=<sss> | client=<zzz> | storage=<sss> | disabled | all"), true},
184 { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
185 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
186 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
187 { NT_("truncate"), truncate_cmd, _("Truncate one or more Volumes"), NT_("volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
188 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
189 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
191 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
192 NT_("storage=<storage-name> [ drive=<num> ] [ device=<dev-name> ]| jobid=<id> | job=<job-name>"), false},
194 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
195 NT_("stats\n\tsnapshot\n\tpool=<poolname>\n\tslots storage=<storage> scan"
196 "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
197 "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
198 "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
199 "\n\t enabled=<yes/no> recyclepool=<pool> actiononpurge=<action>"
200 "\n\t allfrompool=<pool> fromallpools frompool"),true},
201 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_("catalog=<catalog>"), false},
202 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
203 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
204 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
205 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
208 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
210 const char *get_command(int index) {
211 return commands[index].key;
215 * Execute a command from the UA
217 bool do_a_command(UAContext *ua)
223 BSOCK *user = ua->UA_sock;
226 Dmsg1(900, "Command: %s\n", ua->argk[0]);
231 if (ua->jcr->wstorage) {
232 while (ua->jcr->wstorage->size()) {
233 ua->jcr->wstorage->remove(0);
237 len = strlen(ua->argk[0]);
238 for (i=0; i<comsize; i++) { /* search for command */
239 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
241 /* Check if command permitted, but "quit" is always OK */
242 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
243 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
246 /* Check if this command is authorized in RunScript */
247 if (ua->runscript && !commands[i].use_in_rs) {
248 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
251 if (ua->api) user->signal(BNET_CMD_BEGIN);
252 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
253 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
254 found = (user && user->is_stop()) ? false : true;
259 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
266 * This is a common routine used to stuff the Pool DB record defaults
267 * into the Media DB record just before creating a media (Volume)
270 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
272 mr->PoolId = pr->PoolId;
273 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
274 mr->Recycle = pr->Recycle;
275 mr->VolRetention = pr->VolRetention;
276 mr->VolUseDuration = pr->VolUseDuration;
277 mr->ActionOnPurge = pr->ActionOnPurge;
278 mr->RecyclePoolId = pr->RecyclePoolId;
279 mr->MaxVolJobs = pr->MaxVolJobs;
280 mr->MaxVolFiles = pr->MaxVolFiles;
281 mr->MaxVolBytes = pr->MaxVolBytes;
282 mr->LabelType = pr->LabelType;
288 * Add Volumes to an existing Pool
290 static int add_cmd(UAContext *ua, const char *cmd)
294 int num, i, max, startnum;
295 char name[MAX_NAME_LENGTH];
297 int Slot = 0, InChanger = 0;
300 "You probably don't want to be using this command since it\n"
301 "creates database records without labeling the Volumes.\n"
302 "You probably want to use the \"label\" command.\n\n"));
304 if (!open_client_db(ua)) {
308 memset(&pr, 0, sizeof(pr));
310 if (!get_pool_dbr(ua, &pr)) {
314 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
315 pr.MaxVols, pr.PoolType);
317 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
318 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
319 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
322 pr.MaxVols = ua->pint32_val;
326 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
327 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
328 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
332 if (pr.MaxVols == 0) {
335 max = pr.MaxVols - pr.NumVols;
339 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
340 if (!get_pint(ua, buf)) {
343 num = ua->pint32_val;
344 if (num < 0 || num > max) {
345 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
353 if (!get_cmd(ua, _("Enter Volume name: "))) {
357 if (!get_cmd(ua, _("Enter base volume name: "))) {
361 /* Don't allow | in Volume name because it is the volume separator character */
362 if (!is_volume_name_legal(ua, ua->cmd)) {
365 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
366 ua->warning_msg(_("Volume name too long.\n"));
369 if (strlen(ua->cmd) == 0) {
370 ua->warning_msg(_("Volume name must be at least one character long.\n"));
376 bstrncpy(name, ua->cmd, sizeof(name));
378 bstrncat(name, "%04d", sizeof(name));
381 if (!get_pint(ua, _("Enter the starting number: "))) {
384 startnum = ua->pint32_val;
386 ua->warning_msg(_("Start number must be greater than zero.\n"));
396 if (store && store->autochanger) {
397 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
400 Slot = ua->pint32_val;
401 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
404 InChanger = ua->pint32_val;
407 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
408 for (i=startnum; i < num+startnum; i++) {
409 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
411 mr.InChanger = InChanger;
413 set_storageid_in_mr(store, &mr);
414 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
415 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
416 ua->error_msg("%s", db_strerror(ua->db));
419 // if (i == startnum) {
420 // first_id = mr.PoolId;
424 Dmsg0(200, "Update pool record.\n");
425 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
426 ua->warning_msg("%s", db_strerror(ua->db));
429 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
435 * Turn auto mount on/off
440 int automount_cmd(UAContext *ua, const char *cmd)
445 if (!get_cmd(ua, _("Turn on or off? "))) {
453 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
458 * Cancel/Stop a job -- Stop marks it as Incomplete
459 * so that it can be restarted.
461 static int cancel_cmd(UAContext *ua, const char *cmd)
466 bool cancel = strcasecmp(commands[ua->cmd_index].key, "cancel") == 0;
467 alist *jcrs = New(alist(5, not_owned_by_alist));
469 nb = select_running_jobs(ua, jcrs, commands[ua->cmd_index].key);
471 foreach_alist(jcr, jcrs) {
472 /* Execute the cancel command only if we don't have an error */
474 ret &= cancel_job(ua, jcr, cancel);
484 * This is a common routine to create or update a
485 * Pool DB base record from a Pool Resource. We handle
486 * the setting of MaxVols and NumVols slightly differently
487 * depending on if we are creating the Pool or we are
488 * simply bringing it into agreement with the resource (updage).
490 * Caution : RecyclePoolId isn't setup in this function.
491 * You can use set_pooldbr_recyclepoolid();
494 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
496 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
497 if (op == POOL_OP_CREATE) {
498 pr->MaxVols = pool->max_volumes;
500 } else { /* update pool */
501 if (pr->MaxVols != pool->max_volumes) {
502 pr->MaxVols = pool->max_volumes;
504 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
505 pr->MaxVols = pr->NumVols;
508 pr->LabelType = pool->LabelType;
509 pr->UseOnce = pool->use_volume_once;
510 pr->UseCatalog = pool->use_catalog;
511 pr->Recycle = pool->Recycle;
512 pr->VolRetention = pool->VolRetention;
513 pr->VolUseDuration = pool->VolUseDuration;
514 pr->MaxVolJobs = pool->MaxVolJobs;
515 pr->MaxVolFiles = pool->MaxVolFiles;
516 pr->MaxVolBytes = pool->MaxVolBytes;
517 pr->AutoPrune = pool->AutoPrune;
518 pr->ActionOnPurge = pool->action_on_purge;
519 pr->Recycle = pool->Recycle;
520 if (pool->label_format) {
521 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
523 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
527 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
528 int update_pool_references(JCR *jcr, BDB *db, POOL *pool)
532 if (pool->ScratchPool == pool) {
533 Jmsg(NULL, M_WARNING, 0,
534 _("The ScratchPool directive for Pool \"%s\" is incorrect. Using default Scratch pool instead.\n"),
536 pool->ScratchPool = NULL;
539 if (!pool->RecyclePool && !pool->ScratchPool) {
543 memset(&pr, 0, sizeof(POOL_DBR));
544 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
546 /* Don't compute NumVols here */
547 if (!db_get_pool_record(jcr, db, &pr)) {
548 return -1; /* not exists in database */
551 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
553 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
554 return -1; /* error */
557 /* NumVols is updated here */
558 if (!db_update_pool_record(jcr, db, &pr)) {
559 return -1; /* error */
564 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
565 * works with set_pooldbr_from_poolres
567 bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool)
572 if (pool->RecyclePool) {
573 memset(&rpool, 0, sizeof(POOL_DBR));
575 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
576 if (db_get_pool_record(jcr, db, &rpool)) {
577 pr->RecyclePoolId = rpool.PoolId;
579 Jmsg(jcr, M_WARNING, 0,
580 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
581 "Try to update it with 'update pool=%s'\n"),
582 pool->name(), rpool.Name, rpool.Name,pool->name());
586 } else { /* no RecyclePool used, set it to 0 */
587 pr->RecyclePoolId = 0;
590 if (pool->ScratchPool) {
591 memset(&rpool, 0, sizeof(POOL_DBR));
593 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
594 if (db_get_pool_record(jcr, db, &rpool)) {
595 pr->ScratchPoolId = rpool.PoolId;
597 Jmsg(jcr, M_WARNING, 0,
598 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
599 "Try to update it with 'update pool=%s'\n"),
600 pool->name(), rpool.Name, rpool.Name,pool->name());
603 } else { /* no ScratchPool used, set it to 0 */
604 pr->ScratchPoolId = 0;
612 * Create a pool record from a given Pool resource
613 * Also called from backup.c
614 * Returns: -1 on error
615 * 0 record already exists
619 int create_pool(JCR *jcr, BDB *db, POOL *pool, e_pool_op op)
622 memset(&pr, 0, sizeof(POOL_DBR));
623 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
625 if (db_get_pool_record(jcr, db, &pr)) {
627 if (op == POOL_OP_UPDATE) { /* update request */
628 set_pooldbr_from_poolres(&pr, pool, op);
629 set_pooldbr_references(jcr, db, &pr, pool);
630 db_update_pool_record(jcr, db, &pr);
632 return 0; /* exists */
635 set_pooldbr_from_poolres(&pr, pool, op);
636 set_pooldbr_references(jcr, db, &pr, pool);
638 if (!db_create_pool_record(jcr, db, &pr)) {
639 return -1; /* error */
647 * Create a Pool Record in the database.
648 * It is always created from the Resource record.
650 static int create_cmd(UAContext *ua, const char *cmd)
654 if (!open_client_db(ua)) {
658 pool = get_pool_resource(ua);
663 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
665 ua->error_msg(_("Error: Pool %s already exists.\n"
666 "Use update to change it.\n"), pool->name());
670 ua->error_msg("%s", db_strerror(ua->db));
676 ua->send_msg(_("Pool %s created.\n"), pool->name());
681 extern DIRRES *director;
682 extern char *configfile;
684 static int setbwlimit_client(UAContext *ua, CLIENT *client, char *Job, int64_t limit)
692 /* Connect to File daemon */
693 old_client = ua->jcr->client;
694 ua->jcr->client = client;
695 ua->jcr->max_bandwidth = limit;
697 /* Try to connect for 15 seconds */
698 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
699 client->name(), client->address, client->FDport);
700 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
701 ua->error_msg(_("Failed to connect to Client.\n"));
704 Dmsg0(120, "Connected to file daemon\n");
706 if (!send_bwlimit(ua->jcr, Job)) {
707 ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
710 /* Note, we add 2000 OK that was sent by FD to us to message */
711 ua->info_msg(_("2000 OK Limiting bandwidth to %lldkb/s %s\n"),
712 limit/1024, *Job?Job:_("on running and future jobs"));
715 ua->jcr->file_bsock->signal(BNET_TERMINATE);
716 free_bsock(ua->jcr->file_bsock);
717 ua->jcr->max_bandwidth = 0;
720 ua->jcr->client = old_client;
724 static int setbwlimit_cmd(UAContext *ua, const char *cmd)
727 CLIENT *client = NULL;
728 char Job[MAX_NAME_LENGTH];
734 const char *lst_all[] = { "job", "jobid", "jobname", "client", NULL };
735 if (find_arg_keyword(ua, lst_all) < 0) {
736 start_prompt(ua, _("Set Bandwidth choice:\n"));
737 add_prompt(ua, _("Running Job")); /* 0 */
738 add_prompt(ua, _("Running and future Jobs for a Client")); /* 1 */
739 action = do_prompt(ua, "item", _("Choose where to limit the bandwidth"),
746 i = find_arg_with_value(ua, "limit");
748 limit = atoi(ua->argv[i]) * 1024LL;
751 if (!get_pint(ua, _("Enter new bandwidth limit kb/s: "))) {
754 limit = ua->pint32_val * 1024LL; /* kb/s */
757 const char *lst[] = { "job", "jobid", "jobname", NULL };
758 if (action == 0 || find_arg_keyword(ua, lst) > 0) {
759 alist *jcrs = New(alist(10, not_owned_by_alist));
760 select_running_jobs(ua, jcrs, "limit");
761 foreach_alist(jcr, jcrs) {
762 jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
763 bstrncpy(Job, jcr->Job, sizeof(Job));
764 client = jcr->client;
765 setbwlimit_client(ua, client, Job, limit);
770 client = get_client_resource(ua);
772 setbwlimit_client(ua, client, Job, limit);
779 * Set a new address in a Client resource. We do this only
780 * if the Console name is the same as the Client name
781 * and the Console can access the client.
783 static int setip_cmd(UAContext *ua, const char *cmd)
787 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
788 ua->error_msg(_("Unauthorized command from this console.\n"));
792 client = GetClientResWithName(ua->cons->name());
795 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
798 if (client->address) {
799 free(client->address);
801 /* MA Bug 6 remove ifdef */
802 sockaddr_to_ascii(&(ua->UA_sock->client_addr),
803 sizeof(ua->UA_sock->client_addr), buf, sizeof(buf));
804 client->address = bstrdup(buf);
805 ua->send_msg(_("Client \"%s\" address set to %s\n"),
806 client->name(), client->address);
813 * Does all sorts of enable/disable commands: batch, scheduler (not implemented)
814 * job, client, schedule, storage
816 static void do_enable_disable_cmd(UAContext *ua, bool setting)
819 CLIENT *client = NULL;
823 if (find_arg(ua, NT_("batch")) > 0) {
824 ua->send_msg(_("Job Attributes Insertion %sabled\n"), setting?"en":"dis");
825 db_disable_batch_insert(setting);
830 * if (find_arg(ua, NT_("scheduler")) > 0) {
831 * ua->send_msg(_("Job Scheduler %sabled\n"), setting?"en":"dis");
836 i = find_arg(ua, NT_("job"));
840 job = GetJobResWithName(ua->argv[i]);
843 job = select_enable_disable_job_resource(ua, setting);
850 if (!acl_access_ok(ua, Job_ACL, job->name())) {
851 ua->error_msg(_("Unauthorized command from this console.\n"));
854 job->enabled = setting;
855 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
858 i = find_arg(ua, NT_("client"));
862 client = GetClientResWithName(ua->argv[i]);
865 client = select_enable_disable_client_resource(ua, setting);
872 if (!acl_access_ok(ua, Client_ACL, client->name())) {
873 ua->error_msg(_("Unauthorized command from this console.\n"));
876 client->enabled = setting;
877 ua->send_msg(_("Client \"%s\" %sabled\n"), client->name(), setting?"en":"dis");
880 i = find_arg(ua, NT_("schedule"));
884 sched = (SCHED *)GetResWithName(R_SCHEDULE, ua->argv[i]);
887 sched = select_enable_disable_schedule_resource(ua, setting);
894 if (!acl_access_ok(ua, Schedule_ACL, sched->name())) {
895 ua->error_msg(_("Unauthorized command from this console.\n"));
898 sched->enabled = setting;
899 ua->send_msg(_("Schedule \"%s\" %sabled\n"), sched->name(), setting?"en":"dis");
902 i = find_arg(ua, NT_("storage"));
904 do_storage_cmd(ua, setting?"enable":"disable");
907 if (i < 0 && !sched && !client && !job) {
908 ua->error_msg(_("You must enter one of the following keywords: job, client, schedule, or storage.\n"));
914 static int enable_cmd(UAContext *ua, const char *cmd)
916 do_enable_disable_cmd(ua, true);
920 static int disable_cmd(UAContext *ua, const char *cmd)
922 do_enable_disable_cmd(ua, false);
926 static void do_dir_setdebug(UAContext *ua, int64_t level, int trace_flag, char *options, int64_t tags)
929 debug_level_tags = tags;
930 set_trace(trace_flag);
931 set_debug_flags(options);
934 static void do_storage_setdebug(UAContext *ua, STORE *store,
935 int64_t level, int trace_flag, int hangup, int blowup,
936 char *options, char *tags)
941 lstore.store = store;
942 pm_strcpy(lstore.store_source, _("unknown source"));
943 set_wstorage(ua->jcr, &lstore);
944 /* Try connecting for up to 15 seconds */
945 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
946 store->name(), store->address, store->SDport);
947 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
948 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
951 Dmsg0(120, _("Connected to storage daemon\n"));
952 sd = ua->jcr->store_bsock;
953 sd->fsend("setdebug=%ld trace=%ld hangup=%ld blowup=%ld options=%s tags=%s\n",
954 (int32_t)level, trace_flag, hangup, blowup, options, NPRTB(tags));
955 if (sd->recv() >= 0) {
956 ua->send_msg("%s", sd->msg);
958 sd->signal(BNET_TERMINATE);
959 free_bsock(ua->jcr->store_bsock);
964 * For the client, we have the following values that can be set
965 * level = debug level
966 * trace = send debug output to a file
967 * options = various options for debug or specific FD behavior
968 * hangup = how many records to send to FD before hanging up
969 * obviously this is most useful for testing restarting
971 * blowup = how many records to send to FD before blowing up the FD.
973 static void do_client_setdebug(UAContext *ua, CLIENT *client,
974 int64_t level, int trace, int hangup, int blowup,
975 char *options, char *tags)
980 /* Connect to File daemon */
982 old_client = ua->jcr->client;
983 ua->jcr->client = client;
984 /* Try to connect for 15 seconds */
985 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
986 client->name(), client->address, client->FDport);
987 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
988 ua->error_msg(_("Failed to connect to Client.\n"));
989 ua->jcr->client = old_client;
992 Dmsg0(120, "Connected to file daemon\n");
994 fd = ua->jcr->file_bsock;
995 if (ua->jcr->FDVersion <= 10) {
996 fd->fsend("setdebug=%ld trace=%d hangup=%d\n",
997 (int32_t)level, trace, hangup);
999 fd->fsend("setdebug=%ld trace=%d hangup=%d blowup=%d options=%s tags=%s\n",
1000 (int32_t)level, trace, hangup, blowup, options, NPRTB(tags));
1002 if (fd->recv() >= 0) {
1003 ua->send_msg("%s", fd->msg);
1005 fd->signal(BNET_TERMINATE);
1006 free_bsock(ua->jcr->file_bsock);
1007 ua->jcr->client = old_client;
1012 static void do_all_setdebug(UAContext *ua, int64_t level,
1013 int trace_flag, int hangup, int blowup,
1014 char *options, char *tags)
1016 STORE *store, **unique_store;
1017 CLIENT *client, **unique_client;
1022 debug_parse_tags(tags, &t);
1023 do_dir_setdebug(ua, level, trace_flag, options, t);
1025 /* Count Storage items */
1029 foreach_res(store, R_STORAGE) {
1032 unique_store = (STORE **) malloc(i * sizeof(STORE));
1033 /* Find Unique Storage address/port */
1034 store = (STORE *)GetNextRes(R_STORAGE, NULL);
1036 unique_store[i++] = store;
1037 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
1039 for (j=0; j<i; j++) {
1040 if (strcmp(unique_store[j]->address, store->address) == 0 &&
1041 unique_store[j]->SDport == store->SDport) {
1047 unique_store[i++] = store;
1048 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
1053 /* Call each unique Storage daemon */
1054 for (j=0; j<i; j++) {
1055 do_storage_setdebug(ua, unique_store[j], level, trace_flag,
1056 hangup, blowup, options, tags);
1060 /* Count Client items */
1064 foreach_res(client, R_CLIENT) {
1067 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
1068 /* Find Unique Client address/port */
1069 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
1071 unique_client[i++] = client;
1072 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
1074 for (j=0; j<i; j++) {
1075 if (strcmp(unique_client[j]->address, client->address) == 0 &&
1076 unique_client[j]->FDport == client->FDport) {
1082 unique_client[i++] = client;
1083 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1088 /* Call each unique File daemon */
1089 for (j=0; j<i; j++) {
1090 do_client_setdebug(ua, unique_client[j], level, trace_flag,
1091 hangup, blowup, options, tags);
1093 free(unique_client);
1097 * setdebug level=nn all trace=1/0
1099 static int setdebug_cmd(UAContext *ua, const char *cmd)
1103 int64_t level=0, tags=0;
1104 int trace_flag = -1;
1108 char *tags_str=NULL;
1111 Dmsg1(120, "setdebug:%s:\n", cmd);
1114 i = find_arg_with_value(ua, "options");
1116 bstrncpy(options, ua->argv[i], sizeof(options) - 1);
1119 i = find_arg_with_value(ua, "level");
1121 level = str_to_int64(ua->argv[i]);
1124 if (!get_pint(ua, _("Enter new debug level: "))) {
1127 level = ua->pint32_val;
1130 /* Better to send the tag string instead of tweaking the level
1131 * in case where we extend the tag or change the representation
1133 i = find_arg_with_value(ua, "tags");
1135 tags_str = ua->argv[i];
1136 if (!debug_parse_tags(tags_str, &tags)) {
1137 ua->error_msg(_("Incorrect tags found on command line %s\n"), tags_str);
1142 /* Look for trace flag. -1 => not change */
1143 i = find_arg_with_value(ua, "trace");
1145 trace_flag = atoi(ua->argv[i]);
1146 if (trace_flag > 0) {
1151 /* Look for hangup (debug only) flag. -1 => not change */
1152 i = find_arg_with_value(ua, "hangup");
1154 hangup = atoi(ua->argv[i]);
1157 /* Look for blowup (debug only) flag. -1 => not change */
1158 i = find_arg_with_value(ua, "blowup");
1160 blowup = atoi(ua->argv[i]);
1163 /* General debug? */
1164 for (i=1; i<ua->argc; i++) {
1165 if (strcasecmp(ua->argk[i], "all") == 0) {
1166 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1169 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1170 strcasecmp(ua->argk[i], "director") == 0) {
1171 do_dir_setdebug(ua, level, trace_flag, options, tags);
1174 if (strcasecmp(ua->argk[i], "client") == 0 ||
1175 strcasecmp(ua->argk[i], "fd") == 0) {
1178 client = GetClientResWithName(ua->argv[i]);
1180 do_client_setdebug(ua, client, level, trace_flag,
1181 hangup, blowup, options, tags_str);
1185 client = select_client_resource(ua);
1187 do_client_setdebug(ua, client, level, trace_flag,
1188 hangup, blowup, options, tags_str);
1193 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1194 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1195 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1198 store = GetStoreResWithName(ua->argv[i]);
1200 do_storage_setdebug(ua, store, level, trace_flag,
1201 hangup, blowup, options, tags_str);
1205 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1207 do_storage_setdebug(ua, store, level, trace_flag,
1208 hangup, blowup, options, tags_str);
1214 * We didn't find an appropriate keyword above, so
1217 start_prompt(ua, _("Available daemons are: \n"));
1218 add_prompt(ua, _("Director"));
1219 add_prompt(ua, _("Storage"));
1220 add_prompt(ua, _("Client"));
1221 add_prompt(ua, _("All"));
1222 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1223 case 0: /* Director */
1224 do_dir_setdebug(ua, level, trace_flag, options, tags);
1227 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1229 do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
1234 client = select_client_resource(ua);
1236 do_client_setdebug(ua, client, level, trace_flag, hangup, blowup,
1241 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1250 * Turn debug tracing to file on/off
1252 static int trace_cmd(UAContext *ua, const char *cmd)
1256 if (ua->argc != 2) {
1257 if (!get_cmd(ua, _("Turn on or off? "))) {
1262 onoff = ua->argk[1];
1265 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1269 static int var_cmd(UAContext *ua, const char *cmd)
1271 POOLMEM *val = get_pool_memory(PM_FNAME);
1274 if (!open_client_db(ua)) {
1277 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1280 while (*var == ' ') { /* skip spaces */
1283 Dmsg1(100, "Var=%s:\n", var);
1284 variable_expansion(ua->jcr, var, &val);
1285 ua->send_msg("%s\n", val);
1286 free_pool_memory(val);
1290 static int estimate_cmd(UAContext *ua, const char *cmd)
1293 CLIENT *client = NULL;
1294 FILESET *fileset = NULL;
1296 char since[MAXSTRING];
1300 jcr->setJobLevel(L_FULL);
1301 for (int i=1; i<ua->argc; i++) {
1302 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1303 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1305 client = GetClientResWithName(ua->argv[i]);
1307 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1310 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1311 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1316 ua->error_msg(_("Client name missing.\n"));
1320 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1322 job = GetJobResWithName(ua->argv[i]);
1324 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1327 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1328 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1333 ua->error_msg(_("Job name missing.\n"));
1338 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1340 fileset = GetFileSetResWithName(ua->argv[i]);
1342 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1345 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1346 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1351 ua->error_msg(_("Fileset name missing.\n"));
1355 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1359 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1361 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1362 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1367 ua->error_msg(_("Level value missing.\n"));
1371 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1373 if (!is_yesno(ua->argv[i], &accurate)) {
1374 ua->error_msg(_("Invalid value for accurate. "
1375 "It must be yes or no.\n"));
1380 ua->error_msg(_("Accurate value missing.\n"));
1385 if (!job && !(client && fileset)) {
1386 if (!(job = select_job_resource(ua))) {
1391 job = GetJobResWithName(ua->argk[1]);
1393 ua->error_msg(_("No job specified.\n"));
1396 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1397 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1402 client = job->client;
1405 fileset = job->fileset;
1407 jcr->client = client;
1408 jcr->fileset = fileset;
1410 if (job->pool->catalog) {
1411 ua->catalog = job->pool->catalog;
1413 ua->catalog = client->catalog;
1421 jcr->setJobType(JT_BACKUP);
1422 jcr->start_time = time(NULL);
1423 init_jcr_job_record(jcr);
1425 if (!get_or_create_client_record(jcr)) {
1428 if (!get_or_create_fileset_record(jcr)) {
1432 get_level_since_time(ua->jcr, since, sizeof(since));
1434 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1435 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1436 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1437 ua->error_msg(_("Failed to connect to Client.\n"));
1441 /* The level string change if accurate mode is enabled */
1442 if (accurate >= 0) {
1443 jcr->accurate = accurate;
1445 jcr->accurate = job->accurate;
1448 if (!send_level_command(jcr)) {
1452 if (!send_include_list(jcr)) {
1453 ua->error_msg(_("Error sending include list.\n"));
1457 if (!send_exclude_list(jcr)) {
1458 ua->error_msg(_("Error sending exclude list.\n"));
1463 * If the job is in accurate mode, we send the list of
1466 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1467 if (!send_accurate_current_files(jcr)) {
1471 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1472 while (jcr->file_bsock->recv() >= 0) {
1473 ua->send_msg("%s", jcr->file_bsock->msg);
1477 if (jcr->file_bsock) {
1478 jcr->file_bsock->signal(BNET_TERMINATE);
1479 free_bsock(ua->jcr->file_bsock);
1487 static int time_cmd(UAContext *ua, const char *cmd)
1490 time_t ttime = time(NULL);
1492 (void)localtime_r(&ttime, &tm);
1493 strftime(sdt, sizeof(sdt), "%a %d-%b-%Y %H:%M:%S", &tm);
1494 ua->send_msg("%s\n", sdt);
1499 * reload the conf file
1501 extern "C" void reload_config(int sig);
1503 static int reload_cmd(UAContext *ua, const char *cmd)
1510 * Delete Pool records (should purge Media with it).
1512 * delete pool=<pool-name>
1513 * delete volume pool=<pool-name> volume=<name>
1516 static int delete_cmd(UAContext *ua, const char *cmd)
1518 static const char *keywords[] = {
1525 /* Deleting large jobs can take time! */
1526 if (!open_new_client_db(ua)) {
1530 switch (find_arg_keyword(ua, keywords)) {
1539 while ((i=find_arg(ua, "jobid")) > 0) {
1541 *ua->argk[i] = 0; /* zap keyword already visited */
1545 delete_snapshot(ua);
1552 "In general it is not a good idea to delete either a\n"
1553 "Pool or a Volume since they may contain data.\n\n"));
1555 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1566 delete_snapshot(ua);
1569 ua->warning_msg(_("Nothing done.\n"));
1576 * delete_job has been modified to parse JobID lists like the
1578 * delete JobID=3,4,6,7-11,14
1580 * Thanks to Phil Stracchino for the above addition.
1582 static void delete_job(UAContext *ua)
1584 int JobId; /* not JobId_t because it's unsigned and not compatible with sellist */
1588 int i = find_arg_with_value(ua, NT_("jobid"));
1590 if (!sl.set_string(ua->argv[i], true)) {
1591 ua->warning_msg("%s", sl.get_errmsg());
1595 if (sl.size() > 25 && (find_arg(ua, "yes") < 0)) {
1596 bsnprintf(buf, sizeof(buf),
1597 _("Are you sure you want to delete %d JobIds ? (yes/no): "), sl.size());
1598 if (!get_yesno(ua, buf)) {
1603 foreach_sellist(JobId, &sl) {
1604 do_job_delete(ua, JobId);
1607 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1611 JobId = ua->int64_val;
1612 do_job_delete(ua, JobId);
1617 * do_job_delete now performs the actual delete operation atomically
1619 static void do_job_delete(UAContext *ua, JobId_t JobId)
1623 edit_int64(JobId, ed1);
1624 purge_jobs_from_catalog(ua, ed1);
1625 ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1);
1629 * Delete media records from database -- dangerous
1631 static int delete_volume(UAContext *ua)
1637 if (!select_media_dbr(ua, &mr)) {
1640 ua->warning_msg(_("\nThis command will delete volume %s\n"
1641 "and all Jobs saved on that volume from the Catalog\n"),
1644 if (find_arg(ua, "yes") >= 0) {
1645 ua->pint32_val = 1; /* Have "yes" on command line already" */
1647 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1649 if (!get_yesno(ua, buf)) {
1653 if (!ua->pint32_val) {
1657 /* If not purged, do it */
1658 if (strcmp(mr.VolStatus, "Purged") != 0) {
1659 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1660 ua->error_msg(_("Can't list jobs on this volume\n"));
1664 purge_jobs_from_catalog(ua, lst.list);
1668 db_delete_media_record(ua->jcr, ua->db, &mr);
1673 * Delete a pool record from the database -- dangerous
1675 static int delete_pool(UAContext *ua)
1680 memset(&pr, 0, sizeof(pr));
1682 if (!get_pool_dbr(ua, &pr)) {
1685 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1687 if (!get_yesno(ua, buf)) {
1690 if (ua->pint32_val) {
1691 db_delete_pool_record(ua->jcr, ua->db, &pr);
1696 int memory_cmd(UAContext *ua, const char *cmd)
1698 garbage_collect_memory();
1699 list_dir_status_header(ua);
1700 sm_dump(false, true);
1704 static void do_storage_cmd(UAContext *ua, const char *command)
1709 char dev_name[MAX_NAME_LENGTH];
1713 if (!open_client_db(ua)) {
1716 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1718 store.store = get_storage_resource(ua, true/*arg is storage*/);
1722 pm_strcpy(store.store_source, _("unknown source"));
1723 set_wstorage(jcr, &store);
1724 drive = get_storage_drive(ua, store.store);
1725 slot = get_storage_slot(ua, store.store);
1727 /* Users may set a device name directly on the command line */
1728 if ((i = find_arg_with_value(ua, "device")) > 0) {
1729 POOLMEM *errmsg = get_pool_memory(PM_NAME);
1730 if (!is_name_valid(ua->argv[i], &errmsg)) {
1731 ua->error_msg(_("Invalid device name. %s"), errmsg);
1732 free_pool_memory(errmsg);
1735 free_pool_memory(errmsg);
1736 bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1738 } else { /* We take the default device name */
1739 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1742 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1743 store.store->media_type, store.store->dev_name(), drive);
1744 Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1746 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1747 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1750 sd = jcr->store_bsock;
1751 bash_spaces(dev_name);
1752 sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1753 while (sd->recv() >= 0) {
1754 ua->send_msg("%s", sd->msg);
1756 sd->signal(BNET_TERMINATE);
1757 free_bsock(ua->jcr->store_bsock);
1761 * mount [storage=<name>] [drive=nn] [slot=mm]
1763 static int mount_cmd(UAContext *ua, const char *cmd)
1765 do_storage_cmd(ua, "mount") ; /* mount */
1771 * unmount [storage=<name>] [drive=nn]
1773 static int unmount_cmd(UAContext *ua, const char *cmd)
1775 do_storage_cmd(ua, "unmount"); /* unmount */
1781 * release [storage=<name>] [drive=nn]
1783 static int release_cmd(UAContext *ua, const char *cmd)
1785 do_storage_cmd(ua, "release"); /* release */
1792 * use catalog=<name>
1794 static int use_cmd(UAContext *ua, const char *cmd)
1796 CAT *oldcatalog, *catalog;
1799 close_db(ua); /* close any previously open db */
1800 oldcatalog = ua->catalog;
1802 if (!(catalog = get_catalog_resource(ua))) {
1803 ua->catalog = oldcatalog;
1805 ua->catalog = catalog;
1808 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1809 ua->catalog->name(), ua->catalog->db_name);
1814 int quit_cmd(UAContext *ua, const char *cmd)
1820 /* Handler to get job status */
1821 static int status_handler(void *ctx, int num_fields, char **row)
1823 char *val = (char *)ctx;
1828 *val = '?'; /* Unknown by default */
1835 * Wait until no job is running
1837 int wait_cmd(UAContext *ua, const char *cmd)
1841 time_t stop_time = 0;
1845 * Wait until no job is running
1847 if (ua->argc == 1) {
1848 bmicrosleep(0, 200000); /* let job actually start */
1849 for (bool running=true; running; ) {
1852 if (!jcr->is_internal_job()) {
1866 i = find_arg_with_value(ua, NT_("timeout"));
1867 if (i > 0 && ua->argv[i]) {
1868 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1871 /* we have jobid, jobname or ujobid argument */
1873 uint32_t jobid = 0 ;
1875 if (!open_client_db(ua)) {
1876 ua->error_msg(_("ERR: Can't open db\n")) ;
1880 for (int i=1; i<ua->argc; i++) {
1881 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1885 jobid = str_to_int64(ua->argv[i]);
1887 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1888 strcasecmp(ua->argk[i], "job") == 0) {
1892 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1894 jobid = jcr->JobId ;
1898 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1902 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1904 jobid = jcr->JobId ;
1908 /* Wait for a mount request */
1909 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1910 for (bool waiting=false; !waiting; ) {
1912 if (!jcr->is_internal_job() &&
1913 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1922 if (stop_time && (time(NULL) >= stop_time)) {
1923 ua->warning_msg(_("Wait on mount timed out\n"));
1933 ua->error_msg(_("ERR: Job was not found\n"));
1938 * We wait the end of a specific job
1941 bmicrosleep(0, 200000); /* let job actually start */
1942 for (bool running=true; running; ) {
1945 jcr=get_jcr_by_id(jobid) ;
1958 * We have to get JobStatus
1962 char jobstatus = '?'; /* Unknown by default */
1965 bsnprintf(buf, sizeof(buf),
1966 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1969 db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
1971 switch (jobstatus) {
1973 status = 1 ; /* Warning */
1978 case JS_ErrorTerminated:
1980 status = 2 ; /* Critical */
1985 status = 0 ; /* Ok */
1989 status = 3 ; /* Unknown */
1993 ua->send_msg("JobId=%i\n", jobid) ;
1994 ua->send_msg("JobStatus=%s (%c)\n",
1995 job_status_to_str(jobstatus, 0),
1998 if (ua->gui || ua->api) {
1999 ua->send_msg("ExitStatus=%i\n", status) ;
2006 static int help_cmd(UAContext *ua, const char *cmd)
2009 ua->send_msg(_(" Command Description\n ======= ===========\n"));
2010 for (i=0; i<comsize; i++) {
2011 if (ua->argc == 2) {
2012 if (!strcasecmp(ua->argk[1], commands[i].key)) {
2013 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2014 commands[i].help, commands[i].usage);
2018 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
2021 if (i == comsize && ua->argc == 2) {
2022 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2024 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2028 int qhelp_cmd(UAContext *ua, const char *cmd)
2031 /* Want to display only commands */
2032 j = find_arg(ua, NT_("all"));
2034 for (i=0; i<comsize; i++) {
2035 ua->send_msg("%s\n", commands[i].key);
2039 /* Want to display a specific help section */
2040 j = find_arg_with_value(ua, NT_("item"));
2041 if (j >= 0 && ua->argk[j]) {
2042 for (i=0; i<comsize; i++) {
2043 if (bstrcmp(commands[i].key, ua->argv[j])) {
2044 ua->send_msg("%s\n", commands[i].usage);
2050 /* Want to display everything */
2051 for (i=0; i<comsize; i++) {
2052 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2058 static int version_cmd(UAContext *ua, const char *cmd)
2060 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2061 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2066 * Test code -- turned on only for debug testing
2068 static int version_cmd(UAContext *ua, const char *cmd)
2071 POOL_MEM query(PM_MESSAGE);
2073 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2074 db_get_query_dbids(ua->jcr, ua->db, query, ids);
2075 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2076 for (int i=0; i < ids.num_ids; i++) {
2077 ua->send_msg("id=%d\n", ids.DBId[i]);
2085 * This call uses open_client_db() and force a
2086 * new dedicated connection to the catalog
2088 bool open_new_client_db(UAContext *ua)
2092 /* Force a new dedicated connection */
2093 ua->force_mult_db_connections = true;
2094 ret = open_client_db(ua);
2095 ua->force_mult_db_connections = false;
2101 * This call explicitly checks for a catalog=xxx and
2102 * if given, opens that catalog. It also checks for
2103 * client=xxx and if found, opens the catalog
2104 * corresponding to that client. If we still don't
2105 * have a catalog, look for a Job keyword and get the
2106 * catalog from its client record.
2108 bool open_client_db(UAContext *ua)
2115 /* Try for catalog keyword */
2116 i = find_arg_with_value(ua, NT_("catalog"));
2118 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2119 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2122 catalog = GetCatalogResWithName(ua->argv[i]);
2124 if (ua->catalog && ua->catalog != catalog) {
2127 ua->catalog = catalog;
2132 /* Try for client keyword */
2133 i = find_arg_with_value(ua, NT_("client"));
2135 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2136 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2139 client = GetClientResWithName(ua->argv[i]);
2141 catalog = client->catalog;
2142 if (ua->catalog && ua->catalog != catalog) {
2145 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2146 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2149 ua->catalog = catalog;
2154 /* Try for Job keyword */
2155 i = find_arg_with_value(ua, NT_("job"));
2157 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2158 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2161 job = GetJobResWithName(ua->argv[i]);
2163 catalog = job->client->catalog;
2164 if (ua->catalog && ua->catalog != catalog) {
2167 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2168 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2171 ua->catalog = catalog;
2181 * Open the catalog database.
2183 bool open_db(UAContext *ua)
2187 /* The force_mult_db_connections is telling us if we modify the
2188 * private or the shared link
2190 if (ua->force_mult_db_connections) {
2191 ua->db = ua->private_db;
2194 ua->db = ua->shared_db;
2202 ua->catalog = get_catalog_resource(ua);
2204 ua->error_msg( _("Could not find a Catalog resource\n"));
2209 /* Some modules like bvfs need their own catalog connection */
2210 mult_db_conn = ua->catalog->mult_db_connections;
2211 if (ua->force_mult_db_connections) {
2212 mult_db_conn = true;
2215 ua->jcr->catalog = ua->catalog;
2217 Dmsg0(100, "UA Open database\n");
2218 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2219 ua->catalog->db_name,
2220 ua->catalog->db_user,
2221 ua->catalog->db_password, ua->catalog->db_address,
2222 ua->catalog->db_port, ua->catalog->db_socket,
2223 ua->catalog->db_ssl_key, ua->catalog->db_ssl_cert,
2224 ua->catalog->db_ssl_ca, ua->catalog->db_ssl_capath,
2225 ua->catalog->db_ssl_cipher,
2226 mult_db_conn, ua->catalog->disable_batch_insert);
2227 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2228 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2229 ua->catalog->db_name);
2231 ua->error_msg("%s", db_strerror(ua->db));
2236 ua->jcr->db = ua->db;
2238 /* Depending on the type of connection, we set the right variable */
2239 if (ua->force_mult_db_connections) {
2240 ua->private_db = ua->db;
2243 ua->shared_db = ua->db;
2247 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2249 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2253 void close_db(UAContext *ua)
2259 if (ua->shared_db) {
2260 db_close_database(ua->jcr, ua->shared_db);
2261 ua->shared_db = NULL;
2264 if (ua->private_db) {
2265 db_close_database(ua->jcr, ua->private_db);
2266 ua->private_db = NULL;