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"),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 while (ua->jcr->wstorage->size()) {
232 ua->jcr->wstorage->remove(0);
235 len = strlen(ua->argk[0]);
236 for (i=0; i<comsize; i++) { /* search for command */
237 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
239 /* Check if command permitted, but "quit" is always OK */
240 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
241 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
244 /* Check if this command is authorized in RunScript */
245 if (ua->runscript && !commands[i].use_in_rs) {
246 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
249 if (ua->api) user->signal(BNET_CMD_BEGIN);
250 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
251 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
252 found = (user && user->is_stop()) ? false : true;
257 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
264 * This is a common routine used to stuff the Pool DB record defaults
265 * into the Media DB record just before creating a media (Volume)
268 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
270 mr->PoolId = pr->PoolId;
271 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
272 mr->Recycle = pr->Recycle;
273 mr->VolRetention = pr->VolRetention;
274 mr->VolUseDuration = pr->VolUseDuration;
275 mr->ActionOnPurge = pr->ActionOnPurge;
276 mr->RecyclePoolId = pr->RecyclePoolId;
277 mr->MaxVolJobs = pr->MaxVolJobs;
278 mr->MaxVolFiles = pr->MaxVolFiles;
279 mr->MaxVolBytes = pr->MaxVolBytes;
280 mr->LabelType = pr->LabelType;
286 * Add Volumes to an existing Pool
288 static int add_cmd(UAContext *ua, const char *cmd)
292 int num, i, max, startnum;
293 char name[MAX_NAME_LENGTH];
295 int Slot = 0, InChanger = 0;
298 "You probably don't want to be using this command since it\n"
299 "creates database records without labeling the Volumes.\n"
300 "You probably want to use the \"label\" command.\n\n"));
302 if (!open_client_db(ua)) {
306 memset(&pr, 0, sizeof(pr));
308 if (!get_pool_dbr(ua, &pr)) {
312 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
313 pr.MaxVols, pr.PoolType);
315 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
316 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
317 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
320 pr.MaxVols = ua->pint32_val;
324 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
325 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
326 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
330 if (pr.MaxVols == 0) {
333 max = pr.MaxVols - pr.NumVols;
337 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
338 if (!get_pint(ua, buf)) {
341 num = ua->pint32_val;
342 if (num < 0 || num > max) {
343 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
351 if (!get_cmd(ua, _("Enter Volume name: "))) {
355 if (!get_cmd(ua, _("Enter base volume name: "))) {
359 /* Don't allow | in Volume name because it is the volume separator character */
360 if (!is_volume_name_legal(ua, ua->cmd)) {
363 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
364 ua->warning_msg(_("Volume name too long.\n"));
367 if (strlen(ua->cmd) == 0) {
368 ua->warning_msg(_("Volume name must be at least one character long.\n"));
374 bstrncpy(name, ua->cmd, sizeof(name));
376 bstrncat(name, "%04d", sizeof(name));
379 if (!get_pint(ua, _("Enter the starting number: "))) {
382 startnum = ua->pint32_val;
384 ua->warning_msg(_("Start number must be greater than zero.\n"));
394 if (store && store->autochanger) {
395 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
398 Slot = ua->pint32_val;
399 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
402 InChanger = ua->pint32_val;
405 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
406 for (i=startnum; i < num+startnum; i++) {
407 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
409 mr.InChanger = InChanger;
411 set_storageid_in_mr(store, &mr);
412 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
413 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
414 ua->error_msg("%s", db_strerror(ua->db));
417 // if (i == startnum) {
418 // first_id = mr.PoolId;
422 Dmsg0(200, "Update pool record.\n");
423 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
424 ua->warning_msg("%s", db_strerror(ua->db));
427 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
433 * Turn auto mount on/off
438 int automount_cmd(UAContext *ua, const char *cmd)
443 if (!get_cmd(ua, _("Turn on or off? "))) {
451 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
456 * Cancel/Stop a job -- Stop marks it as Incomplete
457 * so that it can be restarted.
459 static int cancel_cmd(UAContext *ua, const char *cmd)
464 bool cancel = strcasecmp(commands[ua->cmd_index].key, "cancel") == 0;
465 alist *jcrs = New(alist(5, not_owned_by_alist));
467 nb = select_running_jobs(ua, jcrs, commands[ua->cmd_index].key);
469 foreach_alist(jcr, jcrs) {
470 /* Execute the cancel command only if we don't have an error */
472 ret &= cancel_job(ua, jcr, cancel);
482 * This is a common routine to create or update a
483 * Pool DB base record from a Pool Resource. We handle
484 * the setting of MaxVols and NumVols slightly differently
485 * depending on if we are creating the Pool or we are
486 * simply bringing it into agreement with the resource (updage).
488 * Caution : RecyclePoolId isn't setup in this function.
489 * You can use set_pooldbr_recyclepoolid();
492 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
494 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
495 if (op == POOL_OP_CREATE) {
496 pr->MaxVols = pool->max_volumes;
498 } else { /* update pool */
499 if (pr->MaxVols != pool->max_volumes) {
500 pr->MaxVols = pool->max_volumes;
502 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
503 pr->MaxVols = pr->NumVols;
506 pr->LabelType = pool->LabelType;
507 pr->UseOnce = pool->use_volume_once;
508 pr->UseCatalog = pool->use_catalog;
509 pr->Recycle = pool->Recycle;
510 pr->VolRetention = pool->VolRetention;
511 pr->VolUseDuration = pool->VolUseDuration;
512 pr->MaxVolJobs = pool->MaxVolJobs;
513 pr->MaxVolFiles = pool->MaxVolFiles;
514 pr->MaxVolBytes = pool->MaxVolBytes;
515 pr->AutoPrune = pool->AutoPrune;
516 pr->ActionOnPurge = pool->action_on_purge;
517 pr->Recycle = pool->Recycle;
518 if (pool->label_format) {
519 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
521 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
525 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
526 int update_pool_references(JCR *jcr, BDB *db, POOL *pool)
530 if (!pool->RecyclePool && !pool->ScratchPool) {
534 memset(&pr, 0, sizeof(POOL_DBR));
535 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
537 /* Don't compute NumVols here */
538 if (!db_get_pool_record(jcr, db, &pr)) {
539 return -1; /* not exists in database */
542 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
544 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
545 return -1; /* error */
548 /* NumVols is updated here */
549 if (!db_update_pool_record(jcr, db, &pr)) {
550 return -1; /* error */
555 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
556 * works with set_pooldbr_from_poolres
558 bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool)
563 if (pool->RecyclePool) {
564 memset(&rpool, 0, sizeof(POOL_DBR));
566 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
567 if (db_get_pool_record(jcr, db, &rpool)) {
568 pr->RecyclePoolId = rpool.PoolId;
570 Jmsg(jcr, M_WARNING, 0,
571 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
572 "Try to update it with 'update pool=%s'\n"),
573 pool->name(), rpool.Name, rpool.Name,pool->name());
577 } else { /* no RecyclePool used, set it to 0 */
578 pr->RecyclePoolId = 0;
581 if (pool->ScratchPool) {
582 memset(&rpool, 0, sizeof(POOL_DBR));
584 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
585 if (db_get_pool_record(jcr, db, &rpool)) {
586 pr->ScratchPoolId = rpool.PoolId;
588 Jmsg(jcr, M_WARNING, 0,
589 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
590 "Try to update it with 'update pool=%s'\n"),
591 pool->name(), rpool.Name, rpool.Name,pool->name());
594 } else { /* no ScratchPool used, set it to 0 */
595 pr->ScratchPoolId = 0;
603 * Create a pool record from a given Pool resource
604 * Also called from backup.c
605 * Returns: -1 on error
606 * 0 record already exists
610 int create_pool(JCR *jcr, BDB *db, POOL *pool, e_pool_op op)
613 memset(&pr, 0, sizeof(POOL_DBR));
614 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
616 if (db_get_pool_record(jcr, db, &pr)) {
618 if (op == POOL_OP_UPDATE) { /* update request */
619 set_pooldbr_from_poolres(&pr, pool, op);
620 set_pooldbr_references(jcr, db, &pr, pool);
621 db_update_pool_record(jcr, db, &pr);
623 return 0; /* exists */
626 set_pooldbr_from_poolres(&pr, pool, op);
627 set_pooldbr_references(jcr, db, &pr, pool);
629 if (!db_create_pool_record(jcr, db, &pr)) {
630 return -1; /* error */
638 * Create a Pool Record in the database.
639 * It is always created from the Resource record.
641 static int create_cmd(UAContext *ua, const char *cmd)
645 if (!open_client_db(ua)) {
649 pool = get_pool_resource(ua);
654 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
656 ua->error_msg(_("Error: Pool %s already exists.\n"
657 "Use update to change it.\n"), pool->name());
661 ua->error_msg("%s", db_strerror(ua->db));
667 ua->send_msg(_("Pool %s created.\n"), pool->name());
672 extern DIRRES *director;
673 extern char *configfile;
675 static int setbwlimit_client(UAContext *ua, CLIENT *client, char *Job, int64_t limit)
683 /* Connect to File daemon */
684 old_client = ua->jcr->client;
685 ua->jcr->client = client;
686 ua->jcr->max_bandwidth = limit;
688 /* Try to connect for 15 seconds */
689 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
690 client->name(), client->address, client->FDport);
691 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
692 ua->error_msg(_("Failed to connect to Client.\n"));
695 Dmsg0(120, "Connected to file daemon\n");
697 if (!send_bwlimit(ua->jcr, Job)) {
698 ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
701 /* Note, we add 2000 OK that was sent by FD to us to message */
702 ua->info_msg(_("2000 OK Limiting bandwidth to %lldkb/s %s\n"),
703 limit/1024, *Job?Job:_("on running and future jobs"));
706 ua->jcr->file_bsock->signal(BNET_TERMINATE);
707 free_bsock(ua->jcr->file_bsock);
708 ua->jcr->max_bandwidth = 0;
711 ua->jcr->client = old_client;
715 static int setbwlimit_cmd(UAContext *ua, const char *cmd)
718 CLIENT *client = NULL;
719 char Job[MAX_NAME_LENGTH];
725 const char *lst_all[] = { "job", "jobid", "jobname", "client", NULL };
726 if (find_arg_keyword(ua, lst_all) < 0) {
727 start_prompt(ua, _("Set Bandwidth choice:\n"));
728 add_prompt(ua, _("Running Job")); /* 0 */
729 add_prompt(ua, _("Running and future Jobs for a Client")); /* 1 */
730 action = do_prompt(ua, "item", _("Choose where to limit the bandwidth"),
737 i = find_arg_with_value(ua, "limit");
739 limit = atoi(ua->argv[i]) * 1024LL;
742 if (!get_pint(ua, _("Enter new bandwidth limit kb/s: "))) {
745 limit = ua->pint32_val * 1024LL; /* kb/s */
748 const char *lst[] = { "job", "jobid", "jobname", NULL };
749 if (action == 0 || find_arg_keyword(ua, lst) > 0) {
750 alist *jcrs = New(alist(10, not_owned_by_alist));
751 select_running_jobs(ua, jcrs, "limit");
752 foreach_alist(jcr, jcrs) {
753 jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
754 bstrncpy(Job, jcr->Job, sizeof(Job));
755 client = jcr->client;
756 setbwlimit_client(ua, client, Job, limit);
761 client = get_client_resource(ua);
763 setbwlimit_client(ua, client, Job, limit);
770 * Set a new address in a Client resource. We do this only
771 * if the Console name is the same as the Client name
772 * and the Console can access the client.
774 static int setip_cmd(UAContext *ua, const char *cmd)
778 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
779 ua->error_msg(_("Unauthorized command from this console.\n"));
783 client = GetClientResWithName(ua->cons->name());
786 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
789 if (client->address) {
790 free(client->address);
792 /* MA Bug 6 remove ifdef */
793 sockaddr_to_ascii(&(ua->UA_sock->client_addr),
794 sizeof(ua->UA_sock->client_addr), buf, sizeof(buf));
795 client->address = bstrdup(buf);
796 ua->send_msg(_("Client \"%s\" address set to %s\n"),
797 client->name(), client->address);
804 * Does all sorts of enable/disable commands: batch, scheduler (not implemented)
805 * job, client, schedule, storage
807 static void do_enable_disable_cmd(UAContext *ua, bool setting)
810 CLIENT *client = NULL;
814 if (find_arg(ua, NT_("batch")) > 0) {
815 ua->send_msg(_("Job Attributes Insertion %sabled\n"), setting?"en":"dis");
816 db_disable_batch_insert(setting);
821 * if (find_arg(ua, NT_("scheduler")) > 0) {
822 * ua->send_msg(_("Job Scheduler %sabled\n"), setting?"en":"dis");
827 i = find_arg(ua, NT_("job"));
831 job = GetJobResWithName(ua->argv[i]);
834 job = select_enable_disable_job_resource(ua, setting);
841 if (!acl_access_ok(ua, Job_ACL, job->name())) {
842 ua->error_msg(_("Unauthorized command from this console.\n"));
845 job->enabled = setting;
846 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
849 i = find_arg(ua, NT_("client"));
853 client = GetClientResWithName(ua->argv[i]);
856 client = select_enable_disable_client_resource(ua, setting);
863 if (!acl_access_ok(ua, Client_ACL, client->name())) {
864 ua->error_msg(_("Unauthorized command from this console.\n"));
867 client->enabled = setting;
868 ua->send_msg(_("Client \"%s\" %sabled\n"), client->name(), setting?"en":"dis");
871 i = find_arg(ua, NT_("schedule"));
875 sched = (SCHED *)GetResWithName(R_SCHEDULE, ua->argv[i]);
878 sched = select_enable_disable_schedule_resource(ua, setting);
885 if (!acl_access_ok(ua, Schedule_ACL, sched->name())) {
886 ua->error_msg(_("Unauthorized command from this console.\n"));
889 sched->enabled = setting;
890 ua->send_msg(_("Schedule \"%s\" %sabled\n"), sched->name(), setting?"en":"dis");
893 i = find_arg(ua, NT_("storage"));
895 do_storage_cmd(ua, setting?"enable":"disable");
898 if (i < 0 && !sched && !client && !job) {
899 ua->error_msg(_("You must enter one of the following keywords: job, client, schedule, or storage.\n"));
905 static int enable_cmd(UAContext *ua, const char *cmd)
907 do_enable_disable_cmd(ua, true);
911 static int disable_cmd(UAContext *ua, const char *cmd)
913 do_enable_disable_cmd(ua, false);
917 static void do_dir_setdebug(UAContext *ua, int64_t level, int trace_flag, char *options, int64_t tags)
920 debug_level_tags = tags;
921 set_trace(trace_flag);
922 set_debug_flags(options);
925 static void do_storage_setdebug(UAContext *ua, STORE *store,
926 int64_t level, int trace_flag, int hangup, int blowup,
927 char *options, char *tags)
932 lstore.store = store;
933 pm_strcpy(lstore.store_source, _("unknown source"));
934 set_wstorage(ua->jcr, &lstore);
935 /* Try connecting for up to 15 seconds */
936 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
937 store->name(), store->address, store->SDport);
938 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
939 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
942 Dmsg0(120, _("Connected to storage daemon\n"));
943 sd = ua->jcr->store_bsock;
944 sd->fsend("setdebug=%ld trace=%ld hangup=%ld blowup=%ld options=%s tags=%s\n",
945 (int32_t)level, trace_flag, hangup, blowup, options, NPRTB(tags));
946 if (sd->recv() >= 0) {
947 ua->send_msg("%s", sd->msg);
949 sd->signal(BNET_TERMINATE);
950 free_bsock(ua->jcr->store_bsock);
955 * For the client, we have the following values that can be set
956 * level = debug level
957 * trace = send debug output to a file
958 * options = various options for debug or specific FD behavior
959 * hangup = how many records to send to FD before hanging up
960 * obviously this is most useful for testing restarting
962 * blowup = how many records to send to FD before blowing up the FD.
964 static void do_client_setdebug(UAContext *ua, CLIENT *client,
965 int64_t level, int trace, int hangup, int blowup,
966 char *options, char *tags)
971 /* Connect to File daemon */
973 old_client = ua->jcr->client;
974 ua->jcr->client = client;
975 /* Try to connect for 15 seconds */
976 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
977 client->name(), client->address, client->FDport);
978 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
979 ua->error_msg(_("Failed to connect to Client.\n"));
980 ua->jcr->client = old_client;
983 Dmsg0(120, "Connected to file daemon\n");
985 fd = ua->jcr->file_bsock;
986 if (ua->jcr->FDVersion <= 10) {
987 fd->fsend("setdebug=%ld trace=%d hangup=%d\n",
988 (int32_t)level, trace, hangup);
990 fd->fsend("setdebug=%ld trace=%d hangup=%d blowup=%d options=%s tags=%s\n",
991 (int32_t)level, trace, hangup, blowup, options, NPRTB(tags));
993 if (fd->recv() >= 0) {
994 ua->send_msg("%s", fd->msg);
996 fd->signal(BNET_TERMINATE);
997 free_bsock(ua->jcr->file_bsock);
998 ua->jcr->client = old_client;
1003 static void do_all_setdebug(UAContext *ua, int64_t level,
1004 int trace_flag, int hangup, int blowup,
1005 char *options, char *tags)
1007 STORE *store, **unique_store;
1008 CLIENT *client, **unique_client;
1013 debug_parse_tags(tags, &t);
1014 do_dir_setdebug(ua, level, trace_flag, options, t);
1016 /* Count Storage items */
1020 foreach_res(store, R_STORAGE) {
1023 unique_store = (STORE **) malloc(i * sizeof(STORE));
1024 /* Find Unique Storage address/port */
1025 store = (STORE *)GetNextRes(R_STORAGE, NULL);
1027 unique_store[i++] = store;
1028 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
1030 for (j=0; j<i; j++) {
1031 if (strcmp(unique_store[j]->address, store->address) == 0 &&
1032 unique_store[j]->SDport == store->SDport) {
1038 unique_store[i++] = store;
1039 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
1044 /* Call each unique Storage daemon */
1045 for (j=0; j<i; j++) {
1046 do_storage_setdebug(ua, unique_store[j], level, trace_flag,
1047 hangup, blowup, options, tags);
1051 /* Count Client items */
1055 foreach_res(client, R_CLIENT) {
1058 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
1059 /* Find Unique Client address/port */
1060 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
1062 unique_client[i++] = client;
1063 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
1065 for (j=0; j<i; j++) {
1066 if (strcmp(unique_client[j]->address, client->address) == 0 &&
1067 unique_client[j]->FDport == client->FDport) {
1073 unique_client[i++] = client;
1074 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1079 /* Call each unique File daemon */
1080 for (j=0; j<i; j++) {
1081 do_client_setdebug(ua, unique_client[j], level, trace_flag,
1082 hangup, blowup, options, tags);
1084 free(unique_client);
1088 * setdebug level=nn all trace=1/0
1090 static int setdebug_cmd(UAContext *ua, const char *cmd)
1094 int64_t level=0, tags=0;
1095 int trace_flag = -1;
1099 char *tags_str=NULL;
1102 Dmsg1(120, "setdebug:%s:\n", cmd);
1105 i = find_arg_with_value(ua, "options");
1107 bstrncpy(options, ua->argv[i], sizeof(options) - 1);
1110 i = find_arg_with_value(ua, "level");
1112 level = str_to_int64(ua->argv[i]);
1115 if (!get_pint(ua, _("Enter new debug level: "))) {
1118 level = ua->pint32_val;
1121 /* Better to send the tag string instead of tweaking the level
1122 * in case where we extend the tag or change the representation
1124 i = find_arg_with_value(ua, "tags");
1126 tags_str = ua->argv[i];
1127 if (!debug_parse_tags(tags_str, &tags)) {
1128 ua->error_msg(_("Incorrect tags found on command line %s\n"), tags_str);
1133 /* Look for trace flag. -1 => not change */
1134 i = find_arg_with_value(ua, "trace");
1136 trace_flag = atoi(ua->argv[i]);
1137 if (trace_flag > 0) {
1142 /* Look for hangup (debug only) flag. -1 => not change */
1143 i = find_arg_with_value(ua, "hangup");
1145 hangup = atoi(ua->argv[i]);
1148 /* Look for blowup (debug only) flag. -1 => not change */
1149 i = find_arg_with_value(ua, "blowup");
1151 blowup = atoi(ua->argv[i]);
1154 /* General debug? */
1155 for (i=1; i<ua->argc; i++) {
1156 if (strcasecmp(ua->argk[i], "all") == 0) {
1157 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1160 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1161 strcasecmp(ua->argk[i], "director") == 0) {
1162 do_dir_setdebug(ua, level, trace_flag, options, tags);
1165 if (strcasecmp(ua->argk[i], "client") == 0 ||
1166 strcasecmp(ua->argk[i], "fd") == 0) {
1169 client = GetClientResWithName(ua->argv[i]);
1171 do_client_setdebug(ua, client, level, trace_flag,
1172 hangup, blowup, options, tags_str);
1176 client = select_client_resource(ua);
1178 do_client_setdebug(ua, client, level, trace_flag,
1179 hangup, blowup, options, tags_str);
1184 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1185 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1186 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1189 store = GetStoreResWithName(ua->argv[i]);
1191 do_storage_setdebug(ua, store, level, trace_flag,
1192 hangup, blowup, options, tags_str);
1196 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1198 do_storage_setdebug(ua, store, level, trace_flag,
1199 hangup, blowup, options, tags_str);
1205 * We didn't find an appropriate keyword above, so
1208 start_prompt(ua, _("Available daemons are: \n"));
1209 add_prompt(ua, _("Director"));
1210 add_prompt(ua, _("Storage"));
1211 add_prompt(ua, _("Client"));
1212 add_prompt(ua, _("All"));
1213 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1214 case 0: /* Director */
1215 do_dir_setdebug(ua, level, trace_flag, options, tags);
1218 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1220 do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
1225 client = select_client_resource(ua);
1227 do_client_setdebug(ua, client, level, trace_flag, hangup, blowup,
1232 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1241 * Turn debug tracing to file on/off
1243 static int trace_cmd(UAContext *ua, const char *cmd)
1247 if (ua->argc != 2) {
1248 if (!get_cmd(ua, _("Turn on or off? "))) {
1253 onoff = ua->argk[1];
1256 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1260 static int var_cmd(UAContext *ua, const char *cmd)
1262 POOLMEM *val = get_pool_memory(PM_FNAME);
1265 if (!open_client_db(ua)) {
1268 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1271 while (*var == ' ') { /* skip spaces */
1274 Dmsg1(100, "Var=%s:\n", var);
1275 variable_expansion(ua->jcr, var, &val);
1276 ua->send_msg("%s\n", val);
1277 free_pool_memory(val);
1281 static int estimate_cmd(UAContext *ua, const char *cmd)
1284 CLIENT *client = NULL;
1285 FILESET *fileset = NULL;
1287 char since[MAXSTRING];
1291 jcr->setJobLevel(L_FULL);
1292 for (int i=1; i<ua->argc; i++) {
1293 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1294 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1296 client = GetClientResWithName(ua->argv[i]);
1298 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1301 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1302 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1307 ua->error_msg(_("Client name missing.\n"));
1311 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1313 job = GetJobResWithName(ua->argv[i]);
1315 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1318 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1319 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1324 ua->error_msg(_("Job name missing.\n"));
1329 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1331 fileset = GetFileSetResWithName(ua->argv[i]);
1333 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1336 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1337 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1342 ua->error_msg(_("Fileset name missing.\n"));
1346 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1350 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1352 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1353 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1358 ua->error_msg(_("Level value missing.\n"));
1362 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1364 if (!is_yesno(ua->argv[i], &accurate)) {
1365 ua->error_msg(_("Invalid value for accurate. "
1366 "It must be yes or no.\n"));
1371 ua->error_msg(_("Accurate value missing.\n"));
1376 if (!job && !(client && fileset)) {
1377 if (!(job = select_job_resource(ua))) {
1382 job = GetJobResWithName(ua->argk[1]);
1384 ua->error_msg(_("No job specified.\n"));
1387 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1388 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1393 client = job->client;
1396 fileset = job->fileset;
1398 jcr->client = client;
1399 jcr->fileset = fileset;
1401 if (job->pool->catalog) {
1402 ua->catalog = job->pool->catalog;
1404 ua->catalog = client->catalog;
1412 jcr->setJobType(JT_BACKUP);
1413 jcr->start_time = time(NULL);
1414 init_jcr_job_record(jcr);
1416 if (!get_or_create_client_record(jcr)) {
1419 if (!get_or_create_fileset_record(jcr)) {
1423 get_level_since_time(ua->jcr, since, sizeof(since));
1425 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1426 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1427 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1428 ua->error_msg(_("Failed to connect to Client.\n"));
1432 /* The level string change if accurate mode is enabled */
1433 if (accurate >= 0) {
1434 jcr->accurate = accurate;
1436 jcr->accurate = job->accurate;
1439 if (!send_level_command(jcr)) {
1443 if (!send_include_list(jcr)) {
1444 ua->error_msg(_("Error sending include list.\n"));
1448 if (!send_exclude_list(jcr)) {
1449 ua->error_msg(_("Error sending exclude list.\n"));
1454 * If the job is in accurate mode, we send the list of
1457 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1458 if (!send_accurate_current_files(jcr)) {
1462 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1463 while (jcr->file_bsock->recv() >= 0) {
1464 ua->send_msg("%s", jcr->file_bsock->msg);
1468 if (jcr->file_bsock) {
1469 jcr->file_bsock->signal(BNET_TERMINATE);
1470 free_bsock(ua->jcr->file_bsock);
1478 static int time_cmd(UAContext *ua, const char *cmd)
1481 time_t ttime = time(NULL);
1483 (void)localtime_r(&ttime, &tm);
1484 strftime(sdt, sizeof(sdt), "%a %d-%b-%Y %H:%M:%S", &tm);
1485 ua->send_msg("%s\n", sdt);
1490 * reload the conf file
1492 extern "C" void reload_config(int sig);
1494 static int reload_cmd(UAContext *ua, const char *cmd)
1501 * Delete Pool records (should purge Media with it).
1503 * delete pool=<pool-name>
1504 * delete volume pool=<pool-name> volume=<name>
1507 static int delete_cmd(UAContext *ua, const char *cmd)
1509 static const char *keywords[] = {
1516 /* Deleting large jobs can take time! */
1517 if (!open_new_client_db(ua)) {
1521 switch (find_arg_keyword(ua, keywords)) {
1530 while ((i=find_arg(ua, "jobid")) > 0) {
1532 *ua->argk[i] = 0; /* zap keyword already visited */
1536 delete_snapshot(ua);
1543 "In general it is not a good idea to delete either a\n"
1544 "Pool or a Volume since they may contain data.\n\n"));
1546 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1557 delete_snapshot(ua);
1560 ua->warning_msg(_("Nothing done.\n"));
1567 * delete_job has been modified to parse JobID lists like the
1569 * delete JobID=3,4,6,7-11,14
1571 * Thanks to Phil Stracchino for the above addition.
1573 static void delete_job(UAContext *ua)
1575 int JobId; /* not JobId_t because it's unsigned and not compatible with sellist */
1579 int i = find_arg_with_value(ua, NT_("jobid"));
1581 if (!sl.set_string(ua->argv[i], true)) {
1582 ua->warning_msg("%s", sl.get_errmsg());
1586 if (sl.size() > 25 && (find_arg(ua, "yes") < 0)) {
1587 bsnprintf(buf, sizeof(buf),
1588 _("Are you sure you want to delete %d JobIds ? (yes/no): "), sl.size());
1589 if (!get_yesno(ua, buf)) {
1594 foreach_sellist(JobId, &sl) {
1595 do_job_delete(ua, JobId);
1598 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1602 JobId = ua->int64_val;
1603 do_job_delete(ua, JobId);
1608 * do_job_delete now performs the actual delete operation atomically
1610 static void do_job_delete(UAContext *ua, JobId_t JobId)
1614 edit_int64(JobId, ed1);
1615 purge_jobs_from_catalog(ua, ed1);
1616 ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1);
1620 * Delete media records from database -- dangerous
1622 static int delete_volume(UAContext *ua)
1628 if (!select_media_dbr(ua, &mr)) {
1631 ua->warning_msg(_("\nThis command will delete volume %s\n"
1632 "and all Jobs saved on that volume from the Catalog\n"),
1635 if (find_arg(ua, "yes") >= 0) {
1636 ua->pint32_val = 1; /* Have "yes" on command line already" */
1638 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1640 if (!get_yesno(ua, buf)) {
1644 if (!ua->pint32_val) {
1648 /* If not purged, do it */
1649 if (strcmp(mr.VolStatus, "Purged") != 0) {
1650 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1651 ua->error_msg(_("Can't list jobs on this volume\n"));
1655 purge_jobs_from_catalog(ua, lst.list);
1659 db_delete_media_record(ua->jcr, ua->db, &mr);
1664 * Delete a pool record from the database -- dangerous
1666 static int delete_pool(UAContext *ua)
1671 memset(&pr, 0, sizeof(pr));
1673 if (!get_pool_dbr(ua, &pr)) {
1676 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1678 if (!get_yesno(ua, buf)) {
1681 if (ua->pint32_val) {
1682 db_delete_pool_record(ua->jcr, ua->db, &pr);
1687 int memory_cmd(UAContext *ua, const char *cmd)
1689 garbage_collect_memory();
1690 list_dir_status_header(ua);
1691 sm_dump(false, true);
1695 static void do_storage_cmd(UAContext *ua, const char *command)
1700 char dev_name[MAX_NAME_LENGTH];
1704 if (!open_client_db(ua)) {
1707 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1709 store.store = get_storage_resource(ua, true/*arg is storage*/);
1713 pm_strcpy(store.store_source, _("unknown source"));
1714 set_wstorage(jcr, &store);
1715 drive = get_storage_drive(ua, store.store);
1716 slot = get_storage_slot(ua, store.store);
1718 /* Users may set a device name directly on the command line */
1719 if ((i = find_arg_with_value(ua, "device")) > 0) {
1720 POOLMEM *errmsg = get_pool_memory(PM_NAME);
1721 if (!is_name_valid(ua->argv[i], &errmsg)) {
1722 ua->error_msg(_("Invalid device name. %s"), errmsg);
1723 free_pool_memory(errmsg);
1726 free_pool_memory(errmsg);
1727 bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1729 } else { /* We take the default device name */
1730 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1733 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1734 store.store->media_type, store.store->dev_name(), drive);
1735 Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1737 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1738 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1741 sd = jcr->store_bsock;
1742 bash_spaces(dev_name);
1743 sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1744 while (sd->recv() >= 0) {
1745 ua->send_msg("%s", sd->msg);
1747 sd->signal(BNET_TERMINATE);
1748 free_bsock(ua->jcr->store_bsock);
1752 * mount [storage=<name>] [drive=nn] [slot=mm]
1754 static int mount_cmd(UAContext *ua, const char *cmd)
1756 do_storage_cmd(ua, "mount") ; /* mount */
1762 * unmount [storage=<name>] [drive=nn]
1764 static int unmount_cmd(UAContext *ua, const char *cmd)
1766 do_storage_cmd(ua, "unmount"); /* unmount */
1772 * release [storage=<name>] [drive=nn]
1774 static int release_cmd(UAContext *ua, const char *cmd)
1776 do_storage_cmd(ua, "release"); /* release */
1783 * use catalog=<name>
1785 static int use_cmd(UAContext *ua, const char *cmd)
1787 CAT *oldcatalog, *catalog;
1790 close_db(ua); /* close any previously open db */
1791 oldcatalog = ua->catalog;
1793 if (!(catalog = get_catalog_resource(ua))) {
1794 ua->catalog = oldcatalog;
1796 ua->catalog = catalog;
1799 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1800 ua->catalog->name(), ua->catalog->db_name);
1805 int quit_cmd(UAContext *ua, const char *cmd)
1811 /* Handler to get job status */
1812 static int status_handler(void *ctx, int num_fields, char **row)
1814 char *val = (char *)ctx;
1819 *val = '?'; /* Unknown by default */
1826 * Wait until no job is running
1828 int wait_cmd(UAContext *ua, const char *cmd)
1832 time_t stop_time = 0;
1836 * Wait until no job is running
1838 if (ua->argc == 1) {
1839 bmicrosleep(0, 200000); /* let job actually start */
1840 for (bool running=true; running; ) {
1843 if (jcr->JobId != 0) {
1857 i = find_arg_with_value(ua, NT_("timeout"));
1858 if (i > 0 && ua->argv[i]) {
1859 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1862 /* we have jobid, jobname or ujobid argument */
1864 uint32_t jobid = 0 ;
1866 if (!open_client_db(ua)) {
1867 ua->error_msg(_("ERR: Can't open db\n")) ;
1871 for (int i=1; i<ua->argc; i++) {
1872 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1876 jobid = str_to_int64(ua->argv[i]);
1878 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1879 strcasecmp(ua->argk[i], "job") == 0) {
1883 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1885 jobid = jcr->JobId ;
1889 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1893 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1895 jobid = jcr->JobId ;
1899 /* Wait for a mount request */
1900 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1901 for (bool waiting=false; !waiting; ) {
1903 if (jcr->JobId != 0 &&
1904 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1913 if (stop_time && (time(NULL) >= stop_time)) {
1914 ua->warning_msg(_("Wait on mount timed out\n"));
1924 ua->error_msg(_("ERR: Job was not found\n"));
1929 * We wait the end of a specific job
1932 bmicrosleep(0, 200000); /* let job actually start */
1933 for (bool running=true; running; ) {
1936 jcr=get_jcr_by_id(jobid) ;
1949 * We have to get JobStatus
1953 char jobstatus = '?'; /* Unknown by default */
1956 bsnprintf(buf, sizeof(buf),
1957 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1960 db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
1962 switch (jobstatus) {
1964 status = 1 ; /* Warning */
1969 case JS_ErrorTerminated:
1971 status = 2 ; /* Critical */
1976 status = 0 ; /* Ok */
1980 status = 3 ; /* Unknown */
1984 ua->send_msg("JobId=%i\n", jobid) ;
1985 ua->send_msg("JobStatus=%s (%c)\n",
1986 job_status_to_str(jobstatus, 0),
1989 if (ua->gui || ua->api) {
1990 ua->send_msg("ExitStatus=%i\n", status) ;
1997 static int help_cmd(UAContext *ua, const char *cmd)
2000 ua->send_msg(_(" Command Description\n ======= ===========\n"));
2001 for (i=0; i<comsize; i++) {
2002 if (ua->argc == 2) {
2003 if (!strcasecmp(ua->argk[1], commands[i].key)) {
2004 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2005 commands[i].help, commands[i].usage);
2009 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
2012 if (i == comsize && ua->argc == 2) {
2013 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2015 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2019 int qhelp_cmd(UAContext *ua, const char *cmd)
2022 /* Want to display only commands */
2023 j = find_arg(ua, NT_("all"));
2025 for (i=0; i<comsize; i++) {
2026 ua->send_msg("%s\n", commands[i].key);
2030 /* Want to display a specific help section */
2031 j = find_arg_with_value(ua, NT_("item"));
2032 if (j >= 0 && ua->argk[j]) {
2033 for (i=0; i<comsize; i++) {
2034 if (bstrcmp(commands[i].key, ua->argv[j])) {
2035 ua->send_msg("%s\n", commands[i].usage);
2041 /* Want to display everything */
2042 for (i=0; i<comsize; i++) {
2043 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2049 static int version_cmd(UAContext *ua, const char *cmd)
2051 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2052 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2057 * Test code -- turned on only for debug testing
2059 static int version_cmd(UAContext *ua, const char *cmd)
2062 POOL_MEM query(PM_MESSAGE);
2064 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2065 db_get_query_dbids(ua->jcr, ua->db, query, ids);
2066 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2067 for (int i=0; i < ids.num_ids; i++) {
2068 ua->send_msg("id=%d\n", ids.DBId[i]);
2076 * This call uses open_client_db() and force a
2077 * new dedicated connection to the catalog
2079 bool open_new_client_db(UAContext *ua)
2083 /* Force a new dedicated connection */
2084 ua->force_mult_db_connections = true;
2085 ret = open_client_db(ua);
2086 ua->force_mult_db_connections = false;
2092 * This call explicitly checks for a catalog=xxx and
2093 * if given, opens that catalog. It also checks for
2094 * client=xxx and if found, opens the catalog
2095 * corresponding to that client. If we still don't
2096 * have a catalog, look for a Job keyword and get the
2097 * catalog from its client record.
2099 bool open_client_db(UAContext *ua)
2106 /* Try for catalog keyword */
2107 i = find_arg_with_value(ua, NT_("catalog"));
2109 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2110 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2113 catalog = GetCatalogResWithName(ua->argv[i]);
2115 if (ua->catalog && ua->catalog != catalog) {
2118 ua->catalog = catalog;
2123 /* Try for client keyword */
2124 i = find_arg_with_value(ua, NT_("client"));
2126 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2127 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2130 client = GetClientResWithName(ua->argv[i]);
2132 catalog = client->catalog;
2133 if (ua->catalog && ua->catalog != catalog) {
2136 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2137 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2140 ua->catalog = catalog;
2145 /* Try for Job keyword */
2146 i = find_arg_with_value(ua, NT_("job"));
2148 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2149 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2152 job = GetJobResWithName(ua->argv[i]);
2154 catalog = job->client->catalog;
2155 if (ua->catalog && ua->catalog != catalog) {
2158 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2159 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2162 ua->catalog = catalog;
2172 * Open the catalog database.
2174 bool open_db(UAContext *ua)
2178 /* The force_mult_db_connections is telling us if we modify the
2179 * private or the shared link
2181 if (ua->force_mult_db_connections) {
2182 ua->db = ua->private_db;
2185 ua->db = ua->shared_db;
2193 ua->catalog = get_catalog_resource(ua);
2195 ua->error_msg( _("Could not find a Catalog resource\n"));
2200 /* Some modules like bvfs need their own catalog connection */
2201 mult_db_conn = ua->catalog->mult_db_connections;
2202 if (ua->force_mult_db_connections) {
2203 mult_db_conn = true;
2206 ua->jcr->catalog = ua->catalog;
2208 Dmsg0(100, "UA Open database\n");
2209 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2210 ua->catalog->db_name,
2211 ua->catalog->db_user,
2212 ua->catalog->db_password, ua->catalog->db_address,
2213 ua->catalog->db_port, ua->catalog->db_socket,
2214 ua->catalog->db_ssl_key, ua->catalog->db_ssl_cert,
2215 ua->catalog->db_ssl_ca, ua->catalog->db_ssl_capath,
2216 ua->catalog->db_ssl_cipher,
2217 mult_db_conn, ua->catalog->disable_batch_insert);
2218 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2219 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2220 ua->catalog->db_name);
2222 ua->error_msg("%s", db_strerror(ua->db));
2227 ua->jcr->db = ua->db;
2229 /* Depending on the type of connection, we set the right variable */
2230 if (ua->force_mult_db_connections) {
2231 ua->private_db = ua->db;
2234 ua->shared_db = ua->db;
2238 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2240 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2244 void close_db(UAContext *ua)
2250 if (ua->shared_db) {
2251 db_close_database(ua->jcr, ua->shared_db);
2252 ua->shared_db = NULL;
2255 if (ua->private_db) {
2256 db_close_database(ua->jcr, ua->private_db);
2257 ua->private_db = NULL;