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 help on specific command"),
135 NT_("add autodisplay automount cancel create delete disable enable estimate exit gui label list llist messages "
136 "memory mount prune purge python quit query restore relabel release reload run status setdebug setip show "
137 "sqlquery time trace unmount umount update use var version wait"), false},
139 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool>"), false},
140 { NT_("list"), list_cmd, _("List objects from catalog"),
141 NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
143 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
144 NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
146 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
147 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
148 { NT_("mount"), mount_cmd, _("Mount storage"),
149 NT_("storage=<storage-name> slot=<num> drive=<num> [ jobid=<id> | job=<job-name> ]"), false},
151 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
152 NT_("files | jobs | client=<client-name> | volume=<volume-name> "), true},
154 { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("volume=<vol>"), true},
155 { NT_("python"), python_cmd, _("Python control commands"), NT_(""), false},
156 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
157 { NT_("query"), querycmd, _("Query catalog"), NT_(""), false},
158 { NT_("restore"), restore_cmd, _("Restore files"),
159 NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> jobid=<jobid> done select all"), false},
161 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
162 NT_("storage=<storage-name> oldvolume=<old-volume-name> volume=<newvolume-name>"), false},
164 { NT_("release"), release_cmd, _("Release storage"), NT_("storage-name"), false},
165 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
166 { NT_("run"), run_cmd, _("Run a job"),
167 NT_("job=<job-name> client=<client-name> fileset=<FileSet-name> level=<level-keyword> storage=<storage-name> where=<directory-prefix> "
168 "when=<universal-time-specification> yes"), false}, /* need to be check */
170 { NT_("status"), status_cmd, _("Report status"),
171 NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
173 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
174 NT_("level=<nn> trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
176 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
177 { NT_("show"), show_cmd, _("Show resource records"),
178 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | all"), true},
180 { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
181 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
182 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
183 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
184 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
186 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
187 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
189 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
190 NT_("pool=<poolname> | slots storage=<storage> scan| stats | volume=<volname> volstatus=<status> volretention=<time-def> "
191 "pool=<pool> recycle=<yes/no> slot=<number> inchanger=<yes/no>"),true},
192 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_(""), false},
193 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
194 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
195 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
196 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
199 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
202 * Execute a command from the UA
204 bool do_a_command(UAContext *ua)
210 BSOCK *user = ua->UA_sock;
213 Dmsg1(900, "Command: %s\n", ua->argk[0]);
218 while (ua->jcr->wstorage->size()) {
219 ua->jcr->wstorage->remove(0);
222 len = strlen(ua->argk[0]);
223 for (i=0; i<comsize; i++) { /* search for command */
224 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
225 /* Check if command permitted, but "quit" is always OK */
226 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
227 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
230 /* Check if this command is authorized in RunScript */
231 if (ua->runscript && !commands[i].use_in_rs) {
232 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
235 if (ua->api) user->signal(BNET_CMD_BEGIN);
236 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
237 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
243 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
250 * This is a common routine used to stuff the Pool DB record defaults
251 * into the Media DB record just before creating a media (Volume)
254 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
256 mr->PoolId = pr->PoolId;
257 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
258 mr->Recycle = pr->Recycle;
259 mr->VolRetention = pr->VolRetention;
260 mr->VolUseDuration = pr->VolUseDuration;
261 mr->ActionOnPurge = pr->ActionOnPurge;
262 mr->RecyclePoolId = pr->RecyclePoolId;
263 mr->MaxVolJobs = pr->MaxVolJobs;
264 mr->MaxVolFiles = pr->MaxVolFiles;
265 mr->MaxVolBytes = pr->MaxVolBytes;
266 mr->LabelType = pr->LabelType;
272 * Add Volumes to an existing Pool
274 static int add_cmd(UAContext *ua, const char *cmd)
278 int num, i, max, startnum;
280 char name[MAX_NAME_LENGTH];
282 int Slot = 0, InChanger = 0;
285 "You probably don't want to be using this command since it\n"
286 "creates database records without labeling the Volumes.\n"
287 "You probably want to use the \"label\" command.\n\n"));
289 if (!open_client_db(ua)) {
293 memset(&pr, 0, sizeof(pr));
294 memset(&mr, 0, sizeof(mr));
296 if (!get_pool_dbr(ua, &pr)) {
300 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
301 pr.MaxVols, pr.PoolType);
303 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
304 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
305 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
308 pr.MaxVols = ua->pint32_val;
312 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
313 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
314 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
318 if (pr.MaxVols == 0) {
321 max = pr.MaxVols - pr.NumVols;
325 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
326 if (!get_pint(ua, buf)) {
329 num = ua->pint32_val;
330 if (num < 0 || num > max) {
331 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
339 if (!get_cmd(ua, _("Enter Volume name: "))) {
343 if (!get_cmd(ua, _("Enter base volume name: "))) {
347 /* Don't allow | in Volume name because it is the volume separator character */
348 if (!is_volume_name_legal(ua, ua->cmd)) {
351 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
352 ua->warning_msg(_("Volume name too long.\n"));
355 if (strlen(ua->cmd) == 0) {
356 ua->warning_msg(_("Volume name must be at least one character long.\n"));
362 bstrncpy(name, ua->cmd, sizeof(name));
364 bstrncat(name, "%04d", sizeof(name));
367 if (!get_pint(ua, _("Enter the starting number: "))) {
370 startnum = ua->pint32_val;
372 ua->warning_msg(_("Start number must be greater than zero.\n"));
382 if (store && store->autochanger) {
383 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
386 Slot = ua->pint32_val;
387 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
390 InChanger = ua->pint32_val;
393 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
394 for (i=startnum; i < num+startnum; i++) {
395 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
397 mr.InChanger = InChanger;
398 mr.StorageId = store->StorageId;
400 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
401 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
402 ua->error_msg("%s", db_strerror(ua->db));
406 first_id = mr.PoolId;
410 Dmsg0(200, "Update pool record.\n");
411 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
412 ua->warning_msg("%s", db_strerror(ua->db));
415 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
421 * Turn auto mount on/off
426 int automount_cmd(UAContext *ua, const char *cmd)
431 if (!get_cmd(ua, _("Turn on or off? "))) {
439 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
447 static int cancel_cmd(UAContext *ua, const char *cmd)
452 char JobName[MAX_NAME_LENGTH];
454 for (i=1; i<ua->argc; i++) {
455 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
460 JobId = str_to_int64(ua->argv[i]);
461 if (!(jcr=get_jcr_by_id(JobId))) {
462 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
466 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
470 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
471 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
472 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
473 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
476 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
480 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
481 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
482 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
483 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
490 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
491 ua->error_msg(_("Unauthorized command from this console.\n"));
496 * If we still do not have a jcr,
497 * throw up a list and ask the user to select one.
500 int tjobs = 0; /* total # number jobs */
501 /* Count Jobs running */
503 if (jcr->JobId == 0) { /* this is us */
506 tjobs++; /* count of all jobs */
507 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
508 continue; /* skip not authorized */
510 njobs++; /* count of authorized jobs */
514 if (njobs == 0) { /* no authorized */
516 ua->send_msg(_("No Jobs running.\n"));
518 ua->send_msg(_("None of your jobs are running.\n"));
523 start_prompt(ua, _("Select Job:\n"));
526 if (jcr->JobId == 0) { /* this is us */
529 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
530 continue; /* skip not authorized */
532 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
537 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
540 if (ua->api && njobs == 1) {
542 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
543 _("Confirm cancel?"));
544 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
549 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
554 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
555 jcr = get_jcr_by_full_name(JobName);
557 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
562 ret = cancel_job(ua, jcr);
568 * This is a common routine to create or update a
569 * Pool DB base record from a Pool Resource. We handle
570 * the setting of MaxVols and NumVols slightly differently
571 * depending on if we are creating the Pool or we are
572 * simply bringing it into agreement with the resource (updage).
574 * Caution : RecyclePoolId isn't setup in this function.
575 * You can use set_pooldbr_recyclepoolid();
578 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
580 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
581 if (op == POOL_OP_CREATE) {
582 pr->MaxVols = pool->max_volumes;
584 } else { /* update pool */
585 if (pr->MaxVols != pool->max_volumes) {
586 pr->MaxVols = pool->max_volumes;
588 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
589 pr->MaxVols = pr->NumVols;
592 pr->LabelType = pool->LabelType;
593 pr->UseOnce = pool->use_volume_once;
594 pr->UseCatalog = pool->use_catalog;
595 pr->Recycle = pool->Recycle;
596 pr->VolRetention = pool->VolRetention;
597 pr->VolUseDuration = pool->VolUseDuration;
598 pr->MaxVolJobs = pool->MaxVolJobs;
599 pr->MaxVolFiles = pool->MaxVolFiles;
600 pr->MaxVolBytes = pool->MaxVolBytes;
601 pr->AutoPrune = pool->AutoPrune;
602 pr->ActionOnPurge = pool->action_on_purge;
603 pr->Recycle = pool->Recycle;
604 if (pool->label_format) {
605 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
607 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
611 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
612 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
616 if (!pool->RecyclePool && !pool->ScratchPool) {
620 memset(&pr, 0, sizeof(POOL_DBR));
621 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
623 if (!db_get_pool_record(jcr, db, &pr)) {
624 return -1; /* not exists in database */
627 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
629 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
630 return -1; /* error */
633 if (!db_update_pool_record(jcr, db, &pr)) {
634 return -1; /* error */
639 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
640 * works with set_pooldbr_from_poolres
642 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
647 if (pool->RecyclePool) {
648 memset(&rpool, 0, sizeof(POOL_DBR));
650 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
651 if (db_get_pool_record(jcr, db, &rpool)) {
652 pr->RecyclePoolId = rpool.PoolId;
654 Jmsg(jcr, M_WARNING, 0,
655 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
656 "Try to update it with 'update pool=%s'\n"),
657 pool->name(), rpool.Name, rpool.Name,pool->name());
661 } else { /* no RecyclePool used, set it to 0 */
662 pr->RecyclePoolId = 0;
665 if (pool->ScratchPool) {
666 memset(&rpool, 0, sizeof(POOL_DBR));
668 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
669 if (db_get_pool_record(jcr, db, &rpool)) {
670 pr->ScratchPoolId = rpool.PoolId;
672 Jmsg(jcr, M_WARNING, 0,
673 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
674 "Try to update it with 'update pool=%s'\n"),
675 pool->name(), rpool.Name, rpool.Name,pool->name());
678 } else { /* no ScratchPool used, set it to 0 */
679 pr->ScratchPoolId = 0;
687 * Create a pool record from a given Pool resource
688 * Also called from backup.c
689 * Returns: -1 on error
690 * 0 record already exists
694 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
698 memset(&pr, 0, sizeof(POOL_DBR));
700 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
702 if (db_get_pool_record(jcr, db, &pr)) {
704 if (op == POOL_OP_UPDATE) { /* update request */
705 set_pooldbr_from_poolres(&pr, pool, op);
706 set_pooldbr_references(jcr, db, &pr, pool);
707 db_update_pool_record(jcr, db, &pr);
709 return 0; /* exists */
712 set_pooldbr_from_poolres(&pr, pool, op);
713 set_pooldbr_references(jcr, db, &pr, pool);
715 if (!db_create_pool_record(jcr, db, &pr)) {
716 return -1; /* error */
724 * Create a Pool Record in the database.
725 * It is always created from the Resource record.
727 static int create_cmd(UAContext *ua, const char *cmd)
731 if (!open_client_db(ua)) {
735 pool = get_pool_resource(ua);
740 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
742 ua->error_msg(_("Error: Pool %s already exists.\n"
743 "Use update to change it.\n"), pool->name());
747 ua->error_msg("%s", db_strerror(ua->db));
753 ua->send_msg(_("Pool %s created.\n"), pool->name());
758 extern DIRRES *director;
759 extern char *configfile;
762 * Python control command
763 * python restart (restarts interpreter)
765 static int python_cmd(UAContext *ua, const char *cmd)
768 init_python_interpreter_args python_args;
770 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
771 term_python_interpreter();
773 python_args.progname = director->name();
774 python_args.scriptdir = director->scripts_directory;
775 python_args.modulename = "DirStartUp";
776 python_args.configfile = configfile;
777 python_args.workingdir = director->working_directory;
778 python_args.job_getattr = job_getattr;
779 python_args.job_setattr = job_setattr;
781 init_python_interpreter(&python_args);
783 ua->send_msg(_("Python interpreter restarted.\n"));
785 #endif /* HAVE_PYTHON */
786 ua->warning_msg(_("Nothing done.\n"));
789 #endif /* HAVE_PYTHON */
795 * Set a new address in a Client resource. We do this only
796 * if the Console name is the same as the Client name
797 * and the Console can access the client.
799 static int setip_cmd(UAContext *ua, const char *cmd)
803 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
804 ua->error_msg(_("Unauthorized command from this console.\n"));
808 client = GetClientResWithName(ua->cons->name());
811 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
814 if (client->address) {
815 free(client->address);
817 /* MA Bug 6 remove ifdef */
818 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
819 client->address = bstrdup(buf);
820 ua->send_msg(_("Client \"%s\" address set to %s\n"),
821 client->name(), client->address);
828 static void do_en_disable_cmd(UAContext *ua, bool setting)
833 i = find_arg_with_value(ua, NT_("job"));
835 job = select_enable_disable_job_resource(ua, setting);
841 job = GetJobResWithName(ua->argv[i]);
845 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
849 if (!acl_access_ok(ua, Job_ACL, job->name())) {
850 ua->error_msg(_("Unauthorized command from this console.\n"));
853 job->enabled = setting;
854 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
858 static int enable_cmd(UAContext *ua, const char *cmd)
860 do_en_disable_cmd(ua, true);
864 static int disable_cmd(UAContext *ua, const char *cmd)
866 do_en_disable_cmd(ua, false);
871 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
877 lstore.store = store;
878 pm_strcpy(lstore.store_source, _("unknown source"));
879 set_wstorage(jcr, &lstore);
880 /* Try connecting for up to 15 seconds */
881 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
882 store->name(), store->address, store->SDport);
883 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
884 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
887 Dmsg0(120, _("Connected to storage daemon\n"));
888 sd = jcr->store_bsock;
889 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
890 if (sd->recv() >= 0) {
891 ua->send_msg("%s", sd->msg);
893 sd->signal(BNET_TERMINATE);
895 jcr->store_bsock = NULL;
899 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
903 /* Connect to File daemon */
905 ua->jcr->client = client;
906 /* Try to connect for 15 seconds */
907 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
908 client->name(), client->address, client->FDport);
909 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
910 ua->error_msg(_("Failed to connect to Client.\n"));
913 Dmsg0(120, "Connected to file daemon\n");
914 fd = ua->jcr->file_bsock;
915 fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
916 if (fd->recv() >= 0) {
917 ua->send_msg("%s", fd->msg);
919 fd->signal(BNET_TERMINATE);
921 ua->jcr->file_bsock = NULL;
926 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
928 STORE *store, **unique_store;
929 CLIENT *client, **unique_client;
935 /* Count Storage items */
939 foreach_res(store, R_STORAGE) {
942 unique_store = (STORE **) malloc(i * sizeof(STORE));
943 /* Find Unique Storage address/port */
944 store = (STORE *)GetNextRes(R_STORAGE, NULL);
946 unique_store[i++] = store;
947 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
949 for (j=0; j<i; j++) {
950 if (strcmp(unique_store[j]->address, store->address) == 0 &&
951 unique_store[j]->SDport == store->SDport) {
957 unique_store[i++] = store;
958 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
963 /* Call each unique Storage daemon */
964 for (j=0; j<i; j++) {
965 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
969 /* Count Client items */
973 foreach_res(client, R_CLIENT) {
976 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
977 /* Find Unique Client address/port */
978 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
980 unique_client[i++] = client;
981 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
983 for (j=0; j<i; j++) {
984 if (strcmp(unique_client[j]->address, client->address) == 0 &&
985 unique_client[j]->FDport == client->FDport) {
991 unique_client[i++] = client;
992 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
997 /* Call each unique File daemon */
998 for (j=0; j<i; j++) {
999 do_client_setdebug(ua, unique_client[j], level, trace_flag);
1001 free(unique_client);
1005 * setdebug level=nn all trace=1/0
1007 static int setdebug_cmd(UAContext *ua, const char *cmd)
1012 int trace_flag = -1;
1015 Dmsg1(120, "setdebug:%s:\n", cmd);
1018 i = find_arg_with_value(ua, "level");
1020 level = atoi(ua->argv[i]);
1023 if (!get_pint(ua, _("Enter new debug level: "))) {
1026 level = ua->pint32_val;
1029 /* Look for trace flag. -1 => not change */
1030 i = find_arg_with_value(ua, "trace");
1032 trace_flag = atoi(ua->argv[i]);
1033 if (trace_flag > 0) {
1038 /* General debug? */
1039 for (i=1; i<ua->argc; i++) {
1040 if (strcasecmp(ua->argk[i], "all") == 0) {
1041 do_all_setdebug(ua, level, trace_flag);
1044 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1045 strcasecmp(ua->argk[i], "director") == 0) {
1046 debug_level = level;
1047 set_trace(trace_flag);
1050 if (strcasecmp(ua->argk[i], "client") == 0 ||
1051 strcasecmp(ua->argk[i], "fd") == 0) {
1054 client = GetClientResWithName(ua->argv[i]);
1056 do_client_setdebug(ua, client, level, trace_flag);
1060 client = select_client_resource(ua);
1062 do_client_setdebug(ua, client, level, trace_flag);
1067 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1068 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1069 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1072 store = GetStoreResWithName(ua->argv[i]);
1074 do_storage_setdebug(ua, store, level, trace_flag);
1078 store = get_storage_resource(ua, false/*no default*/);
1080 do_storage_setdebug(ua, store, level, trace_flag);
1086 * We didn't find an appropriate keyword above, so
1089 start_prompt(ua, _("Available daemons are: \n"));
1090 add_prompt(ua, _("Director"));
1091 add_prompt(ua, _("Storage"));
1092 add_prompt(ua, _("Client"));
1093 add_prompt(ua, _("All"));
1094 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1095 case 0: /* Director */
1096 debug_level = level;
1097 set_trace(trace_flag);
1100 store = get_storage_resource(ua, false/*no default*/);
1102 do_storage_setdebug(ua, store, level, trace_flag);
1106 client = select_client_resource(ua);
1108 do_client_setdebug(ua, client, level, trace_flag);
1112 do_all_setdebug(ua, level, trace_flag);
1121 * Turn debug tracing to file on/off
1123 static int trace_cmd(UAContext *ua, const char *cmd)
1127 if (ua->argc != 2) {
1128 if (!get_cmd(ua, _("Turn on or off? "))) {
1133 onoff = ua->argk[1];
1136 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1141 static int var_cmd(UAContext *ua, const char *cmd)
1143 POOLMEM *val = get_pool_memory(PM_FNAME);
1146 if (!open_client_db(ua)) {
1149 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1152 while (*var == ' ') { /* skip spaces */
1155 Dmsg1(100, "Var=%s:\n", var);
1156 variable_expansion(ua->jcr, var, &val);
1157 ua->send_msg("%s\n", val);
1158 free_pool_memory(val);
1162 static int estimate_cmd(UAContext *ua, const char *cmd)
1165 CLIENT *client = NULL;
1166 FILESET *fileset = NULL;
1168 char since[MAXSTRING];
1172 jcr->set_JobLevel(L_FULL);
1173 for (int i=1; i<ua->argc; i++) {
1174 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1175 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1177 client = GetClientResWithName(ua->argv[i]);
1179 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1182 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1183 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1188 ua->error_msg(_("Client name missing.\n"));
1192 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1194 job = GetJobResWithName(ua->argv[i]);
1196 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1199 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1200 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1205 ua->error_msg(_("Job name missing.\n"));
1210 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1212 fileset = GetFileSetResWithName(ua->argv[i]);
1214 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1217 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1218 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1223 ua->error_msg(_("Fileset name missing.\n"));
1227 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1231 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1233 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1234 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1238 ua->error_msg(_("Level value missing.\n"));
1242 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1243 if (!is_yesno(ua->argv[i], &accurate)) {
1244 ua->error_msg(_("Invalid value for accurate. "
1245 "It must be yes or no.\n"));
1249 if (!job && !(client && fileset)) {
1250 if (!(job = select_job_resource(ua))) {
1255 job = GetJobResWithName(ua->argk[1]);
1257 ua->error_msg(_("No job specified.\n"));
1260 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1261 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1266 client = job->client;
1269 fileset = job->fileset;
1271 jcr->client = client;
1272 jcr->fileset = fileset;
1274 if (job->pool->catalog) {
1275 ua->catalog = job->pool->catalog;
1277 ua->catalog = client->catalog;
1285 jcr->set_JobType(JT_BACKUP);
1286 init_jcr_job_record(jcr);
1288 if (!get_or_create_client_record(jcr)) {
1291 if (!get_or_create_fileset_record(jcr)) {
1295 get_level_since_time(ua->jcr, since, sizeof(since));
1297 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1298 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1299 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1300 ua->error_msg(_("Failed to connect to Client.\n"));
1304 if (!send_include_list(jcr)) {
1305 ua->error_msg(_("Error sending include list.\n"));
1309 if (!send_exclude_list(jcr)) {
1310 ua->error_msg(_("Error sending exclude list.\n"));
1314 /* The level string change if accurate mode is enabled */
1315 if (accurate >= 0) {
1316 jcr->accurate = accurate;
1318 jcr->accurate = job->accurate;
1321 if (!send_level_command(jcr)) {
1326 * If the job is in accurate mode, we send the list of
1329 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1330 if (!send_accurate_current_files(jcr)) {
1334 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1335 while (bnet_recv(jcr->file_bsock) >= 0) {
1336 ua->send_msg("%s", jcr->file_bsock->msg);
1340 if (jcr->file_bsock) {
1341 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1342 bnet_close(jcr->file_bsock);
1343 jcr->file_bsock = NULL;
1352 static int time_cmd(UAContext *ua, const char *cmd)
1355 time_t ttime = time(NULL);
1357 (void)localtime_r(&ttime, &tm);
1358 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1359 ua->send_msg("%s\n", sdt);
1364 * reload the conf file
1366 extern "C" void reload_config(int sig);
1368 static int reload_cmd(UAContext *ua, const char *cmd)
1375 * Delete Pool records (should purge Media with it).
1377 * delete pool=<pool-name>
1378 * delete volume pool=<pool-name> volume=<name>
1381 static int delete_cmd(UAContext *ua, const char *cmd)
1383 static const char *keywords[] = {
1389 if (!open_client_db(ua)) {
1393 switch (find_arg_keyword(ua, keywords)) {
1402 while ((i=find_arg(ua, "jobid")) > 0) {
1404 *ua->argk[i] = 0; /* zap keyword already visited */
1412 "In general it is not a good idea to delete either a\n"
1413 "Pool or a Volume since they may contain data.\n\n"));
1415 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1426 ua->warning_msg(_("Nothing done.\n"));
1434 * delete_job has been modified to parse JobID lists like the
1436 * delete JobID=3,4,6,7-11,14
1438 * Thanks to Phil Stracchino for the above addition.
1441 static void delete_job(UAContext *ua)
1446 int i = find_arg_with_value(ua, NT_("jobid"));
1448 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1449 s = bstrdup(ua->argv[i]);
1452 * We could use strtok() here. But we're not going to, because:
1453 * (a) strtok() is deprecated, having been replaced by strsep();
1454 * (b) strtok() is broken in significant ways.
1455 * we could use strsep() instead, but it's not universally available.
1456 * so we grow our own using strchr().
1458 sep = strchr(tok, ',');
1459 while (sep != NULL) {
1461 if (strchr(tok, '-')) {
1462 delete_job_id_range(ua, tok);
1464 JobId = str_to_int64(tok);
1465 do_job_delete(ua, JobId);
1468 sep = strchr(tok, ',');
1470 /* pick up the last token */
1471 if (strchr(tok, '-')) {
1472 delete_job_id_range(ua, tok);
1474 JobId = str_to_int64(tok);
1475 do_job_delete(ua, JobId);
1480 JobId = str_to_int64(ua->argv[i]);
1481 do_job_delete(ua, JobId);
1483 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1486 JobId = ua->int64_val;
1487 do_job_delete(ua, JobId);
1492 * we call delete_job_id_range to parse range tokens and iterate over ranges
1494 static void delete_job_id_range(UAContext *ua, char *tok)
1499 tok2 = strchr(tok, '-');
1502 j1 = str_to_int64(tok);
1503 j2 = str_to_int64(tok2);
1504 for (j=j1; j<=j2; j++) {
1505 do_job_delete(ua, j);
1510 * do_job_delete now performs the actual delete operation atomically
1512 static void do_job_delete(UAContext *ua, JobId_t JobId)
1516 edit_int64(JobId, ed1);
1517 purge_jobs_from_catalog(ua, ed1);
1518 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1522 * Delete media records from database -- dangerous
1524 static int delete_volume(UAContext *ua)
1529 if (!select_media_dbr(ua, &mr)) {
1532 ua->warning_msg(_("\nThis command will delete volume %s\n"
1533 "and all Jobs saved on that volume from the Catalog\n"),
1536 if (find_arg(ua, "yes") >= 0) {
1537 ua->pint32_val = 1; /* Have "yes" on command line already" */
1539 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1541 if (!get_yesno(ua, buf)) {
1545 if (ua->pint32_val) {
1546 db_delete_media_record(ua->jcr, ua->db, &mr);
1552 * Delete a pool record from the database -- dangerous
1554 static int delete_pool(UAContext *ua)
1559 memset(&pr, 0, sizeof(pr));
1561 if (!get_pool_dbr(ua, &pr)) {
1564 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1566 if (!get_yesno(ua, buf)) {
1569 if (ua->pint32_val) {
1570 db_delete_pool_record(ua->jcr, ua->db, &pr);
1575 int memory_cmd(UAContext *ua, const char *cmd)
1577 list_dir_status_header(ua);
1578 sm_dump(false, true);
1582 static void do_mount_cmd(UAContext *ua, const char *command)
1587 char dev_name[MAX_NAME_LENGTH];
1591 if (!open_client_db(ua)) {
1594 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1596 store.store = get_storage_resource(ua, true/*arg is storage*/);
1600 pm_strcpy(store.store_source, _("unknown source"));
1601 set_wstorage(jcr, &store);
1602 drive = get_storage_drive(ua, store.store);
1603 if (strcmp(command, "mount") == 0) {
1604 slot = get_storage_slot(ua, store.store);
1607 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1608 store.store->media_type, store.store->dev_name(), drive);
1610 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1611 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1614 sd = jcr->store_bsock;
1615 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1616 bash_spaces(dev_name);
1618 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1620 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1622 while (bnet_recv(sd) >= 0) {
1623 ua->send_msg("%s", sd->msg);
1625 bnet_sig(sd, BNET_TERMINATE);
1627 jcr->store_bsock = NULL;
1631 * mount [storage=<name>] [drive=nn] [slot=mm]
1633 static int mount_cmd(UAContext *ua, const char *cmd)
1635 do_mount_cmd(ua, "mount"); /* mount */
1641 * unmount [storage=<name>] [drive=nn]
1643 static int unmount_cmd(UAContext *ua, const char *cmd)
1645 do_mount_cmd(ua, "unmount"); /* unmount */
1651 * release [storage=<name>] [drive=nn]
1653 static int release_cmd(UAContext *ua, const char *cmd)
1655 do_mount_cmd(ua, "release"); /* release */
1662 * use catalog=<name>
1664 static int use_cmd(UAContext *ua, const char *cmd)
1666 CAT *oldcatalog, *catalog;
1669 close_db(ua); /* close any previously open db */
1670 oldcatalog = ua->catalog;
1672 if (!(catalog = get_catalog_resource(ua))) {
1673 ua->catalog = oldcatalog;
1675 ua->catalog = catalog;
1678 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1679 ua->catalog->name(), ua->catalog->db_name);
1684 int quit_cmd(UAContext *ua, const char *cmd)
1690 /* Handler to get job status */
1691 static int status_handler(void *ctx, int num_fields, char **row)
1693 char *val = (char *)ctx;
1698 *val = '?'; /* Unknown by default */
1705 * Wait until no job is running
1707 int wait_cmd(UAContext *ua, const char *cmd)
1711 time_t stop_time = 0;
1715 * Wait until no job is running
1717 if (ua->argc == 1) {
1718 bmicrosleep(0, 200000); /* let job actually start */
1719 for (bool running=true; running; ) {
1722 if (jcr->JobId != 0) {
1736 i = find_arg_with_value(ua, NT_("timeout"));
1737 if (i > 0 && ua->argv[i]) {
1738 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1741 /* we have jobid, jobname or ujobid argument */
1743 uint32_t jobid = 0 ;
1745 if (!open_client_db(ua)) {
1746 ua->error_msg(_("ERR: Can't open db\n")) ;
1750 for (int i=1; i<ua->argc; i++) {
1751 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1755 jobid = str_to_int64(ua->argv[i]);
1757 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1758 strcasecmp(ua->argk[i], "job") == 0) {
1762 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1764 jobid = jcr->JobId ;
1768 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1772 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1774 jobid = jcr->JobId ;
1778 /* Wait for a mount request */
1779 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1780 for (bool waiting=false; !waiting; ) {
1782 if (jcr->JobId != 0 &&
1783 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1792 if (stop_time && (time(NULL) >= stop_time)) {
1793 ua->warning_msg(_("Wait on mount timed out\n"));
1803 ua->error_msg(_("ERR: Job was not found\n"));
1808 * We wait the end of a specific job
1811 bmicrosleep(0, 200000); /* let job actually start */
1812 for (bool running=true; running; ) {
1815 jcr=get_jcr_by_id(jobid) ;
1828 * We have to get JobStatus
1832 char jobstatus = '?'; /* Unknown by default */
1835 bsnprintf(buf, sizeof(buf),
1836 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1839 db_sql_query(ua->db, buf,
1840 status_handler, (void *)&jobstatus);
1842 switch (jobstatus) {
1844 status = 1 ; /* Warning */
1848 case JS_ErrorTerminated:
1850 status = 2 ; /* Critical */
1855 status = 0 ; /* Ok */
1859 status = 3 ; /* Unknown */
1863 ua->send_msg("JobId=%i\n", jobid) ;
1864 ua->send_msg("JobStatus=%s (%c)\n",
1865 job_status_to_str(jobstatus),
1868 if (ua->gui || ua->api) {
1869 ua->send_msg("ExitStatus=%i\n", status) ;
1876 static int help_cmd(UAContext *ua, const char *cmd)
1880 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1881 for (i=0; i<comsize; i++) {
1882 if (ua->argc == 2) {
1883 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1884 ua->send_msg(_(" %-13s %s\n %s\n"), commands[i].key, commands[i].help, commands[i].usage);
1888 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1891 if (i == comsize && ua->argc == 2) {
1892 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1894 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1898 int qhelp_cmd(UAContext *ua, const char *cmd)
1901 /* Want to display only commands */
1902 j = find_arg(ua, NT_("all"));
1904 for (i=0; i<comsize; i++) {
1905 ua->send_msg("%s\n", commands[i].key);
1909 /* Want to display a specific help section */
1910 j = find_arg_with_value(ua, NT_("item"));
1911 if (j >= 0 && ua->argk[j]) {
1912 for (i=0; i<comsize; i++) {
1913 if (bstrcmp(commands[i].key, ua->argv[j])) {
1914 ua->send_msg("%s\n", commands[i].usage);
1920 /* Want to display everything */
1921 for (i=0; i<comsize; i++) {
1922 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1928 static int version_cmd(UAContext *ua, const char *cmd)
1930 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1931 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1936 * Test code -- turned on only for debug testing
1938 static int version_cmd(UAContext *ua, const char *cmd)
1941 POOL_MEM query(PM_MESSAGE);
1943 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1944 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1945 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1946 for (int i=0; i < ids.num_ids; i++) {
1947 ua->send_msg("id=%d\n", ids.DBId[i]);
1955 * This call explicitly checks for a catalog=xxx and
1956 * if given, opens that catalog. It also checks for
1957 * client=xxx and if found, opens the catalog
1958 * corresponding to that client. If we still don't
1959 * have a catalog, look for a Job keyword and get the
1960 * catalog from its client record.
1962 bool open_client_db(UAContext *ua)
1969 /* Try for catalog keyword */
1970 i = find_arg_with_value(ua, NT_("catalog"));
1972 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1973 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1976 catalog = GetCatalogResWithName(ua->argv[i]);
1978 if (ua->catalog && ua->catalog != catalog) {
1981 ua->catalog = catalog;
1986 /* Try for client keyword */
1987 i = find_arg_with_value(ua, NT_("client"));
1989 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1990 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1993 client = GetClientResWithName(ua->argv[i]);
1995 catalog = client->catalog;
1996 if (ua->catalog && ua->catalog != catalog) {
1999 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2000 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2003 ua->catalog = catalog;
2008 /* Try for Job keyword */
2009 i = find_arg_with_value(ua, NT_("job"));
2011 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2012 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2015 job = GetJobResWithName(ua->argv[i]);
2017 catalog = job->client->catalog;
2018 if (ua->catalog && ua->catalog != catalog) {
2021 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2022 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2025 ua->catalog = catalog;
2035 * Open the catalog database.
2037 bool open_db(UAContext *ua)
2043 ua->catalog = get_catalog_resource(ua);
2045 ua->error_msg( _("Could not find a Catalog resource\n"));
2050 ua->jcr->catalog = ua->catalog;
2052 Dmsg0(100, "UA Open database\n");
2053 ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2054 ua->catalog->db_user,
2055 ua->catalog->db_password, ua->catalog->db_address,
2056 ua->catalog->db_port, ua->catalog->db_socket,
2057 ua->catalog->mult_db_connections);
2058 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2059 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2060 ua->catalog->db_name);
2062 ua->error_msg("%s", db_strerror(ua->db));
2067 ua->jcr->db = ua->db;
2069 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2071 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2075 void close_db(UAContext *ua)
2078 db_close_database(ua->jcr, ua->db);