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_storage_setdebug(UAContext *ua, STORE *store,
918 int64_t level, int trace_flag, int hangup, int blowup,
919 char *options, char *tags)
924 lstore.store = store;
925 pm_strcpy(lstore.store_source, _("unknown source"));
926 set_wstorage(ua->jcr, &lstore);
927 /* Try connecting for up to 15 seconds */
928 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
929 store->name(), store->address, store->SDport);
930 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
931 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
934 Dmsg0(120, _("Connected to storage daemon\n"));
935 sd = ua->jcr->store_bsock;
936 sd->fsend("setdebug=%ld trace=%ld hangup=%ld blowup=%ld options=%s tags=%s\n",
937 (int32_t)level, trace_flag, hangup, blowup, options, NPRTB(tags));
938 if (sd->recv() >= 0) {
939 ua->send_msg("%s", sd->msg);
941 sd->signal(BNET_TERMINATE);
942 free_bsock(ua->jcr->store_bsock);
947 * For the client, we have the following values that can be set
948 * level = debug level
949 * trace = send debug output to a file
950 * options = various options for debug or specific FD behavior
951 * hangup = how many records to send to FD before hanging up
952 * obviously this is most useful for testing restarting
954 * blowup = how many records to send to FD before blowing up the FD.
956 static void do_client_setdebug(UAContext *ua, CLIENT *client,
957 int64_t level, int trace, int hangup, int blowup,
958 char *options, char *tags)
963 /* Connect to File daemon */
965 old_client = ua->jcr->client;
966 ua->jcr->client = client;
967 /* Try to connect for 15 seconds */
968 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
969 client->name(), client->address, client->FDport);
970 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
971 ua->error_msg(_("Failed to connect to Client.\n"));
972 ua->jcr->client = old_client;
975 Dmsg0(120, "Connected to file daemon\n");
977 fd = ua->jcr->file_bsock;
978 if (ua->jcr->FDVersion <= 10) {
979 fd->fsend("setdebug=%ld trace=%d hangup=%d\n",
980 (int32_t)level, trace, hangup);
982 fd->fsend("setdebug=%ld trace=%d hangup=%d blowup=%d options=%s tags=%s\n",
983 (int32_t)level, trace, hangup, blowup, options, NPRTB(tags));
985 if (fd->recv() >= 0) {
986 ua->send_msg("%s", fd->msg);
988 fd->signal(BNET_TERMINATE);
989 free_bsock(ua->jcr->file_bsock);
990 ua->jcr->client = old_client;
995 static void do_all_setdebug(UAContext *ua, int64_t level,
996 int trace_flag, int hangup, int blowup,
997 char *options, char *tags)
999 STORE *store, **unique_store;
1000 CLIENT *client, **unique_client;
1004 debug_level = level;
1006 /* Count Storage items */
1010 foreach_res(store, R_STORAGE) {
1013 unique_store = (STORE **) malloc(i * sizeof(STORE));
1014 /* Find Unique Storage address/port */
1015 store = (STORE *)GetNextRes(R_STORAGE, NULL);
1017 unique_store[i++] = store;
1018 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
1020 for (j=0; j<i; j++) {
1021 if (strcmp(unique_store[j]->address, store->address) == 0 &&
1022 unique_store[j]->SDport == store->SDport) {
1028 unique_store[i++] = store;
1029 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
1034 /* Call each unique Storage daemon */
1035 for (j=0; j<i; j++) {
1036 do_storage_setdebug(ua, unique_store[j], level, trace_flag,
1037 hangup, blowup, options, tags);
1041 /* Count Client items */
1045 foreach_res(client, R_CLIENT) {
1048 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
1049 /* Find Unique Client address/port */
1050 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
1052 unique_client[i++] = client;
1053 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
1055 for (j=0; j<i; j++) {
1056 if (strcmp(unique_client[j]->address, client->address) == 0 &&
1057 unique_client[j]->FDport == client->FDport) {
1063 unique_client[i++] = client;
1064 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1069 /* Call each unique File daemon */
1070 for (j=0; j<i; j++) {
1071 do_client_setdebug(ua, unique_client[j], level, trace_flag,
1072 hangup, blowup, options, tags);
1074 free(unique_client);
1078 * setdebug level=nn all trace=1/0
1080 static int setdebug_cmd(UAContext *ua, const char *cmd)
1084 int64_t level=0, tags=0;
1085 int trace_flag = -1;
1089 char *tags_str=NULL;
1092 Dmsg1(120, "setdebug:%s:\n", cmd);
1095 i = find_arg_with_value(ua, "options");
1097 bstrncpy(options, ua->argv[i], sizeof(options) - 1);
1100 i = find_arg_with_value(ua, "level");
1102 level = str_to_int64(ua->argv[i]);
1105 if (!get_pint(ua, _("Enter new debug level: "))) {
1108 level = ua->pint32_val;
1111 /* Better to send the tag string instead of tweaking the level
1112 * in case where we extend the tag or change the representation
1114 i = find_arg_with_value(ua, "tags");
1116 tags_str = ua->argv[i];
1117 if (!debug_parse_tags(tags_str, &tags)) {
1118 ua->error_msg(_("Incorrect tags found on command line %s\n"), tags_str);
1123 /* Look for trace flag. -1 => not change */
1124 i = find_arg_with_value(ua, "trace");
1126 trace_flag = atoi(ua->argv[i]);
1127 if (trace_flag > 0) {
1132 /* Look for hangup (debug only) flag. -1 => not change */
1133 i = find_arg_with_value(ua, "hangup");
1135 hangup = atoi(ua->argv[i]);
1138 /* Look for blowup (debug only) flag. -1 => not change */
1139 i = find_arg_with_value(ua, "blowup");
1141 blowup = atoi(ua->argv[i]);
1144 /* General debug? */
1145 for (i=1; i<ua->argc; i++) {
1146 if (strcasecmp(ua->argk[i], "all") == 0) {
1147 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1150 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1151 strcasecmp(ua->argk[i], "director") == 0) {
1152 debug_level = level;
1153 debug_level_tags = tags;
1154 set_trace(trace_flag);
1155 set_debug_flags(options);
1158 if (strcasecmp(ua->argk[i], "client") == 0 ||
1159 strcasecmp(ua->argk[i], "fd") == 0) {
1162 client = GetClientResWithName(ua->argv[i]);
1164 do_client_setdebug(ua, client, level, trace_flag,
1165 hangup, blowup, options, tags_str);
1169 client = select_client_resource(ua);
1171 do_client_setdebug(ua, client, level, trace_flag,
1172 hangup, blowup, options, tags_str);
1177 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1178 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1179 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1182 store = GetStoreResWithName(ua->argv[i]);
1184 do_storage_setdebug(ua, store, level, trace_flag,
1185 hangup, blowup, options, tags_str);
1189 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1191 do_storage_setdebug(ua, store, level, trace_flag,
1192 hangup, blowup, options, tags_str);
1198 * We didn't find an appropriate keyword above, so
1201 start_prompt(ua, _("Available daemons are: \n"));
1202 add_prompt(ua, _("Director"));
1203 add_prompt(ua, _("Storage"));
1204 add_prompt(ua, _("Client"));
1205 add_prompt(ua, _("All"));
1206 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1207 case 0: /* Director */
1208 debug_level = level;
1209 set_trace(trace_flag);
1210 set_debug_flags(options);
1213 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1215 do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
1220 client = select_client_resource(ua);
1222 do_client_setdebug(ua, client, level, trace_flag, hangup, blowup,
1227 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1236 * Turn debug tracing to file on/off
1238 static int trace_cmd(UAContext *ua, const char *cmd)
1242 if (ua->argc != 2) {
1243 if (!get_cmd(ua, _("Turn on or off? "))) {
1248 onoff = ua->argk[1];
1251 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1255 static int var_cmd(UAContext *ua, const char *cmd)
1257 POOLMEM *val = get_pool_memory(PM_FNAME);
1260 if (!open_client_db(ua)) {
1263 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1266 while (*var == ' ') { /* skip spaces */
1269 Dmsg1(100, "Var=%s:\n", var);
1270 variable_expansion(ua->jcr, var, &val);
1271 ua->send_msg("%s\n", val);
1272 free_pool_memory(val);
1276 static int estimate_cmd(UAContext *ua, const char *cmd)
1279 CLIENT *client = NULL;
1280 FILESET *fileset = NULL;
1282 char since[MAXSTRING];
1286 jcr->setJobLevel(L_FULL);
1287 for (int i=1; i<ua->argc; i++) {
1288 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1289 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1291 client = GetClientResWithName(ua->argv[i]);
1293 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1296 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1297 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1302 ua->error_msg(_("Client name missing.\n"));
1306 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1308 job = GetJobResWithName(ua->argv[i]);
1310 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1313 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1314 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1319 ua->error_msg(_("Job name missing.\n"));
1324 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1326 fileset = GetFileSetResWithName(ua->argv[i]);
1328 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1331 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1332 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1337 ua->error_msg(_("Fileset name missing.\n"));
1341 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1345 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1347 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1348 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1353 ua->error_msg(_("Level value missing.\n"));
1357 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1359 if (!is_yesno(ua->argv[i], &accurate)) {
1360 ua->error_msg(_("Invalid value for accurate. "
1361 "It must be yes or no.\n"));
1366 ua->error_msg(_("Accurate value missing.\n"));
1371 if (!job && !(client && fileset)) {
1372 if (!(job = select_job_resource(ua))) {
1377 job = GetJobResWithName(ua->argk[1]);
1379 ua->error_msg(_("No job specified.\n"));
1382 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1383 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1388 client = job->client;
1391 fileset = job->fileset;
1393 jcr->client = client;
1394 jcr->fileset = fileset;
1396 if (job->pool->catalog) {
1397 ua->catalog = job->pool->catalog;
1399 ua->catalog = client->catalog;
1407 jcr->setJobType(JT_BACKUP);
1408 jcr->start_time = time(NULL);
1409 init_jcr_job_record(jcr);
1411 if (!get_or_create_client_record(jcr)) {
1414 if (!get_or_create_fileset_record(jcr)) {
1418 get_level_since_time(ua->jcr, since, sizeof(since));
1420 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1421 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1422 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1423 ua->error_msg(_("Failed to connect to Client.\n"));
1427 /* The level string change if accurate mode is enabled */
1428 if (accurate >= 0) {
1429 jcr->accurate = accurate;
1431 jcr->accurate = job->accurate;
1434 if (!send_level_command(jcr)) {
1438 if (!send_include_list(jcr)) {
1439 ua->error_msg(_("Error sending include list.\n"));
1443 if (!send_exclude_list(jcr)) {
1444 ua->error_msg(_("Error sending exclude list.\n"));
1449 * If the job is in accurate mode, we send the list of
1452 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1453 if (!send_accurate_current_files(jcr)) {
1457 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1458 while (jcr->file_bsock->recv() >= 0) {
1459 ua->send_msg("%s", jcr->file_bsock->msg);
1463 if (jcr->file_bsock) {
1464 jcr->file_bsock->signal(BNET_TERMINATE);
1465 free_bsock(ua->jcr->file_bsock);
1473 static int time_cmd(UAContext *ua, const char *cmd)
1476 time_t ttime = time(NULL);
1478 (void)localtime_r(&ttime, &tm);
1479 strftime(sdt, sizeof(sdt), "%a %d-%b-%Y %H:%M:%S", &tm);
1480 ua->send_msg("%s\n", sdt);
1485 * reload the conf file
1487 extern "C" void reload_config(int sig);
1489 static int reload_cmd(UAContext *ua, const char *cmd)
1496 * Delete Pool records (should purge Media with it).
1498 * delete pool=<pool-name>
1499 * delete volume pool=<pool-name> volume=<name>
1502 static int delete_cmd(UAContext *ua, const char *cmd)
1504 static const char *keywords[] = {
1511 /* Deleting large jobs can take time! */
1512 if (!open_new_client_db(ua)) {
1516 switch (find_arg_keyword(ua, keywords)) {
1525 while ((i=find_arg(ua, "jobid")) > 0) {
1527 *ua->argk[i] = 0; /* zap keyword already visited */
1531 delete_snapshot(ua);
1538 "In general it is not a good idea to delete either a\n"
1539 "Pool or a Volume since they may contain data.\n\n"));
1541 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1552 delete_snapshot(ua);
1555 ua->warning_msg(_("Nothing done.\n"));
1562 * delete_job has been modified to parse JobID lists like the
1564 * delete JobID=3,4,6,7-11,14
1566 * Thanks to Phil Stracchino for the above addition.
1568 static void delete_job(UAContext *ua)
1570 int JobId; /* not JobId_t because it's unsigned and not compatible with sellist */
1574 int i = find_arg_with_value(ua, NT_("jobid"));
1576 if (!sl.set_string(ua->argv[i], true)) {
1577 ua->warning_msg("%s", sl.get_errmsg());
1581 if (sl.size() > 25 && (find_arg(ua, "yes") < 0)) {
1582 bsnprintf(buf, sizeof(buf),
1583 _("Are you sure you want to delete %d JobIds ? (yes/no): "), sl.size());
1584 if (!get_yesno(ua, buf)) {
1589 foreach_sellist(JobId, &sl) {
1590 do_job_delete(ua, JobId);
1593 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1597 JobId = ua->int64_val;
1598 do_job_delete(ua, JobId);
1603 * do_job_delete now performs the actual delete operation atomically
1605 static void do_job_delete(UAContext *ua, JobId_t JobId)
1609 edit_int64(JobId, ed1);
1610 purge_jobs_from_catalog(ua, ed1);
1611 ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1);
1615 * Delete media records from database -- dangerous
1617 static int delete_volume(UAContext *ua)
1623 if (!select_media_dbr(ua, &mr)) {
1626 ua->warning_msg(_("\nThis command will delete volume %s\n"
1627 "and all Jobs saved on that volume from the Catalog\n"),
1630 if (find_arg(ua, "yes") >= 0) {
1631 ua->pint32_val = 1; /* Have "yes" on command line already" */
1633 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1635 if (!get_yesno(ua, buf)) {
1639 if (!ua->pint32_val) {
1643 /* If not purged, do it */
1644 if (strcmp(mr.VolStatus, "Purged") != 0) {
1645 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1646 ua->error_msg(_("Can't list jobs on this volume\n"));
1650 purge_jobs_from_catalog(ua, lst.list);
1654 db_delete_media_record(ua->jcr, ua->db, &mr);
1659 * Delete a pool record from the database -- dangerous
1661 static int delete_pool(UAContext *ua)
1666 memset(&pr, 0, sizeof(pr));
1668 if (!get_pool_dbr(ua, &pr)) {
1671 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1673 if (!get_yesno(ua, buf)) {
1676 if (ua->pint32_val) {
1677 db_delete_pool_record(ua->jcr, ua->db, &pr);
1682 int memory_cmd(UAContext *ua, const char *cmd)
1684 garbage_collect_memory();
1685 list_dir_status_header(ua);
1686 sm_dump(false, true);
1690 static void do_storage_cmd(UAContext *ua, const char *command)
1695 char dev_name[MAX_NAME_LENGTH];
1699 if (!open_client_db(ua)) {
1702 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1704 store.store = get_storage_resource(ua, true/*arg is storage*/);
1708 pm_strcpy(store.store_source, _("unknown source"));
1709 set_wstorage(jcr, &store);
1710 drive = get_storage_drive(ua, store.store);
1711 slot = get_storage_slot(ua, store.store);
1713 /* Users may set a device name directly on the command line */
1714 if ((i = find_arg_with_value(ua, "device")) > 0) {
1715 POOLMEM *errmsg = get_pool_memory(PM_NAME);
1716 if (!is_name_valid(ua->argv[i], &errmsg)) {
1717 ua->error_msg(_("Invalid device name. %s"), errmsg);
1718 free_pool_memory(errmsg);
1721 free_pool_memory(errmsg);
1722 bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1724 } else { /* We take the default device name */
1725 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1728 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1729 store.store->media_type, store.store->dev_name(), drive);
1730 Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1732 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1733 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1736 sd = jcr->store_bsock;
1737 bash_spaces(dev_name);
1738 sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1739 while (sd->recv() >= 0) {
1740 ua->send_msg("%s", sd->msg);
1742 sd->signal(BNET_TERMINATE);
1743 free_bsock(ua->jcr->store_bsock);
1747 * mount [storage=<name>] [drive=nn] [slot=mm]
1749 static int mount_cmd(UAContext *ua, const char *cmd)
1751 do_storage_cmd(ua, "mount") ; /* mount */
1757 * unmount [storage=<name>] [drive=nn]
1759 static int unmount_cmd(UAContext *ua, const char *cmd)
1761 do_storage_cmd(ua, "unmount"); /* unmount */
1767 * release [storage=<name>] [drive=nn]
1769 static int release_cmd(UAContext *ua, const char *cmd)
1771 do_storage_cmd(ua, "release"); /* release */
1778 * use catalog=<name>
1780 static int use_cmd(UAContext *ua, const char *cmd)
1782 CAT *oldcatalog, *catalog;
1785 close_db(ua); /* close any previously open db */
1786 oldcatalog = ua->catalog;
1788 if (!(catalog = get_catalog_resource(ua))) {
1789 ua->catalog = oldcatalog;
1791 ua->catalog = catalog;
1794 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1795 ua->catalog->name(), ua->catalog->db_name);
1800 int quit_cmd(UAContext *ua, const char *cmd)
1806 /* Handler to get job status */
1807 static int status_handler(void *ctx, int num_fields, char **row)
1809 char *val = (char *)ctx;
1814 *val = '?'; /* Unknown by default */
1821 * Wait until no job is running
1823 int wait_cmd(UAContext *ua, const char *cmd)
1827 time_t stop_time = 0;
1831 * Wait until no job is running
1833 if (ua->argc == 1) {
1834 bmicrosleep(0, 200000); /* let job actually start */
1835 for (bool running=true; running; ) {
1838 if (jcr->JobId != 0) {
1852 i = find_arg_with_value(ua, NT_("timeout"));
1853 if (i > 0 && ua->argv[i]) {
1854 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1857 /* we have jobid, jobname or ujobid argument */
1859 uint32_t jobid = 0 ;
1861 if (!open_client_db(ua)) {
1862 ua->error_msg(_("ERR: Can't open db\n")) ;
1866 for (int i=1; i<ua->argc; i++) {
1867 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1871 jobid = str_to_int64(ua->argv[i]);
1873 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1874 strcasecmp(ua->argk[i], "job") == 0) {
1878 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1880 jobid = jcr->JobId ;
1884 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1888 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1890 jobid = jcr->JobId ;
1894 /* Wait for a mount request */
1895 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1896 for (bool waiting=false; !waiting; ) {
1898 if (jcr->JobId != 0 &&
1899 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1908 if (stop_time && (time(NULL) >= stop_time)) {
1909 ua->warning_msg(_("Wait on mount timed out\n"));
1919 ua->error_msg(_("ERR: Job was not found\n"));
1924 * We wait the end of a specific job
1927 bmicrosleep(0, 200000); /* let job actually start */
1928 for (bool running=true; running; ) {
1931 jcr=get_jcr_by_id(jobid) ;
1944 * We have to get JobStatus
1948 char jobstatus = '?'; /* Unknown by default */
1951 bsnprintf(buf, sizeof(buf),
1952 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1955 db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
1957 switch (jobstatus) {
1959 status = 1 ; /* Warning */
1964 case JS_ErrorTerminated:
1966 status = 2 ; /* Critical */
1971 status = 0 ; /* Ok */
1975 status = 3 ; /* Unknown */
1979 ua->send_msg("JobId=%i\n", jobid) ;
1980 ua->send_msg("JobStatus=%s (%c)\n",
1981 job_status_to_str(jobstatus, 0),
1984 if (ua->gui || ua->api) {
1985 ua->send_msg("ExitStatus=%i\n", status) ;
1992 static int help_cmd(UAContext *ua, const char *cmd)
1995 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1996 for (i=0; i<comsize; i++) {
1997 if (ua->argc == 2) {
1998 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1999 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2000 commands[i].help, commands[i].usage);
2004 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
2007 if (i == comsize && ua->argc == 2) {
2008 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2010 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2014 int qhelp_cmd(UAContext *ua, const char *cmd)
2017 /* Want to display only commands */
2018 j = find_arg(ua, NT_("all"));
2020 for (i=0; i<comsize; i++) {
2021 ua->send_msg("%s\n", commands[i].key);
2025 /* Want to display a specific help section */
2026 j = find_arg_with_value(ua, NT_("item"));
2027 if (j >= 0 && ua->argk[j]) {
2028 for (i=0; i<comsize; i++) {
2029 if (bstrcmp(commands[i].key, ua->argv[j])) {
2030 ua->send_msg("%s\n", commands[i].usage);
2036 /* Want to display everything */
2037 for (i=0; i<comsize; i++) {
2038 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2044 static int version_cmd(UAContext *ua, const char *cmd)
2046 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2047 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2052 * Test code -- turned on only for debug testing
2054 static int version_cmd(UAContext *ua, const char *cmd)
2057 POOL_MEM query(PM_MESSAGE);
2059 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2060 db_get_query_dbids(ua->jcr, ua->db, query, ids);
2061 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2062 for (int i=0; i < ids.num_ids; i++) {
2063 ua->send_msg("id=%d\n", ids.DBId[i]);
2071 * This call uses open_client_db() and force a
2072 * new dedicated connection to the catalog
2074 bool open_new_client_db(UAContext *ua)
2078 /* Force a new dedicated connection */
2079 ua->force_mult_db_connections = true;
2080 ret = open_client_db(ua);
2081 ua->force_mult_db_connections = false;
2087 * This call explicitly checks for a catalog=xxx and
2088 * if given, opens that catalog. It also checks for
2089 * client=xxx and if found, opens the catalog
2090 * corresponding to that client. If we still don't
2091 * have a catalog, look for a Job keyword and get the
2092 * catalog from its client record.
2094 bool open_client_db(UAContext *ua)
2101 /* Try for catalog keyword */
2102 i = find_arg_with_value(ua, NT_("catalog"));
2104 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2105 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2108 catalog = GetCatalogResWithName(ua->argv[i]);
2110 if (ua->catalog && ua->catalog != catalog) {
2113 ua->catalog = catalog;
2118 /* Try for client keyword */
2119 i = find_arg_with_value(ua, NT_("client"));
2121 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2122 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2125 client = GetClientResWithName(ua->argv[i]);
2127 catalog = client->catalog;
2128 if (ua->catalog && ua->catalog != catalog) {
2131 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2132 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2135 ua->catalog = catalog;
2140 /* Try for Job keyword */
2141 i = find_arg_with_value(ua, NT_("job"));
2143 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2144 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2147 job = GetJobResWithName(ua->argv[i]);
2149 catalog = job->client->catalog;
2150 if (ua->catalog && ua->catalog != catalog) {
2153 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2154 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2157 ua->catalog = catalog;
2167 * Open the catalog database.
2169 bool open_db(UAContext *ua)
2173 /* The force_mult_db_connections is telling us if we modify the
2174 * private or the shared link
2176 if (ua->force_mult_db_connections) {
2177 ua->db = ua->private_db;
2180 ua->db = ua->shared_db;
2188 ua->catalog = get_catalog_resource(ua);
2190 ua->error_msg( _("Could not find a Catalog resource\n"));
2195 /* Some modules like bvfs need their own catalog connection */
2196 mult_db_conn = ua->catalog->mult_db_connections;
2197 if (ua->force_mult_db_connections) {
2198 mult_db_conn = true;
2201 ua->jcr->catalog = ua->catalog;
2203 Dmsg0(100, "UA Open database\n");
2204 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2205 ua->catalog->db_name,
2206 ua->catalog->db_user,
2207 ua->catalog->db_password, ua->catalog->db_address,
2208 ua->catalog->db_port, ua->catalog->db_socket,
2209 mult_db_conn, ua->catalog->disable_batch_insert);
2210 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2211 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2212 ua->catalog->db_name);
2214 ua->error_msg("%s", db_strerror(ua->db));
2219 ua->jcr->db = ua->db;
2221 /* Depending on the type of connection, we set the right variable */
2222 if (ua->force_mult_db_connections) {
2223 ua->private_db = ua->db;
2226 ua->shared_db = ua->db;
2230 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2232 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2236 void close_db(UAContext *ua)
2242 if (ua->shared_db) {
2243 db_close_database(ua->jcr, ua->shared_db);
2244 ua->shared_db = NULL;
2247 if (ua->private_db) {
2248 db_close_database(ua->jcr, ua->private_db);
2249 ua->private_db = NULL;