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-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 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1346 bnet_close(jcr->file_bsock);
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)
1533 if (!select_media_dbr(ua, &mr)) {
1536 ua->warning_msg(_("\nThis command will delete volume %s\n"
1537 "and all Jobs saved on that volume from the Catalog\n"),
1540 if (find_arg(ua, "yes") >= 0) {
1541 ua->pint32_val = 1; /* Have "yes" on command line already" */
1543 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1545 if (!get_yesno(ua, buf)) {
1549 if (ua->pint32_val) {
1550 db_delete_media_record(ua->jcr, ua->db, &mr);
1556 * Delete a pool record from the database -- dangerous
1558 static int delete_pool(UAContext *ua)
1563 memset(&pr, 0, sizeof(pr));
1565 if (!get_pool_dbr(ua, &pr)) {
1568 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1570 if (!get_yesno(ua, buf)) {
1573 if (ua->pint32_val) {
1574 db_delete_pool_record(ua->jcr, ua->db, &pr);
1579 int memory_cmd(UAContext *ua, const char *cmd)
1581 list_dir_status_header(ua);
1582 sm_dump(false, true);
1586 static void do_mount_cmd(UAContext *ua, const char *command)
1591 char dev_name[MAX_NAME_LENGTH];
1595 if (!open_client_db(ua)) {
1598 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1600 store.store = get_storage_resource(ua, true/*arg is storage*/);
1604 pm_strcpy(store.store_source, _("unknown source"));
1605 set_wstorage(jcr, &store);
1606 drive = get_storage_drive(ua, store.store);
1607 if (strcmp(command, "mount") == 0) {
1608 slot = get_storage_slot(ua, store.store);
1611 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1612 store.store->media_type, store.store->dev_name(), drive);
1614 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1615 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1618 sd = jcr->store_bsock;
1619 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1620 bash_spaces(dev_name);
1622 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1624 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1626 while (bnet_recv(sd) >= 0) {
1627 ua->send_msg("%s", sd->msg);
1629 bnet_sig(sd, BNET_TERMINATE);
1631 jcr->store_bsock = NULL;
1635 * mount [storage=<name>] [drive=nn] [slot=mm]
1637 static int mount_cmd(UAContext *ua, const char *cmd)
1639 do_mount_cmd(ua, "mount"); /* mount */
1645 * unmount [storage=<name>] [drive=nn]
1647 static int unmount_cmd(UAContext *ua, const char *cmd)
1649 do_mount_cmd(ua, "unmount"); /* unmount */
1655 * release [storage=<name>] [drive=nn]
1657 static int release_cmd(UAContext *ua, const char *cmd)
1659 do_mount_cmd(ua, "release"); /* release */
1666 * use catalog=<name>
1668 static int use_cmd(UAContext *ua, const char *cmd)
1670 CAT *oldcatalog, *catalog;
1673 close_db(ua); /* close any previously open db */
1674 oldcatalog = ua->catalog;
1676 if (!(catalog = get_catalog_resource(ua))) {
1677 ua->catalog = oldcatalog;
1679 ua->catalog = catalog;
1682 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1683 ua->catalog->name(), ua->catalog->db_name);
1688 int quit_cmd(UAContext *ua, const char *cmd)
1694 /* Handler to get job status */
1695 static int status_handler(void *ctx, int num_fields, char **row)
1697 char *val = (char *)ctx;
1702 *val = '?'; /* Unknown by default */
1709 * Wait until no job is running
1711 int wait_cmd(UAContext *ua, const char *cmd)
1715 time_t stop_time = 0;
1719 * Wait until no job is running
1721 if (ua->argc == 1) {
1722 bmicrosleep(0, 200000); /* let job actually start */
1723 for (bool running=true; running; ) {
1726 if (jcr->JobId != 0) {
1740 i = find_arg_with_value(ua, NT_("timeout"));
1741 if (i > 0 && ua->argv[i]) {
1742 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1745 /* we have jobid, jobname or ujobid argument */
1747 uint32_t jobid = 0 ;
1749 if (!open_client_db(ua)) {
1750 ua->error_msg(_("ERR: Can't open db\n")) ;
1754 for (int i=1; i<ua->argc; i++) {
1755 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1759 jobid = str_to_int64(ua->argv[i]);
1761 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1762 strcasecmp(ua->argk[i], "job") == 0) {
1766 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1768 jobid = jcr->JobId ;
1772 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1776 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1778 jobid = jcr->JobId ;
1782 /* Wait for a mount request */
1783 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1784 for (bool waiting=false; !waiting; ) {
1786 if (jcr->JobId != 0 &&
1787 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1796 if (stop_time && (time(NULL) >= stop_time)) {
1797 ua->warning_msg(_("Wait on mount timed out\n"));
1807 ua->error_msg(_("ERR: Job was not found\n"));
1812 * We wait the end of a specific job
1815 bmicrosleep(0, 200000); /* let job actually start */
1816 for (bool running=true; running; ) {
1819 jcr=get_jcr_by_id(jobid) ;
1832 * We have to get JobStatus
1836 char jobstatus = '?'; /* Unknown by default */
1839 bsnprintf(buf, sizeof(buf),
1840 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1843 db_sql_query(ua->db, buf,
1844 status_handler, (void *)&jobstatus);
1846 switch (jobstatus) {
1848 status = 1 ; /* Warning */
1852 case JS_ErrorTerminated:
1854 status = 2 ; /* Critical */
1859 status = 0 ; /* Ok */
1863 status = 3 ; /* Unknown */
1867 ua->send_msg("JobId=%i\n", jobid) ;
1868 ua->send_msg("JobStatus=%s (%c)\n",
1869 job_status_to_str(jobstatus),
1872 if (ua->gui || ua->api) {
1873 ua->send_msg("ExitStatus=%i\n", status) ;
1880 static int help_cmd(UAContext *ua, const char *cmd)
1883 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1884 for (i=0; i<comsize; i++) {
1885 if (ua->argc == 2) {
1886 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1887 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1888 commands[i].help, commands[i].usage);
1892 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1895 if (i == comsize && ua->argc == 2) {
1896 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1898 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1902 int qhelp_cmd(UAContext *ua, const char *cmd)
1905 /* Want to display only commands */
1906 j = find_arg(ua, NT_("all"));
1908 for (i=0; i<comsize; i++) {
1909 ua->send_msg("%s\n", commands[i].key);
1913 /* Want to display a specific help section */
1914 j = find_arg_with_value(ua, NT_("item"));
1915 if (j >= 0 && ua->argk[j]) {
1916 for (i=0; i<comsize; i++) {
1917 if (bstrcmp(commands[i].key, ua->argv[j])) {
1918 ua->send_msg("%s\n", commands[i].usage);
1924 /* Want to display everything */
1925 for (i=0; i<comsize; i++) {
1926 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1932 static int version_cmd(UAContext *ua, const char *cmd)
1934 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1935 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1940 * Test code -- turned on only for debug testing
1942 static int version_cmd(UAContext *ua, const char *cmd)
1945 POOL_MEM query(PM_MESSAGE);
1947 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1948 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1949 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1950 for (int i=0; i < ids.num_ids; i++) {
1951 ua->send_msg("id=%d\n", ids.DBId[i]);
1959 * This call uses open_client_db() and force a
1960 * new dedicated connection to the catalog
1962 bool open_new_client_db(UAContext *ua)
1966 /* Force a new dedicated connection */
1968 ua->force_mult_db_connections = true;
1969 ret = open_client_db(ua);
1970 ua->force_mult_db_connections = false;
1975 * This call explicitly checks for a catalog=xxx and
1976 * if given, opens that catalog. It also checks for
1977 * client=xxx and if found, opens the catalog
1978 * corresponding to that client. If we still don't
1979 * have a catalog, look for a Job keyword and get the
1980 * catalog from its client record.
1982 bool open_client_db(UAContext *ua)
1989 /* Try for catalog keyword */
1990 i = find_arg_with_value(ua, NT_("catalog"));
1992 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1993 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1996 catalog = GetCatalogResWithName(ua->argv[i]);
1998 if (ua->catalog && ua->catalog != catalog) {
2001 ua->catalog = catalog;
2006 /* Try for client keyword */
2007 i = find_arg_with_value(ua, NT_("client"));
2009 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2010 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2013 client = GetClientResWithName(ua->argv[i]);
2015 catalog = client->catalog;
2016 if (ua->catalog && ua->catalog != catalog) {
2019 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2020 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2023 ua->catalog = catalog;
2028 /* Try for Job keyword */
2029 i = find_arg_with_value(ua, NT_("job"));
2031 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2032 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2035 job = GetJobResWithName(ua->argv[i]);
2037 catalog = job->client->catalog;
2038 if (ua->catalog && ua->catalog != catalog) {
2041 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2042 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2045 ua->catalog = catalog;
2055 * Open the catalog database.
2057 bool open_db(UAContext *ua)
2065 ua->catalog = get_catalog_resource(ua);
2067 ua->error_msg( _("Could not find a Catalog resource\n"));
2072 /* Some modules like bvfs need their own catalog connection */
2073 mult_db_conn = ua->catalog->mult_db_connections;
2074 if (ua->force_mult_db_connections) {
2075 mult_db_conn = true;
2078 ua->jcr->catalog = ua->catalog;
2080 Dmsg0(100, "UA Open database\n");
2081 ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2082 ua->catalog->db_user,
2083 ua->catalog->db_password, ua->catalog->db_address,
2084 ua->catalog->db_port, ua->catalog->db_socket,
2086 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2087 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2088 ua->catalog->db_name);
2090 ua->error_msg("%s", db_strerror(ua->db));
2095 ua->jcr->db = ua->db;
2097 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2099 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2103 void close_db(UAContext *ua)
2106 db_close_database(ua->jcr, ua->db);