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);
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;
904 * For the client, we have the following values that can be set
905 * level = debug level
906 * trace = send debug output to a file
907 * hangup = how many records to send to SD before hanging up
908 * obviously this is most useful for testing restarting
911 static void do_client_setdebug(UAContext *ua, CLIENT *client,
912 int level, int trace, int hangup)
916 /* Connect to File daemon */
918 ua->jcr->client = client;
919 /* Try to connect for 15 seconds */
920 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
921 client->name(), client->address, client->FDport);
922 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
923 ua->error_msg(_("Failed to connect to Client.\n"));
926 Dmsg0(120, "Connected to file daemon\n");
927 fd = ua->jcr->file_bsock;
928 fd->fsend("setdebug=%d trace=%d hangup=%d\n", level, trace, hangup);
929 if (fd->recv() >= 0) {
930 ua->send_msg("%s", fd->msg);
932 fd->signal(BNET_TERMINATE);
934 ua->jcr->file_bsock = NULL;
939 static void do_all_setdebug(UAContext *ua, int level, int trace_flag, int hangup)
941 STORE *store, **unique_store;
942 CLIENT *client, **unique_client;
948 /* Count Storage items */
952 foreach_res(store, R_STORAGE) {
955 unique_store = (STORE **) malloc(i * sizeof(STORE));
956 /* Find Unique Storage address/port */
957 store = (STORE *)GetNextRes(R_STORAGE, NULL);
959 unique_store[i++] = store;
960 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
962 for (j=0; j<i; j++) {
963 if (strcmp(unique_store[j]->address, store->address) == 0 &&
964 unique_store[j]->SDport == store->SDport) {
970 unique_store[i++] = store;
971 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
976 /* Call each unique Storage daemon */
977 for (j=0; j<i; j++) {
978 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
982 /* Count Client items */
986 foreach_res(client, R_CLIENT) {
989 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
990 /* Find Unique Client address/port */
991 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
993 unique_client[i++] = client;
994 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
996 for (j=0; j<i; j++) {
997 if (strcmp(unique_client[j]->address, client->address) == 0 &&
998 unique_client[j]->FDport == client->FDport) {
1004 unique_client[i++] = client;
1005 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1010 /* Call each unique File daemon */
1011 for (j=0; j<i; j++) {
1012 do_client_setdebug(ua, unique_client[j], level, trace_flag, hangup);
1014 free(unique_client);
1018 * setdebug level=nn all trace=1/0
1020 static int setdebug_cmd(UAContext *ua, const char *cmd)
1025 int trace_flag = -1;
1029 Dmsg1(120, "setdebug:%s:\n", cmd);
1032 i = find_arg_with_value(ua, "level");
1034 level = atoi(ua->argv[i]);
1037 if (!get_pint(ua, _("Enter new debug level: "))) {
1040 level = ua->pint32_val;
1043 /* Look for trace flag. -1 => not change */
1044 i = find_arg_with_value(ua, "trace");
1046 trace_flag = atoi(ua->argv[i]);
1047 if (trace_flag > 0) {
1052 /* Look for hangup (debug only)flag. -1 => not change */
1053 i = find_arg_with_value(ua, "hangup");
1055 hangup = atoi(ua->argv[i]);
1059 /* General debug? */
1060 for (i=1; i<ua->argc; i++) {
1061 if (strcasecmp(ua->argk[i], "all") == 0) {
1062 do_all_setdebug(ua, level, trace_flag, hangup);
1065 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1066 strcasecmp(ua->argk[i], "director") == 0) {
1067 debug_level = level;
1068 set_trace(trace_flag);
1071 if (strcasecmp(ua->argk[i], "client") == 0 ||
1072 strcasecmp(ua->argk[i], "fd") == 0) {
1075 client = GetClientResWithName(ua->argv[i]);
1077 do_client_setdebug(ua, client, level, trace_flag, hangup);
1081 client = select_client_resource(ua);
1083 do_client_setdebug(ua, client, level, trace_flag, hangup);
1088 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1089 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1090 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1093 store = GetStoreResWithName(ua->argv[i]);
1095 do_storage_setdebug(ua, store, level, trace_flag);
1099 store = get_storage_resource(ua, false/*no default*/);
1101 do_storage_setdebug(ua, store, level, trace_flag);
1107 * We didn't find an appropriate keyword above, so
1110 start_prompt(ua, _("Available daemons are: \n"));
1111 add_prompt(ua, _("Director"));
1112 add_prompt(ua, _("Storage"));
1113 add_prompt(ua, _("Client"));
1114 add_prompt(ua, _("All"));
1115 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1116 case 0: /* Director */
1117 debug_level = level;
1118 set_trace(trace_flag);
1121 store = get_storage_resource(ua, false/*no default*/);
1123 do_storage_setdebug(ua, store, level, trace_flag);
1127 client = select_client_resource(ua);
1129 do_client_setdebug(ua, client, level, trace_flag, hangup);
1133 do_all_setdebug(ua, level, trace_flag, hangup);
1142 * Turn debug tracing to file on/off
1144 static int trace_cmd(UAContext *ua, const char *cmd)
1148 if (ua->argc != 2) {
1149 if (!get_cmd(ua, _("Turn on or off? "))) {
1154 onoff = ua->argk[1];
1157 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1162 static int var_cmd(UAContext *ua, const char *cmd)
1164 POOLMEM *val = get_pool_memory(PM_FNAME);
1167 if (!open_client_db(ua)) {
1170 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1173 while (*var == ' ') { /* skip spaces */
1176 Dmsg1(100, "Var=%s:\n", var);
1177 variable_expansion(ua->jcr, var, &val);
1178 ua->send_msg("%s\n", val);
1179 free_pool_memory(val);
1183 static int estimate_cmd(UAContext *ua, const char *cmd)
1186 CLIENT *client = NULL;
1187 FILESET *fileset = NULL;
1189 char since[MAXSTRING];
1193 jcr->setJobLevel(L_FULL);
1194 for (int i=1; i<ua->argc; i++) {
1195 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1196 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1198 client = GetClientResWithName(ua->argv[i]);
1200 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1203 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1204 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1209 ua->error_msg(_("Client name missing.\n"));
1213 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1215 job = GetJobResWithName(ua->argv[i]);
1217 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1220 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1221 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1226 ua->error_msg(_("Job name missing.\n"));
1231 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1233 fileset = GetFileSetResWithName(ua->argv[i]);
1235 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1238 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1239 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1244 ua->error_msg(_("Fileset name missing.\n"));
1248 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1252 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1254 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1255 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1259 ua->error_msg(_("Level value missing.\n"));
1263 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1264 if (!is_yesno(ua->argv[i], &accurate)) {
1265 ua->error_msg(_("Invalid value for accurate. "
1266 "It must be yes or no.\n"));
1270 if (!job && !(client && fileset)) {
1271 if (!(job = select_job_resource(ua))) {
1276 job = GetJobResWithName(ua->argk[1]);
1278 ua->error_msg(_("No job specified.\n"));
1281 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1282 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1287 client = job->client;
1290 fileset = job->fileset;
1292 jcr->client = client;
1293 jcr->fileset = fileset;
1295 if (job->pool->catalog) {
1296 ua->catalog = job->pool->catalog;
1298 ua->catalog = client->catalog;
1306 jcr->setJobType(JT_BACKUP);
1307 init_jcr_job_record(jcr);
1309 if (!get_or_create_client_record(jcr)) {
1312 if (!get_or_create_fileset_record(jcr)) {
1316 get_level_since_time(ua->jcr, since, sizeof(since));
1318 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1319 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1320 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1321 ua->error_msg(_("Failed to connect to Client.\n"));
1325 if (!send_include_list(jcr)) {
1326 ua->error_msg(_("Error sending include list.\n"));
1330 if (!send_exclude_list(jcr)) {
1331 ua->error_msg(_("Error sending exclude list.\n"));
1335 /* The level string change if accurate mode is enabled */
1336 if (accurate >= 0) {
1337 jcr->accurate = accurate;
1339 jcr->accurate = job->accurate;
1342 if (!send_level_command(jcr)) {
1347 * If the job is in accurate mode, we send the list of
1350 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1351 if (!send_accurate_current_files(jcr)) {
1355 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1356 while (jcr->file_bsock->recv() >= 0) {
1357 ua->send_msg("%s", jcr->file_bsock->msg);
1361 if (jcr->file_bsock) {
1362 jcr->file_bsock->signal(BNET_TERMINATE);
1363 jcr->file_bsock->close();
1364 jcr->file_bsock = NULL;
1373 static int time_cmd(UAContext *ua, const char *cmd)
1376 time_t ttime = time(NULL);
1378 (void)localtime_r(&ttime, &tm);
1379 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1380 ua->send_msg("%s\n", sdt);
1385 * reload the conf file
1387 extern "C" void reload_config(int sig);
1389 static int reload_cmd(UAContext *ua, const char *cmd)
1396 * Delete Pool records (should purge Media with it).
1398 * delete pool=<pool-name>
1399 * delete volume pool=<pool-name> volume=<name>
1402 static int delete_cmd(UAContext *ua, const char *cmd)
1404 static const char *keywords[] = {
1410 if (!open_client_db(ua)) {
1414 switch (find_arg_keyword(ua, keywords)) {
1423 while ((i=find_arg(ua, "jobid")) > 0) {
1425 *ua->argk[i] = 0; /* zap keyword already visited */
1433 "In general it is not a good idea to delete either a\n"
1434 "Pool or a Volume since they may contain data.\n\n"));
1436 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1447 ua->warning_msg(_("Nothing done.\n"));
1455 * delete_job has been modified to parse JobID lists like the
1457 * delete JobID=3,4,6,7-11,14
1459 * Thanks to Phil Stracchino for the above addition.
1462 static void delete_job(UAContext *ua)
1467 int i = find_arg_with_value(ua, NT_("jobid"));
1469 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1470 s = bstrdup(ua->argv[i]);
1473 * We could use strtok() here. But we're not going to, because:
1474 * (a) strtok() is deprecated, having been replaced by strsep();
1475 * (b) strtok() is broken in significant ways.
1476 * we could use strsep() instead, but it's not universally available.
1477 * so we grow our own using strchr().
1479 sep = strchr(tok, ',');
1480 while (sep != NULL) {
1482 if (!delete_job_id_range(ua, tok)) {
1483 JobId = str_to_int64(tok);
1484 do_job_delete(ua, JobId);
1487 sep = strchr(tok, ',');
1489 /* pick up the last token */
1490 if (!delete_job_id_range(ua, tok)) {
1491 JobId = str_to_int64(tok);
1492 do_job_delete(ua, JobId);
1497 JobId = str_to_int64(ua->argv[i]);
1498 do_job_delete(ua, JobId);
1500 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1503 JobId = ua->int64_val;
1504 do_job_delete(ua, JobId);
1509 * we call delete_job_id_range to parse range tokens and iterate over ranges
1511 static bool delete_job_id_range(UAContext *ua, char *tok)
1516 tok2 = strchr(tok, '-');
1522 j1 = str_to_int64(tok);
1523 j2 = str_to_int64(tok2);
1524 for (j=j1; j<=j2; j++) {
1525 do_job_delete(ua, j);
1531 * do_job_delete now performs the actual delete operation atomically
1533 static void do_job_delete(UAContext *ua, JobId_t JobId)
1537 edit_int64(JobId, ed1);
1538 purge_jobs_from_catalog(ua, ed1);
1539 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1543 * Delete media records from database -- dangerous
1545 static int delete_volume(UAContext *ua)
1551 if (!select_media_dbr(ua, &mr)) {
1554 ua->warning_msg(_("\nThis command will delete volume %s\n"
1555 "and all Jobs saved on that volume from the Catalog\n"),
1558 if (find_arg(ua, "yes") >= 0) {
1559 ua->pint32_val = 1; /* Have "yes" on command line already" */
1561 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1563 if (!get_yesno(ua, buf)) {
1567 if (!ua->pint32_val) {
1571 /* If not purged, do it */
1572 if (strcmp(mr.VolStatus, "Purged") != 0) {
1573 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1574 ua->error_msg(_("Can't list jobs on this volume\n"));
1578 purge_jobs_from_catalog(ua, lst.list);
1582 db_delete_media_record(ua->jcr, ua->db, &mr);
1587 * Delete a pool record from the database -- dangerous
1589 static int delete_pool(UAContext *ua)
1594 memset(&pr, 0, sizeof(pr));
1596 if (!get_pool_dbr(ua, &pr)) {
1599 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1601 if (!get_yesno(ua, buf)) {
1604 if (ua->pint32_val) {
1605 db_delete_pool_record(ua->jcr, ua->db, &pr);
1610 int memory_cmd(UAContext *ua, const char *cmd)
1612 garbage_collect_memory();
1613 list_dir_status_header(ua);
1614 sm_dump(false, true);
1618 static void do_mount_cmd(UAContext *ua, const char *command)
1623 char dev_name[MAX_NAME_LENGTH];
1627 if (!open_client_db(ua)) {
1630 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1632 store.store = get_storage_resource(ua, true/*arg is storage*/);
1636 pm_strcpy(store.store_source, _("unknown source"));
1637 set_wstorage(jcr, &store);
1638 drive = get_storage_drive(ua, store.store);
1639 if (strcmp(command, "mount") == 0) {
1640 slot = get_storage_slot(ua, store.store);
1643 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1644 store.store->media_type, store.store->dev_name(), drive);
1646 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1647 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1650 sd = jcr->store_bsock;
1651 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1652 bash_spaces(dev_name);
1654 sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1656 sd->fsend("%s %s drive=%d", command, dev_name, drive);
1658 while (sd->recv() >= 0) {
1659 ua->send_msg("%s", sd->msg);
1661 sd->signal(BNET_TERMINATE);
1663 jcr->store_bsock = NULL;
1667 * mount [storage=<name>] [drive=nn] [slot=mm]
1669 static int mount_cmd(UAContext *ua, const char *cmd)
1671 do_mount_cmd(ua, "mount"); /* mount */
1677 * unmount [storage=<name>] [drive=nn]
1679 static int unmount_cmd(UAContext *ua, const char *cmd)
1681 do_mount_cmd(ua, "unmount"); /* unmount */
1687 * release [storage=<name>] [drive=nn]
1689 static int release_cmd(UAContext *ua, const char *cmd)
1691 do_mount_cmd(ua, "release"); /* release */
1698 * use catalog=<name>
1700 static int use_cmd(UAContext *ua, const char *cmd)
1702 CAT *oldcatalog, *catalog;
1705 close_db(ua); /* close any previously open db */
1706 oldcatalog = ua->catalog;
1708 if (!(catalog = get_catalog_resource(ua))) {
1709 ua->catalog = oldcatalog;
1711 ua->catalog = catalog;
1714 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1715 ua->catalog->name(), ua->catalog->db_name);
1720 int quit_cmd(UAContext *ua, const char *cmd)
1726 /* Handler to get job status */
1727 static int status_handler(void *ctx, int num_fields, char **row)
1729 char *val = (char *)ctx;
1734 *val = '?'; /* Unknown by default */
1741 * Wait until no job is running
1743 int wait_cmd(UAContext *ua, const char *cmd)
1747 time_t stop_time = 0;
1751 * Wait until no job is running
1753 if (ua->argc == 1) {
1754 bmicrosleep(0, 200000); /* let job actually start */
1755 for (bool running=true; running; ) {
1758 if (jcr->JobId != 0) {
1772 i = find_arg_with_value(ua, NT_("timeout"));
1773 if (i > 0 && ua->argv[i]) {
1774 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1777 /* we have jobid, jobname or ujobid argument */
1779 uint32_t jobid = 0 ;
1781 if (!open_client_db(ua)) {
1782 ua->error_msg(_("ERR: Can't open db\n")) ;
1786 for (int i=1; i<ua->argc; i++) {
1787 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1791 jobid = str_to_int64(ua->argv[i]);
1793 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1794 strcasecmp(ua->argk[i], "job") == 0) {
1798 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1800 jobid = jcr->JobId ;
1804 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1808 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1810 jobid = jcr->JobId ;
1814 /* Wait for a mount request */
1815 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1816 for (bool waiting=false; !waiting; ) {
1818 if (jcr->JobId != 0 &&
1819 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1828 if (stop_time && (time(NULL) >= stop_time)) {
1829 ua->warning_msg(_("Wait on mount timed out\n"));
1839 ua->error_msg(_("ERR: Job was not found\n"));
1844 * We wait the end of a specific job
1847 bmicrosleep(0, 200000); /* let job actually start */
1848 for (bool running=true; running; ) {
1851 jcr=get_jcr_by_id(jobid) ;
1864 * We have to get JobStatus
1868 char jobstatus = '?'; /* Unknown by default */
1871 bsnprintf(buf, sizeof(buf),
1872 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1875 db_sql_query(ua->db, buf,
1876 status_handler, (void *)&jobstatus);
1878 switch (jobstatus) {
1880 status = 1 ; /* Warning */
1884 case JS_ErrorTerminated:
1886 status = 2 ; /* Critical */
1891 status = 0 ; /* Ok */
1895 status = 3 ; /* Unknown */
1899 ua->send_msg("JobId=%i\n", jobid) ;
1900 ua->send_msg("JobStatus=%s (%c)\n",
1901 job_status_to_str(jobstatus),
1904 if (ua->gui || ua->api) {
1905 ua->send_msg("ExitStatus=%i\n", status) ;
1912 static int help_cmd(UAContext *ua, const char *cmd)
1915 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1916 for (i=0; i<comsize; i++) {
1917 if (ua->argc == 2) {
1918 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1919 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1920 commands[i].help, commands[i].usage);
1924 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1927 if (i == comsize && ua->argc == 2) {
1928 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1930 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1934 int qhelp_cmd(UAContext *ua, const char *cmd)
1937 /* Want to display only commands */
1938 j = find_arg(ua, NT_("all"));
1940 for (i=0; i<comsize; i++) {
1941 ua->send_msg("%s\n", commands[i].key);
1945 /* Want to display a specific help section */
1946 j = find_arg_with_value(ua, NT_("item"));
1947 if (j >= 0 && ua->argk[j]) {
1948 for (i=0; i<comsize; i++) {
1949 if (bstrcmp(commands[i].key, ua->argv[j])) {
1950 ua->send_msg("%s\n", commands[i].usage);
1956 /* Want to display everything */
1957 for (i=0; i<comsize; i++) {
1958 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1964 static int version_cmd(UAContext *ua, const char *cmd)
1966 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1967 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1972 * Test code -- turned on only for debug testing
1974 static int version_cmd(UAContext *ua, const char *cmd)
1977 POOL_MEM query(PM_MESSAGE);
1979 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1980 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1981 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1982 for (int i=0; i < ids.num_ids; i++) {
1983 ua->send_msg("id=%d\n", ids.DBId[i]);
1991 * This call uses open_client_db() and force a
1992 * new dedicated connection to the catalog
1994 bool open_new_client_db(UAContext *ua)
1998 /* Force a new dedicated connection */
2000 ua->force_mult_db_connections = true;
2001 ret = open_client_db(ua);
2002 ua->force_mult_db_connections = false;
2007 * This call explicitly checks for a catalog=xxx and
2008 * if given, opens that catalog. It also checks for
2009 * client=xxx and if found, opens the catalog
2010 * corresponding to that client. If we still don't
2011 * have a catalog, look for a Job keyword and get the
2012 * catalog from its client record.
2014 bool open_client_db(UAContext *ua)
2021 /* Try for catalog keyword */
2022 i = find_arg_with_value(ua, NT_("catalog"));
2024 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2025 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2028 catalog = GetCatalogResWithName(ua->argv[i]);
2030 if (ua->catalog && ua->catalog != catalog) {
2033 ua->catalog = catalog;
2038 /* Try for client keyword */
2039 i = find_arg_with_value(ua, NT_("client"));
2041 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2042 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2045 client = GetClientResWithName(ua->argv[i]);
2047 catalog = client->catalog;
2048 if (ua->catalog && ua->catalog != catalog) {
2051 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2052 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2055 ua->catalog = catalog;
2060 /* Try for Job keyword */
2061 i = find_arg_with_value(ua, NT_("job"));
2063 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2064 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2067 job = GetJobResWithName(ua->argv[i]);
2069 catalog = job->client->catalog;
2070 if (ua->catalog && ua->catalog != catalog) {
2073 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2074 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2077 ua->catalog = catalog;
2087 * Open the catalog database.
2089 bool open_db(UAContext *ua)
2097 ua->catalog = get_catalog_resource(ua);
2099 ua->error_msg( _("Could not find a Catalog resource\n"));
2104 /* Some modules like bvfs need their own catalog connection */
2105 mult_db_conn = ua->catalog->mult_db_connections;
2106 if (ua->force_mult_db_connections) {
2107 mult_db_conn = true;
2110 ua->jcr->catalog = ua->catalog;
2112 Dmsg0(100, "UA Open database\n");
2113 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2114 ua->catalog->db_user,
2115 ua->catalog->db_password, ua->catalog->db_address,
2116 ua->catalog->db_port, ua->catalog->db_socket,
2117 mult_db_conn, ua->catalog->disable_batch_insert);
2118 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2119 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2120 ua->catalog->db_name);
2122 ua->error_msg("%s", db_strerror(ua->db));
2127 ua->jcr->db = ua->db;
2129 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2131 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2135 void close_db(UAContext *ua)
2138 db_close_database(ua->jcr, ua->db);