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 three of the GNU Affero 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 Affero 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 bool 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> level=<level> 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\n\tenable estimate exit gui label list llist"
136 "\n\tmessages memory mount prune purge python quit query\n\trestore relabel release reload run status"
137 "\n\tsetdebug setip show sqlquery time trace unmount umount\n\tupdate 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 | volume | 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 | pool=<pool> | client=<client-name> | volume=<volume-name> "), true},
154 { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [action=<action> devicetype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), 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>"
160 "\n\tcomment=<text> jobid=<jobid> done select all"), false},
162 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
163 NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
165 { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name>"), false},
166 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
167 { NT_("run"), run_cmd, _("Run a job"),
168 NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
169 "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tcomment=<text> yes"), false},
171 { NT_("status"), status_cmd, _("Report status"),
172 NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
174 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
175 NT_("level=<nn> trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
177 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
178 { NT_("show"), show_cmd, _("Show resource records"),
179 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | disabled | all"), true},
181 { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
182 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
183 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
184 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
185 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
187 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
188 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
190 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
191 NT_("stats\n\tpool=<poolname>\n\tslots storage=<storage> scan"
192 "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
193 "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
194 "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
195 "\n\t enable=<yes/no> recyclepool=<pool> actiononpurge=<action>"),true},
196 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_(""), false},
197 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
198 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
199 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
200 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
203 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
206 * Execute a command from the UA
208 bool do_a_command(UAContext *ua)
214 BSOCK *user = ua->UA_sock;
217 Dmsg1(900, "Command: %s\n", ua->argk[0]);
222 while (ua->jcr->wstorage->size()) {
223 ua->jcr->wstorage->remove(0);
226 len = strlen(ua->argk[0]);
227 for (i=0; i<comsize; i++) { /* search for command */
228 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
229 /* Check if command permitted, but "quit" is always OK */
230 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
231 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
234 /* Check if this command is authorized in RunScript */
235 if (ua->runscript && !commands[i].use_in_rs) {
236 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
239 if (ua->api) user->signal(BNET_CMD_BEGIN);
240 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
241 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
247 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
254 * This is a common routine used to stuff the Pool DB record defaults
255 * into the Media DB record just before creating a media (Volume)
258 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
260 mr->PoolId = pr->PoolId;
261 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
262 mr->Recycle = pr->Recycle;
263 mr->VolRetention = pr->VolRetention;
264 mr->VolUseDuration = pr->VolUseDuration;
265 mr->ActionOnPurge = pr->ActionOnPurge;
266 mr->RecyclePoolId = pr->RecyclePoolId;
267 mr->MaxVolJobs = pr->MaxVolJobs;
268 mr->MaxVolFiles = pr->MaxVolFiles;
269 mr->MaxVolBytes = pr->MaxVolBytes;
270 mr->LabelType = pr->LabelType;
276 * Add Volumes to an existing Pool
278 static int add_cmd(UAContext *ua, const char *cmd)
282 int num, i, max, startnum;
284 char name[MAX_NAME_LENGTH];
286 int Slot = 0, InChanger = 0;
289 "You probably don't want to be using this command since it\n"
290 "creates database records without labeling the Volumes.\n"
291 "You probably want to use the \"label\" command.\n\n"));
293 if (!open_client_db(ua)) {
297 memset(&pr, 0, sizeof(pr));
298 memset(&mr, 0, sizeof(mr));
300 if (!get_pool_dbr(ua, &pr)) {
304 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
305 pr.MaxVols, pr.PoolType);
307 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
308 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
309 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
312 pr.MaxVols = ua->pint32_val;
316 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
317 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
318 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
322 if (pr.MaxVols == 0) {
325 max = pr.MaxVols - pr.NumVols;
329 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
330 if (!get_pint(ua, buf)) {
333 num = ua->pint32_val;
334 if (num < 0 || num > max) {
335 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
343 if (!get_cmd(ua, _("Enter Volume name: "))) {
347 if (!get_cmd(ua, _("Enter base volume name: "))) {
351 /* Don't allow | in Volume name because it is the volume separator character */
352 if (!is_volume_name_legal(ua, ua->cmd)) {
355 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
356 ua->warning_msg(_("Volume name too long.\n"));
359 if (strlen(ua->cmd) == 0) {
360 ua->warning_msg(_("Volume name must be at least one character long.\n"));
366 bstrncpy(name, ua->cmd, sizeof(name));
368 bstrncat(name, "%04d", sizeof(name));
371 if (!get_pint(ua, _("Enter the starting number: "))) {
374 startnum = ua->pint32_val;
376 ua->warning_msg(_("Start number must be greater than zero.\n"));
386 if (store && store->autochanger) {
387 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
390 Slot = ua->pint32_val;
391 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
394 InChanger = ua->pint32_val;
397 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
398 for (i=startnum; i < num+startnum; i++) {
399 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
401 mr.InChanger = InChanger;
402 mr.StorageId = store->StorageId;
404 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
405 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
406 ua->error_msg("%s", db_strerror(ua->db));
410 first_id = mr.PoolId;
414 Dmsg0(200, "Update pool record.\n");
415 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
416 ua->warning_msg("%s", db_strerror(ua->db));
419 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
425 * Turn auto mount on/off
430 int automount_cmd(UAContext *ua, const char *cmd)
435 if (!get_cmd(ua, _("Turn on or off? "))) {
443 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
451 static int cancel_cmd(UAContext *ua, const char *cmd)
456 char JobName[MAX_NAME_LENGTH];
458 for (i=1; i<ua->argc; i++) {
459 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
461 JobId = str_to_int64(ua->argv[i]);
465 if (!(jcr=get_jcr_by_id(JobId))) {
466 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
470 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
474 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
475 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
476 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
477 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
480 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
484 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
485 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
486 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
487 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
494 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
495 ua->error_msg(_("Unauthorized command from this console.\n"));
500 * If we still do not have a jcr,
501 * throw up a list and ask the user to select one.
504 int tjobs = 0; /* total # number jobs */
505 /* Count Jobs running */
507 if (jcr->JobId == 0) { /* this is us */
510 tjobs++; /* count of all jobs */
511 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
512 continue; /* skip not authorized */
514 njobs++; /* count of authorized jobs */
518 if (njobs == 0) { /* no authorized */
520 ua->send_msg(_("No Jobs running.\n"));
522 ua->send_msg(_("None of your jobs are running.\n"));
527 start_prompt(ua, _("Select Job:\n"));
530 if (jcr->JobId == 0) { /* this is us */
533 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
534 continue; /* skip not authorized */
536 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
541 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
544 if (ua->api && njobs == 1) {
546 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
547 _("Confirm cancel?"));
548 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
553 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
558 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
559 jcr = get_jcr_by_full_name(JobName);
561 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
566 ret = cancel_job(ua, jcr);
572 * This is a common routine to create or update a
573 * Pool DB base record from a Pool Resource. We handle
574 * the setting of MaxVols and NumVols slightly differently
575 * depending on if we are creating the Pool or we are
576 * simply bringing it into agreement with the resource (updage).
578 * Caution : RecyclePoolId isn't setup in this function.
579 * You can use set_pooldbr_recyclepoolid();
582 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
584 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
585 if (op == POOL_OP_CREATE) {
586 pr->MaxVols = pool->max_volumes;
588 } else { /* update pool */
589 if (pr->MaxVols != pool->max_volumes) {
590 pr->MaxVols = pool->max_volumes;
592 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
593 pr->MaxVols = pr->NumVols;
596 pr->LabelType = pool->LabelType;
597 pr->UseOnce = pool->use_volume_once;
598 pr->UseCatalog = pool->use_catalog;
599 pr->Recycle = pool->Recycle;
600 pr->VolRetention = pool->VolRetention;
601 pr->VolUseDuration = pool->VolUseDuration;
602 pr->MaxVolJobs = pool->MaxVolJobs;
603 pr->MaxVolFiles = pool->MaxVolFiles;
604 pr->MaxVolBytes = pool->MaxVolBytes;
605 pr->AutoPrune = pool->AutoPrune;
606 pr->ActionOnPurge = pool->action_on_purge;
607 pr->Recycle = pool->Recycle;
608 if (pool->label_format) {
609 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
611 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
615 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
616 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
620 if (!pool->RecyclePool && !pool->ScratchPool) {
624 memset(&pr, 0, sizeof(POOL_DBR));
625 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
627 if (!db_get_pool_record(jcr, db, &pr)) {
628 return -1; /* not exists in database */
631 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
633 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
634 return -1; /* error */
637 if (!db_update_pool_record(jcr, db, &pr)) {
638 return -1; /* error */
643 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
644 * works with set_pooldbr_from_poolres
646 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
651 if (pool->RecyclePool) {
652 memset(&rpool, 0, sizeof(POOL_DBR));
654 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
655 if (db_get_pool_record(jcr, db, &rpool)) {
656 pr->RecyclePoolId = rpool.PoolId;
658 Jmsg(jcr, M_WARNING, 0,
659 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
660 "Try to update it with 'update pool=%s'\n"),
661 pool->name(), rpool.Name, rpool.Name,pool->name());
665 } else { /* no RecyclePool used, set it to 0 */
666 pr->RecyclePoolId = 0;
669 if (pool->ScratchPool) {
670 memset(&rpool, 0, sizeof(POOL_DBR));
672 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
673 if (db_get_pool_record(jcr, db, &rpool)) {
674 pr->ScratchPoolId = rpool.PoolId;
676 Jmsg(jcr, M_WARNING, 0,
677 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
678 "Try to update it with 'update pool=%s'\n"),
679 pool->name(), rpool.Name, rpool.Name,pool->name());
682 } else { /* no ScratchPool used, set it to 0 */
683 pr->ScratchPoolId = 0;
691 * Create a pool record from a given Pool resource
692 * Also called from backup.c
693 * Returns: -1 on error
694 * 0 record already exists
698 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
702 memset(&pr, 0, sizeof(POOL_DBR));
704 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
706 if (db_get_pool_record(jcr, db, &pr)) {
708 if (op == POOL_OP_UPDATE) { /* update request */
709 set_pooldbr_from_poolres(&pr, pool, op);
710 set_pooldbr_references(jcr, db, &pr, pool);
711 db_update_pool_record(jcr, db, &pr);
713 return 0; /* exists */
716 set_pooldbr_from_poolres(&pr, pool, op);
717 set_pooldbr_references(jcr, db, &pr, pool);
719 if (!db_create_pool_record(jcr, db, &pr)) {
720 return -1; /* error */
728 * Create a Pool Record in the database.
729 * It is always created from the Resource record.
731 static int create_cmd(UAContext *ua, const char *cmd)
735 if (!open_client_db(ua)) {
739 pool = get_pool_resource(ua);
744 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
746 ua->error_msg(_("Error: Pool %s already exists.\n"
747 "Use update to change it.\n"), pool->name());
751 ua->error_msg("%s", db_strerror(ua->db));
757 ua->send_msg(_("Pool %s created.\n"), pool->name());
762 extern DIRRES *director;
763 extern char *configfile;
766 * Python control command
767 * python restart (restarts interpreter)
769 static int python_cmd(UAContext *ua, const char *cmd)
772 init_python_interpreter_args python_args;
774 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
775 term_python_interpreter();
777 python_args.progname = director->name();
778 python_args.scriptdir = director->scripts_directory;
779 python_args.modulename = "DirStartUp";
780 python_args.configfile = configfile;
781 python_args.workingdir = director->working_directory;
782 python_args.job_getattr = job_getattr;
783 python_args.job_setattr = job_setattr;
785 init_python_interpreter(&python_args);
787 ua->send_msg(_("Python interpreter restarted.\n"));
789 #endif /* HAVE_PYTHON */
790 ua->warning_msg(_("Nothing done.\n"));
793 #endif /* HAVE_PYTHON */
799 * Set a new address in a Client resource. We do this only
800 * if the Console name is the same as the Client name
801 * and the Console can access the client.
803 static int setip_cmd(UAContext *ua, const char *cmd)
807 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
808 ua->error_msg(_("Unauthorized command from this console.\n"));
812 client = GetClientResWithName(ua->cons->name());
815 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
818 if (client->address) {
819 free(client->address);
821 /* MA Bug 6 remove ifdef */
822 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
823 client->address = bstrdup(buf);
824 ua->send_msg(_("Client \"%s\" address set to %s\n"),
825 client->name(), client->address);
832 static void do_en_disable_cmd(UAContext *ua, bool setting)
837 i = find_arg_with_value(ua, NT_("job"));
839 job = select_enable_disable_job_resource(ua, setting);
845 job = GetJobResWithName(ua->argv[i]);
849 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
853 if (!acl_access_ok(ua, Job_ACL, job->name())) {
854 ua->error_msg(_("Unauthorized command from this console.\n"));
857 job->enabled = setting;
858 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
862 static int enable_cmd(UAContext *ua, const char *cmd)
864 do_en_disable_cmd(ua, true);
868 static int disable_cmd(UAContext *ua, const char *cmd)
870 do_en_disable_cmd(ua, false);
875 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
881 lstore.store = store;
882 pm_strcpy(lstore.store_source, _("unknown source"));
883 set_wstorage(jcr, &lstore);
884 /* Try connecting for up to 15 seconds */
885 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
886 store->name(), store->address, store->SDport);
887 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
888 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
891 Dmsg0(120, _("Connected to storage daemon\n"));
892 sd = jcr->store_bsock;
893 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
894 if (sd->recv() >= 0) {
895 ua->send_msg("%s", sd->msg);
897 sd->signal(BNET_TERMINATE);
899 jcr->store_bsock = NULL;
903 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
907 /* Connect to File daemon */
909 ua->jcr->client = client;
910 /* Try to connect for 15 seconds */
911 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
912 client->name(), client->address, client->FDport);
913 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
914 ua->error_msg(_("Failed to connect to Client.\n"));
917 Dmsg0(120, "Connected to file daemon\n");
918 fd = ua->jcr->file_bsock;
919 fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
920 if (fd->recv() >= 0) {
921 ua->send_msg("%s", fd->msg);
923 fd->signal(BNET_TERMINATE);
925 ua->jcr->file_bsock = NULL;
930 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
932 STORE *store, **unique_store;
933 CLIENT *client, **unique_client;
939 /* Count Storage items */
943 foreach_res(store, R_STORAGE) {
946 unique_store = (STORE **) malloc(i * sizeof(STORE));
947 /* Find Unique Storage address/port */
948 store = (STORE *)GetNextRes(R_STORAGE, NULL);
950 unique_store[i++] = store;
951 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
953 for (j=0; j<i; j++) {
954 if (strcmp(unique_store[j]->address, store->address) == 0 &&
955 unique_store[j]->SDport == store->SDport) {
961 unique_store[i++] = store;
962 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
967 /* Call each unique Storage daemon */
968 for (j=0; j<i; j++) {
969 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
973 /* Count Client items */
977 foreach_res(client, R_CLIENT) {
980 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
981 /* Find Unique Client address/port */
982 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
984 unique_client[i++] = client;
985 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
987 for (j=0; j<i; j++) {
988 if (strcmp(unique_client[j]->address, client->address) == 0 &&
989 unique_client[j]->FDport == client->FDport) {
995 unique_client[i++] = client;
996 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1001 /* Call each unique File daemon */
1002 for (j=0; j<i; j++) {
1003 do_client_setdebug(ua, unique_client[j], level, trace_flag);
1005 free(unique_client);
1009 * setdebug level=nn all trace=1/0
1011 static int setdebug_cmd(UAContext *ua, const char *cmd)
1016 int trace_flag = -1;
1019 Dmsg1(120, "setdebug:%s:\n", cmd);
1022 i = find_arg_with_value(ua, "level");
1024 level = atoi(ua->argv[i]);
1027 if (!get_pint(ua, _("Enter new debug level: "))) {
1030 level = ua->pint32_val;
1033 /* Look for trace flag. -1 => not change */
1034 i = find_arg_with_value(ua, "trace");
1036 trace_flag = atoi(ua->argv[i]);
1037 if (trace_flag > 0) {
1042 /* General debug? */
1043 for (i=1; i<ua->argc; i++) {
1044 if (strcasecmp(ua->argk[i], "all") == 0) {
1045 do_all_setdebug(ua, level, trace_flag);
1048 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1049 strcasecmp(ua->argk[i], "director") == 0) {
1050 debug_level = level;
1051 set_trace(trace_flag);
1054 if (strcasecmp(ua->argk[i], "client") == 0 ||
1055 strcasecmp(ua->argk[i], "fd") == 0) {
1058 client = GetClientResWithName(ua->argv[i]);
1060 do_client_setdebug(ua, client, level, trace_flag);
1064 client = select_client_resource(ua);
1066 do_client_setdebug(ua, client, level, trace_flag);
1071 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1072 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1073 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1076 store = GetStoreResWithName(ua->argv[i]);
1078 do_storage_setdebug(ua, store, level, trace_flag);
1082 store = get_storage_resource(ua, false/*no default*/);
1084 do_storage_setdebug(ua, store, level, trace_flag);
1090 * We didn't find an appropriate keyword above, so
1093 start_prompt(ua, _("Available daemons are: \n"));
1094 add_prompt(ua, _("Director"));
1095 add_prompt(ua, _("Storage"));
1096 add_prompt(ua, _("Client"));
1097 add_prompt(ua, _("All"));
1098 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1099 case 0: /* Director */
1100 debug_level = level;
1101 set_trace(trace_flag);
1104 store = get_storage_resource(ua, false/*no default*/);
1106 do_storage_setdebug(ua, store, level, trace_flag);
1110 client = select_client_resource(ua);
1112 do_client_setdebug(ua, client, level, trace_flag);
1116 do_all_setdebug(ua, level, trace_flag);
1125 * Turn debug tracing to file on/off
1127 static int trace_cmd(UAContext *ua, const char *cmd)
1131 if (ua->argc != 2) {
1132 if (!get_cmd(ua, _("Turn on or off? "))) {
1137 onoff = ua->argk[1];
1140 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1145 static int var_cmd(UAContext *ua, const char *cmd)
1147 POOLMEM *val = get_pool_memory(PM_FNAME);
1150 if (!open_client_db(ua)) {
1153 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1156 while (*var == ' ') { /* skip spaces */
1159 Dmsg1(100, "Var=%s:\n", var);
1160 variable_expansion(ua->jcr, var, &val);
1161 ua->send_msg("%s\n", val);
1162 free_pool_memory(val);
1166 static int estimate_cmd(UAContext *ua, const char *cmd)
1169 CLIENT *client = NULL;
1170 FILESET *fileset = NULL;
1172 char since[MAXSTRING];
1176 jcr->set_JobLevel(L_FULL);
1177 for (int i=1; i<ua->argc; i++) {
1178 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1179 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1181 client = GetClientResWithName(ua->argv[i]);
1183 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1186 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1187 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1192 ua->error_msg(_("Client name missing.\n"));
1196 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1198 job = GetJobResWithName(ua->argv[i]);
1200 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1203 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1204 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1209 ua->error_msg(_("Job name missing.\n"));
1214 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1216 fileset = GetFileSetResWithName(ua->argv[i]);
1218 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1221 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1222 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1227 ua->error_msg(_("Fileset name missing.\n"));
1231 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1235 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1237 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1238 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1242 ua->error_msg(_("Level value missing.\n"));
1246 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1247 if (!is_yesno(ua->argv[i], &accurate)) {
1248 ua->error_msg(_("Invalid value for accurate. "
1249 "It must be yes or no.\n"));
1253 if (!job && !(client && fileset)) {
1254 if (!(job = select_job_resource(ua))) {
1259 job = GetJobResWithName(ua->argk[1]);
1261 ua->error_msg(_("No job specified.\n"));
1264 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1265 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1270 client = job->client;
1273 fileset = job->fileset;
1275 jcr->client = client;
1276 jcr->fileset = fileset;
1278 if (job->pool->catalog) {
1279 ua->catalog = job->pool->catalog;
1281 ua->catalog = client->catalog;
1289 jcr->set_JobType(JT_BACKUP);
1290 init_jcr_job_record(jcr);
1292 if (!get_or_create_client_record(jcr)) {
1295 if (!get_or_create_fileset_record(jcr)) {
1299 get_level_since_time(ua->jcr, since, sizeof(since));
1301 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1302 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1303 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1304 ua->error_msg(_("Failed to connect to Client.\n"));
1308 if (!send_include_list(jcr)) {
1309 ua->error_msg(_("Error sending include list.\n"));
1313 if (!send_exclude_list(jcr)) {
1314 ua->error_msg(_("Error sending exclude list.\n"));
1318 /* The level string change if accurate mode is enabled */
1319 if (accurate >= 0) {
1320 jcr->accurate = accurate;
1322 jcr->accurate = job->accurate;
1325 if (!send_level_command(jcr)) {
1330 * If the job is in accurate mode, we send the list of
1333 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1334 if (!send_accurate_current_files(jcr)) {
1338 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1339 while (bnet_recv(jcr->file_bsock) >= 0) {
1340 ua->send_msg("%s", jcr->file_bsock->msg);
1344 if (jcr->file_bsock) {
1345 jcr->file_bsock->signal(BNET_TERMINATE);
1346 jcr->file_bsock->close();
1347 jcr->file_bsock = NULL;
1356 static int time_cmd(UAContext *ua, const char *cmd)
1359 time_t ttime = time(NULL);
1361 (void)localtime_r(&ttime, &tm);
1362 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1363 ua->send_msg("%s\n", sdt);
1368 * reload the conf file
1370 extern "C" void reload_config(int sig);
1372 static int reload_cmd(UAContext *ua, const char *cmd)
1379 * Delete Pool records (should purge Media with it).
1381 * delete pool=<pool-name>
1382 * delete volume pool=<pool-name> volume=<name>
1385 static int delete_cmd(UAContext *ua, const char *cmd)
1387 static const char *keywords[] = {
1393 if (!open_client_db(ua)) {
1397 switch (find_arg_keyword(ua, keywords)) {
1406 while ((i=find_arg(ua, "jobid")) > 0) {
1408 *ua->argk[i] = 0; /* zap keyword already visited */
1416 "In general it is not a good idea to delete either a\n"
1417 "Pool or a Volume since they may contain data.\n\n"));
1419 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1430 ua->warning_msg(_("Nothing done.\n"));
1438 * delete_job has been modified to parse JobID lists like the
1440 * delete JobID=3,4,6,7-11,14
1442 * Thanks to Phil Stracchino for the above addition.
1445 static void delete_job(UAContext *ua)
1450 int i = find_arg_with_value(ua, NT_("jobid"));
1452 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1453 s = bstrdup(ua->argv[i]);
1456 * We could use strtok() here. But we're not going to, because:
1457 * (a) strtok() is deprecated, having been replaced by strsep();
1458 * (b) strtok() is broken in significant ways.
1459 * we could use strsep() instead, but it's not universally available.
1460 * so we grow our own using strchr().
1462 sep = strchr(tok, ',');
1463 while (sep != NULL) {
1465 if (!delete_job_id_range(ua, tok)) {
1466 JobId = str_to_int64(tok);
1467 do_job_delete(ua, JobId);
1470 sep = strchr(tok, ',');
1472 /* pick up the last token */
1473 if (!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 bool delete_job_id_range(UAContext *ua, char *tok)
1499 tok2 = strchr(tok, '-');
1505 j1 = str_to_int64(tok);
1506 j2 = str_to_int64(tok2);
1507 for (j=j1; j<=j2; j++) {
1508 do_job_delete(ua, j);
1514 * do_job_delete now performs the actual delete operation atomically
1516 static void do_job_delete(UAContext *ua, JobId_t JobId)
1520 edit_int64(JobId, ed1);
1521 purge_jobs_from_catalog(ua, ed1);
1522 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1526 * Delete media records from database -- dangerous
1528 static int delete_volume(UAContext *ua)
1534 if (!select_media_dbr(ua, &mr)) {
1537 ua->warning_msg(_("\nThis command will delete volume %s\n"
1538 "and all Jobs saved on that volume from the Catalog\n"),
1541 if (find_arg(ua, "yes") >= 0) {
1542 ua->pint32_val = 1; /* Have "yes" on command line already" */
1544 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1546 if (!get_yesno(ua, buf)) {
1550 if (!ua->pint32_val) {
1554 /* If not purged, do it */
1555 if (strcmp(mr.VolStatus, "Purged") != 0) {
1556 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1557 ua->error_msg(_("Can't list jobs on this volume\n"));
1561 purge_jobs_from_catalog(ua, lst.list);
1565 db_delete_media_record(ua->jcr, ua->db, &mr);
1570 * Delete a pool record from the database -- dangerous
1572 static int delete_pool(UAContext *ua)
1577 memset(&pr, 0, sizeof(pr));
1579 if (!get_pool_dbr(ua, &pr)) {
1582 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1584 if (!get_yesno(ua, buf)) {
1587 if (ua->pint32_val) {
1588 db_delete_pool_record(ua->jcr, ua->db, &pr);
1593 int memory_cmd(UAContext *ua, const char *cmd)
1595 list_dir_status_header(ua);
1596 sm_dump(false, true);
1600 static void do_mount_cmd(UAContext *ua, const char *command)
1605 char dev_name[MAX_NAME_LENGTH];
1609 if (!open_client_db(ua)) {
1612 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1614 store.store = get_storage_resource(ua, true/*arg is storage*/);
1618 pm_strcpy(store.store_source, _("unknown source"));
1619 set_wstorage(jcr, &store);
1620 drive = get_storage_drive(ua, store.store);
1621 if (strcmp(command, "mount") == 0) {
1622 slot = get_storage_slot(ua, store.store);
1625 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1626 store.store->media_type, store.store->dev_name(), drive);
1628 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1629 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1632 sd = jcr->store_bsock;
1633 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1634 bash_spaces(dev_name);
1636 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1638 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1640 while (bnet_recv(sd) >= 0) {
1641 ua->send_msg("%s", sd->msg);
1643 bnet_sig(sd, BNET_TERMINATE);
1645 jcr->store_bsock = NULL;
1649 * mount [storage=<name>] [drive=nn] [slot=mm]
1651 static int mount_cmd(UAContext *ua, const char *cmd)
1653 do_mount_cmd(ua, "mount"); /* mount */
1659 * unmount [storage=<name>] [drive=nn]
1661 static int unmount_cmd(UAContext *ua, const char *cmd)
1663 do_mount_cmd(ua, "unmount"); /* unmount */
1669 * release [storage=<name>] [drive=nn]
1671 static int release_cmd(UAContext *ua, const char *cmd)
1673 do_mount_cmd(ua, "release"); /* release */
1680 * use catalog=<name>
1682 static int use_cmd(UAContext *ua, const char *cmd)
1684 CAT *oldcatalog, *catalog;
1687 close_db(ua); /* close any previously open db */
1688 oldcatalog = ua->catalog;
1690 if (!(catalog = get_catalog_resource(ua))) {
1691 ua->catalog = oldcatalog;
1693 ua->catalog = catalog;
1696 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1697 ua->catalog->name(), ua->catalog->db_name);
1702 int quit_cmd(UAContext *ua, const char *cmd)
1708 /* Handler to get job status */
1709 static int status_handler(void *ctx, int num_fields, char **row)
1711 char *val = (char *)ctx;
1716 *val = '?'; /* Unknown by default */
1723 * Wait until no job is running
1725 int wait_cmd(UAContext *ua, const char *cmd)
1729 time_t stop_time = 0;
1733 * Wait until no job is running
1735 if (ua->argc == 1) {
1736 bmicrosleep(0, 200000); /* let job actually start */
1737 for (bool running=true; running; ) {
1740 if (jcr->JobId != 0) {
1754 i = find_arg_with_value(ua, NT_("timeout"));
1755 if (i > 0 && ua->argv[i]) {
1756 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1759 /* we have jobid, jobname or ujobid argument */
1761 uint32_t jobid = 0 ;
1763 if (!open_client_db(ua)) {
1764 ua->error_msg(_("ERR: Can't open db\n")) ;
1768 for (int i=1; i<ua->argc; i++) {
1769 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1773 jobid = str_to_int64(ua->argv[i]);
1775 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1776 strcasecmp(ua->argk[i], "job") == 0) {
1780 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1782 jobid = jcr->JobId ;
1786 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1790 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1792 jobid = jcr->JobId ;
1796 /* Wait for a mount request */
1797 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1798 for (bool waiting=false; !waiting; ) {
1800 if (jcr->JobId != 0 &&
1801 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1810 if (stop_time && (time(NULL) >= stop_time)) {
1811 ua->warning_msg(_("Wait on mount timed out\n"));
1821 ua->error_msg(_("ERR: Job was not found\n"));
1826 * We wait the end of a specific job
1829 bmicrosleep(0, 200000); /* let job actually start */
1830 for (bool running=true; running; ) {
1833 jcr=get_jcr_by_id(jobid) ;
1846 * We have to get JobStatus
1850 char jobstatus = '?'; /* Unknown by default */
1853 bsnprintf(buf, sizeof(buf),
1854 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1857 db_sql_query(ua->db, buf,
1858 status_handler, (void *)&jobstatus);
1860 switch (jobstatus) {
1862 status = 1 ; /* Warning */
1866 case JS_ErrorTerminated:
1868 status = 2 ; /* Critical */
1873 status = 0 ; /* Ok */
1877 status = 3 ; /* Unknown */
1881 ua->send_msg("JobId=%i\n", jobid) ;
1882 ua->send_msg("JobStatus=%s (%c)\n",
1883 job_status_to_str(jobstatus),
1886 if (ua->gui || ua->api) {
1887 ua->send_msg("ExitStatus=%i\n", status) ;
1894 static int help_cmd(UAContext *ua, const char *cmd)
1897 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1898 for (i=0; i<comsize; i++) {
1899 if (ua->argc == 2) {
1900 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1901 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1902 commands[i].help, commands[i].usage);
1906 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1909 if (i == comsize && ua->argc == 2) {
1910 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1912 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1916 int qhelp_cmd(UAContext *ua, const char *cmd)
1919 /* Want to display only commands */
1920 j = find_arg(ua, NT_("all"));
1922 for (i=0; i<comsize; i++) {
1923 ua->send_msg("%s\n", commands[i].key);
1927 /* Want to display a specific help section */
1928 j = find_arg_with_value(ua, NT_("item"));
1929 if (j >= 0 && ua->argk[j]) {
1930 for (i=0; i<comsize; i++) {
1931 if (bstrcmp(commands[i].key, ua->argv[j])) {
1932 ua->send_msg("%s\n", commands[i].usage);
1938 /* Want to display everything */
1939 for (i=0; i<comsize; i++) {
1940 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1946 static int version_cmd(UAContext *ua, const char *cmd)
1948 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1949 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1954 * Test code -- turned on only for debug testing
1956 static int version_cmd(UAContext *ua, const char *cmd)
1959 POOL_MEM query(PM_MESSAGE);
1961 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1962 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1963 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1964 for (int i=0; i < ids.num_ids; i++) {
1965 ua->send_msg("id=%d\n", ids.DBId[i]);
1973 * This call uses open_client_db() and force a
1974 * new dedicated connection to the catalog
1976 bool open_new_client_db(UAContext *ua)
1980 /* Force a new dedicated connection */
1982 ua->force_mult_db_connections = true;
1983 ret = open_client_db(ua);
1984 ua->force_mult_db_connections = false;
1989 * This call explicitly checks for a catalog=xxx and
1990 * if given, opens that catalog. It also checks for
1991 * client=xxx and if found, opens the catalog
1992 * corresponding to that client. If we still don't
1993 * have a catalog, look for a Job keyword and get the
1994 * catalog from its client record.
1996 bool open_client_db(UAContext *ua)
2003 /* Try for catalog keyword */
2004 i = find_arg_with_value(ua, NT_("catalog"));
2006 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2007 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2010 catalog = GetCatalogResWithName(ua->argv[i]);
2012 if (ua->catalog && ua->catalog != catalog) {
2015 ua->catalog = catalog;
2020 /* Try for client keyword */
2021 i = find_arg_with_value(ua, NT_("client"));
2023 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2024 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2027 client = GetClientResWithName(ua->argv[i]);
2029 catalog = client->catalog;
2030 if (ua->catalog && ua->catalog != catalog) {
2033 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2034 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2037 ua->catalog = catalog;
2042 /* Try for Job keyword */
2043 i = find_arg_with_value(ua, NT_("job"));
2045 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2046 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2049 job = GetJobResWithName(ua->argv[i]);
2051 catalog = job->client->catalog;
2052 if (ua->catalog && ua->catalog != catalog) {
2055 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2056 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2059 ua->catalog = catalog;
2069 * Open the catalog database.
2071 bool open_db(UAContext *ua)
2079 ua->catalog = get_catalog_resource(ua);
2081 ua->error_msg( _("Could not find a Catalog resource\n"));
2086 /* Some modules like bvfs need their own catalog connection */
2087 mult_db_conn = ua->catalog->mult_db_connections;
2088 if (ua->force_mult_db_connections) {
2089 mult_db_conn = true;
2092 ua->jcr->catalog = ua->catalog;
2094 Dmsg0(100, "UA Open database\n");
2095 ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2096 ua->catalog->db_user,
2097 ua->catalog->db_password, ua->catalog->db_address,
2098 ua->catalog->db_port, ua->catalog->db_socket,
2100 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2101 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2102 ua->catalog->db_name);
2104 ua->error_msg("%s", db_strerror(ua->db));
2109 ua->jcr->db = ua->db;
2111 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2113 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2117 void close_db(UAContext *ua)
2120 db_close_database(ua->jcr, ua->db);