2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Commands
32 * Kern Sibbald, September MM
42 #undef _POSIX_C_SOURCE
45 #include "lib/pythonlib.h"
47 /* Imported Functions */
48 extern PyObject *job_getattr(PyObject *self, char *attrname);
49 extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
51 #endif /* HAVE_PYTHON */
53 /* Imported subroutines */
55 /* Imported variables */
56 extern jobq_t job_queue; /* job queue */
59 /* Imported functions */
60 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
61 extern int gui_cmd(UAContext *ua, const char *cmd);
62 extern int label_cmd(UAContext *ua, const char *cmd);
63 extern int list_cmd(UAContext *ua, const char *cmd);
64 extern int llist_cmd(UAContext *ua, const char *cmd);
65 extern int messagescmd(UAContext *ua, const char *cmd);
66 extern int prunecmd(UAContext *ua, const char *cmd);
67 extern int purgecmd(UAContext *ua, const char *cmd);
68 extern int querycmd(UAContext *ua, const char *cmd);
69 extern int relabel_cmd(UAContext *ua, const char *cmd);
70 extern int restore_cmd(UAContext *ua, const char *cmd);
71 extern int retentioncmd(UAContext *ua, const char *cmd);
72 extern int show_cmd(UAContext *ua, const char *cmd);
73 extern int sqlquerycmd(UAContext *ua, const char *cmd);
74 extern int status_cmd(UAContext *ua, const char *cmd);
75 extern int update_cmd(UAContext *ua, const char *cmd);
77 /* Forward referenced functions */
78 static int add_cmd(UAContext *ua, const char *cmd);
79 static int automount_cmd(UAContext *ua, const char *cmd);
80 static int cancel_cmd(UAContext *ua, const char *cmd);
81 static int create_cmd(UAContext *ua, const char *cmd);
82 static int delete_cmd(UAContext *ua, const char *cmd);
83 static int disable_cmd(UAContext *ua, const char *cmd);
84 static int enable_cmd(UAContext *ua, const char *cmd);
85 static int estimate_cmd(UAContext *ua, const char *cmd);
86 static int help_cmd(UAContext *ua, const char *cmd);
87 static int memory_cmd(UAContext *ua, const char *cmd);
88 static int mount_cmd(UAContext *ua, const char *cmd);
89 static int python_cmd(UAContext *ua, const char *cmd);
90 static int release_cmd(UAContext *ua, const char *cmd);
91 static int reload_cmd(UAContext *ua, const char *cmd);
92 static int setdebug_cmd(UAContext *ua, const char *cmd);
93 static int setip_cmd(UAContext *ua, const char *cmd);
94 static int time_cmd(UAContext *ua, const char *cmd);
95 static int trace_cmd(UAContext *ua, const char *cmd);
96 static int unmount_cmd(UAContext *ua, const char *cmd);
97 static int use_cmd(UAContext *ua, const char *cmd);
98 static int var_cmd(UAContext *ua, const char *cmd);
99 static int version_cmd(UAContext *ua, const char *cmd);
100 static int wait_cmd(UAContext *ua, const char *cmd);
102 static void do_job_delete(UAContext *ua, JobId_t JobId);
103 static void delete_job_id_range(UAContext *ua, char *tok);
104 static int delete_volume(UAContext *ua);
105 static int delete_pool(UAContext *ua);
106 static void delete_job(UAContext *ua);
108 int qhelp_cmd(UAContext *ua, const char *cmd);
109 int quit_cmd(UAContext *ua, const char *cmd);
111 /* not all in alphabetical order. New commands are added after existing commands with similar letters
112 to prevent breakage of existing user scripts. */
114 const char *key; /* command */
115 int (*func)(UAContext *ua, const char *cmd); /* handler */
116 const char *help; /* main purpose */
117 const char *usage; /* all arguments to build usage */
118 const bool use_in_rs; /* Can use it in Console RunScript */
120 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
121 { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
122 { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
123 { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
124 { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid=<number> job=<job-name> ujobid=<unique-jobid>"), false},
125 { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
126 { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume=<vol-name> pool=<pool-name> jobid=<id>"), true},
127 { NT_("disable"), disable_cmd, _("Disable a job"), NT_("job=<name>"), true},
128 { NT_("enable"), enable_cmd, _("Enable a job"), NT_("job=<name>"), true},
129 { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
130 NT_("fileset=<fs> client=<cli> accurate=<yes/no> job=<job> listing"), true},
132 { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
133 { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
134 { NT_("help"), help_cmd, _("Print this command"), NT_(""), false},
135 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool>"), false},
136 { NT_("list"), list_cmd, _("List objects from catalog"),
137 NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
139 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
140 NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
142 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
143 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
144 { NT_("mount"), mount_cmd, _("Mount storage"),
145 NT_("storage=<storage-name> slot=<num> drive=<num> [ jobid=<id> | job=<job-name> ]"), false},
147 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
148 NT_("files | jobs | client=<client-name> | volume=<volume-name> "), true},
150 { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("volume=<vol>"), true},
151 { NT_("python"), python_cmd, _("Python control commands"), NT_(""), false},
152 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
153 { NT_("query"), querycmd, _("Query catalog"), NT_(""), false},
154 { NT_("restore"), restore_cmd, _("Restore files"),
155 NT_("where=/path client=<client> storage=<storage> bootstrap=<file> jobid=<jobid> done select all"), false},
157 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
158 NT_("storage=<storage-name> oldvolume=<old-volume-name> volume=<newvolume-name>"), false},
160 { NT_("release"), release_cmd, _("Release storage"), NT_("storage-name"), false},
161 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
162 { NT_("run"), run_cmd, _("Run a job"),
163 NT_("job=<job-name> client=<client-name> fileset=<FileSet-name> level=<level-keyword> storage=<storage-name> where=<directory-prefix> "
164 "when=<universal-time-specification> yes"), false}, /* need to be check */
166 { NT_("status"), status_cmd, _("Report status"),
167 NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
169 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
170 NT_("level=nn trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
172 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
173 { NT_("show"), show_cmd, _("Show resource records"),
174 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | all"), true},
176 { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
177 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
178 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
179 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
180 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
182 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
183 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
185 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
186 NT_("pool=<poolname> | slots | stats | volume=<volname> volstatus=<status> volretention=<time-def> "
187 "pool=<pool> recycle=<yes/no> slot=<number> inchanger=<yes/no>"),true},
188 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_(""), false},
189 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
190 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
191 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
192 NT_("<jobname=name> | <jobid=nnn> | <ujobid=complete_name>"), false}
195 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
198 * Execute a command from the UA
200 bool do_a_command(UAContext *ua)
206 BSOCK *user = ua->UA_sock;
209 Dmsg1(900, "Command: %s\n", ua->argk[0]);
214 while (ua->jcr->wstorage->size()) {
215 ua->jcr->wstorage->remove(0);
218 len = strlen(ua->argk[0]);
219 for (i=0; i<comsize; i++) { /* search for command */
220 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
221 /* Check if command permitted, but "quit" is always OK */
222 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
223 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
226 /* Check if this command is authorized in RunScript */
227 if (ua->runscript && !commands[i].use_in_rs) {
228 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
231 if (ua->api) user->signal(BNET_CMD_BEGIN);
232 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
233 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
239 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
246 * This is a common routine used to stuff the Pool DB record defaults
247 * into the Media DB record just before creating a media (Volume)
250 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
252 mr->PoolId = pr->PoolId;
253 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
254 mr->Recycle = pr->Recycle;
255 mr->VolRetention = pr->VolRetention;
256 mr->VolUseDuration = pr->VolUseDuration;
257 mr->ActionOnPurge = pr->ActionOnPurge;
258 mr->RecyclePoolId = pr->RecyclePoolId;
259 mr->MaxVolJobs = pr->MaxVolJobs;
260 mr->MaxVolFiles = pr->MaxVolFiles;
261 mr->MaxVolBytes = pr->MaxVolBytes;
262 mr->LabelType = pr->LabelType;
268 * Add Volumes to an existing Pool
270 static int add_cmd(UAContext *ua, const char *cmd)
274 int num, i, max, startnum;
276 char name[MAX_NAME_LENGTH];
278 int Slot = 0, InChanger = 0;
281 "You probably don't want to be using this command since it\n"
282 "creates database records without labeling the Volumes.\n"
283 "You probably want to use the \"label\" command.\n\n"));
285 if (!open_client_db(ua)) {
289 memset(&pr, 0, sizeof(pr));
290 memset(&mr, 0, sizeof(mr));
292 if (!get_pool_dbr(ua, &pr)) {
296 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
297 pr.MaxVols, pr.PoolType);
299 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
300 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
301 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
304 pr.MaxVols = ua->pint32_val;
308 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
309 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
310 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
314 if (pr.MaxVols == 0) {
317 max = pr.MaxVols - pr.NumVols;
321 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
322 if (!get_pint(ua, buf)) {
325 num = ua->pint32_val;
326 if (num < 0 || num > max) {
327 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
335 if (!get_cmd(ua, _("Enter Volume name: "))) {
339 if (!get_cmd(ua, _("Enter base volume name: "))) {
343 /* Don't allow | in Volume name because it is the volume separator character */
344 if (!is_volume_name_legal(ua, ua->cmd)) {
347 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
348 ua->warning_msg(_("Volume name too long.\n"));
351 if (strlen(ua->cmd) == 0) {
352 ua->warning_msg(_("Volume name must be at least one character long.\n"));
358 bstrncpy(name, ua->cmd, sizeof(name));
360 bstrncat(name, "%04d", sizeof(name));
363 if (!get_pint(ua, _("Enter the starting number: "))) {
366 startnum = ua->pint32_val;
368 ua->warning_msg(_("Start number must be greater than zero.\n"));
378 if (store && store->autochanger) {
379 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
382 Slot = ua->pint32_val;
383 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
386 InChanger = ua->pint32_val;
389 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
390 for (i=startnum; i < num+startnum; i++) {
391 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
393 mr.InChanger = InChanger;
394 mr.StorageId = store->StorageId;
396 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
397 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
398 ua->error_msg("%s", db_strerror(ua->db));
402 first_id = mr.PoolId;
406 Dmsg0(200, "Update pool record.\n");
407 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
408 ua->warning_msg("%s", db_strerror(ua->db));
411 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
417 * Turn auto mount on/off
422 int automount_cmd(UAContext *ua, const char *cmd)
427 if (!get_cmd(ua, _("Turn on or off? "))) {
435 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
443 static int cancel_cmd(UAContext *ua, const char *cmd)
448 char JobName[MAX_NAME_LENGTH];
450 for (i=1; i<ua->argc; i++) {
451 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
456 JobId = str_to_int64(ua->argv[i]);
457 if (!(jcr=get_jcr_by_id(JobId))) {
458 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
462 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
466 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
467 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
468 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
469 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
472 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
476 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
477 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
478 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
479 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
486 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
487 ua->error_msg(_("Unauthorized command from this console.\n"));
492 * If we still do not have a jcr,
493 * throw up a list and ask the user to select one.
496 int tjobs = 0; /* total # number jobs */
497 /* Count Jobs running */
499 if (jcr->JobId == 0) { /* this is us */
502 tjobs++; /* count of all jobs */
503 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
504 continue; /* skip not authorized */
506 njobs++; /* count of authorized jobs */
510 if (njobs == 0) { /* no authorized */
512 ua->send_msg(_("No Jobs running.\n"));
514 ua->send_msg(_("None of your jobs are running.\n"));
519 start_prompt(ua, _("Select Job:\n"));
522 if (jcr->JobId == 0) { /* this is us */
525 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
526 continue; /* skip not authorized */
528 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
533 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
536 if (ua->api && njobs == 1) {
538 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
539 _("Confirm cancel?"));
540 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
545 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
550 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
551 jcr = get_jcr_by_full_name(JobName);
553 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
558 ret = cancel_job(ua, jcr);
564 * This is a common routine to create or update a
565 * Pool DB base record from a Pool Resource. We handle
566 * the setting of MaxVols and NumVols slightly differently
567 * depending on if we are creating the Pool or we are
568 * simply bringing it into agreement with the resource (updage).
570 * Caution : RecyclePoolId isn't setup in this function.
571 * You can use set_pooldbr_recyclepoolid();
574 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
576 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
577 if (op == POOL_OP_CREATE) {
578 pr->MaxVols = pool->max_volumes;
580 } else { /* update pool */
581 if (pr->MaxVols != pool->max_volumes) {
582 pr->MaxVols = pool->max_volumes;
584 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
585 pr->MaxVols = pr->NumVols;
588 pr->LabelType = pool->LabelType;
589 pr->UseOnce = pool->use_volume_once;
590 pr->UseCatalog = pool->use_catalog;
591 pr->Recycle = pool->Recycle;
592 pr->VolRetention = pool->VolRetention;
593 pr->VolUseDuration = pool->VolUseDuration;
594 pr->MaxVolJobs = pool->MaxVolJobs;
595 pr->MaxVolFiles = pool->MaxVolFiles;
596 pr->MaxVolBytes = pool->MaxVolBytes;
597 pr->AutoPrune = pool->AutoPrune;
598 pr->ActionOnPurge = pool->action_on_purge;
599 pr->Recycle = pool->Recycle;
600 if (pool->label_format) {
601 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
603 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
607 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
608 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
612 if (!pool->RecyclePool && !pool->ScratchPool) {
616 memset(&pr, 0, sizeof(POOL_DBR));
617 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
619 if (!db_get_pool_record(jcr, db, &pr)) {
620 return -1; /* not exists in database */
623 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
625 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
626 return -1; /* error */
629 if (!db_update_pool_record(jcr, db, &pr)) {
630 return -1; /* error */
635 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
636 * works with set_pooldbr_from_poolres
638 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
643 if (pool->RecyclePool) {
644 memset(&rpool, 0, sizeof(POOL_DBR));
646 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
647 if (db_get_pool_record(jcr, db, &rpool)) {
648 pr->RecyclePoolId = rpool.PoolId;
650 Jmsg(jcr, M_WARNING, 0,
651 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
652 "Try to update it with 'update pool=%s'\n"),
653 pool->name(), rpool.Name, rpool.Name,pool->name());
657 } else { /* no RecyclePool used, set it to 0 */
658 pr->RecyclePoolId = 0;
661 if (pool->ScratchPool) {
662 memset(&rpool, 0, sizeof(POOL_DBR));
664 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
665 if (db_get_pool_record(jcr, db, &rpool)) {
666 pr->ScratchPoolId = rpool.PoolId;
668 Jmsg(jcr, M_WARNING, 0,
669 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
670 "Try to update it with 'update pool=%s'\n"),
671 pool->name(), rpool.Name, rpool.Name,pool->name());
674 } else { /* no ScratchPool used, set it to 0 */
675 pr->ScratchPoolId = 0;
683 * Create a pool record from a given Pool resource
684 * Also called from backup.c
685 * Returns: -1 on error
686 * 0 record already exists
690 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
694 memset(&pr, 0, sizeof(POOL_DBR));
696 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
698 if (db_get_pool_record(jcr, db, &pr)) {
700 if (op == POOL_OP_UPDATE) { /* update request */
701 set_pooldbr_from_poolres(&pr, pool, op);
702 set_pooldbr_references(jcr, db, &pr, pool);
703 db_update_pool_record(jcr, db, &pr);
705 return 0; /* exists */
708 set_pooldbr_from_poolres(&pr, pool, op);
709 set_pooldbr_references(jcr, db, &pr, pool);
711 if (!db_create_pool_record(jcr, db, &pr)) {
712 return -1; /* error */
720 * Create a Pool Record in the database.
721 * It is always created from the Resource record.
723 static int create_cmd(UAContext *ua, const char *cmd)
727 if (!open_client_db(ua)) {
731 pool = get_pool_resource(ua);
736 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
738 ua->error_msg(_("Error: Pool %s already exists.\n"
739 "Use update to change it.\n"), pool->name());
743 ua->error_msg("%s", db_strerror(ua->db));
749 ua->send_msg(_("Pool %s created.\n"), pool->name());
754 extern DIRRES *director;
755 extern char *configfile;
758 * Python control command
759 * python restart (restarts interpreter)
761 static int python_cmd(UAContext *ua, const char *cmd)
764 init_python_interpreter_args python_args;
766 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
767 term_python_interpreter();
769 python_args.progname = director->name();
770 python_args.scriptdir = director->scripts_directory;
771 python_args.modulename = "DirStartUp";
772 python_args.configfile = configfile;
773 python_args.workingdir = director->working_directory;
774 python_args.job_getattr = job_getattr;
775 python_args.job_setattr = job_setattr;
777 init_python_interpreter(&python_args);
779 ua->send_msg(_("Python interpreter restarted.\n"));
781 #endif /* HAVE_PYTHON */
782 ua->warning_msg(_("Nothing done.\n"));
785 #endif /* HAVE_PYTHON */
791 * Set a new address in a Client resource. We do this only
792 * if the Console name is the same as the Client name
793 * and the Console can access the client.
795 static int setip_cmd(UAContext *ua, const char *cmd)
799 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
800 ua->error_msg(_("Unauthorized command from this console.\n"));
804 client = GetClientResWithName(ua->cons->name());
807 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
810 if (client->address) {
811 free(client->address);
813 /* MA Bug 6 remove ifdef */
814 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
815 client->address = bstrdup(buf);
816 ua->send_msg(_("Client \"%s\" address set to %s\n"),
817 client->name(), client->address);
824 static void do_en_disable_cmd(UAContext *ua, bool setting)
829 i = find_arg_with_value(ua, NT_("job"));
831 job = select_enable_disable_job_resource(ua, setting);
837 job = GetJobResWithName(ua->argv[i]);
841 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
845 if (!acl_access_ok(ua, Job_ACL, job->name())) {
846 ua->error_msg(_("Unauthorized command from this console.\n"));
849 job->enabled = setting;
850 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
854 static int enable_cmd(UAContext *ua, const char *cmd)
856 do_en_disable_cmd(ua, true);
860 static int disable_cmd(UAContext *ua, const char *cmd)
862 do_en_disable_cmd(ua, false);
867 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
873 lstore.store = store;
874 pm_strcpy(lstore.store_source, _("unknown source"));
875 set_wstorage(jcr, &lstore);
876 /* Try connecting for up to 15 seconds */
877 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
878 store->name(), store->address, store->SDport);
879 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
880 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
883 Dmsg0(120, _("Connected to storage daemon\n"));
884 sd = jcr->store_bsock;
885 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
886 if (sd->recv() >= 0) {
887 ua->send_msg("%s", sd->msg);
889 sd->signal(BNET_TERMINATE);
891 jcr->store_bsock = NULL;
895 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
899 /* Connect to File daemon */
901 ua->jcr->client = client;
902 /* Try to connect for 15 seconds */
903 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
904 client->name(), client->address, client->FDport);
905 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
906 ua->error_msg(_("Failed to connect to Client.\n"));
909 Dmsg0(120, "Connected to file daemon\n");
910 fd = ua->jcr->file_bsock;
911 fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
912 if (fd->recv() >= 0) {
913 ua->send_msg("%s", fd->msg);
915 fd->signal(BNET_TERMINATE);
917 ua->jcr->file_bsock = NULL;
922 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
924 STORE *store, **unique_store;
925 CLIENT *client, **unique_client;
931 /* Count Storage items */
935 foreach_res(store, R_STORAGE) {
938 unique_store = (STORE **) malloc(i * sizeof(STORE));
939 /* Find Unique Storage address/port */
940 store = (STORE *)GetNextRes(R_STORAGE, NULL);
942 unique_store[i++] = store;
943 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
945 for (j=0; j<i; j++) {
946 if (strcmp(unique_store[j]->address, store->address) == 0 &&
947 unique_store[j]->SDport == store->SDport) {
953 unique_store[i++] = store;
954 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
959 /* Call each unique Storage daemon */
960 for (j=0; j<i; j++) {
961 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
965 /* Count Client items */
969 foreach_res(client, R_CLIENT) {
972 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
973 /* Find Unique Client address/port */
974 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
976 unique_client[i++] = client;
977 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
979 for (j=0; j<i; j++) {
980 if (strcmp(unique_client[j]->address, client->address) == 0 &&
981 unique_client[j]->FDport == client->FDport) {
987 unique_client[i++] = client;
988 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
993 /* Call each unique File daemon */
994 for (j=0; j<i; j++) {
995 do_client_setdebug(ua, unique_client[j], level, trace_flag);
1001 * setdebug level=nn all trace=1/0
1003 static int setdebug_cmd(UAContext *ua, const char *cmd)
1008 int trace_flag = -1;
1011 Dmsg1(120, "setdebug:%s:\n", cmd);
1014 i = find_arg_with_value(ua, "level");
1016 level = atoi(ua->argv[i]);
1019 if (!get_pint(ua, _("Enter new debug level: "))) {
1022 level = ua->pint32_val;
1025 /* Look for trace flag. -1 => not change */
1026 i = find_arg_with_value(ua, "trace");
1028 trace_flag = atoi(ua->argv[i]);
1029 if (trace_flag > 0) {
1034 /* General debug? */
1035 for (i=1; i<ua->argc; i++) {
1036 if (strcasecmp(ua->argk[i], "all") == 0) {
1037 do_all_setdebug(ua, level, trace_flag);
1040 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1041 strcasecmp(ua->argk[i], "director") == 0) {
1042 debug_level = level;
1043 set_trace(trace_flag);
1046 if (strcasecmp(ua->argk[i], "client") == 0 ||
1047 strcasecmp(ua->argk[i], "fd") == 0) {
1050 client = GetClientResWithName(ua->argv[i]);
1052 do_client_setdebug(ua, client, level, trace_flag);
1056 client = select_client_resource(ua);
1058 do_client_setdebug(ua, client, level, trace_flag);
1063 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1064 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1065 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1068 store = GetStoreResWithName(ua->argv[i]);
1070 do_storage_setdebug(ua, store, level, trace_flag);
1074 store = get_storage_resource(ua, false/*no default*/);
1076 do_storage_setdebug(ua, store, level, trace_flag);
1082 * We didn't find an appropriate keyword above, so
1085 start_prompt(ua, _("Available daemons are: \n"));
1086 add_prompt(ua, _("Director"));
1087 add_prompt(ua, _("Storage"));
1088 add_prompt(ua, _("Client"));
1089 add_prompt(ua, _("All"));
1090 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1091 case 0: /* Director */
1092 debug_level = level;
1093 set_trace(trace_flag);
1096 store = get_storage_resource(ua, false/*no default*/);
1098 do_storage_setdebug(ua, store, level, trace_flag);
1102 client = select_client_resource(ua);
1104 do_client_setdebug(ua, client, level, trace_flag);
1108 do_all_setdebug(ua, level, trace_flag);
1117 * Turn debug tracing to file on/off
1119 static int trace_cmd(UAContext *ua, const char *cmd)
1123 if (ua->argc != 2) {
1124 if (!get_cmd(ua, _("Turn on or off? "))) {
1129 onoff = ua->argk[1];
1132 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1137 static int var_cmd(UAContext *ua, const char *cmd)
1139 POOLMEM *val = get_pool_memory(PM_FNAME);
1142 if (!open_client_db(ua)) {
1145 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1148 while (*var == ' ') { /* skip spaces */
1151 Dmsg1(100, "Var=%s:\n", var);
1152 variable_expansion(ua->jcr, var, &val);
1153 ua->send_msg("%s\n", val);
1154 free_pool_memory(val);
1158 static int estimate_cmd(UAContext *ua, const char *cmd)
1161 CLIENT *client = NULL;
1162 FILESET *fileset = NULL;
1164 char since[MAXSTRING];
1168 jcr->set_JobLevel(L_FULL);
1169 for (int i=1; i<ua->argc; i++) {
1170 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1171 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1173 client = GetClientResWithName(ua->argv[i]);
1175 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1178 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1179 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1184 ua->error_msg(_("Client name missing.\n"));
1188 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1190 job = GetJobResWithName(ua->argv[i]);
1192 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1195 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1196 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1201 ua->error_msg(_("Job name missing.\n"));
1206 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1208 fileset = GetFileSetResWithName(ua->argv[i]);
1210 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1213 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1214 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1219 ua->error_msg(_("Fileset name missing.\n"));
1223 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1227 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1229 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1230 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1234 ua->error_msg(_("Level value missing.\n"));
1238 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1239 if (!is_yesno(ua->argv[i], &accurate)) {
1240 ua->error_msg(_("Invalid value for accurate. "
1241 "It must be yes or no.\n"));
1245 if (!job && !(client && fileset)) {
1246 if (!(job = select_job_resource(ua))) {
1251 job = GetJobResWithName(ua->argk[1]);
1253 ua->error_msg(_("No job specified.\n"));
1256 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1257 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1262 client = job->client;
1265 fileset = job->fileset;
1267 jcr->client = client;
1268 jcr->fileset = fileset;
1270 if (job->pool->catalog) {
1271 ua->catalog = job->pool->catalog;
1273 ua->catalog = client->catalog;
1281 jcr->set_JobType(JT_BACKUP);
1282 init_jcr_job_record(jcr);
1284 if (!get_or_create_client_record(jcr)) {
1287 if (!get_or_create_fileset_record(jcr)) {
1291 get_level_since_time(ua->jcr, since, sizeof(since));
1293 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1294 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1295 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1296 ua->error_msg(_("Failed to connect to Client.\n"));
1300 if (!send_include_list(jcr)) {
1301 ua->error_msg(_("Error sending include list.\n"));
1305 if (!send_exclude_list(jcr)) {
1306 ua->error_msg(_("Error sending exclude list.\n"));
1310 /* The level string change if accurate mode is enabled */
1311 if (accurate >= 0) {
1312 jcr->accurate = accurate;
1314 jcr->accurate = job->accurate;
1317 if (!send_level_command(jcr)) {
1322 * If the job is in accurate mode, we send the list of
1325 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1326 if (!send_accurate_current_files(jcr)) {
1330 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1331 while (bnet_recv(jcr->file_bsock) >= 0) {
1332 ua->send_msg("%s", jcr->file_bsock->msg);
1336 if (jcr->file_bsock) {
1337 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1338 bnet_close(jcr->file_bsock);
1339 jcr->file_bsock = NULL;
1348 static int time_cmd(UAContext *ua, const char *cmd)
1351 time_t ttime = time(NULL);
1353 (void)localtime_r(&ttime, &tm);
1354 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1355 ua->send_msg("%s\n", sdt);
1360 * reload the conf file
1362 extern "C" void reload_config(int sig);
1364 static int reload_cmd(UAContext *ua, const char *cmd)
1371 * Delete Pool records (should purge Media with it).
1373 * delete pool=<pool-name>
1374 * delete volume pool=<pool-name> volume=<name>
1377 static int delete_cmd(UAContext *ua, const char *cmd)
1379 static const char *keywords[] = {
1385 if (!open_client_db(ua)) {
1389 switch (find_arg_keyword(ua, keywords)) {
1398 while ((i=find_arg(ua, "jobid")) > 0) {
1400 *ua->argk[i] = 0; /* zap keyword already visited */
1408 "In general it is not a good idea to delete either a\n"
1409 "Pool or a Volume since they may contain data.\n\n"));
1411 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1422 ua->warning_msg(_("Nothing done.\n"));
1430 * delete_job has been modified to parse JobID lists like the
1432 * delete JobID=3,4,6,7-11,14
1434 * Thanks to Phil Stracchino for the above addition.
1437 static void delete_job(UAContext *ua)
1442 int i = find_arg_with_value(ua, NT_("jobid"));
1444 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1445 s = bstrdup(ua->argv[i]);
1448 * We could use strtok() here. But we're not going to, because:
1449 * (a) strtok() is deprecated, having been replaced by strsep();
1450 * (b) strtok() is broken in significant ways.
1451 * we could use strsep() instead, but it's not universally available.
1452 * so we grow our own using strchr().
1454 sep = strchr(tok, ',');
1455 while (sep != NULL) {
1457 if (strchr(tok, '-')) {
1458 delete_job_id_range(ua, tok);
1460 JobId = str_to_int64(tok);
1461 do_job_delete(ua, JobId);
1464 sep = strchr(tok, ',');
1466 /* pick up the last token */
1467 if (strchr(tok, '-')) {
1468 delete_job_id_range(ua, tok);
1470 JobId = str_to_int64(tok);
1471 do_job_delete(ua, JobId);
1476 JobId = str_to_int64(ua->argv[i]);
1477 do_job_delete(ua, JobId);
1479 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1482 JobId = ua->int64_val;
1483 do_job_delete(ua, JobId);
1488 * we call delete_job_id_range to parse range tokens and iterate over ranges
1490 static void delete_job_id_range(UAContext *ua, char *tok)
1495 tok2 = strchr(tok, '-');
1498 j1 = str_to_int64(tok);
1499 j2 = str_to_int64(tok2);
1500 for (j=j1; j<=j2; j++) {
1501 do_job_delete(ua, j);
1506 * do_job_delete now performs the actual delete operation atomically
1508 static void do_job_delete(UAContext *ua, JobId_t JobId)
1512 edit_int64(JobId, ed1);
1513 purge_jobs_from_catalog(ua, ed1);
1514 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1518 * Delete media records from database -- dangerous
1520 static int delete_volume(UAContext *ua)
1525 if (!select_media_dbr(ua, &mr)) {
1528 ua->warning_msg(_("\nThis command will delete volume %s\n"
1529 "and all Jobs saved on that volume from the Catalog\n"),
1532 if (find_arg(ua, "yes") >= 0) {
1533 ua->pint32_val = 1; /* Have "yes" on command line already" */
1535 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1537 if (!get_yesno(ua, buf)) {
1541 if (ua->pint32_val) {
1542 db_delete_media_record(ua->jcr, ua->db, &mr);
1548 * Delete a pool record from the database -- dangerous
1550 static int delete_pool(UAContext *ua)
1555 memset(&pr, 0, sizeof(pr));
1557 if (!get_pool_dbr(ua, &pr)) {
1560 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1562 if (!get_yesno(ua, buf)) {
1565 if (ua->pint32_val) {
1566 db_delete_pool_record(ua->jcr, ua->db, &pr);
1571 int memory_cmd(UAContext *ua, const char *cmd)
1573 list_dir_status_header(ua);
1574 sm_dump(false, true);
1578 static void do_mount_cmd(UAContext *ua, const char *command)
1583 char dev_name[MAX_NAME_LENGTH];
1587 if (!open_client_db(ua)) {
1590 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1592 store.store = get_storage_resource(ua, true/*arg is storage*/);
1596 pm_strcpy(store.store_source, _("unknown source"));
1597 set_wstorage(jcr, &store);
1598 drive = get_storage_drive(ua, store.store);
1599 if (strcmp(command, "mount") == 0) {
1600 slot = get_storage_slot(ua, store.store);
1603 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1604 store.store->media_type, store.store->dev_name(), drive);
1606 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1607 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1610 sd = jcr->store_bsock;
1611 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1612 bash_spaces(dev_name);
1614 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1616 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1618 while (bnet_recv(sd) >= 0) {
1619 ua->send_msg("%s", sd->msg);
1621 bnet_sig(sd, BNET_TERMINATE);
1623 jcr->store_bsock = NULL;
1627 * mount [storage=<name>] [drive=nn] [slot=mm]
1629 static int mount_cmd(UAContext *ua, const char *cmd)
1631 do_mount_cmd(ua, "mount"); /* mount */
1637 * unmount [storage=<name>] [drive=nn]
1639 static int unmount_cmd(UAContext *ua, const char *cmd)
1641 do_mount_cmd(ua, "unmount"); /* unmount */
1647 * release [storage=<name>] [drive=nn]
1649 static int release_cmd(UAContext *ua, const char *cmd)
1651 do_mount_cmd(ua, "release"); /* release */
1658 * use catalog=<name>
1660 static int use_cmd(UAContext *ua, const char *cmd)
1662 CAT *oldcatalog, *catalog;
1665 close_db(ua); /* close any previously open db */
1666 oldcatalog = ua->catalog;
1668 if (!(catalog = get_catalog_resource(ua))) {
1669 ua->catalog = oldcatalog;
1671 ua->catalog = catalog;
1674 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1675 ua->catalog->name(), ua->catalog->db_name);
1680 int quit_cmd(UAContext *ua, const char *cmd)
1686 /* Handler to get job status */
1687 static int status_handler(void *ctx, int num_fields, char **row)
1689 char *val = (char *)ctx;
1694 *val = '?'; /* Unknown by default */
1701 * Wait until no job is running
1703 int wait_cmd(UAContext *ua, const char *cmd)
1707 time_t stop_time = 0;
1711 * Wait until no job is running
1713 if (ua->argc == 1) {
1714 bmicrosleep(0, 200000); /* let job actually start */
1715 for (bool running=true; running; ) {
1718 if (jcr->JobId != 0) {
1732 i = find_arg_with_value(ua, NT_("timeout"));
1733 if (i > 0 && ua->argv[i]) {
1734 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1737 /* we have jobid, jobname or ujobid argument */
1739 uint32_t jobid = 0 ;
1741 if (!open_client_db(ua)) {
1742 ua->error_msg(_("ERR: Can't open db\n")) ;
1746 for (int i=1; i<ua->argc; i++) {
1747 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1751 jobid = str_to_int64(ua->argv[i]);
1753 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1754 strcasecmp(ua->argk[i], "job") == 0) {
1758 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1760 jobid = jcr->JobId ;
1764 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1768 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1770 jobid = jcr->JobId ;
1774 /* Wait for a mount request */
1775 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1776 for (bool waiting=false; !waiting; ) {
1778 if (jcr->JobId != 0 &&
1779 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1788 if (stop_time && (time(NULL) >= stop_time)) {
1789 ua->warning_msg(_("Wait on mount timed out\n"));
1799 ua->error_msg(_("ERR: Job was not found\n"));
1804 * We wait the end of a specific job
1807 bmicrosleep(0, 200000); /* let job actually start */
1808 for (bool running=true; running; ) {
1811 jcr=get_jcr_by_id(jobid) ;
1824 * We have to get JobStatus
1828 char jobstatus = '?'; /* Unknown by default */
1831 bsnprintf(buf, sizeof(buf),
1832 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1835 db_sql_query(ua->db, buf,
1836 status_handler, (void *)&jobstatus);
1838 switch (jobstatus) {
1840 status = 1 ; /* Warning */
1844 case JS_ErrorTerminated:
1846 status = 2 ; /* Critical */
1851 status = 0 ; /* Ok */
1855 status = 3 ; /* Unknown */
1859 ua->send_msg("JobId=%i\n", jobid) ;
1860 ua->send_msg("JobStatus=%s (%c)\n",
1861 job_status_to_str(jobstatus),
1864 if (ua->gui || ua->api) {
1865 ua->send_msg("ExitStatus=%i\n", status) ;
1872 static int help_cmd(UAContext *ua, const char *cmd)
1876 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1877 for (i=0; i<comsize; i++) {
1878 if (ua->argc == 2) {
1879 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1880 ua->send_msg(_(" %-13s %s\n %s\n"), commands[i].key, commands[i].help, commands[i].usage);
1884 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1887 if (i == comsize && ua->argc == 2) {
1888 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1890 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1894 int qhelp_cmd(UAContext *ua, const char *cmd)
1897 /* Want to display only commands */
1898 j = find_arg(ua, NT_("all"));
1900 for (i=0; i<comsize; i++) {
1901 ua->send_msg("%s\n", commands[i].key);
1905 /* Want to display a specific help section */
1906 j = find_arg_with_value(ua, NT_("item"));
1907 if (j >= 0 && ua->argk[j]) {
1908 for (i=0; i<comsize; i++) {
1909 if (bstrcmp(commands[i].key, ua->argv[j])) {
1910 ua->send_msg("%s\n", _(commands[i].help));
1916 /* Want to display everything */
1917 for (i=0; i<comsize; i++) {
1918 ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1924 static int version_cmd(UAContext *ua, const char *cmd)
1926 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1927 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1932 * Test code -- turned on only for debug testing
1934 static int version_cmd(UAContext *ua, const char *cmd)
1937 POOL_MEM query(PM_MESSAGE);
1939 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1940 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1941 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1942 for (int i=0; i < ids.num_ids; i++) {
1943 ua->send_msg("id=%d\n", ids.DBId[i]);
1951 * This call explicitly checks for a catalog=xxx and
1952 * if given, opens that catalog. It also checks for
1953 * client=xxx and if found, opens the catalog
1954 * corresponding to that client. If we still don't
1955 * have a catalog, look for a Job keyword and get the
1956 * catalog from its client record.
1958 bool open_client_db(UAContext *ua)
1965 /* Try for catalog keyword */
1966 i = find_arg_with_value(ua, NT_("catalog"));
1968 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1969 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1972 catalog = GetCatalogResWithName(ua->argv[i]);
1974 if (ua->catalog && ua->catalog != catalog) {
1977 ua->catalog = catalog;
1982 /* Try for client keyword */
1983 i = find_arg_with_value(ua, NT_("client"));
1985 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1986 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1989 client = GetClientResWithName(ua->argv[i]);
1991 catalog = client->catalog;
1992 if (ua->catalog && ua->catalog != catalog) {
1995 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1996 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1999 ua->catalog = catalog;
2004 /* Try for Job keyword */
2005 i = find_arg_with_value(ua, NT_("job"));
2007 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2008 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2011 job = GetJobResWithName(ua->argv[i]);
2013 catalog = job->client->catalog;
2014 if (ua->catalog && ua->catalog != catalog) {
2017 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2018 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2021 ua->catalog = catalog;
2031 * Open the catalog database.
2033 bool open_db(UAContext *ua)
2039 ua->catalog = get_catalog_resource(ua);
2041 ua->error_msg( _("Could not find a Catalog resource\n"));
2046 ua->jcr->catalog = ua->catalog;
2048 Dmsg0(100, "UA Open database\n");
2049 ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2050 ua->catalog->db_user,
2051 ua->catalog->db_password, ua->catalog->db_address,
2052 ua->catalog->db_port, ua->catalog->db_socket,
2053 ua->catalog->mult_db_connections);
2054 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2055 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2056 ua->catalog->db_name);
2058 ua->error_msg("%s", db_strerror(ua->db));
2063 ua->jcr->db = ua->db;
2065 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2067 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2071 void close_db(UAContext *ua)
2074 db_close_database(ua->jcr, ua->db);