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> "
161 "\n\tcomment=<text> jobid=<jobid> done select all"), false},
163 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
164 NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
166 { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name>"), false},
167 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
168 { NT_("run"), run_cmd, _("Run a job"),
169 NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
170 "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tcomment=<text> yes"), false},
172 { NT_("status"), status_cmd, _("Report status"),
173 NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
175 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
176 NT_("level=<nn> trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
178 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
179 { NT_("show"), show_cmd, _("Show resource records"),
180 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | disabled | all"), true},
182 { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
183 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
184 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
185 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
186 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
188 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
189 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
191 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
192 NT_("stats\n\tpool=<poolname>\n\tslots storage=<storage> scan"
193 "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
194 "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
195 "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
196 "\n\t enable=<yes/no> recyclepool=<pool> actiononpurge=<action>"),true},
197 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_(""), false},
198 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
199 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
200 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
201 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
204 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
207 * Execute a command from the UA
209 bool do_a_command(UAContext *ua)
215 BSOCK *user = ua->UA_sock;
218 Dmsg1(900, "Command: %s\n", ua->argk[0]);
223 while (ua->jcr->wstorage->size()) {
224 ua->jcr->wstorage->remove(0);
227 len = strlen(ua->argk[0]);
228 for (i=0; i<comsize; i++) { /* search for command */
229 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
230 /* Check if command permitted, but "quit" is always OK */
231 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
232 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
235 /* Check if this command is authorized in RunScript */
236 if (ua->runscript && !commands[i].use_in_rs) {
237 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
240 if (ua->api) user->signal(BNET_CMD_BEGIN);
241 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
242 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
248 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
255 * This is a common routine used to stuff the Pool DB record defaults
256 * into the Media DB record just before creating a media (Volume)
259 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
261 mr->PoolId = pr->PoolId;
262 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
263 mr->Recycle = pr->Recycle;
264 mr->VolRetention = pr->VolRetention;
265 mr->VolUseDuration = pr->VolUseDuration;
266 mr->ActionOnPurge = pr->ActionOnPurge;
267 mr->RecyclePoolId = pr->RecyclePoolId;
268 mr->MaxVolJobs = pr->MaxVolJobs;
269 mr->MaxVolFiles = pr->MaxVolFiles;
270 mr->MaxVolBytes = pr->MaxVolBytes;
271 mr->LabelType = pr->LabelType;
277 * Add Volumes to an existing Pool
279 static int add_cmd(UAContext *ua, const char *cmd)
283 int num, i, max, startnum;
285 char name[MAX_NAME_LENGTH];
287 int Slot = 0, InChanger = 0;
290 "You probably don't want to be using this command since it\n"
291 "creates database records without labeling the Volumes.\n"
292 "You probably want to use the \"label\" command.\n\n"));
294 if (!open_client_db(ua)) {
298 memset(&pr, 0, sizeof(pr));
299 memset(&mr, 0, sizeof(mr));
301 if (!get_pool_dbr(ua, &pr)) {
305 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
306 pr.MaxVols, pr.PoolType);
308 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
309 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
310 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
313 pr.MaxVols = ua->pint32_val;
317 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
318 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
319 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
323 if (pr.MaxVols == 0) {
326 max = pr.MaxVols - pr.NumVols;
330 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
331 if (!get_pint(ua, buf)) {
334 num = ua->pint32_val;
335 if (num < 0 || num > max) {
336 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
344 if (!get_cmd(ua, _("Enter Volume name: "))) {
348 if (!get_cmd(ua, _("Enter base volume name: "))) {
352 /* Don't allow | in Volume name because it is the volume separator character */
353 if (!is_volume_name_legal(ua, ua->cmd)) {
356 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
357 ua->warning_msg(_("Volume name too long.\n"));
360 if (strlen(ua->cmd) == 0) {
361 ua->warning_msg(_("Volume name must be at least one character long.\n"));
367 bstrncpy(name, ua->cmd, sizeof(name));
369 bstrncat(name, "%04d", sizeof(name));
372 if (!get_pint(ua, _("Enter the starting number: "))) {
375 startnum = ua->pint32_val;
377 ua->warning_msg(_("Start number must be greater than zero.\n"));
387 if (store && store->autochanger) {
388 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
391 Slot = ua->pint32_val;
392 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
395 InChanger = ua->pint32_val;
398 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
399 for (i=startnum; i < num+startnum; i++) {
400 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
402 mr.InChanger = InChanger;
403 mr.StorageId = store->StorageId;
405 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
406 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
407 ua->error_msg("%s", db_strerror(ua->db));
411 first_id = mr.PoolId;
415 Dmsg0(200, "Update pool record.\n");
416 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
417 ua->warning_msg("%s", db_strerror(ua->db));
420 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
426 * Turn auto mount on/off
431 int automount_cmd(UAContext *ua, const char *cmd)
436 if (!get_cmd(ua, _("Turn on or off? "))) {
444 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
452 static int cancel_cmd(UAContext *ua, const char *cmd)
457 char JobName[MAX_NAME_LENGTH];
459 for (i=1; i<ua->argc; i++) {
460 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
462 JobId = str_to_int64(ua->argv[i]);
466 if (!(jcr=get_jcr_by_id(JobId))) {
467 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
471 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
475 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
476 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
477 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
478 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
481 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
485 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
486 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
487 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
488 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
495 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
496 ua->error_msg(_("Unauthorized command from this console.\n"));
501 * If we still do not have a jcr,
502 * throw up a list and ask the user to select one.
505 int tjobs = 0; /* total # number jobs */
506 /* Count Jobs running */
508 if (jcr->JobId == 0) { /* this is us */
511 tjobs++; /* count of all jobs */
512 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
513 continue; /* skip not authorized */
515 njobs++; /* count of authorized jobs */
519 if (njobs == 0) { /* no authorized */
521 ua->send_msg(_("No Jobs running.\n"));
523 ua->send_msg(_("None of your jobs are running.\n"));
528 start_prompt(ua, _("Select Job:\n"));
531 if (jcr->JobId == 0) { /* this is us */
534 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
535 continue; /* skip not authorized */
537 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
542 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
545 if (ua->api && njobs == 1) {
547 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
548 _("Confirm cancel?"));
549 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
554 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
559 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
560 jcr = get_jcr_by_full_name(JobName);
562 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
567 ret = cancel_job(ua, jcr);
573 * This is a common routine to create or update a
574 * Pool DB base record from a Pool Resource. We handle
575 * the setting of MaxVols and NumVols slightly differently
576 * depending on if we are creating the Pool or we are
577 * simply bringing it into agreement with the resource (updage).
579 * Caution : RecyclePoolId isn't setup in this function.
580 * You can use set_pooldbr_recyclepoolid();
583 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
585 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
586 if (op == POOL_OP_CREATE) {
587 pr->MaxVols = pool->max_volumes;
589 } else { /* update pool */
590 if (pr->MaxVols != pool->max_volumes) {
591 pr->MaxVols = pool->max_volumes;
593 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
594 pr->MaxVols = pr->NumVols;
597 pr->LabelType = pool->LabelType;
598 pr->UseOnce = pool->use_volume_once;
599 pr->UseCatalog = pool->use_catalog;
600 pr->Recycle = pool->Recycle;
601 pr->VolRetention = pool->VolRetention;
602 pr->VolUseDuration = pool->VolUseDuration;
603 pr->MaxVolJobs = pool->MaxVolJobs;
604 pr->MaxVolFiles = pool->MaxVolFiles;
605 pr->MaxVolBytes = pool->MaxVolBytes;
606 pr->AutoPrune = pool->AutoPrune;
607 pr->ActionOnPurge = pool->action_on_purge;
608 pr->Recycle = pool->Recycle;
609 if (pool->label_format) {
610 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
612 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
616 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
617 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
621 if (!pool->RecyclePool && !pool->ScratchPool) {
625 memset(&pr, 0, sizeof(POOL_DBR));
626 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
628 if (!db_get_pool_record(jcr, db, &pr)) {
629 return -1; /* not exists in database */
632 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
634 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
635 return -1; /* error */
638 if (!db_update_pool_record(jcr, db, &pr)) {
639 return -1; /* error */
644 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
645 * works with set_pooldbr_from_poolres
647 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
652 if (pool->RecyclePool) {
653 memset(&rpool, 0, sizeof(POOL_DBR));
655 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
656 if (db_get_pool_record(jcr, db, &rpool)) {
657 pr->RecyclePoolId = rpool.PoolId;
659 Jmsg(jcr, M_WARNING, 0,
660 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
661 "Try to update it with 'update pool=%s'\n"),
662 pool->name(), rpool.Name, rpool.Name,pool->name());
666 } else { /* no RecyclePool used, set it to 0 */
667 pr->RecyclePoolId = 0;
670 if (pool->ScratchPool) {
671 memset(&rpool, 0, sizeof(POOL_DBR));
673 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
674 if (db_get_pool_record(jcr, db, &rpool)) {
675 pr->ScratchPoolId = rpool.PoolId;
677 Jmsg(jcr, M_WARNING, 0,
678 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
679 "Try to update it with 'update pool=%s'\n"),
680 pool->name(), rpool.Name, rpool.Name,pool->name());
683 } else { /* no ScratchPool used, set it to 0 */
684 pr->ScratchPoolId = 0;
692 * Create a pool record from a given Pool resource
693 * Also called from backup.c
694 * Returns: -1 on error
695 * 0 record already exists
699 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
703 memset(&pr, 0, sizeof(POOL_DBR));
705 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
707 if (db_get_pool_record(jcr, db, &pr)) {
709 if (op == POOL_OP_UPDATE) { /* update request */
710 set_pooldbr_from_poolres(&pr, pool, op);
711 set_pooldbr_references(jcr, db, &pr, pool);
712 db_update_pool_record(jcr, db, &pr);
714 return 0; /* exists */
717 set_pooldbr_from_poolres(&pr, pool, op);
718 set_pooldbr_references(jcr, db, &pr, pool);
720 if (!db_create_pool_record(jcr, db, &pr)) {
721 return -1; /* error */
729 * Create a Pool Record in the database.
730 * It is always created from the Resource record.
732 static int create_cmd(UAContext *ua, const char *cmd)
736 if (!open_client_db(ua)) {
740 pool = get_pool_resource(ua);
745 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
747 ua->error_msg(_("Error: Pool %s already exists.\n"
748 "Use update to change it.\n"), pool->name());
752 ua->error_msg("%s", db_strerror(ua->db));
758 ua->send_msg(_("Pool %s created.\n"), pool->name());
763 extern DIRRES *director;
764 extern char *configfile;
767 * Python control command
768 * python restart (restarts interpreter)
770 static int python_cmd(UAContext *ua, const char *cmd)
773 init_python_interpreter_args python_args;
775 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
776 term_python_interpreter();
778 python_args.progname = director->name();
779 python_args.scriptdir = director->scripts_directory;
780 python_args.modulename = "DirStartUp";
781 python_args.configfile = configfile;
782 python_args.workingdir = director->working_directory;
783 python_args.job_getattr = job_getattr;
784 python_args.job_setattr = job_setattr;
786 init_python_interpreter(&python_args);
788 ua->send_msg(_("Python interpreter restarted.\n"));
790 #endif /* HAVE_PYTHON */
791 ua->warning_msg(_("Nothing done.\n"));
794 #endif /* HAVE_PYTHON */
800 * Set a new address in a Client resource. We do this only
801 * if the Console name is the same as the Client name
802 * and the Console can access the client.
804 static int setip_cmd(UAContext *ua, const char *cmd)
808 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
809 ua->error_msg(_("Unauthorized command from this console.\n"));
813 client = GetClientResWithName(ua->cons->name());
816 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
819 if (client->address) {
820 free(client->address);
822 /* MA Bug 6 remove ifdef */
823 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
824 client->address = bstrdup(buf);
825 ua->send_msg(_("Client \"%s\" address set to %s\n"),
826 client->name(), client->address);
833 static void do_en_disable_cmd(UAContext *ua, bool setting)
838 i = find_arg_with_value(ua, NT_("job"));
840 job = select_enable_disable_job_resource(ua, setting);
846 job = GetJobResWithName(ua->argv[i]);
850 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
854 if (!acl_access_ok(ua, Job_ACL, job->name())) {
855 ua->error_msg(_("Unauthorized command from this console.\n"));
858 job->enabled = setting;
859 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
863 static int enable_cmd(UAContext *ua, const char *cmd)
865 do_en_disable_cmd(ua, true);
869 static int disable_cmd(UAContext *ua, const char *cmd)
871 do_en_disable_cmd(ua, false);
876 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
882 lstore.store = store;
883 pm_strcpy(lstore.store_source, _("unknown source"));
884 set_wstorage(jcr, &lstore);
885 /* Try connecting for up to 15 seconds */
886 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
887 store->name(), store->address, store->SDport);
888 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
889 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
892 Dmsg0(120, _("Connected to storage daemon\n"));
893 sd = jcr->store_bsock;
894 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
895 if (sd->recv() >= 0) {
896 ua->send_msg("%s", sd->msg);
898 sd->signal(BNET_TERMINATE);
900 jcr->store_bsock = NULL;
904 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
908 /* Connect to File daemon */
910 ua->jcr->client = client;
911 /* Try to connect for 15 seconds */
912 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
913 client->name(), client->address, client->FDport);
914 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
915 ua->error_msg(_("Failed to connect to Client.\n"));
918 Dmsg0(120, "Connected to file daemon\n");
919 fd = ua->jcr->file_bsock;
920 fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
921 if (fd->recv() >= 0) {
922 ua->send_msg("%s", fd->msg);
924 fd->signal(BNET_TERMINATE);
926 ua->jcr->file_bsock = NULL;
931 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
933 STORE *store, **unique_store;
934 CLIENT *client, **unique_client;
940 /* Count Storage items */
944 foreach_res(store, R_STORAGE) {
947 unique_store = (STORE **) malloc(i * sizeof(STORE));
948 /* Find Unique Storage address/port */
949 store = (STORE *)GetNextRes(R_STORAGE, NULL);
951 unique_store[i++] = store;
952 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
954 for (j=0; j<i; j++) {
955 if (strcmp(unique_store[j]->address, store->address) == 0 &&
956 unique_store[j]->SDport == store->SDport) {
962 unique_store[i++] = store;
963 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
968 /* Call each unique Storage daemon */
969 for (j=0; j<i; j++) {
970 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
974 /* Count Client items */
978 foreach_res(client, R_CLIENT) {
981 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
982 /* Find Unique Client address/port */
983 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
985 unique_client[i++] = client;
986 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
988 for (j=0; j<i; j++) {
989 if (strcmp(unique_client[j]->address, client->address) == 0 &&
990 unique_client[j]->FDport == client->FDport) {
996 unique_client[i++] = client;
997 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1002 /* Call each unique File daemon */
1003 for (j=0; j<i; j++) {
1004 do_client_setdebug(ua, unique_client[j], level, trace_flag);
1006 free(unique_client);
1010 * setdebug level=nn all trace=1/0
1012 static int setdebug_cmd(UAContext *ua, const char *cmd)
1017 int trace_flag = -1;
1020 Dmsg1(120, "setdebug:%s:\n", cmd);
1023 i = find_arg_with_value(ua, "level");
1025 level = atoi(ua->argv[i]);
1028 if (!get_pint(ua, _("Enter new debug level: "))) {
1031 level = ua->pint32_val;
1034 /* Look for trace flag. -1 => not change */
1035 i = find_arg_with_value(ua, "trace");
1037 trace_flag = atoi(ua->argv[i]);
1038 if (trace_flag > 0) {
1043 /* General debug? */
1044 for (i=1; i<ua->argc; i++) {
1045 if (strcasecmp(ua->argk[i], "all") == 0) {
1046 do_all_setdebug(ua, level, trace_flag);
1049 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1050 strcasecmp(ua->argk[i], "director") == 0) {
1051 debug_level = level;
1052 set_trace(trace_flag);
1055 if (strcasecmp(ua->argk[i], "client") == 0 ||
1056 strcasecmp(ua->argk[i], "fd") == 0) {
1059 client = GetClientResWithName(ua->argv[i]);
1061 do_client_setdebug(ua, client, level, trace_flag);
1065 client = select_client_resource(ua);
1067 do_client_setdebug(ua, client, level, trace_flag);
1072 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1073 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1074 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1077 store = GetStoreResWithName(ua->argv[i]);
1079 do_storage_setdebug(ua, store, level, trace_flag);
1083 store = get_storage_resource(ua, false/*no default*/);
1085 do_storage_setdebug(ua, store, level, trace_flag);
1091 * We didn't find an appropriate keyword above, so
1094 start_prompt(ua, _("Available daemons are: \n"));
1095 add_prompt(ua, _("Director"));
1096 add_prompt(ua, _("Storage"));
1097 add_prompt(ua, _("Client"));
1098 add_prompt(ua, _("All"));
1099 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1100 case 0: /* Director */
1101 debug_level = level;
1102 set_trace(trace_flag);
1105 store = get_storage_resource(ua, false/*no default*/);
1107 do_storage_setdebug(ua, store, level, trace_flag);
1111 client = select_client_resource(ua);
1113 do_client_setdebug(ua, client, level, trace_flag);
1117 do_all_setdebug(ua, level, trace_flag);
1126 * Turn debug tracing to file on/off
1128 static int trace_cmd(UAContext *ua, const char *cmd)
1132 if (ua->argc != 2) {
1133 if (!get_cmd(ua, _("Turn on or off? "))) {
1138 onoff = ua->argk[1];
1141 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1146 static int var_cmd(UAContext *ua, const char *cmd)
1148 POOLMEM *val = get_pool_memory(PM_FNAME);
1151 if (!open_client_db(ua)) {
1154 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1157 while (*var == ' ') { /* skip spaces */
1160 Dmsg1(100, "Var=%s:\n", var);
1161 variable_expansion(ua->jcr, var, &val);
1162 ua->send_msg("%s\n", val);
1163 free_pool_memory(val);
1167 static int estimate_cmd(UAContext *ua, const char *cmd)
1170 CLIENT *client = NULL;
1171 FILESET *fileset = NULL;
1173 char since[MAXSTRING];
1177 jcr->set_JobLevel(L_FULL);
1178 for (int i=1; i<ua->argc; i++) {
1179 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1180 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1182 client = GetClientResWithName(ua->argv[i]);
1184 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1187 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1188 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1193 ua->error_msg(_("Client name missing.\n"));
1197 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1199 job = GetJobResWithName(ua->argv[i]);
1201 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1204 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1205 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1210 ua->error_msg(_("Job name missing.\n"));
1215 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1217 fileset = GetFileSetResWithName(ua->argv[i]);
1219 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1222 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1223 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1228 ua->error_msg(_("Fileset name missing.\n"));
1232 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1236 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1238 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1239 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1243 ua->error_msg(_("Level value missing.\n"));
1247 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1248 if (!is_yesno(ua->argv[i], &accurate)) {
1249 ua->error_msg(_("Invalid value for accurate. "
1250 "It must be yes or no.\n"));
1254 if (!job && !(client && fileset)) {
1255 if (!(job = select_job_resource(ua))) {
1260 job = GetJobResWithName(ua->argk[1]);
1262 ua->error_msg(_("No job specified.\n"));
1265 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1266 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1271 client = job->client;
1274 fileset = job->fileset;
1276 jcr->client = client;
1277 jcr->fileset = fileset;
1279 if (job->pool->catalog) {
1280 ua->catalog = job->pool->catalog;
1282 ua->catalog = client->catalog;
1290 jcr->set_JobType(JT_BACKUP);
1291 init_jcr_job_record(jcr);
1293 if (!get_or_create_client_record(jcr)) {
1296 if (!get_or_create_fileset_record(jcr)) {
1300 get_level_since_time(ua->jcr, since, sizeof(since));
1302 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1303 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1304 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1305 ua->error_msg(_("Failed to connect to Client.\n"));
1309 if (!send_include_list(jcr)) {
1310 ua->error_msg(_("Error sending include list.\n"));
1314 if (!send_exclude_list(jcr)) {
1315 ua->error_msg(_("Error sending exclude list.\n"));
1319 /* The level string change if accurate mode is enabled */
1320 if (accurate >= 0) {
1321 jcr->accurate = accurate;
1323 jcr->accurate = job->accurate;
1326 if (!send_level_command(jcr)) {
1331 * If the job is in accurate mode, we send the list of
1334 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1335 if (!send_accurate_current_files(jcr)) {
1339 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1340 while (bnet_recv(jcr->file_bsock) >= 0) {
1341 ua->send_msg("%s", jcr->file_bsock->msg);
1345 if (jcr->file_bsock) {
1346 jcr->file_bsock->signal(BNET_TERMINATE);
1347 jcr->file_bsock->close();
1348 jcr->file_bsock = NULL;
1357 static int time_cmd(UAContext *ua, const char *cmd)
1360 time_t ttime = time(NULL);
1362 (void)localtime_r(&ttime, &tm);
1363 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1364 ua->send_msg("%s\n", sdt);
1369 * reload the conf file
1371 extern "C" void reload_config(int sig);
1373 static int reload_cmd(UAContext *ua, const char *cmd)
1380 * Delete Pool records (should purge Media with it).
1382 * delete pool=<pool-name>
1383 * delete volume pool=<pool-name> volume=<name>
1386 static int delete_cmd(UAContext *ua, const char *cmd)
1388 static const char *keywords[] = {
1394 if (!open_client_db(ua)) {
1398 switch (find_arg_keyword(ua, keywords)) {
1407 while ((i=find_arg(ua, "jobid")) > 0) {
1409 *ua->argk[i] = 0; /* zap keyword already visited */
1417 "In general it is not a good idea to delete either a\n"
1418 "Pool or a Volume since they may contain data.\n\n"));
1420 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1431 ua->warning_msg(_("Nothing done.\n"));
1439 * delete_job has been modified to parse JobID lists like the
1441 * delete JobID=3,4,6,7-11,14
1443 * Thanks to Phil Stracchino for the above addition.
1446 static void delete_job(UAContext *ua)
1451 int i = find_arg_with_value(ua, NT_("jobid"));
1453 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1454 s = bstrdup(ua->argv[i]);
1457 * We could use strtok() here. But we're not going to, because:
1458 * (a) strtok() is deprecated, having been replaced by strsep();
1459 * (b) strtok() is broken in significant ways.
1460 * we could use strsep() instead, but it's not universally available.
1461 * so we grow our own using strchr().
1463 sep = strchr(tok, ',');
1464 while (sep != NULL) {
1466 if (!delete_job_id_range(ua, tok)) {
1467 JobId = str_to_int64(tok);
1468 do_job_delete(ua, JobId);
1471 sep = strchr(tok, ',');
1473 /* pick up the last token */
1474 if (!delete_job_id_range(ua, tok)) {
1475 JobId = str_to_int64(tok);
1476 do_job_delete(ua, JobId);
1481 JobId = str_to_int64(ua->argv[i]);
1482 do_job_delete(ua, JobId);
1484 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1487 JobId = ua->int64_val;
1488 do_job_delete(ua, JobId);
1493 * we call delete_job_id_range to parse range tokens and iterate over ranges
1495 static bool delete_job_id_range(UAContext *ua, char *tok)
1500 tok2 = strchr(tok, '-');
1506 j1 = str_to_int64(tok);
1507 j2 = str_to_int64(tok2);
1508 for (j=j1; j<=j2; j++) {
1509 do_job_delete(ua, j);
1515 * do_job_delete now performs the actual delete operation atomically
1517 static void do_job_delete(UAContext *ua, JobId_t JobId)
1521 edit_int64(JobId, ed1);
1522 purge_jobs_from_catalog(ua, ed1);
1523 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1527 * Delete media records from database -- dangerous
1529 static int delete_volume(UAContext *ua)
1535 if (!select_media_dbr(ua, &mr)) {
1538 ua->warning_msg(_("\nThis command will delete volume %s\n"
1539 "and all Jobs saved on that volume from the Catalog\n"),
1542 if (find_arg(ua, "yes") >= 0) {
1543 ua->pint32_val = 1; /* Have "yes" on command line already" */
1545 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1547 if (!get_yesno(ua, buf)) {
1551 if (!ua->pint32_val) {
1555 /* If not purged, do it */
1556 if (strcmp(mr.VolStatus, "Purged") != 0) {
1557 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1558 ua->error_msg(_("Can't list jobs on this volume\n"));
1562 purge_jobs_from_catalog(ua, lst.list);
1566 db_delete_media_record(ua->jcr, ua->db, &mr);
1571 * Delete a pool record from the database -- dangerous
1573 static int delete_pool(UAContext *ua)
1578 memset(&pr, 0, sizeof(pr));
1580 if (!get_pool_dbr(ua, &pr)) {
1583 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1585 if (!get_yesno(ua, buf)) {
1588 if (ua->pint32_val) {
1589 db_delete_pool_record(ua->jcr, ua->db, &pr);
1594 int memory_cmd(UAContext *ua, const char *cmd)
1596 garbage_collect_memory();
1597 list_dir_status_header(ua);
1598 sm_dump(false, true);
1602 static void do_mount_cmd(UAContext *ua, const char *command)
1607 char dev_name[MAX_NAME_LENGTH];
1611 if (!open_client_db(ua)) {
1614 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1616 store.store = get_storage_resource(ua, true/*arg is storage*/);
1620 pm_strcpy(store.store_source, _("unknown source"));
1621 set_wstorage(jcr, &store);
1622 drive = get_storage_drive(ua, store.store);
1623 if (strcmp(command, "mount") == 0) {
1624 slot = get_storage_slot(ua, store.store);
1627 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1628 store.store->media_type, store.store->dev_name(), drive);
1630 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1631 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1634 sd = jcr->store_bsock;
1635 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1636 bash_spaces(dev_name);
1638 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1640 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1642 while (bnet_recv(sd) >= 0) {
1643 ua->send_msg("%s", sd->msg);
1645 bnet_sig(sd, BNET_TERMINATE);
1647 jcr->store_bsock = NULL;
1651 * mount [storage=<name>] [drive=nn] [slot=mm]
1653 static int mount_cmd(UAContext *ua, const char *cmd)
1655 do_mount_cmd(ua, "mount"); /* mount */
1661 * unmount [storage=<name>] [drive=nn]
1663 static int unmount_cmd(UAContext *ua, const char *cmd)
1665 do_mount_cmd(ua, "unmount"); /* unmount */
1671 * release [storage=<name>] [drive=nn]
1673 static int release_cmd(UAContext *ua, const char *cmd)
1675 do_mount_cmd(ua, "release"); /* release */
1682 * use catalog=<name>
1684 static int use_cmd(UAContext *ua, const char *cmd)
1686 CAT *oldcatalog, *catalog;
1689 close_db(ua); /* close any previously open db */
1690 oldcatalog = ua->catalog;
1692 if (!(catalog = get_catalog_resource(ua))) {
1693 ua->catalog = oldcatalog;
1695 ua->catalog = catalog;
1698 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1699 ua->catalog->name(), ua->catalog->db_name);
1704 int quit_cmd(UAContext *ua, const char *cmd)
1710 /* Handler to get job status */
1711 static int status_handler(void *ctx, int num_fields, char **row)
1713 char *val = (char *)ctx;
1718 *val = '?'; /* Unknown by default */
1725 * Wait until no job is running
1727 int wait_cmd(UAContext *ua, const char *cmd)
1731 time_t stop_time = 0;
1735 * Wait until no job is running
1737 if (ua->argc == 1) {
1738 bmicrosleep(0, 200000); /* let job actually start */
1739 for (bool running=true; running; ) {
1742 if (jcr->JobId != 0) {
1756 i = find_arg_with_value(ua, NT_("timeout"));
1757 if (i > 0 && ua->argv[i]) {
1758 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1761 /* we have jobid, jobname or ujobid argument */
1763 uint32_t jobid = 0 ;
1765 if (!open_client_db(ua)) {
1766 ua->error_msg(_("ERR: Can't open db\n")) ;
1770 for (int i=1; i<ua->argc; i++) {
1771 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1775 jobid = str_to_int64(ua->argv[i]);
1777 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1778 strcasecmp(ua->argk[i], "job") == 0) {
1782 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1784 jobid = jcr->JobId ;
1788 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1792 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1794 jobid = jcr->JobId ;
1798 /* Wait for a mount request */
1799 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1800 for (bool waiting=false; !waiting; ) {
1802 if (jcr->JobId != 0 &&
1803 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1812 if (stop_time && (time(NULL) >= stop_time)) {
1813 ua->warning_msg(_("Wait on mount timed out\n"));
1823 ua->error_msg(_("ERR: Job was not found\n"));
1828 * We wait the end of a specific job
1831 bmicrosleep(0, 200000); /* let job actually start */
1832 for (bool running=true; running; ) {
1835 jcr=get_jcr_by_id(jobid) ;
1848 * We have to get JobStatus
1852 char jobstatus = '?'; /* Unknown by default */
1855 bsnprintf(buf, sizeof(buf),
1856 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1859 db_sql_query(ua->db, buf,
1860 status_handler, (void *)&jobstatus);
1862 switch (jobstatus) {
1864 status = 1 ; /* Warning */
1868 case JS_ErrorTerminated:
1870 status = 2 ; /* Critical */
1875 status = 0 ; /* Ok */
1879 status = 3 ; /* Unknown */
1883 ua->send_msg("JobId=%i\n", jobid) ;
1884 ua->send_msg("JobStatus=%s (%c)\n",
1885 job_status_to_str(jobstatus),
1888 if (ua->gui || ua->api) {
1889 ua->send_msg("ExitStatus=%i\n", status) ;
1896 static int help_cmd(UAContext *ua, const char *cmd)
1899 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1900 for (i=0; i<comsize; i++) {
1901 if (ua->argc == 2) {
1902 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1903 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1904 commands[i].help, commands[i].usage);
1908 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1911 if (i == comsize && ua->argc == 2) {
1912 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1914 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1918 int qhelp_cmd(UAContext *ua, const char *cmd)
1921 /* Want to display only commands */
1922 j = find_arg(ua, NT_("all"));
1924 for (i=0; i<comsize; i++) {
1925 ua->send_msg("%s\n", commands[i].key);
1929 /* Want to display a specific help section */
1930 j = find_arg_with_value(ua, NT_("item"));
1931 if (j >= 0 && ua->argk[j]) {
1932 for (i=0; i<comsize; i++) {
1933 if (bstrcmp(commands[i].key, ua->argv[j])) {
1934 ua->send_msg("%s\n", commands[i].usage);
1940 /* Want to display everything */
1941 for (i=0; i<comsize; i++) {
1942 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1948 static int version_cmd(UAContext *ua, const char *cmd)
1950 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1951 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1956 * Test code -- turned on only for debug testing
1958 static int version_cmd(UAContext *ua, const char *cmd)
1961 POOL_MEM query(PM_MESSAGE);
1963 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1964 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1965 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1966 for (int i=0; i < ids.num_ids; i++) {
1967 ua->send_msg("id=%d\n", ids.DBId[i]);
1975 * This call uses open_client_db() and force a
1976 * new dedicated connection to the catalog
1978 bool open_new_client_db(UAContext *ua)
1982 /* Force a new dedicated connection */
1984 ua->force_mult_db_connections = true;
1985 ret = open_client_db(ua);
1986 ua->force_mult_db_connections = false;
1991 * This call explicitly checks for a catalog=xxx and
1992 * if given, opens that catalog. It also checks for
1993 * client=xxx and if found, opens the catalog
1994 * corresponding to that client. If we still don't
1995 * have a catalog, look for a Job keyword and get the
1996 * catalog from its client record.
1998 bool open_client_db(UAContext *ua)
2005 /* Try for catalog keyword */
2006 i = find_arg_with_value(ua, NT_("catalog"));
2008 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2009 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2012 catalog = GetCatalogResWithName(ua->argv[i]);
2014 if (ua->catalog && ua->catalog != catalog) {
2017 ua->catalog = catalog;
2022 /* Try for client keyword */
2023 i = find_arg_with_value(ua, NT_("client"));
2025 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2026 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2029 client = GetClientResWithName(ua->argv[i]);
2031 catalog = client->catalog;
2032 if (ua->catalog && ua->catalog != catalog) {
2035 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2036 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2039 ua->catalog = catalog;
2044 /* Try for Job keyword */
2045 i = find_arg_with_value(ua, NT_("job"));
2047 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2048 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2051 job = GetJobResWithName(ua->argv[i]);
2053 catalog = job->client->catalog;
2054 if (ua->catalog && ua->catalog != catalog) {
2057 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2058 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2061 ua->catalog = catalog;
2071 * Open the catalog database.
2073 bool open_db(UAContext *ua)
2081 ua->catalog = get_catalog_resource(ua);
2083 ua->error_msg( _("Could not find a Catalog resource\n"));
2088 /* Some modules like bvfs need their own catalog connection */
2089 mult_db_conn = ua->catalog->mult_db_connections;
2090 if (ua->force_mult_db_connections) {
2091 mult_db_conn = true;
2094 ua->jcr->catalog = ua->catalog;
2096 Dmsg0(100, "UA Open database\n");
2097 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2098 ua->catalog->db_user,
2099 ua->catalog->db_password, ua->catalog->db_address,
2100 ua->catalog->db_port, ua->catalog->db_socket,
2101 mult_db_conn, ua->catalog->disable_batch_insert);
2102 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2103 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2104 ua->catalog->db_name);
2106 ua->error_msg("%s", db_strerror(ua->db));
2111 ua->jcr->db = ua->db;
2113 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2115 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2119 void close_db(UAContext *ua)
2122 db_close_database(ua->jcr, ua->db);