2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2011 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
41 #undef _POSIX_C_SOURCE
44 #include "lib/pythonlib.h"
46 /* Imported Functions */
47 extern PyObject *job_getattr(PyObject *self, char *attrname);
48 extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
50 #endif /* HAVE_PYTHON */
52 /* Imported subroutines */
54 /* Imported variables */
55 extern jobq_t job_queue; /* job queue */
58 /* Imported functions */
59 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
60 extern int gui_cmd(UAContext *ua, const char *cmd);
61 extern int label_cmd(UAContext *ua, const char *cmd);
62 extern int list_cmd(UAContext *ua, const char *cmd);
63 extern int llist_cmd(UAContext *ua, const char *cmd);
64 extern int messagescmd(UAContext *ua, const char *cmd);
65 extern int prunecmd(UAContext *ua, const char *cmd);
66 extern int purgecmd(UAContext *ua, const char *cmd);
67 extern int querycmd(UAContext *ua, const char *cmd);
68 extern int relabel_cmd(UAContext *ua, const char *cmd);
69 extern int restore_cmd(UAContext *ua, const char *cmd);
70 extern int retentioncmd(UAContext *ua, const char *cmd);
71 extern int show_cmd(UAContext *ua, const char *cmd);
72 extern int sqlquerycmd(UAContext *ua, const char *cmd);
73 extern int status_cmd(UAContext *ua, const char *cmd);
74 extern int update_cmd(UAContext *ua, const char *cmd);
76 /* Forward referenced functions */
77 static int add_cmd(UAContext *ua, const char *cmd);
78 static int automount_cmd(UAContext *ua, const char *cmd);
79 static int cancel_cmd(UAContext *ua, const char *cmd);
80 static int create_cmd(UAContext *ua, const char *cmd);
81 static int delete_cmd(UAContext *ua, const char *cmd);
82 static int disable_cmd(UAContext *ua, const char *cmd);
83 static int enable_cmd(UAContext *ua, const char *cmd);
84 static int estimate_cmd(UAContext *ua, const char *cmd);
85 static int help_cmd(UAContext *ua, const char *cmd);
86 static int memory_cmd(UAContext *ua, const char *cmd);
87 static int mount_cmd(UAContext *ua, const char *cmd);
88 static int python_cmd(UAContext *ua, const char *cmd);
89 static int release_cmd(UAContext *ua, const char *cmd);
90 static int reload_cmd(UAContext *ua, const char *cmd);
91 static int setdebug_cmd(UAContext *ua, const char *cmd);
92 static int setip_cmd(UAContext *ua, const char *cmd);
93 static int time_cmd(UAContext *ua, const char *cmd);
94 static int trace_cmd(UAContext *ua, const char *cmd);
95 static int unmount_cmd(UAContext *ua, const char *cmd);
96 static int use_cmd(UAContext *ua, const char *cmd);
97 static int var_cmd(UAContext *ua, const char *cmd);
98 static int version_cmd(UAContext *ua, const char *cmd);
99 static int wait_cmd(UAContext *ua, const char *cmd);
101 static void do_job_delete(UAContext *ua, JobId_t JobId);
102 static bool delete_job_id_range(UAContext *ua, char *tok);
103 static int delete_volume(UAContext *ua);
104 static int delete_pool(UAContext *ua);
105 static void delete_job(UAContext *ua);
107 int qhelp_cmd(UAContext *ua, const char *cmd);
108 int quit_cmd(UAContext *ua, const char *cmd);
110 /* not all in alphabetical order. New commands are added after existing commands with similar letters
111 to prevent breakage of existing user scripts. */
113 const char *key; /* command */
114 int (*func)(UAContext *ua, const char *cmd); /* handler */
115 const char *help; /* main purpose */
116 const char *usage; /* all arguments to build usage */
117 const bool use_in_rs; /* Can use it in Console RunScript */
119 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
120 { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
121 { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
122 { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
123 { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid=<number> job=<job-name> ujobid=<unique-jobid>"), false},
124 { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
125 { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume=<vol-name> pool=<pool-name> jobid=<id>"), true},
126 { NT_("disable"), disable_cmd, _("Disable a job"), NT_("job=<name>"), true},
127 { NT_("enable"), enable_cmd, _("Enable a job"), NT_("job=<name>"), true},
128 { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
129 NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
131 { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
132 { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
133 { NT_("help"), help_cmd, _("Print help on specific command"),
134 NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
135 "\n\tmessages memory mount prune purge python quit query\n\trestore relabel release reload run status"
136 "\n\tsetdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"), false},
138 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> barcodes"), false},
139 { NT_("list"), list_cmd, _("List objects from catalog"),
140 NT_("pools | jobs | jobtotals | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
142 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
143 NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
145 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
146 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
147 { NT_("mount"), mount_cmd, _("Mount storage"),
148 NT_("storage=<storage-name> slot=<num> drive=<num> [ jobid=<id> | job=<job-name> ]"), false},
150 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
151 NT_("files | jobs | pool=<pool> | client=<client-name> | volume=<volume-name> "), true},
153 { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [action=<action> devicetype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
154 { NT_("python"), python_cmd, _("Python control commands"), NT_(""), false},
155 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
156 { NT_("query"), querycmd, _("Query catalog"), NT_(""), false},
157 { NT_("restore"), restore_cmd, _("Restore files"),
158 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_("catalog=<catalog>"), 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 */
798 * Set a new address in a Client resource. We do this only
799 * if the Console name is the same as the Client name
800 * and the Console can access the client.
802 static int setip_cmd(UAContext *ua, const char *cmd)
806 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
807 ua->error_msg(_("Unauthorized command from this console.\n"));
811 client = GetClientResWithName(ua->cons->name());
814 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
817 if (client->address) {
818 free(client->address);
820 /* MA Bug 6 remove ifdef */
821 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
822 client->address = bstrdup(buf);
823 ua->send_msg(_("Client \"%s\" address set to %s\n"),
824 client->name(), client->address);
831 static void do_en_disable_cmd(UAContext *ua, bool setting)
836 i = find_arg_with_value(ua, NT_("job"));
838 job = select_enable_disable_job_resource(ua, setting);
844 job = GetJobResWithName(ua->argv[i]);
848 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
852 if (!acl_access_ok(ua, Job_ACL, job->name())) {
853 ua->error_msg(_("Unauthorized command from this console.\n"));
856 job->enabled = setting;
857 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
861 static int enable_cmd(UAContext *ua, const char *cmd)
863 do_en_disable_cmd(ua, true);
867 static int disable_cmd(UAContext *ua, const char *cmd)
869 do_en_disable_cmd(ua, false);
873 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
879 lstore.store = store;
880 pm_strcpy(lstore.store_source, _("unknown source"));
881 set_wstorage(jcr, &lstore);
882 /* Try connecting for up to 15 seconds */
883 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
884 store->name(), store->address, store->SDport);
885 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
886 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
889 Dmsg0(120, _("Connected to storage daemon\n"));
890 sd = jcr->store_bsock;
891 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
892 if (sd->recv() >= 0) {
893 ua->send_msg("%s", sd->msg);
895 sd->signal(BNET_TERMINATE);
897 jcr->store_bsock = NULL;
902 * For the client, we have the following values that can be set
903 * level = debug level
904 * trace = send debug output to a file
905 * hangup = how many records to send to SD before hanging up
906 * obviously this is most useful for testing restarting
909 static void do_client_setdebug(UAContext *ua, CLIENT *client,
910 int level, int trace, int hangup)
914 /* Connect to File daemon */
916 ua->jcr->client = client;
917 /* Try to connect for 15 seconds */
918 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
919 client->name(), client->address, client->FDport);
920 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
921 ua->error_msg(_("Failed to connect to Client.\n"));
924 Dmsg0(120, "Connected to file daemon\n");
925 fd = ua->jcr->file_bsock;
926 fd->fsend("setdebug=%d trace=%d hangup=%d\n", level, trace, hangup);
927 if (fd->recv() >= 0) {
928 ua->send_msg("%s", fd->msg);
930 fd->signal(BNET_TERMINATE);
932 ua->jcr->file_bsock = NULL;
937 static void do_all_setdebug(UAContext *ua, int level, int trace_flag, int hangup)
939 STORE *store, **unique_store;
940 CLIENT *client, **unique_client;
946 /* Count Storage items */
950 foreach_res(store, R_STORAGE) {
953 unique_store = (STORE **) malloc(i * sizeof(STORE));
954 /* Find Unique Storage address/port */
955 store = (STORE *)GetNextRes(R_STORAGE, NULL);
957 unique_store[i++] = store;
958 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
960 for (j=0; j<i; j++) {
961 if (strcmp(unique_store[j]->address, store->address) == 0 &&
962 unique_store[j]->SDport == store->SDport) {
968 unique_store[i++] = store;
969 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
974 /* Call each unique Storage daemon */
975 for (j=0; j<i; j++) {
976 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
980 /* Count Client items */
984 foreach_res(client, R_CLIENT) {
987 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
988 /* Find Unique Client address/port */
989 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
991 unique_client[i++] = client;
992 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
994 for (j=0; j<i; j++) {
995 if (strcmp(unique_client[j]->address, client->address) == 0 &&
996 unique_client[j]->FDport == client->FDport) {
1002 unique_client[i++] = client;
1003 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1008 /* Call each unique File daemon */
1009 for (j=0; j<i; j++) {
1010 do_client_setdebug(ua, unique_client[j], level, trace_flag, hangup);
1012 free(unique_client);
1016 * setdebug level=nn all trace=1/0
1018 static int setdebug_cmd(UAContext *ua, const char *cmd)
1023 int trace_flag = -1;
1027 Dmsg1(120, "setdebug:%s:\n", cmd);
1030 i = find_arg_with_value(ua, "level");
1032 level = atoi(ua->argv[i]);
1035 if (!get_pint(ua, _("Enter new debug level: "))) {
1038 level = ua->pint32_val;
1041 /* Look for trace flag. -1 => not change */
1042 i = find_arg_with_value(ua, "trace");
1044 trace_flag = atoi(ua->argv[i]);
1045 if (trace_flag > 0) {
1050 /* Look for hangup (debug only)flag. -1 => not change */
1051 i = find_arg_with_value(ua, "hangup");
1053 hangup = atoi(ua->argv[i]);
1057 /* General debug? */
1058 for (i=1; i<ua->argc; i++) {
1059 if (strcasecmp(ua->argk[i], "all") == 0) {
1060 do_all_setdebug(ua, level, trace_flag, hangup);
1063 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1064 strcasecmp(ua->argk[i], "director") == 0) {
1065 debug_level = level;
1066 set_trace(trace_flag);
1069 if (strcasecmp(ua->argk[i], "client") == 0 ||
1070 strcasecmp(ua->argk[i], "fd") == 0) {
1073 client = GetClientResWithName(ua->argv[i]);
1075 do_client_setdebug(ua, client, level, trace_flag, hangup);
1079 client = select_client_resource(ua);
1081 do_client_setdebug(ua, client, level, trace_flag, hangup);
1086 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1087 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1088 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1091 store = GetStoreResWithName(ua->argv[i]);
1093 do_storage_setdebug(ua, store, level, trace_flag);
1097 store = get_storage_resource(ua, false/*no default*/);
1099 do_storage_setdebug(ua, store, level, trace_flag);
1105 * We didn't find an appropriate keyword above, so
1108 start_prompt(ua, _("Available daemons are: \n"));
1109 add_prompt(ua, _("Director"));
1110 add_prompt(ua, _("Storage"));
1111 add_prompt(ua, _("Client"));
1112 add_prompt(ua, _("All"));
1113 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1114 case 0: /* Director */
1115 debug_level = level;
1116 set_trace(trace_flag);
1119 store = get_storage_resource(ua, false/*no default*/);
1121 do_storage_setdebug(ua, store, level, trace_flag);
1125 client = select_client_resource(ua);
1127 do_client_setdebug(ua, client, level, trace_flag, hangup);
1131 do_all_setdebug(ua, level, trace_flag, hangup);
1140 * Turn debug tracing to file on/off
1142 static int trace_cmd(UAContext *ua, const char *cmd)
1146 if (ua->argc != 2) {
1147 if (!get_cmd(ua, _("Turn on or off? "))) {
1152 onoff = ua->argk[1];
1155 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1160 static int var_cmd(UAContext *ua, const char *cmd)
1162 POOLMEM *val = get_pool_memory(PM_FNAME);
1165 if (!open_client_db(ua)) {
1168 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1171 while (*var == ' ') { /* skip spaces */
1174 Dmsg1(100, "Var=%s:\n", var);
1175 variable_expansion(ua->jcr, var, &val);
1176 ua->send_msg("%s\n", val);
1177 free_pool_memory(val);
1181 static int estimate_cmd(UAContext *ua, const char *cmd)
1184 CLIENT *client = NULL;
1185 FILESET *fileset = NULL;
1187 char since[MAXSTRING];
1191 jcr->setJobLevel(L_FULL);
1192 for (int i=1; i<ua->argc; i++) {
1193 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1194 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1196 client = GetClientResWithName(ua->argv[i]);
1198 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1201 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1202 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1207 ua->error_msg(_("Client name missing.\n"));
1211 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1213 job = GetJobResWithName(ua->argv[i]);
1215 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1218 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1219 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1224 ua->error_msg(_("Job name missing.\n"));
1229 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1231 fileset = GetFileSetResWithName(ua->argv[i]);
1233 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1236 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1237 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1242 ua->error_msg(_("Fileset name missing.\n"));
1246 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1250 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1252 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1253 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1257 ua->error_msg(_("Level value missing.\n"));
1261 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1262 if (!is_yesno(ua->argv[i], &accurate)) {
1263 ua->error_msg(_("Invalid value for accurate. "
1264 "It must be yes or no.\n"));
1268 if (!job && !(client && fileset)) {
1269 if (!(job = select_job_resource(ua))) {
1274 job = GetJobResWithName(ua->argk[1]);
1276 ua->error_msg(_("No job specified.\n"));
1279 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1280 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1285 client = job->client;
1288 fileset = job->fileset;
1290 jcr->client = client;
1291 jcr->fileset = fileset;
1293 if (job->pool->catalog) {
1294 ua->catalog = job->pool->catalog;
1296 ua->catalog = client->catalog;
1304 jcr->setJobType(JT_BACKUP);
1305 init_jcr_job_record(jcr);
1307 if (!get_or_create_client_record(jcr)) {
1310 if (!get_or_create_fileset_record(jcr)) {
1314 get_level_since_time(ua->jcr, since, sizeof(since));
1316 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1317 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1318 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1319 ua->error_msg(_("Failed to connect to Client.\n"));
1323 if (!send_include_list(jcr)) {
1324 ua->error_msg(_("Error sending include list.\n"));
1328 if (!send_exclude_list(jcr)) {
1329 ua->error_msg(_("Error sending exclude list.\n"));
1333 /* The level string change if accurate mode is enabled */
1334 if (accurate >= 0) {
1335 jcr->accurate = accurate;
1337 jcr->accurate = job->accurate;
1340 if (!send_level_command(jcr)) {
1345 * If the job is in accurate mode, we send the list of
1348 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1349 if (!send_accurate_current_files(jcr)) {
1353 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1354 while (jcr->file_bsock->recv() >= 0) {
1355 ua->send_msg("%s", jcr->file_bsock->msg);
1359 if (jcr->file_bsock) {
1360 jcr->file_bsock->signal(BNET_TERMINATE);
1361 jcr->file_bsock->close();
1362 jcr->file_bsock = NULL;
1371 static int time_cmd(UAContext *ua, const char *cmd)
1374 time_t ttime = time(NULL);
1376 (void)localtime_r(&ttime, &tm);
1377 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1378 ua->send_msg("%s\n", sdt);
1383 * reload the conf file
1385 extern "C" void reload_config(int sig);
1387 static int reload_cmd(UAContext *ua, const char *cmd)
1394 * Delete Pool records (should purge Media with it).
1396 * delete pool=<pool-name>
1397 * delete volume pool=<pool-name> volume=<name>
1400 static int delete_cmd(UAContext *ua, const char *cmd)
1402 static const char *keywords[] = {
1408 if (!open_client_db(ua)) {
1412 switch (find_arg_keyword(ua, keywords)) {
1421 while ((i=find_arg(ua, "jobid")) > 0) {
1423 *ua->argk[i] = 0; /* zap keyword already visited */
1431 "In general it is not a good idea to delete either a\n"
1432 "Pool or a Volume since they may contain data.\n\n"));
1434 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1445 ua->warning_msg(_("Nothing done.\n"));
1453 * delete_job has been modified to parse JobID lists like the
1455 * delete JobID=3,4,6,7-11,14
1457 * Thanks to Phil Stracchino for the above addition.
1460 static void delete_job(UAContext *ua)
1465 int i = find_arg_with_value(ua, NT_("jobid"));
1467 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1468 s = bstrdup(ua->argv[i]);
1471 * We could use strtok() here. But we're not going to, because:
1472 * (a) strtok() is deprecated, having been replaced by strsep();
1473 * (b) strtok() is broken in significant ways.
1474 * we could use strsep() instead, but it's not universally available.
1475 * so we grow our own using strchr().
1477 sep = strchr(tok, ',');
1478 while (sep != NULL) {
1480 if (!delete_job_id_range(ua, tok)) {
1481 JobId = str_to_int64(tok);
1482 do_job_delete(ua, JobId);
1485 sep = strchr(tok, ',');
1487 /* pick up the last token */
1488 if (!delete_job_id_range(ua, tok)) {
1489 JobId = str_to_int64(tok);
1490 do_job_delete(ua, JobId);
1495 JobId = str_to_int64(ua->argv[i]);
1496 do_job_delete(ua, JobId);
1498 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1501 JobId = ua->int64_val;
1502 do_job_delete(ua, JobId);
1507 * we call delete_job_id_range to parse range tokens and iterate over ranges
1509 static bool delete_job_id_range(UAContext *ua, char *tok)
1514 tok2 = strchr(tok, '-');
1520 j1 = str_to_int64(tok);
1521 j2 = str_to_int64(tok2);
1522 for (j=j1; j<=j2; j++) {
1523 do_job_delete(ua, j);
1529 * do_job_delete now performs the actual delete operation atomically
1531 static void do_job_delete(UAContext *ua, JobId_t JobId)
1535 edit_int64(JobId, ed1);
1536 purge_jobs_from_catalog(ua, ed1);
1537 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1541 * Delete media records from database -- dangerous
1543 static int delete_volume(UAContext *ua)
1549 if (!select_media_dbr(ua, &mr)) {
1552 ua->warning_msg(_("\nThis command will delete volume %s\n"
1553 "and all Jobs saved on that volume from the Catalog\n"),
1556 if (find_arg(ua, "yes") >= 0) {
1557 ua->pint32_val = 1; /* Have "yes" on command line already" */
1559 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1561 if (!get_yesno(ua, buf)) {
1565 if (!ua->pint32_val) {
1569 /* If not purged, do it */
1570 if (strcmp(mr.VolStatus, "Purged") != 0) {
1571 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1572 ua->error_msg(_("Can't list jobs on this volume\n"));
1576 purge_jobs_from_catalog(ua, lst.list);
1580 db_delete_media_record(ua->jcr, ua->db, &mr);
1585 * Delete a pool record from the database -- dangerous
1587 static int delete_pool(UAContext *ua)
1592 memset(&pr, 0, sizeof(pr));
1594 if (!get_pool_dbr(ua, &pr)) {
1597 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1599 if (!get_yesno(ua, buf)) {
1602 if (ua->pint32_val) {
1603 db_delete_pool_record(ua->jcr, ua->db, &pr);
1608 int memory_cmd(UAContext *ua, const char *cmd)
1610 garbage_collect_memory();
1611 list_dir_status_header(ua);
1612 sm_dump(false, true);
1616 static void do_mount_cmd(UAContext *ua, const char *command)
1621 char dev_name[MAX_NAME_LENGTH];
1625 if (!open_client_db(ua)) {
1628 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1630 store.store = get_storage_resource(ua, true/*arg is storage*/);
1634 pm_strcpy(store.store_source, _("unknown source"));
1635 set_wstorage(jcr, &store);
1636 drive = get_storage_drive(ua, store.store);
1637 if (strcmp(command, "mount") == 0) {
1638 slot = get_storage_slot(ua, store.store);
1641 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1642 store.store->media_type, store.store->dev_name(), drive);
1644 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1645 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1648 sd = jcr->store_bsock;
1649 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1650 bash_spaces(dev_name);
1652 sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1654 sd->fsend("%s %s drive=%d", command, dev_name, drive);
1656 while (sd->recv() >= 0) {
1657 ua->send_msg("%s", sd->msg);
1659 sd->signal(BNET_TERMINATE);
1661 jcr->store_bsock = NULL;
1665 * mount [storage=<name>] [drive=nn] [slot=mm]
1667 static int mount_cmd(UAContext *ua, const char *cmd)
1669 do_mount_cmd(ua, "mount"); /* mount */
1675 * unmount [storage=<name>] [drive=nn]
1677 static int unmount_cmd(UAContext *ua, const char *cmd)
1679 do_mount_cmd(ua, "unmount"); /* unmount */
1685 * release [storage=<name>] [drive=nn]
1687 static int release_cmd(UAContext *ua, const char *cmd)
1689 do_mount_cmd(ua, "release"); /* release */
1696 * use catalog=<name>
1698 static int use_cmd(UAContext *ua, const char *cmd)
1700 CAT *oldcatalog, *catalog;
1703 close_db(ua); /* close any previously open db */
1704 oldcatalog = ua->catalog;
1706 if (!(catalog = get_catalog_resource(ua))) {
1707 ua->catalog = oldcatalog;
1709 ua->catalog = catalog;
1712 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1713 ua->catalog->name(), ua->catalog->db_name);
1718 int quit_cmd(UAContext *ua, const char *cmd)
1724 /* Handler to get job status */
1725 static int status_handler(void *ctx, int num_fields, char **row)
1727 char *val = (char *)ctx;
1732 *val = '?'; /* Unknown by default */
1739 * Wait until no job is running
1741 int wait_cmd(UAContext *ua, const char *cmd)
1745 time_t stop_time = 0;
1749 * Wait until no job is running
1751 if (ua->argc == 1) {
1752 bmicrosleep(0, 200000); /* let job actually start */
1753 for (bool running=true; running; ) {
1756 if (jcr->JobId != 0) {
1770 i = find_arg_with_value(ua, NT_("timeout"));
1771 if (i > 0 && ua->argv[i]) {
1772 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1775 /* we have jobid, jobname or ujobid argument */
1777 uint32_t jobid = 0 ;
1779 if (!open_client_db(ua)) {
1780 ua->error_msg(_("ERR: Can't open db\n")) ;
1784 for (int i=1; i<ua->argc; i++) {
1785 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1789 jobid = str_to_int64(ua->argv[i]);
1791 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1792 strcasecmp(ua->argk[i], "job") == 0) {
1796 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1798 jobid = jcr->JobId ;
1802 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1806 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1808 jobid = jcr->JobId ;
1812 /* Wait for a mount request */
1813 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1814 for (bool waiting=false; !waiting; ) {
1816 if (jcr->JobId != 0 &&
1817 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1826 if (stop_time && (time(NULL) >= stop_time)) {
1827 ua->warning_msg(_("Wait on mount timed out\n"));
1837 ua->error_msg(_("ERR: Job was not found\n"));
1842 * We wait the end of a specific job
1845 bmicrosleep(0, 200000); /* let job actually start */
1846 for (bool running=true; running; ) {
1849 jcr=get_jcr_by_id(jobid) ;
1862 * We have to get JobStatus
1866 char jobstatus = '?'; /* Unknown by default */
1869 bsnprintf(buf, sizeof(buf),
1870 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1873 db_sql_query(ua->db, buf,
1874 status_handler, (void *)&jobstatus);
1876 switch (jobstatus) {
1878 status = 1 ; /* Warning */
1882 case JS_ErrorTerminated:
1884 status = 2 ; /* Critical */
1889 status = 0 ; /* Ok */
1893 status = 3 ; /* Unknown */
1897 ua->send_msg("JobId=%i\n", jobid) ;
1898 ua->send_msg("JobStatus=%s (%c)\n",
1899 job_status_to_str(jobstatus),
1902 if (ua->gui || ua->api) {
1903 ua->send_msg("ExitStatus=%i\n", status) ;
1910 static int help_cmd(UAContext *ua, const char *cmd)
1913 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1914 for (i=0; i<comsize; i++) {
1915 if (ua->argc == 2) {
1916 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1917 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1918 commands[i].help, commands[i].usage);
1922 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1925 if (i == comsize && ua->argc == 2) {
1926 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1928 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1932 int qhelp_cmd(UAContext *ua, const char *cmd)
1935 /* Want to display only commands */
1936 j = find_arg(ua, NT_("all"));
1938 for (i=0; i<comsize; i++) {
1939 ua->send_msg("%s\n", commands[i].key);
1943 /* Want to display a specific help section */
1944 j = find_arg_with_value(ua, NT_("item"));
1945 if (j >= 0 && ua->argk[j]) {
1946 for (i=0; i<comsize; i++) {
1947 if (bstrcmp(commands[i].key, ua->argv[j])) {
1948 ua->send_msg("%s\n", commands[i].usage);
1954 /* Want to display everything */
1955 for (i=0; i<comsize; i++) {
1956 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1962 static int version_cmd(UAContext *ua, const char *cmd)
1964 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1965 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1970 * Test code -- turned on only for debug testing
1972 static int version_cmd(UAContext *ua, const char *cmd)
1975 POOL_MEM query(PM_MESSAGE);
1977 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1978 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1979 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1980 for (int i=0; i < ids.num_ids; i++) {
1981 ua->send_msg("id=%d\n", ids.DBId[i]);
1989 * This call uses open_client_db() and force a
1990 * new dedicated connection to the catalog
1992 bool open_new_client_db(UAContext *ua)
1996 /* Force a new dedicated connection */
1998 ua->force_mult_db_connections = true;
1999 ret = open_client_db(ua);
2000 ua->force_mult_db_connections = false;
2005 * This call explicitly checks for a catalog=xxx and
2006 * if given, opens that catalog. It also checks for
2007 * client=xxx and if found, opens the catalog
2008 * corresponding to that client. If we still don't
2009 * have a catalog, look for a Job keyword and get the
2010 * catalog from its client record.
2012 bool open_client_db(UAContext *ua)
2019 /* Try for catalog keyword */
2020 i = find_arg_with_value(ua, NT_("catalog"));
2022 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2023 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2026 catalog = GetCatalogResWithName(ua->argv[i]);
2028 if (ua->catalog && ua->catalog != catalog) {
2031 ua->catalog = catalog;
2036 /* Try for client keyword */
2037 i = find_arg_with_value(ua, NT_("client"));
2039 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2040 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2043 client = GetClientResWithName(ua->argv[i]);
2045 catalog = client->catalog;
2046 if (ua->catalog && ua->catalog != catalog) {
2049 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2050 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2053 ua->catalog = catalog;
2058 /* Try for Job keyword */
2059 i = find_arg_with_value(ua, NT_("job"));
2061 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2062 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2065 job = GetJobResWithName(ua->argv[i]);
2067 catalog = job->client->catalog;
2068 if (ua->catalog && ua->catalog != catalog) {
2071 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2072 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2075 ua->catalog = catalog;
2085 * Open the catalog database.
2087 bool open_db(UAContext *ua)
2095 ua->catalog = get_catalog_resource(ua);
2097 ua->error_msg( _("Could not find a Catalog resource\n"));
2102 /* Some modules like bvfs need their own catalog connection */
2103 mult_db_conn = ua->catalog->mult_db_connections;
2104 if (ua->force_mult_db_connections) {
2105 mult_db_conn = true;
2108 ua->jcr->catalog = ua->catalog;
2110 Dmsg0(100, "UA Open database\n");
2111 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2112 ua->catalog->db_user,
2113 ua->catalog->db_password, ua->catalog->db_address,
2114 ua->catalog->db_port, ua->catalog->db_socket,
2115 mult_db_conn, ua->catalog->disable_batch_insert);
2116 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2117 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2118 ua->catalog->db_name);
2120 ua->error_msg("%s", db_strerror(ua->db));
2125 ua->jcr->db = ua->db;
2127 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2129 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2133 void close_db(UAContext *ua)
2136 db_close_database(ua->jcr, ua->db);