2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Bacula Director -- User Agent Commands
24 * Kern Sibbald, September MM
31 /* Imported subroutines */
33 /* Imported variables */
34 extern jobq_t job_queue; /* job queue */
37 /* Imported functions */
38 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
39 extern int gui_cmd(UAContext *ua, const char *cmd);
40 extern int label_cmd(UAContext *ua, const char *cmd);
41 extern int list_cmd(UAContext *ua, const char *cmd);
42 extern int llist_cmd(UAContext *ua, const char *cmd);
43 extern int messagescmd(UAContext *ua, const char *cmd);
44 extern int prunecmd(UAContext *ua, const char *cmd);
45 extern int purge_cmd(UAContext *ua, const char *cmd);
46 extern int truncate_cmd(UAContext *ua, const char *cmd); /* in ua_purge.c */
47 extern int querycmd(UAContext *ua, const char *cmd);
48 extern int relabel_cmd(UAContext *ua, const char *cmd);
49 extern int restore_cmd(UAContext *ua, const char *cmd);
50 extern int retentioncmd(UAContext *ua, const char *cmd);
51 extern int show_cmd(UAContext *ua, const char *cmd);
52 extern int sqlquerycmd(UAContext *ua, const char *cmd);
53 extern int status_cmd(UAContext *ua, const char *cmd);
54 extern int update_cmd(UAContext *ua, const char *cmd);
56 /* Forward referenced functions */
57 static int add_cmd(UAContext *ua, const char *cmd);
58 static int automount_cmd(UAContext *ua, const char *cmd);
59 static int cancel_cmd(UAContext *ua, const char *cmd);
60 static int create_cmd(UAContext *ua, const char *cmd);
61 static int delete_cmd(UAContext *ua, const char *cmd);
62 static int disable_cmd(UAContext *ua, const char *cmd);
63 static int enable_cmd(UAContext *ua, const char *cmd);
64 static int estimate_cmd(UAContext *ua, const char *cmd);
65 static int help_cmd(UAContext *ua, const char *cmd);
66 static int memory_cmd(UAContext *ua, const char *cmd);
67 static int mount_cmd(UAContext *ua, const char *cmd);
68 static int release_cmd(UAContext *ua, const char *cmd);
69 static int reload_cmd(UAContext *ua, const char *cmd);
70 static int setdebug_cmd(UAContext *ua, const char *cmd);
71 static int setbwlimit_cmd(UAContext *ua, const char *cmd);
72 static int setip_cmd(UAContext *ua, const char *cmd);
73 static int time_cmd(UAContext *ua, const char *cmd);
74 static int trace_cmd(UAContext *ua, const char *cmd);
75 static int unmount_cmd(UAContext *ua, const char *cmd);
76 static int use_cmd(UAContext *ua, const char *cmd);
77 static int var_cmd(UAContext *ua, const char *cmd);
78 static int version_cmd(UAContext *ua, const char *cmd);
79 static int wait_cmd(UAContext *ua, const char *cmd);
81 static void do_job_delete(UAContext *ua, JobId_t JobId);
82 static int delete_volume(UAContext *ua);
83 static int delete_pool(UAContext *ua);
84 static void delete_job(UAContext *ua);
85 static void do_storage_cmd(UAContext *ua, const char *command);
87 int qhelp_cmd(UAContext *ua, const char *cmd);
88 int quit_cmd(UAContext *ua, const char *cmd);
90 /* not all in alphabetical order. New commands are added after existing commands with similar letters
91 to prevent breakage of existing user scripts. */
93 const char *key; /* command */
94 int (*func)(UAContext *ua, const char *cmd); /* handler */
95 const char *help; /* main purpose */
96 const char *usage; /* all arguments to build usage */
97 const bool use_in_rs; /* Can use it in Console RunScript */
99 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
100 { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
101 { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
102 { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
103 { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid=<number-list> | job=<job-name> | ujobid=<unique-jobid> | inactive client=<client-name> storage=<storage-name> | all"), false},
104 { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
105 { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume=<vol-name> | pool=<pool-name> | jobid=<id> | snapshot"), true},
106 { NT_("disable"), disable_cmd, _("Disable a job, attributes batch process"), NT_("job=<name> | batch"), true},
107 { NT_("enable"), enable_cmd, _("Enable a job, attributes batch process"), NT_("job=<name> | batch"), true},
108 { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
109 NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
111 { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
112 { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
113 { NT_("help"), help_cmd, _("Print help on specific command"),
114 NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
115 "\n\tmessages memory mount prune purge quit query\n\trestore relabel release reload run status"
116 "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"
117 "\n\tsnapshot"), false},
119 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> barcodes"), false},
120 { NT_("list"), list_cmd, _("List objects from catalog"),
121 NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [limit=<n>]|\n"
122 "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
123 "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot"), false},
125 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
126 NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [limit=<n>]|\n"
127 "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
128 "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot"), false},
130 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
131 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
132 { NT_("mount"), mount_cmd, _("Mount storage"),
133 NT_("storage=<storage-name> slot=<num> drive=<num> [ device=<device-name> ] [ jobid=<id> | job=<job-name> ]"), false},
135 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
136 NT_("files | jobs | pool=<pool> | snapshot [client=<client-name>] | client=<client-name> | [ expired ] volume=<volume-name> "), true},
138 { NT_("purge"), purge_cmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
139 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
140 { NT_("query"), querycmd, _("Query catalog"), NT_(""), false},
141 { NT_("restore"), restore_cmd, _("Restore files"),
142 NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
144 "\n\tcomment=<text> jobid=<jobid> copies done select all"), false},
146 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
147 NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
149 { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name> [ device=<device-name> ] "), false},
150 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
151 { NT_("run"), run_cmd, _("Run a job"),
152 NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
153 " where=<directory-prefix>\n\twhen=<universal-time-specification> pool=<pool-name>\n\t"
154 " nextpool=<next-pool-name> comment=<text> accurate=<bool> spooldata=<bool> yes"), false},
156 { NT_("restart"), restart_cmd, _("Restart a job"),
157 NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
158 "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
160 { NT_("resume"), restart_cmd, _("Resume a job"),
161 NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
162 "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
164 { NT_("status"), status_cmd, _("Report status"),
165 NT_("all | dir=<dir-name> | director | client=<client-name> |\n"
166 "\tstorage=<storage-name> slots |\n"
167 "\tschedule [job=<job-name>] [days=<nn>] [limit=<nn>]\n"
168 "\t\t[time=<universal-time-specification>]"), true},
170 { NT_("stop"), cancel_cmd, _("Stop a job"), NT_("jobid=<number-list> job=<job-name> ujobid=<unique-jobid> all"), false},
171 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
172 NT_("level=<nn> tags=<tags> trace=0/1 options=<0tTc> tags=<tags> | client=<client-name> | dir | storage=<storage-name> | all"), true},
174 { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"),
175 NT_("limit=<nn-kbs> client=<client-name> jobid=<number> job=<job-name> ujobid=<unique-jobid>"), true},
177 { NT_("snapshot"), snapshot_cmd, _("Handle snapshots"),
178 NT_("[client=<client-name> | job=<job-name> | jobid=<jobid>] [delete | list | listclient | prune | sync | update]"), true},
180 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
181 { NT_("show"), show_cmd, _("Show resource records"),
182 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> | schedule=<sss> | client=<zzz> | storage=<sss> | disabled | all"), true},
184 { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
185 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
186 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
187 { NT_("truncate"), truncate_cmd, _("Truncate one or more Volumes"), NT_("volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
188 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
189 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
191 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
192 NT_("storage=<storage-name> [ drive=<num> ] [ device=<dev-name> ]| jobid=<id> | job=<job-name>"), false},
194 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
195 NT_("stats\n\tsnapshot\n\tpool=<poolname>\n\tslots storage=<storage> scan"
196 "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
197 "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
198 "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
199 "\n\t enabled=<yes/no> recyclepool=<pool> actiononpurge=<action>"
200 "\n\t allfrompool=<pool> fromallpools frompool"),true},
201 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_("catalog=<catalog>"), false},
202 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
203 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
204 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
205 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
208 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
210 const char *get_command(int index) {
211 return commands[index].key;
215 * Execute a command from the UA
217 bool do_a_command(UAContext *ua)
223 BSOCK *user = ua->UA_sock;
226 Dmsg1(900, "Command: %s\n", ua->argk[0]);
231 if (ua->jcr->wstorage) {
232 while (ua->jcr->wstorage->size()) {
233 ua->jcr->wstorage->remove(0);
237 len = strlen(ua->argk[0]);
238 for (i=0; i<comsize; i++) { /* search for command */
239 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
241 /* Check if command permitted, but "quit" is always OK */
242 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
243 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
246 /* Check if this command is authorized in RunScript */
247 if (ua->runscript && !commands[i].use_in_rs) {
248 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
251 if (ua->api) user->signal(BNET_CMD_BEGIN);
252 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
253 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
254 found = (user && user->is_stop()) ? false : true;
259 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
266 * This is a common routine used to stuff the Pool DB record defaults
267 * into the Media DB record just before creating a media (Volume)
270 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
272 mr->PoolId = pr->PoolId;
273 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
274 mr->Recycle = pr->Recycle;
275 mr->VolRetention = pr->VolRetention;
276 mr->VolUseDuration = pr->VolUseDuration;
277 mr->ActionOnPurge = pr->ActionOnPurge;
278 mr->RecyclePoolId = pr->RecyclePoolId;
279 mr->MaxVolJobs = pr->MaxVolJobs;
280 mr->MaxVolFiles = pr->MaxVolFiles;
281 mr->MaxVolBytes = pr->MaxVolBytes;
282 mr->LabelType = pr->LabelType;
288 * Add Volumes to an existing Pool
290 static int add_cmd(UAContext *ua, const char *cmd)
294 int num, i, max, startnum;
295 char name[MAX_NAME_LENGTH];
297 int Slot = 0, InChanger = 0;
300 "You probably don't want to be using this command since it\n"
301 "creates database records without labeling the Volumes.\n"
302 "You probably want to use the \"label\" command.\n\n"));
304 if (!open_client_db(ua)) {
308 memset(&pr, 0, sizeof(pr));
310 if (!get_pool_dbr(ua, &pr)) {
314 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
315 pr.MaxVols, pr.PoolType);
317 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
318 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
319 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
322 pr.MaxVols = ua->pint32_val;
326 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
327 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
328 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
332 if (pr.MaxVols == 0) {
335 max = pr.MaxVols - pr.NumVols;
339 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
340 if (!get_pint(ua, buf)) {
343 num = ua->pint32_val;
344 if (num < 0 || num > max) {
345 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
353 if (!get_cmd(ua, _("Enter Volume name: "))) {
357 if (!get_cmd(ua, _("Enter base volume name: "))) {
361 /* Don't allow | in Volume name because it is the volume separator character */
362 if (!is_volume_name_legal(ua, ua->cmd)) {
365 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
366 ua->warning_msg(_("Volume name too long.\n"));
369 if (strlen(ua->cmd) == 0) {
370 ua->warning_msg(_("Volume name must be at least one character long.\n"));
376 bstrncpy(name, ua->cmd, sizeof(name));
378 bstrncat(name, "%04d", sizeof(name));
381 if (!get_pint(ua, _("Enter the starting number: "))) {
384 startnum = ua->pint32_val;
386 ua->warning_msg(_("Start number must be greater than zero.\n"));
396 if (store && store->autochanger) {
397 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
400 Slot = ua->pint32_val;
401 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
404 InChanger = ua->pint32_val;
407 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
408 for (i=startnum; i < num+startnum; i++) {
409 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
411 mr.InChanger = InChanger;
413 set_storageid_in_mr(store, &mr);
414 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
415 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
416 ua->error_msg("%s", db_strerror(ua->db));
419 // if (i == startnum) {
420 // first_id = mr.PoolId;
424 Dmsg0(200, "Update pool record.\n");
425 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
426 ua->warning_msg("%s", db_strerror(ua->db));
429 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
435 * Turn auto mount on/off
440 int automount_cmd(UAContext *ua, const char *cmd)
445 if (!get_cmd(ua, _("Turn on or off? "))) {
453 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
458 * Cancel/Stop a job -- Stop marks it as Incomplete
459 * so that it can be restarted.
461 static int cancel_cmd(UAContext *ua, const char *cmd)
466 bool cancel = strcasecmp(commands[ua->cmd_index].key, "cancel") == 0;
467 alist *jcrs = New(alist(5, not_owned_by_alist));
469 nb = select_running_jobs(ua, jcrs, commands[ua->cmd_index].key);
471 foreach_alist(jcr, jcrs) {
472 /* Execute the cancel command only if we don't have an error */
474 ret &= cancel_job(ua, jcr, cancel);
484 * This is a common routine to create or update a
485 * Pool DB base record from a Pool Resource. We handle
486 * the setting of MaxVols and NumVols slightly differently
487 * depending on if we are creating the Pool or we are
488 * simply bringing it into agreement with the resource (updage).
490 * Caution : RecyclePoolId isn't setup in this function.
491 * You can use set_pooldbr_recyclepoolid();
494 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
496 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
497 if (op == POOL_OP_CREATE) {
498 pr->MaxVols = pool->max_volumes;
500 } else { /* update pool */
501 if (pr->MaxVols != pool->max_volumes) {
502 pr->MaxVols = pool->max_volumes;
504 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
505 pr->MaxVols = pr->NumVols;
508 pr->LabelType = pool->LabelType;
509 pr->UseOnce = pool->use_volume_once;
510 pr->UseCatalog = pool->use_catalog;
511 pr->Recycle = pool->Recycle;
512 pr->VolRetention = pool->VolRetention;
513 pr->VolUseDuration = pool->VolUseDuration;
514 pr->MaxVolJobs = pool->MaxVolJobs;
515 pr->MaxVolFiles = pool->MaxVolFiles;
516 pr->MaxVolBytes = pool->MaxVolBytes;
517 pr->AutoPrune = pool->AutoPrune;
518 pr->ActionOnPurge = pool->action_on_purge;
519 pr->Recycle = pool->Recycle;
520 if (pool->label_format) {
521 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
523 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
527 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
528 int update_pool_references(JCR *jcr, BDB *db, POOL *pool)
532 if (!pool->RecyclePool && !pool->ScratchPool) {
536 memset(&pr, 0, sizeof(POOL_DBR));
537 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
539 /* Don't compute NumVols here */
540 if (!db_get_pool_record(jcr, db, &pr)) {
541 return -1; /* not exists in database */
544 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
546 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
547 return -1; /* error */
550 /* NumVols is updated here */
551 if (!db_update_pool_record(jcr, db, &pr)) {
552 return -1; /* error */
557 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
558 * works with set_pooldbr_from_poolres
560 bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool)
565 if (pool->RecyclePool) {
566 memset(&rpool, 0, sizeof(POOL_DBR));
568 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
569 if (db_get_pool_record(jcr, db, &rpool)) {
570 pr->RecyclePoolId = rpool.PoolId;
572 Jmsg(jcr, M_WARNING, 0,
573 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
574 "Try to update it with 'update pool=%s'\n"),
575 pool->name(), rpool.Name, rpool.Name,pool->name());
579 } else { /* no RecyclePool used, set it to 0 */
580 pr->RecyclePoolId = 0;
583 if (pool->ScratchPool) {
584 memset(&rpool, 0, sizeof(POOL_DBR));
586 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
587 if (db_get_pool_record(jcr, db, &rpool)) {
588 pr->ScratchPoolId = rpool.PoolId;
590 Jmsg(jcr, M_WARNING, 0,
591 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
592 "Try to update it with 'update pool=%s'\n"),
593 pool->name(), rpool.Name, rpool.Name,pool->name());
596 } else { /* no ScratchPool used, set it to 0 */
597 pr->ScratchPoolId = 0;
605 * Create a pool record from a given Pool resource
606 * Also called from backup.c
607 * Returns: -1 on error
608 * 0 record already exists
612 int create_pool(JCR *jcr, BDB *db, POOL *pool, e_pool_op op)
615 memset(&pr, 0, sizeof(POOL_DBR));
616 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
618 if (db_get_pool_record(jcr, db, &pr)) {
620 if (op == POOL_OP_UPDATE) { /* update request */
621 set_pooldbr_from_poolres(&pr, pool, op);
622 set_pooldbr_references(jcr, db, &pr, pool);
623 db_update_pool_record(jcr, db, &pr);
625 return 0; /* exists */
628 set_pooldbr_from_poolres(&pr, pool, op);
629 set_pooldbr_references(jcr, db, &pr, pool);
631 if (!db_create_pool_record(jcr, db, &pr)) {
632 return -1; /* error */
640 * Create a Pool Record in the database.
641 * It is always created from the Resource record.
643 static int create_cmd(UAContext *ua, const char *cmd)
647 if (!open_client_db(ua)) {
651 pool = get_pool_resource(ua);
656 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
658 ua->error_msg(_("Error: Pool %s already exists.\n"
659 "Use update to change it.\n"), pool->name());
663 ua->error_msg("%s", db_strerror(ua->db));
669 ua->send_msg(_("Pool %s created.\n"), pool->name());
674 extern DIRRES *director;
675 extern char *configfile;
677 static int setbwlimit_client(UAContext *ua, CLIENT *client, char *Job, int64_t limit)
685 /* Connect to File daemon */
686 old_client = ua->jcr->client;
687 ua->jcr->client = client;
688 ua->jcr->max_bandwidth = limit;
690 /* Try to connect for 15 seconds */
691 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
692 client->name(), client->address, client->FDport);
693 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
694 ua->error_msg(_("Failed to connect to Client.\n"));
697 Dmsg0(120, "Connected to file daemon\n");
699 if (!send_bwlimit(ua->jcr, Job)) {
700 ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
703 /* Note, we add 2000 OK that was sent by FD to us to message */
704 ua->info_msg(_("2000 OK Limiting bandwidth to %lldkb/s %s\n"),
705 limit/1024, *Job?Job:_("on running and future jobs"));
708 ua->jcr->file_bsock->signal(BNET_TERMINATE);
709 free_bsock(ua->jcr->file_bsock);
710 ua->jcr->max_bandwidth = 0;
713 ua->jcr->client = old_client;
717 static int setbwlimit_cmd(UAContext *ua, const char *cmd)
720 CLIENT *client = NULL;
721 char Job[MAX_NAME_LENGTH];
727 const char *lst_all[] = { "job", "jobid", "jobname", "client", NULL };
728 if (find_arg_keyword(ua, lst_all) < 0) {
729 start_prompt(ua, _("Set Bandwidth choice:\n"));
730 add_prompt(ua, _("Running Job")); /* 0 */
731 add_prompt(ua, _("Running and future Jobs for a Client")); /* 1 */
732 action = do_prompt(ua, "item", _("Choose where to limit the bandwidth"),
739 i = find_arg_with_value(ua, "limit");
741 limit = atoi(ua->argv[i]) * 1024LL;
744 if (!get_pint(ua, _("Enter new bandwidth limit kb/s: "))) {
747 limit = ua->pint32_val * 1024LL; /* kb/s */
750 const char *lst[] = { "job", "jobid", "jobname", NULL };
751 if (action == 0 || find_arg_keyword(ua, lst) > 0) {
752 alist *jcrs = New(alist(10, not_owned_by_alist));
753 select_running_jobs(ua, jcrs, "limit");
754 foreach_alist(jcr, jcrs) {
755 jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
756 bstrncpy(Job, jcr->Job, sizeof(Job));
757 client = jcr->client;
758 setbwlimit_client(ua, client, Job, limit);
763 client = get_client_resource(ua);
765 setbwlimit_client(ua, client, Job, limit);
772 * Set a new address in a Client resource. We do this only
773 * if the Console name is the same as the Client name
774 * and the Console can access the client.
776 static int setip_cmd(UAContext *ua, const char *cmd)
780 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
781 ua->error_msg(_("Unauthorized command from this console.\n"));
785 client = GetClientResWithName(ua->cons->name());
788 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
791 if (client->address) {
792 free(client->address);
794 /* MA Bug 6 remove ifdef */
795 sockaddr_to_ascii(&(ua->UA_sock->client_addr),
796 sizeof(ua->UA_sock->client_addr), buf, sizeof(buf));
797 client->address = bstrdup(buf);
798 ua->send_msg(_("Client \"%s\" address set to %s\n"),
799 client->name(), client->address);
806 * Does all sorts of enable/disable commands: batch, scheduler (not implemented)
807 * job, client, schedule, storage
809 static void do_enable_disable_cmd(UAContext *ua, bool setting)
812 CLIENT *client = NULL;
816 if (find_arg(ua, NT_("batch")) > 0) {
817 ua->send_msg(_("Job Attributes Insertion %sabled\n"), setting?"en":"dis");
818 db_disable_batch_insert(setting);
823 * if (find_arg(ua, NT_("scheduler")) > 0) {
824 * ua->send_msg(_("Job Scheduler %sabled\n"), setting?"en":"dis");
829 i = find_arg(ua, NT_("job"));
833 job = GetJobResWithName(ua->argv[i]);
836 job = select_enable_disable_job_resource(ua, setting);
843 if (!acl_access_ok(ua, Job_ACL, job->name())) {
844 ua->error_msg(_("Unauthorized command from this console.\n"));
847 job->enabled = setting;
848 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
851 i = find_arg(ua, NT_("client"));
855 client = GetClientResWithName(ua->argv[i]);
858 client = select_enable_disable_client_resource(ua, setting);
865 if (!acl_access_ok(ua, Client_ACL, client->name())) {
866 ua->error_msg(_("Unauthorized command from this console.\n"));
869 client->enabled = setting;
870 ua->send_msg(_("Client \"%s\" %sabled\n"), client->name(), setting?"en":"dis");
873 i = find_arg(ua, NT_("schedule"));
877 sched = (SCHED *)GetResWithName(R_SCHEDULE, ua->argv[i]);
880 sched = select_enable_disable_schedule_resource(ua, setting);
887 if (!acl_access_ok(ua, Schedule_ACL, sched->name())) {
888 ua->error_msg(_("Unauthorized command from this console.\n"));
891 sched->enabled = setting;
892 ua->send_msg(_("Schedule \"%s\" %sabled\n"), sched->name(), setting?"en":"dis");
895 i = find_arg(ua, NT_("storage"));
897 do_storage_cmd(ua, setting?"enable":"disable");
900 if (i < 0 && !sched && !client && !job) {
901 ua->error_msg(_("You must enter one of the following keywords: job, client, schedule, or storage.\n"));
907 static int enable_cmd(UAContext *ua, const char *cmd)
909 do_enable_disable_cmd(ua, true);
913 static int disable_cmd(UAContext *ua, const char *cmd)
915 do_enable_disable_cmd(ua, false);
919 static void do_dir_setdebug(UAContext *ua, int64_t level, int trace_flag, char *options, int64_t tags)
922 debug_level_tags = tags;
923 set_trace(trace_flag);
924 set_debug_flags(options);
927 static void do_storage_setdebug(UAContext *ua, STORE *store,
928 int64_t level, int trace_flag, int hangup, int blowup,
929 char *options, char *tags)
934 lstore.store = store;
935 pm_strcpy(lstore.store_source, _("unknown source"));
936 set_wstorage(ua->jcr, &lstore);
937 /* Try connecting for up to 15 seconds */
938 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
939 store->name(), store->address, store->SDport);
940 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
941 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
944 Dmsg0(120, _("Connected to storage daemon\n"));
945 sd = ua->jcr->store_bsock;
946 sd->fsend("setdebug=%ld trace=%ld hangup=%ld blowup=%ld options=%s tags=%s\n",
947 (int32_t)level, trace_flag, hangup, blowup, options, NPRTB(tags));
948 if (sd->recv() >= 0) {
949 ua->send_msg("%s", sd->msg);
951 sd->signal(BNET_TERMINATE);
952 free_bsock(ua->jcr->store_bsock);
957 * For the client, we have the following values that can be set
958 * level = debug level
959 * trace = send debug output to a file
960 * options = various options for debug or specific FD behavior
961 * hangup = how many records to send to FD before hanging up
962 * obviously this is most useful for testing restarting
964 * blowup = how many records to send to FD before blowing up the FD.
966 static void do_client_setdebug(UAContext *ua, CLIENT *client,
967 int64_t level, int trace, int hangup, int blowup,
968 char *options, char *tags)
973 /* Connect to File daemon */
975 old_client = ua->jcr->client;
976 ua->jcr->client = client;
977 /* Try to connect for 15 seconds */
978 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
979 client->name(), client->address, client->FDport);
980 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
981 ua->error_msg(_("Failed to connect to Client.\n"));
982 ua->jcr->client = old_client;
985 Dmsg0(120, "Connected to file daemon\n");
987 fd = ua->jcr->file_bsock;
988 if (ua->jcr->FDVersion <= 10) {
989 fd->fsend("setdebug=%ld trace=%d hangup=%d\n",
990 (int32_t)level, trace, hangup);
992 fd->fsend("setdebug=%ld trace=%d hangup=%d blowup=%d options=%s tags=%s\n",
993 (int32_t)level, trace, hangup, blowup, options, NPRTB(tags));
995 if (fd->recv() >= 0) {
996 ua->send_msg("%s", fd->msg);
998 fd->signal(BNET_TERMINATE);
999 free_bsock(ua->jcr->file_bsock);
1000 ua->jcr->client = old_client;
1005 static void do_all_setdebug(UAContext *ua, int64_t level,
1006 int trace_flag, int hangup, int blowup,
1007 char *options, char *tags)
1009 STORE *store, **unique_store;
1010 CLIENT *client, **unique_client;
1015 debug_parse_tags(tags, &t);
1016 do_dir_setdebug(ua, level, trace_flag, options, t);
1018 /* Count Storage items */
1022 foreach_res(store, R_STORAGE) {
1025 unique_store = (STORE **) malloc(i * sizeof(STORE));
1026 /* Find Unique Storage address/port */
1027 store = (STORE *)GetNextRes(R_STORAGE, NULL);
1029 unique_store[i++] = store;
1030 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
1032 for (j=0; j<i; j++) {
1033 if (strcmp(unique_store[j]->address, store->address) == 0 &&
1034 unique_store[j]->SDport == store->SDport) {
1040 unique_store[i++] = store;
1041 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
1046 /* Call each unique Storage daemon */
1047 for (j=0; j<i; j++) {
1048 do_storage_setdebug(ua, unique_store[j], level, trace_flag,
1049 hangup, blowup, options, tags);
1053 /* Count Client items */
1057 foreach_res(client, R_CLIENT) {
1060 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
1061 /* Find Unique Client address/port */
1062 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
1064 unique_client[i++] = client;
1065 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
1067 for (j=0; j<i; j++) {
1068 if (strcmp(unique_client[j]->address, client->address) == 0 &&
1069 unique_client[j]->FDport == client->FDport) {
1075 unique_client[i++] = client;
1076 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1081 /* Call each unique File daemon */
1082 for (j=0; j<i; j++) {
1083 do_client_setdebug(ua, unique_client[j], level, trace_flag,
1084 hangup, blowup, options, tags);
1086 free(unique_client);
1090 * setdebug level=nn all trace=1/0
1092 static int setdebug_cmd(UAContext *ua, const char *cmd)
1096 int64_t level=0, tags=0;
1097 int trace_flag = -1;
1101 char *tags_str=NULL;
1104 Dmsg1(120, "setdebug:%s:\n", cmd);
1107 i = find_arg_with_value(ua, "options");
1109 bstrncpy(options, ua->argv[i], sizeof(options) - 1);
1112 i = find_arg_with_value(ua, "level");
1114 level = str_to_int64(ua->argv[i]);
1117 if (!get_pint(ua, _("Enter new debug level: "))) {
1120 level = ua->pint32_val;
1123 /* Better to send the tag string instead of tweaking the level
1124 * in case where we extend the tag or change the representation
1126 i = find_arg_with_value(ua, "tags");
1128 tags_str = ua->argv[i];
1129 if (!debug_parse_tags(tags_str, &tags)) {
1130 ua->error_msg(_("Incorrect tags found on command line %s\n"), tags_str);
1135 /* Look for trace flag. -1 => not change */
1136 i = find_arg_with_value(ua, "trace");
1138 trace_flag = atoi(ua->argv[i]);
1139 if (trace_flag > 0) {
1144 /* Look for hangup (debug only) flag. -1 => not change */
1145 i = find_arg_with_value(ua, "hangup");
1147 hangup = atoi(ua->argv[i]);
1150 /* Look for blowup (debug only) flag. -1 => not change */
1151 i = find_arg_with_value(ua, "blowup");
1153 blowup = atoi(ua->argv[i]);
1156 /* General debug? */
1157 for (i=1; i<ua->argc; i++) {
1158 if (strcasecmp(ua->argk[i], "all") == 0) {
1159 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1162 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1163 strcasecmp(ua->argk[i], "director") == 0) {
1164 do_dir_setdebug(ua, level, trace_flag, options, tags);
1167 if (strcasecmp(ua->argk[i], "client") == 0 ||
1168 strcasecmp(ua->argk[i], "fd") == 0) {
1171 client = GetClientResWithName(ua->argv[i]);
1173 do_client_setdebug(ua, client, level, trace_flag,
1174 hangup, blowup, options, tags_str);
1178 client = select_client_resource(ua);
1180 do_client_setdebug(ua, client, level, trace_flag,
1181 hangup, blowup, options, tags_str);
1186 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1187 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1188 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1191 store = GetStoreResWithName(ua->argv[i]);
1193 do_storage_setdebug(ua, store, level, trace_flag,
1194 hangup, blowup, options, tags_str);
1198 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1200 do_storage_setdebug(ua, store, level, trace_flag,
1201 hangup, blowup, options, tags_str);
1207 * We didn't find an appropriate keyword above, so
1210 start_prompt(ua, _("Available daemons are: \n"));
1211 add_prompt(ua, _("Director"));
1212 add_prompt(ua, _("Storage"));
1213 add_prompt(ua, _("Client"));
1214 add_prompt(ua, _("All"));
1215 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1216 case 0: /* Director */
1217 do_dir_setdebug(ua, level, trace_flag, options, tags);
1220 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1222 do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
1227 client = select_client_resource(ua);
1229 do_client_setdebug(ua, client, level, trace_flag, hangup, blowup,
1234 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1243 * Turn debug tracing to file on/off
1245 static int trace_cmd(UAContext *ua, const char *cmd)
1249 if (ua->argc != 2) {
1250 if (!get_cmd(ua, _("Turn on or off? "))) {
1255 onoff = ua->argk[1];
1258 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1262 static int var_cmd(UAContext *ua, const char *cmd)
1264 POOLMEM *val = get_pool_memory(PM_FNAME);
1267 if (!open_client_db(ua)) {
1270 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1273 while (*var == ' ') { /* skip spaces */
1276 Dmsg1(100, "Var=%s:\n", var);
1277 variable_expansion(ua->jcr, var, &val);
1278 ua->send_msg("%s\n", val);
1279 free_pool_memory(val);
1283 static int estimate_cmd(UAContext *ua, const char *cmd)
1286 CLIENT *client = NULL;
1287 FILESET *fileset = NULL;
1289 char since[MAXSTRING];
1293 jcr->setJobLevel(L_FULL);
1294 for (int i=1; i<ua->argc; i++) {
1295 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1296 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1298 client = GetClientResWithName(ua->argv[i]);
1300 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1303 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1304 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1309 ua->error_msg(_("Client name missing.\n"));
1313 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1315 job = GetJobResWithName(ua->argv[i]);
1317 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1320 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1321 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1326 ua->error_msg(_("Job name missing.\n"));
1331 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1333 fileset = GetFileSetResWithName(ua->argv[i]);
1335 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1338 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1339 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1344 ua->error_msg(_("Fileset name missing.\n"));
1348 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1352 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1354 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1355 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1360 ua->error_msg(_("Level value missing.\n"));
1364 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1366 if (!is_yesno(ua->argv[i], &accurate)) {
1367 ua->error_msg(_("Invalid value for accurate. "
1368 "It must be yes or no.\n"));
1373 ua->error_msg(_("Accurate value missing.\n"));
1378 if (!job && !(client && fileset)) {
1379 if (!(job = select_job_resource(ua))) {
1384 job = GetJobResWithName(ua->argk[1]);
1386 ua->error_msg(_("No job specified.\n"));
1389 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1390 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1395 client = job->client;
1398 fileset = job->fileset;
1400 jcr->client = client;
1401 jcr->fileset = fileset;
1403 if (job->pool->catalog) {
1404 ua->catalog = job->pool->catalog;
1406 ua->catalog = client->catalog;
1414 jcr->setJobType(JT_BACKUP);
1415 jcr->start_time = time(NULL);
1416 init_jcr_job_record(jcr);
1418 if (!get_or_create_client_record(jcr)) {
1421 if (!get_or_create_fileset_record(jcr)) {
1425 get_level_since_time(ua->jcr, since, sizeof(since));
1427 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1428 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1429 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1430 ua->error_msg(_("Failed to connect to Client.\n"));
1434 /* The level string change if accurate mode is enabled */
1435 if (accurate >= 0) {
1436 jcr->accurate = accurate;
1438 jcr->accurate = job->accurate;
1441 if (!send_level_command(jcr)) {
1445 if (!send_include_list(jcr)) {
1446 ua->error_msg(_("Error sending include list.\n"));
1450 if (!send_exclude_list(jcr)) {
1451 ua->error_msg(_("Error sending exclude list.\n"));
1456 * If the job is in accurate mode, we send the list of
1459 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1460 if (!send_accurate_current_files(jcr)) {
1464 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1465 while (jcr->file_bsock->recv() >= 0) {
1466 ua->send_msg("%s", jcr->file_bsock->msg);
1470 if (jcr->file_bsock) {
1471 jcr->file_bsock->signal(BNET_TERMINATE);
1472 free_bsock(ua->jcr->file_bsock);
1480 static int time_cmd(UAContext *ua, const char *cmd)
1483 time_t ttime = time(NULL);
1485 (void)localtime_r(&ttime, &tm);
1486 strftime(sdt, sizeof(sdt), "%a %d-%b-%Y %H:%M:%S", &tm);
1487 ua->send_msg("%s\n", sdt);
1492 * reload the conf file
1494 extern "C" void reload_config(int sig);
1496 static int reload_cmd(UAContext *ua, const char *cmd)
1503 * Delete Pool records (should purge Media with it).
1505 * delete pool=<pool-name>
1506 * delete volume pool=<pool-name> volume=<name>
1509 static int delete_cmd(UAContext *ua, const char *cmd)
1511 static const char *keywords[] = {
1518 /* Deleting large jobs can take time! */
1519 if (!open_new_client_db(ua)) {
1523 switch (find_arg_keyword(ua, keywords)) {
1532 while ((i=find_arg(ua, "jobid")) > 0) {
1534 *ua->argk[i] = 0; /* zap keyword already visited */
1538 delete_snapshot(ua);
1545 "In general it is not a good idea to delete either a\n"
1546 "Pool or a Volume since they may contain data.\n\n"));
1548 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1559 delete_snapshot(ua);
1562 ua->warning_msg(_("Nothing done.\n"));
1569 * delete_job has been modified to parse JobID lists like the
1571 * delete JobID=3,4,6,7-11,14
1573 * Thanks to Phil Stracchino for the above addition.
1575 static void delete_job(UAContext *ua)
1577 int JobId; /* not JobId_t because it's unsigned and not compatible with sellist */
1581 int i = find_arg_with_value(ua, NT_("jobid"));
1583 if (!sl.set_string(ua->argv[i], true)) {
1584 ua->warning_msg("%s", sl.get_errmsg());
1588 if (sl.size() > 25 && (find_arg(ua, "yes") < 0)) {
1589 bsnprintf(buf, sizeof(buf),
1590 _("Are you sure you want to delete %d JobIds ? (yes/no): "), sl.size());
1591 if (!get_yesno(ua, buf)) {
1596 foreach_sellist(JobId, &sl) {
1597 do_job_delete(ua, JobId);
1600 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1604 JobId = ua->int64_val;
1605 do_job_delete(ua, JobId);
1610 * do_job_delete now performs the actual delete operation atomically
1612 static void do_job_delete(UAContext *ua, JobId_t JobId)
1616 edit_int64(JobId, ed1);
1617 purge_jobs_from_catalog(ua, ed1);
1618 ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1);
1622 * Delete media records from database -- dangerous
1624 static int delete_volume(UAContext *ua)
1630 if (!select_media_dbr(ua, &mr)) {
1633 ua->warning_msg(_("\nThis command will delete volume %s\n"
1634 "and all Jobs saved on that volume from the Catalog\n"),
1637 if (find_arg(ua, "yes") >= 0) {
1638 ua->pint32_val = 1; /* Have "yes" on command line already" */
1640 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1642 if (!get_yesno(ua, buf)) {
1646 if (!ua->pint32_val) {
1650 /* If not purged, do it */
1651 if (strcmp(mr.VolStatus, "Purged") != 0) {
1652 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1653 ua->error_msg(_("Can't list jobs on this volume\n"));
1657 purge_jobs_from_catalog(ua, lst.list);
1661 db_delete_media_record(ua->jcr, ua->db, &mr);
1666 * Delete a pool record from the database -- dangerous
1668 static int delete_pool(UAContext *ua)
1673 memset(&pr, 0, sizeof(pr));
1675 if (!get_pool_dbr(ua, &pr)) {
1678 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1680 if (!get_yesno(ua, buf)) {
1683 if (ua->pint32_val) {
1684 db_delete_pool_record(ua->jcr, ua->db, &pr);
1689 int memory_cmd(UAContext *ua, const char *cmd)
1691 garbage_collect_memory();
1692 list_dir_status_header(ua);
1693 sm_dump(false, true);
1697 static void do_storage_cmd(UAContext *ua, const char *command)
1702 char dev_name[MAX_NAME_LENGTH];
1706 if (!open_client_db(ua)) {
1709 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1711 store.store = get_storage_resource(ua, true/*arg is storage*/);
1715 pm_strcpy(store.store_source, _("unknown source"));
1716 set_wstorage(jcr, &store);
1717 drive = get_storage_drive(ua, store.store);
1718 slot = get_storage_slot(ua, store.store);
1720 /* Users may set a device name directly on the command line */
1721 if ((i = find_arg_with_value(ua, "device")) > 0) {
1722 POOLMEM *errmsg = get_pool_memory(PM_NAME);
1723 if (!is_name_valid(ua->argv[i], &errmsg)) {
1724 ua->error_msg(_("Invalid device name. %s"), errmsg);
1725 free_pool_memory(errmsg);
1728 free_pool_memory(errmsg);
1729 bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1731 } else { /* We take the default device name */
1732 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1735 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1736 store.store->media_type, store.store->dev_name(), drive);
1737 Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1739 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1740 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1743 sd = jcr->store_bsock;
1744 bash_spaces(dev_name);
1745 sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1746 while (sd->recv() >= 0) {
1747 ua->send_msg("%s", sd->msg);
1749 sd->signal(BNET_TERMINATE);
1750 free_bsock(ua->jcr->store_bsock);
1754 * mount [storage=<name>] [drive=nn] [slot=mm]
1756 static int mount_cmd(UAContext *ua, const char *cmd)
1758 do_storage_cmd(ua, "mount") ; /* mount */
1764 * unmount [storage=<name>] [drive=nn]
1766 static int unmount_cmd(UAContext *ua, const char *cmd)
1768 do_storage_cmd(ua, "unmount"); /* unmount */
1774 * release [storage=<name>] [drive=nn]
1776 static int release_cmd(UAContext *ua, const char *cmd)
1778 do_storage_cmd(ua, "release"); /* release */
1785 * use catalog=<name>
1787 static int use_cmd(UAContext *ua, const char *cmd)
1789 CAT *oldcatalog, *catalog;
1792 close_db(ua); /* close any previously open db */
1793 oldcatalog = ua->catalog;
1795 if (!(catalog = get_catalog_resource(ua))) {
1796 ua->catalog = oldcatalog;
1798 ua->catalog = catalog;
1801 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1802 ua->catalog->name(), ua->catalog->db_name);
1807 int quit_cmd(UAContext *ua, const char *cmd)
1813 /* Handler to get job status */
1814 static int status_handler(void *ctx, int num_fields, char **row)
1816 char *val = (char *)ctx;
1821 *val = '?'; /* Unknown by default */
1828 * Wait until no job is running
1830 int wait_cmd(UAContext *ua, const char *cmd)
1834 time_t stop_time = 0;
1838 * Wait until no job is running
1840 if (ua->argc == 1) {
1841 bmicrosleep(0, 200000); /* let job actually start */
1842 for (bool running=true; running; ) {
1845 if (!jcr->is_internal_job()) {
1859 i = find_arg_with_value(ua, NT_("timeout"));
1860 if (i > 0 && ua->argv[i]) {
1861 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1864 /* we have jobid, jobname or ujobid argument */
1866 uint32_t jobid = 0 ;
1868 if (!open_client_db(ua)) {
1869 ua->error_msg(_("ERR: Can't open db\n")) ;
1873 for (int i=1; i<ua->argc; i++) {
1874 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1878 jobid = str_to_int64(ua->argv[i]);
1880 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1881 strcasecmp(ua->argk[i], "job") == 0) {
1885 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1887 jobid = jcr->JobId ;
1891 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1895 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1897 jobid = jcr->JobId ;
1901 /* Wait for a mount request */
1902 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1903 for (bool waiting=false; !waiting; ) {
1905 if (!jcr->is_internal_job() &&
1906 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1915 if (stop_time && (time(NULL) >= stop_time)) {
1916 ua->warning_msg(_("Wait on mount timed out\n"));
1926 ua->error_msg(_("ERR: Job was not found\n"));
1931 * We wait the end of a specific job
1934 bmicrosleep(0, 200000); /* let job actually start */
1935 for (bool running=true; running; ) {
1938 jcr=get_jcr_by_id(jobid) ;
1951 * We have to get JobStatus
1955 char jobstatus = '?'; /* Unknown by default */
1958 bsnprintf(buf, sizeof(buf),
1959 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1962 db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
1964 switch (jobstatus) {
1966 status = 1 ; /* Warning */
1971 case JS_ErrorTerminated:
1973 status = 2 ; /* Critical */
1978 status = 0 ; /* Ok */
1982 status = 3 ; /* Unknown */
1986 ua->send_msg("JobId=%i\n", jobid) ;
1987 ua->send_msg("JobStatus=%s (%c)\n",
1988 job_status_to_str(jobstatus, 0),
1991 if (ua->gui || ua->api) {
1992 ua->send_msg("ExitStatus=%i\n", status) ;
1999 static int help_cmd(UAContext *ua, const char *cmd)
2002 ua->send_msg(_(" Command Description\n ======= ===========\n"));
2003 for (i=0; i<comsize; i++) {
2004 if (ua->argc == 2) {
2005 if (!strcasecmp(ua->argk[1], commands[i].key)) {
2006 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2007 commands[i].help, commands[i].usage);
2011 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
2014 if (i == comsize && ua->argc == 2) {
2015 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2017 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2021 int qhelp_cmd(UAContext *ua, const char *cmd)
2024 /* Want to display only commands */
2025 j = find_arg(ua, NT_("all"));
2027 for (i=0; i<comsize; i++) {
2028 ua->send_msg("%s\n", commands[i].key);
2032 /* Want to display a specific help section */
2033 j = find_arg_with_value(ua, NT_("item"));
2034 if (j >= 0 && ua->argk[j]) {
2035 for (i=0; i<comsize; i++) {
2036 if (bstrcmp(commands[i].key, ua->argv[j])) {
2037 ua->send_msg("%s\n", commands[i].usage);
2043 /* Want to display everything */
2044 for (i=0; i<comsize; i++) {
2045 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2051 static int version_cmd(UAContext *ua, const char *cmd)
2053 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2054 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2059 * Test code -- turned on only for debug testing
2061 static int version_cmd(UAContext *ua, const char *cmd)
2064 POOL_MEM query(PM_MESSAGE);
2066 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2067 db_get_query_dbids(ua->jcr, ua->db, query, ids);
2068 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2069 for (int i=0; i < ids.num_ids; i++) {
2070 ua->send_msg("id=%d\n", ids.DBId[i]);
2078 * This call uses open_client_db() and force a
2079 * new dedicated connection to the catalog
2081 bool open_new_client_db(UAContext *ua)
2085 /* Force a new dedicated connection */
2086 ua->force_mult_db_connections = true;
2087 ret = open_client_db(ua);
2088 ua->force_mult_db_connections = false;
2094 * This call explicitly checks for a catalog=xxx and
2095 * if given, opens that catalog. It also checks for
2096 * client=xxx and if found, opens the catalog
2097 * corresponding to that client. If we still don't
2098 * have a catalog, look for a Job keyword and get the
2099 * catalog from its client record.
2101 bool open_client_db(UAContext *ua)
2108 /* Try for catalog keyword */
2109 i = find_arg_with_value(ua, NT_("catalog"));
2111 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2112 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2115 catalog = GetCatalogResWithName(ua->argv[i]);
2117 if (ua->catalog && ua->catalog != catalog) {
2120 ua->catalog = catalog;
2125 /* Try for client keyword */
2126 i = find_arg_with_value(ua, NT_("client"));
2128 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2129 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2132 client = GetClientResWithName(ua->argv[i]);
2134 catalog = client->catalog;
2135 if (ua->catalog && ua->catalog != catalog) {
2138 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2139 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2142 ua->catalog = catalog;
2147 /* Try for Job keyword */
2148 i = find_arg_with_value(ua, NT_("job"));
2150 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2151 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2154 job = GetJobResWithName(ua->argv[i]);
2156 catalog = job->client->catalog;
2157 if (ua->catalog && ua->catalog != catalog) {
2160 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2161 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2164 ua->catalog = catalog;
2174 * Open the catalog database.
2176 bool open_db(UAContext *ua)
2180 /* The force_mult_db_connections is telling us if we modify the
2181 * private or the shared link
2183 if (ua->force_mult_db_connections) {
2184 ua->db = ua->private_db;
2187 ua->db = ua->shared_db;
2195 ua->catalog = get_catalog_resource(ua);
2197 ua->error_msg( _("Could not find a Catalog resource\n"));
2202 /* Some modules like bvfs need their own catalog connection */
2203 mult_db_conn = ua->catalog->mult_db_connections;
2204 if (ua->force_mult_db_connections) {
2205 mult_db_conn = true;
2208 ua->jcr->catalog = ua->catalog;
2210 Dmsg0(100, "UA Open database\n");
2211 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2212 ua->catalog->db_name,
2213 ua->catalog->db_user,
2214 ua->catalog->db_password, ua->catalog->db_address,
2215 ua->catalog->db_port, ua->catalog->db_socket,
2216 ua->catalog->db_ssl_key, ua->catalog->db_ssl_cert,
2217 ua->catalog->db_ssl_ca, ua->catalog->db_ssl_capath,
2218 ua->catalog->db_ssl_cipher,
2219 mult_db_conn, ua->catalog->disable_batch_insert);
2220 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2221 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2222 ua->catalog->db_name);
2224 ua->error_msg("%s", db_strerror(ua->db));
2229 ua->jcr->db = ua->db;
2231 /* Depending on the type of connection, we set the right variable */
2232 if (ua->force_mult_db_connections) {
2233 ua->private_db = ua->db;
2236 ua->shared_db = ua->db;
2240 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2242 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2246 void close_db(UAContext *ua)
2252 if (ua->shared_db) {
2253 db_close_database(ua->jcr, ua->shared_db);
2254 ua->shared_db = NULL;
2257 if (ua->private_db) {
2258 db_close_database(ua->jcr, ua->private_db);
2259 ua->private_db = NULL;