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 setbwlimit_cmd(UAContext *ua, const char *cmd);
94 static int setip_cmd(UAContext *ua, const char *cmd);
95 static int time_cmd(UAContext *ua, const char *cmd);
96 static int trace_cmd(UAContext *ua, const char *cmd);
97 static int unmount_cmd(UAContext *ua, const char *cmd);
98 static int use_cmd(UAContext *ua, const char *cmd);
99 static int var_cmd(UAContext *ua, const char *cmd);
100 static int version_cmd(UAContext *ua, const char *cmd);
101 static int wait_cmd(UAContext *ua, const char *cmd);
103 static void do_job_delete(UAContext *ua, JobId_t JobId);
104 static bool delete_job_id_range(UAContext *ua, char *tok);
105 static int delete_volume(UAContext *ua);
106 static int delete_pool(UAContext *ua);
107 static void delete_job(UAContext *ua);
109 int qhelp_cmd(UAContext *ua, const char *cmd);
110 int quit_cmd(UAContext *ua, const char *cmd);
112 /* not all in alphabetical order. New commands are added after existing commands with similar letters
113 to prevent breakage of existing user scripts. */
115 const char *key; /* command */
116 int (*func)(UAContext *ua, const char *cmd); /* handler */
117 const char *help; /* main purpose */
118 const char *usage; /* all arguments to build usage */
119 const bool use_in_rs; /* Can use it in Console RunScript */
121 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
122 { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
123 { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
124 { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
125 { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid=<number> job=<job-name> ujobid=<unique-jobid>"), false},
126 { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
127 { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume=<vol-name> pool=<pool-name> jobid=<id>"), true},
128 { NT_("disable"), disable_cmd, _("Disable a job"), NT_("job=<name>"), true},
129 { NT_("enable"), enable_cmd, _("Enable a job"), NT_("job=<name>"), true},
130 { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
131 NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
133 { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
134 { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
135 { NT_("help"), help_cmd, _("Print help on specific command"),
136 NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
137 "\n\tmessages memory mount prune purge python quit query\n\trestore relabel release reload run status"
138 "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"), false},
140 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool>"), false},
141 { NT_("list"), list_cmd, _("List objects from catalog"),
142 NT_("pools | jobs | jobtotals | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
144 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
145 NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
147 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
148 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
149 { NT_("mount"), mount_cmd, _("Mount storage"),
150 NT_("storage=<storage-name> slot=<num> drive=<num> [ jobid=<id> | job=<job-name> ]"), false},
152 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
153 NT_("files | jobs | pool=<pool> | client=<client-name> | volume=<volume-name> "), true},
155 { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [action=<action> devicetype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
156 { NT_("python"), python_cmd, _("Python control commands"), NT_(""), false},
157 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
158 { NT_("query"), querycmd, _("Query catalog"), NT_(""), false},
159 { NT_("restore"), restore_cmd, _("Restore files"),
160 NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
162 "\n\tcomment=<text> jobid=<jobid> done select all"), false},
164 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
165 NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
167 { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name>"), false},
168 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
169 { NT_("run"), run_cmd, _("Run a job"),
170 NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
171 "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tcomment=<text> yes"), false},
173 { NT_("status"), status_cmd, _("Report status"),
174 NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
176 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
177 NT_("level=<nn> trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
179 { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"),
180 NT_("limit=<nn-kbs> client=<client-name> jobid=<number> job=<job-name> ujobid=<unique-jobid>"), true},
182 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
183 { NT_("show"), show_cmd, _("Show resource records"),
184 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | disabled | all"), true},
186 { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
187 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
188 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
189 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
190 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
192 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
193 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
195 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
196 NT_("stats\n\tpool=<poolname>\n\tslots storage=<storage> scan"
197 "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
198 "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
199 "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
200 "\n\t enable=<yes/no> recyclepool=<pool> actiononpurge=<action>"),true},
201 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_(""), false},
202 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
203 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
204 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
205 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
208 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
211 * Execute a command from the UA
213 bool do_a_command(UAContext *ua)
219 BSOCK *user = ua->UA_sock;
222 Dmsg1(900, "Command: %s\n", ua->argk[0]);
227 while (ua->jcr->wstorage->size()) {
228 ua->jcr->wstorage->remove(0);
231 len = strlen(ua->argk[0]);
232 for (i=0; i<comsize; i++) { /* search for command */
233 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
234 /* Check if command permitted, but "quit" is always OK */
235 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
236 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
239 /* Check if this command is authorized in RunScript */
240 if (ua->runscript && !commands[i].use_in_rs) {
241 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
244 if (ua->api) user->signal(BNET_CMD_BEGIN);
245 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
246 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
252 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
259 * This is a common routine used to stuff the Pool DB record defaults
260 * into the Media DB record just before creating a media (Volume)
263 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
265 mr->PoolId = pr->PoolId;
266 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
267 mr->Recycle = pr->Recycle;
268 mr->VolRetention = pr->VolRetention;
269 mr->VolUseDuration = pr->VolUseDuration;
270 mr->ActionOnPurge = pr->ActionOnPurge;
271 mr->RecyclePoolId = pr->RecyclePoolId;
272 mr->MaxVolJobs = pr->MaxVolJobs;
273 mr->MaxVolFiles = pr->MaxVolFiles;
274 mr->MaxVolBytes = pr->MaxVolBytes;
275 mr->LabelType = pr->LabelType;
281 * Add Volumes to an existing Pool
283 static int add_cmd(UAContext *ua, const char *cmd)
287 int num, i, max, startnum;
289 char name[MAX_NAME_LENGTH];
291 int Slot = 0, InChanger = 0;
294 "You probably don't want to be using this command since it\n"
295 "creates database records without labeling the Volumes.\n"
296 "You probably want to use the \"label\" command.\n\n"));
298 if (!open_client_db(ua)) {
302 memset(&pr, 0, sizeof(pr));
303 memset(&mr, 0, sizeof(mr));
305 if (!get_pool_dbr(ua, &pr)) {
309 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
310 pr.MaxVols, pr.PoolType);
312 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
313 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
314 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
317 pr.MaxVols = ua->pint32_val;
321 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
322 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
323 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
327 if (pr.MaxVols == 0) {
330 max = pr.MaxVols - pr.NumVols;
334 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
335 if (!get_pint(ua, buf)) {
338 num = ua->pint32_val;
339 if (num < 0 || num > max) {
340 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
348 if (!get_cmd(ua, _("Enter Volume name: "))) {
352 if (!get_cmd(ua, _("Enter base volume name: "))) {
356 /* Don't allow | in Volume name because it is the volume separator character */
357 if (!is_volume_name_legal(ua, ua->cmd)) {
360 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
361 ua->warning_msg(_("Volume name too long.\n"));
364 if (strlen(ua->cmd) == 0) {
365 ua->warning_msg(_("Volume name must be at least one character long.\n"));
371 bstrncpy(name, ua->cmd, sizeof(name));
373 bstrncat(name, "%04d", sizeof(name));
376 if (!get_pint(ua, _("Enter the starting number: "))) {
379 startnum = ua->pint32_val;
381 ua->warning_msg(_("Start number must be greater than zero.\n"));
391 if (store && store->autochanger) {
392 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
395 Slot = ua->pint32_val;
396 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
399 InChanger = ua->pint32_val;
402 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
403 for (i=startnum; i < num+startnum; i++) {
404 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
406 mr.InChanger = InChanger;
407 mr.StorageId = store->StorageId;
409 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
410 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
411 ua->error_msg("%s", db_strerror(ua->db));
415 first_id = mr.PoolId;
419 Dmsg0(200, "Update pool record.\n");
420 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
421 ua->warning_msg("%s", db_strerror(ua->db));
424 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
430 * Turn auto mount on/off
435 int automount_cmd(UAContext *ua, const char *cmd)
440 if (!get_cmd(ua, _("Turn on or off? "))) {
448 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
455 static int cancel_cmd(UAContext *ua, const char *cmd)
457 JCR *jcr = select_running_job(ua, "cancel");
461 int ret = cancel_job(ua, jcr);
467 * This is a common routine to create or update a
468 * Pool DB base record from a Pool Resource. We handle
469 * the setting of MaxVols and NumVols slightly differently
470 * depending on if we are creating the Pool or we are
471 * simply bringing it into agreement with the resource (updage).
473 * Caution : RecyclePoolId isn't setup in this function.
474 * You can use set_pooldbr_recyclepoolid();
477 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
479 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
480 if (op == POOL_OP_CREATE) {
481 pr->MaxVols = pool->max_volumes;
483 } else { /* update pool */
484 if (pr->MaxVols != pool->max_volumes) {
485 pr->MaxVols = pool->max_volumes;
487 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
488 pr->MaxVols = pr->NumVols;
491 pr->LabelType = pool->LabelType;
492 pr->UseOnce = pool->use_volume_once;
493 pr->UseCatalog = pool->use_catalog;
494 pr->Recycle = pool->Recycle;
495 pr->VolRetention = pool->VolRetention;
496 pr->VolUseDuration = pool->VolUseDuration;
497 pr->MaxVolJobs = pool->MaxVolJobs;
498 pr->MaxVolFiles = pool->MaxVolFiles;
499 pr->MaxVolBytes = pool->MaxVolBytes;
500 pr->AutoPrune = pool->AutoPrune;
501 pr->ActionOnPurge = pool->action_on_purge;
502 pr->Recycle = pool->Recycle;
503 if (pool->label_format) {
504 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
506 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
510 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
511 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
515 if (!pool->RecyclePool && !pool->ScratchPool) {
519 memset(&pr, 0, sizeof(POOL_DBR));
520 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
522 if (!db_get_pool_record(jcr, db, &pr)) {
523 return -1; /* not exists in database */
526 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
528 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
529 return -1; /* error */
532 if (!db_update_pool_record(jcr, db, &pr)) {
533 return -1; /* error */
538 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
539 * works with set_pooldbr_from_poolres
541 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
546 if (pool->RecyclePool) {
547 memset(&rpool, 0, sizeof(POOL_DBR));
549 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
550 if (db_get_pool_record(jcr, db, &rpool)) {
551 pr->RecyclePoolId = rpool.PoolId;
553 Jmsg(jcr, M_WARNING, 0,
554 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
555 "Try to update it with 'update pool=%s'\n"),
556 pool->name(), rpool.Name, rpool.Name,pool->name());
560 } else { /* no RecyclePool used, set it to 0 */
561 pr->RecyclePoolId = 0;
564 if (pool->ScratchPool) {
565 memset(&rpool, 0, sizeof(POOL_DBR));
567 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
568 if (db_get_pool_record(jcr, db, &rpool)) {
569 pr->ScratchPoolId = rpool.PoolId;
571 Jmsg(jcr, M_WARNING, 0,
572 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
573 "Try to update it with 'update pool=%s'\n"),
574 pool->name(), rpool.Name, rpool.Name,pool->name());
577 } else { /* no ScratchPool used, set it to 0 */
578 pr->ScratchPoolId = 0;
586 * Create a pool record from a given Pool resource
587 * Also called from backup.c
588 * Returns: -1 on error
589 * 0 record already exists
593 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
597 memset(&pr, 0, sizeof(POOL_DBR));
599 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
601 if (db_get_pool_record(jcr, db, &pr)) {
603 if (op == POOL_OP_UPDATE) { /* update request */
604 set_pooldbr_from_poolres(&pr, pool, op);
605 set_pooldbr_references(jcr, db, &pr, pool);
606 db_update_pool_record(jcr, db, &pr);
608 return 0; /* exists */
611 set_pooldbr_from_poolres(&pr, pool, op);
612 set_pooldbr_references(jcr, db, &pr, pool);
614 if (!db_create_pool_record(jcr, db, &pr)) {
615 return -1; /* error */
623 * Create a Pool Record in the database.
624 * It is always created from the Resource record.
626 static int create_cmd(UAContext *ua, const char *cmd)
630 if (!open_client_db(ua)) {
634 pool = get_pool_resource(ua);
639 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
641 ua->error_msg(_("Error: Pool %s already exists.\n"
642 "Use update to change it.\n"), pool->name());
646 ua->error_msg("%s", db_strerror(ua->db));
652 ua->send_msg(_("Pool %s created.\n"), pool->name());
657 extern DIRRES *director;
658 extern char *configfile;
661 * Python control command
662 * python restart (restarts interpreter)
664 static int python_cmd(UAContext *ua, const char *cmd)
667 init_python_interpreter_args python_args;
669 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
670 term_python_interpreter();
672 python_args.progname = director->name();
673 python_args.scriptdir = director->scripts_directory;
674 python_args.modulename = "DirStartUp";
675 python_args.configfile = configfile;
676 python_args.workingdir = director->working_directory;
677 python_args.job_getattr = job_getattr;
678 python_args.job_setattr = job_setattr;
680 init_python_interpreter(&python_args);
682 ua->send_msg(_("Python interpreter restarted.\n"));
684 #endif /* HAVE_PYTHON */
685 ua->warning_msg(_("Nothing done.\n"));
688 #endif /* HAVE_PYTHON */
692 static int setbwlimit_cmd(UAContext *ua, const char *cmd)
695 char Job[MAX_NAME_LENGTH];
700 i = find_arg_with_value(ua, "limit");
702 limit = atoi(ua->argv[i]) * 1024;
705 if (!get_pint(ua, _("Enter new bandwidth limit kb/s: "))) {
708 limit = ua->pint32_val * 1024; /* kb/s */
711 const char *lst[] = { "job", "jobid", "jobname", NULL };
712 if (find_arg_keyword(ua, lst) > 0) {
713 JCR *jcr = select_running_job(ua, "limit");
715 jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
716 bstrncpy(Job, jcr->Job, sizeof(Job));
717 client = jcr->client;
724 client = get_client_resource(ua);
731 /* Connect to File daemon */
732 ua->jcr->client = client;
733 ua->jcr->max_bandwidth = limit;
735 /* Try to connect for 15 seconds */
736 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
737 client->name(), client->address, client->FDport);
738 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
739 ua->error_msg(_("Failed to connect to Client.\n"));
742 Dmsg0(120, "Connected to file daemon\n");
743 if (!send_bwlimit(ua->jcr, Job)) {
744 ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
747 ua->info_msg(_("OK Limiting bandwidth to %lldkb/s %s\n"),
751 ua->jcr->file_bsock->signal(BNET_TERMINATE);
752 ua->jcr->file_bsock->close();
753 ua->jcr->file_bsock = NULL;
754 ua->jcr->client = NULL;
755 ua->jcr->max_bandwidth = 0;
760 * Set a new address in a Client resource. We do this only
761 * if the Console name is the same as the Client name
762 * and the Console can access the client.
764 static int setip_cmd(UAContext *ua, const char *cmd)
768 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
769 ua->error_msg(_("Unauthorized command from this console.\n"));
773 client = GetClientResWithName(ua->cons->name());
776 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
779 if (client->address) {
780 free(client->address);
782 /* MA Bug 6 remove ifdef */
783 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
784 client->address = bstrdup(buf);
785 ua->send_msg(_("Client \"%s\" address set to %s\n"),
786 client->name(), client->address);
793 static void do_en_disable_cmd(UAContext *ua, bool setting)
798 i = find_arg_with_value(ua, NT_("job"));
800 job = select_enable_disable_job_resource(ua, setting);
806 job = GetJobResWithName(ua->argv[i]);
810 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
814 if (!acl_access_ok(ua, Job_ACL, job->name())) {
815 ua->error_msg(_("Unauthorized command from this console.\n"));
818 job->enabled = setting;
819 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
823 static int enable_cmd(UAContext *ua, const char *cmd)
825 do_en_disable_cmd(ua, true);
829 static int disable_cmd(UAContext *ua, const char *cmd)
831 do_en_disable_cmd(ua, false);
836 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
842 lstore.store = store;
843 pm_strcpy(lstore.store_source, _("unknown source"));
844 set_wstorage(jcr, &lstore);
845 /* Try connecting for up to 15 seconds */
846 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
847 store->name(), store->address, store->SDport);
848 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
849 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
852 Dmsg0(120, _("Connected to storage daemon\n"));
853 sd = jcr->store_bsock;
854 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
855 if (sd->recv() >= 0) {
856 ua->send_msg("%s", sd->msg);
858 sd->signal(BNET_TERMINATE);
860 jcr->store_bsock = NULL;
864 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
868 /* Connect to File daemon */
870 ua->jcr->client = client;
871 /* Try to connect for 15 seconds */
872 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
873 client->name(), client->address, client->FDport);
874 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
875 ua->error_msg(_("Failed to connect to Client.\n"));
878 Dmsg0(120, "Connected to file daemon\n");
879 fd = ua->jcr->file_bsock;
880 fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
881 if (fd->recv() >= 0) {
882 ua->send_msg("%s", fd->msg);
884 fd->signal(BNET_TERMINATE);
886 ua->jcr->file_bsock = NULL;
891 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
893 STORE *store, **unique_store;
894 CLIENT *client, **unique_client;
900 /* Count Storage items */
904 foreach_res(store, R_STORAGE) {
907 unique_store = (STORE **) malloc(i * sizeof(STORE));
908 /* Find Unique Storage address/port */
909 store = (STORE *)GetNextRes(R_STORAGE, NULL);
911 unique_store[i++] = store;
912 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
914 for (j=0; j<i; j++) {
915 if (strcmp(unique_store[j]->address, store->address) == 0 &&
916 unique_store[j]->SDport == store->SDport) {
922 unique_store[i++] = store;
923 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
928 /* Call each unique Storage daemon */
929 for (j=0; j<i; j++) {
930 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
934 /* Count Client items */
938 foreach_res(client, R_CLIENT) {
941 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
942 /* Find Unique Client address/port */
943 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
945 unique_client[i++] = client;
946 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
948 for (j=0; j<i; j++) {
949 if (strcmp(unique_client[j]->address, client->address) == 0 &&
950 unique_client[j]->FDport == client->FDport) {
956 unique_client[i++] = client;
957 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
962 /* Call each unique File daemon */
963 for (j=0; j<i; j++) {
964 do_client_setdebug(ua, unique_client[j], level, trace_flag);
970 * setdebug level=nn all trace=1/0
972 static int setdebug_cmd(UAContext *ua, const char *cmd)
980 Dmsg1(120, "setdebug:%s:\n", cmd);
983 i = find_arg_with_value(ua, "level");
985 level = atoi(ua->argv[i]);
988 if (!get_pint(ua, _("Enter new debug level: "))) {
991 level = ua->pint32_val;
994 /* Look for trace flag. -1 => not change */
995 i = find_arg_with_value(ua, "trace");
997 trace_flag = atoi(ua->argv[i]);
998 if (trace_flag > 0) {
1003 /* General debug? */
1004 for (i=1; i<ua->argc; i++) {
1005 if (strcasecmp(ua->argk[i], "all") == 0) {
1006 do_all_setdebug(ua, level, trace_flag);
1009 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1010 strcasecmp(ua->argk[i], "director") == 0) {
1011 debug_level = level;
1012 set_trace(trace_flag);
1015 if (strcasecmp(ua->argk[i], "client") == 0 ||
1016 strcasecmp(ua->argk[i], "fd") == 0) {
1019 client = GetClientResWithName(ua->argv[i]);
1021 do_client_setdebug(ua, client, level, trace_flag);
1025 client = select_client_resource(ua);
1027 do_client_setdebug(ua, client, level, trace_flag);
1032 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1033 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1034 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1037 store = GetStoreResWithName(ua->argv[i]);
1039 do_storage_setdebug(ua, store, level, trace_flag);
1043 store = get_storage_resource(ua, false/*no default*/);
1045 do_storage_setdebug(ua, store, level, trace_flag);
1051 * We didn't find an appropriate keyword above, so
1054 start_prompt(ua, _("Available daemons are: \n"));
1055 add_prompt(ua, _("Director"));
1056 add_prompt(ua, _("Storage"));
1057 add_prompt(ua, _("Client"));
1058 add_prompt(ua, _("All"));
1059 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1060 case 0: /* Director */
1061 debug_level = level;
1062 set_trace(trace_flag);
1065 store = get_storage_resource(ua, false/*no default*/);
1067 do_storage_setdebug(ua, store, level, trace_flag);
1071 client = select_client_resource(ua);
1073 do_client_setdebug(ua, client, level, trace_flag);
1077 do_all_setdebug(ua, level, trace_flag);
1086 * Turn debug tracing to file on/off
1088 static int trace_cmd(UAContext *ua, const char *cmd)
1092 if (ua->argc != 2) {
1093 if (!get_cmd(ua, _("Turn on or off? "))) {
1098 onoff = ua->argk[1];
1101 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1106 static int var_cmd(UAContext *ua, const char *cmd)
1108 POOLMEM *val = get_pool_memory(PM_FNAME);
1111 if (!open_client_db(ua)) {
1114 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1117 while (*var == ' ') { /* skip spaces */
1120 Dmsg1(100, "Var=%s:\n", var);
1121 variable_expansion(ua->jcr, var, &val);
1122 ua->send_msg("%s\n", val);
1123 free_pool_memory(val);
1127 static int estimate_cmd(UAContext *ua, const char *cmd)
1130 CLIENT *client = NULL;
1131 FILESET *fileset = NULL;
1133 char since[MAXSTRING];
1137 jcr->set_JobLevel(L_FULL);
1138 for (int i=1; i<ua->argc; i++) {
1139 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1140 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1142 client = GetClientResWithName(ua->argv[i]);
1144 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1147 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1148 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1153 ua->error_msg(_("Client name missing.\n"));
1157 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1159 job = GetJobResWithName(ua->argv[i]);
1161 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1164 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1165 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1170 ua->error_msg(_("Job name missing.\n"));
1175 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1177 fileset = GetFileSetResWithName(ua->argv[i]);
1179 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1182 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1183 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1188 ua->error_msg(_("Fileset name missing.\n"));
1192 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1196 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1198 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1199 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1203 ua->error_msg(_("Level value missing.\n"));
1207 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1208 if (!is_yesno(ua->argv[i], &accurate)) {
1209 ua->error_msg(_("Invalid value for accurate. "
1210 "It must be yes or no.\n"));
1214 if (!job && !(client && fileset)) {
1215 if (!(job = select_job_resource(ua))) {
1220 job = GetJobResWithName(ua->argk[1]);
1222 ua->error_msg(_("No job specified.\n"));
1225 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1226 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1231 client = job->client;
1234 fileset = job->fileset;
1236 jcr->client = client;
1237 jcr->fileset = fileset;
1239 if (job->pool->catalog) {
1240 ua->catalog = job->pool->catalog;
1242 ua->catalog = client->catalog;
1250 jcr->set_JobType(JT_BACKUP);
1251 init_jcr_job_record(jcr);
1253 if (!get_or_create_client_record(jcr)) {
1256 if (!get_or_create_fileset_record(jcr)) {
1260 get_level_since_time(ua->jcr, since, sizeof(since));
1262 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1263 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1264 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1265 ua->error_msg(_("Failed to connect to Client.\n"));
1269 if (!send_include_list(jcr)) {
1270 ua->error_msg(_("Error sending include list.\n"));
1274 if (!send_exclude_list(jcr)) {
1275 ua->error_msg(_("Error sending exclude list.\n"));
1279 /* The level string change if accurate mode is enabled */
1280 if (accurate >= 0) {
1281 jcr->accurate = accurate;
1283 jcr->accurate = job->accurate;
1286 if (!send_level_command(jcr)) {
1291 * If the job is in accurate mode, we send the list of
1294 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1295 if (!send_accurate_current_files(jcr)) {
1299 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1300 while (bnet_recv(jcr->file_bsock) >= 0) {
1301 ua->send_msg("%s", jcr->file_bsock->msg);
1305 if (jcr->file_bsock) {
1306 jcr->file_bsock->signal(BNET_TERMINATE);
1307 jcr->file_bsock->close();
1308 jcr->file_bsock = NULL;
1317 static int time_cmd(UAContext *ua, const char *cmd)
1320 time_t ttime = time(NULL);
1322 (void)localtime_r(&ttime, &tm);
1323 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1324 ua->send_msg("%s\n", sdt);
1329 * reload the conf file
1331 extern "C" void reload_config(int sig);
1333 static int reload_cmd(UAContext *ua, const char *cmd)
1340 * Delete Pool records (should purge Media with it).
1342 * delete pool=<pool-name>
1343 * delete volume pool=<pool-name> volume=<name>
1346 static int delete_cmd(UAContext *ua, const char *cmd)
1348 static const char *keywords[] = {
1354 if (!open_client_db(ua)) {
1358 switch (find_arg_keyword(ua, keywords)) {
1367 while ((i=find_arg(ua, "jobid")) > 0) {
1369 *ua->argk[i] = 0; /* zap keyword already visited */
1377 "In general it is not a good idea to delete either a\n"
1378 "Pool or a Volume since they may contain data.\n\n"));
1380 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1391 ua->warning_msg(_("Nothing done.\n"));
1399 * delete_job has been modified to parse JobID lists like the
1401 * delete JobID=3,4,6,7-11,14
1403 * Thanks to Phil Stracchino for the above addition.
1406 static void delete_job(UAContext *ua)
1411 int i = find_arg_with_value(ua, NT_("jobid"));
1413 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1414 s = bstrdup(ua->argv[i]);
1417 * We could use strtok() here. But we're not going to, because:
1418 * (a) strtok() is deprecated, having been replaced by strsep();
1419 * (b) strtok() is broken in significant ways.
1420 * we could use strsep() instead, but it's not universally available.
1421 * so we grow our own using strchr().
1423 sep = strchr(tok, ',');
1424 while (sep != NULL) {
1426 if (!delete_job_id_range(ua, tok)) {
1427 JobId = str_to_int64(tok);
1428 do_job_delete(ua, JobId);
1431 sep = strchr(tok, ',');
1433 /* pick up the last token */
1434 if (!delete_job_id_range(ua, tok)) {
1435 JobId = str_to_int64(tok);
1436 do_job_delete(ua, JobId);
1441 JobId = str_to_int64(ua->argv[i]);
1442 do_job_delete(ua, JobId);
1444 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1447 JobId = ua->int64_val;
1448 do_job_delete(ua, JobId);
1453 * we call delete_job_id_range to parse range tokens and iterate over ranges
1455 static bool delete_job_id_range(UAContext *ua, char *tok)
1460 tok2 = strchr(tok, '-');
1466 j1 = str_to_int64(tok);
1467 j2 = str_to_int64(tok2);
1468 for (j=j1; j<=j2; j++) {
1469 do_job_delete(ua, j);
1475 * do_job_delete now performs the actual delete operation atomically
1477 static void do_job_delete(UAContext *ua, JobId_t JobId)
1481 edit_int64(JobId, ed1);
1482 purge_jobs_from_catalog(ua, ed1);
1483 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1487 * Delete media records from database -- dangerous
1489 static int delete_volume(UAContext *ua)
1495 if (!select_media_dbr(ua, &mr)) {
1498 ua->warning_msg(_("\nThis command will delete volume %s\n"
1499 "and all Jobs saved on that volume from the Catalog\n"),
1502 if (find_arg(ua, "yes") >= 0) {
1503 ua->pint32_val = 1; /* Have "yes" on command line already" */
1505 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1507 if (!get_yesno(ua, buf)) {
1511 if (!ua->pint32_val) {
1515 /* If not purged, do it */
1516 if (strcmp(mr.VolStatus, "Purged") != 0) {
1517 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1518 ua->error_msg(_("Can't list jobs on this volume\n"));
1522 purge_jobs_from_catalog(ua, lst.list);
1526 db_delete_media_record(ua->jcr, ua->db, &mr);
1531 * Delete a pool record from the database -- dangerous
1533 static int delete_pool(UAContext *ua)
1538 memset(&pr, 0, sizeof(pr));
1540 if (!get_pool_dbr(ua, &pr)) {
1543 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1545 if (!get_yesno(ua, buf)) {
1548 if (ua->pint32_val) {
1549 db_delete_pool_record(ua->jcr, ua->db, &pr);
1554 int memory_cmd(UAContext *ua, const char *cmd)
1556 list_dir_status_header(ua);
1557 sm_dump(false, true);
1561 static void do_mount_cmd(UAContext *ua, const char *command)
1566 char dev_name[MAX_NAME_LENGTH];
1570 if (!open_client_db(ua)) {
1573 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1575 store.store = get_storage_resource(ua, true/*arg is storage*/);
1579 pm_strcpy(store.store_source, _("unknown source"));
1580 set_wstorage(jcr, &store);
1581 drive = get_storage_drive(ua, store.store);
1582 if (strcmp(command, "mount") == 0) {
1583 slot = get_storage_slot(ua, store.store);
1586 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1587 store.store->media_type, store.store->dev_name(), drive);
1589 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1590 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1593 sd = jcr->store_bsock;
1594 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1595 bash_spaces(dev_name);
1597 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1599 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1601 while (bnet_recv(sd) >= 0) {
1602 ua->send_msg("%s", sd->msg);
1604 bnet_sig(sd, BNET_TERMINATE);
1606 jcr->store_bsock = NULL;
1610 * mount [storage=<name>] [drive=nn] [slot=mm]
1612 static int mount_cmd(UAContext *ua, const char *cmd)
1614 do_mount_cmd(ua, "mount"); /* mount */
1620 * unmount [storage=<name>] [drive=nn]
1622 static int unmount_cmd(UAContext *ua, const char *cmd)
1624 do_mount_cmd(ua, "unmount"); /* unmount */
1630 * release [storage=<name>] [drive=nn]
1632 static int release_cmd(UAContext *ua, const char *cmd)
1634 do_mount_cmd(ua, "release"); /* release */
1641 * use catalog=<name>
1643 static int use_cmd(UAContext *ua, const char *cmd)
1645 CAT *oldcatalog, *catalog;
1648 close_db(ua); /* close any previously open db */
1649 oldcatalog = ua->catalog;
1651 if (!(catalog = get_catalog_resource(ua))) {
1652 ua->catalog = oldcatalog;
1654 ua->catalog = catalog;
1657 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1658 ua->catalog->name(), ua->catalog->db_name);
1663 int quit_cmd(UAContext *ua, const char *cmd)
1669 /* Handler to get job status */
1670 static int status_handler(void *ctx, int num_fields, char **row)
1672 char *val = (char *)ctx;
1677 *val = '?'; /* Unknown by default */
1684 * Wait until no job is running
1686 int wait_cmd(UAContext *ua, const char *cmd)
1690 time_t stop_time = 0;
1694 * Wait until no job is running
1696 if (ua->argc == 1) {
1697 bmicrosleep(0, 200000); /* let job actually start */
1698 for (bool running=true; running; ) {
1701 if (jcr->JobId != 0) {
1715 i = find_arg_with_value(ua, NT_("timeout"));
1716 if (i > 0 && ua->argv[i]) {
1717 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1720 /* we have jobid, jobname or ujobid argument */
1722 uint32_t jobid = 0 ;
1724 if (!open_client_db(ua)) {
1725 ua->error_msg(_("ERR: Can't open db\n")) ;
1729 for (int i=1; i<ua->argc; i++) {
1730 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1734 jobid = str_to_int64(ua->argv[i]);
1736 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1737 strcasecmp(ua->argk[i], "job") == 0) {
1741 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1743 jobid = jcr->JobId ;
1747 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1751 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1753 jobid = jcr->JobId ;
1757 /* Wait for a mount request */
1758 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1759 for (bool waiting=false; !waiting; ) {
1761 if (jcr->JobId != 0 &&
1762 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1771 if (stop_time && (time(NULL) >= stop_time)) {
1772 ua->warning_msg(_("Wait on mount timed out\n"));
1782 ua->error_msg(_("ERR: Job was not found\n"));
1787 * We wait the end of a specific job
1790 bmicrosleep(0, 200000); /* let job actually start */
1791 for (bool running=true; running; ) {
1794 jcr=get_jcr_by_id(jobid) ;
1807 * We have to get JobStatus
1811 char jobstatus = '?'; /* Unknown by default */
1814 bsnprintf(buf, sizeof(buf),
1815 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1818 db_sql_query(ua->db, buf,
1819 status_handler, (void *)&jobstatus);
1821 switch (jobstatus) {
1823 status = 1 ; /* Warning */
1827 case JS_ErrorTerminated:
1829 status = 2 ; /* Critical */
1834 status = 0 ; /* Ok */
1838 status = 3 ; /* Unknown */
1842 ua->send_msg("JobId=%i\n", jobid) ;
1843 ua->send_msg("JobStatus=%s (%c)\n",
1844 job_status_to_str(jobstatus),
1847 if (ua->gui || ua->api) {
1848 ua->send_msg("ExitStatus=%i\n", status) ;
1855 static int help_cmd(UAContext *ua, const char *cmd)
1858 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1859 for (i=0; i<comsize; i++) {
1860 if (ua->argc == 2) {
1861 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1862 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1863 commands[i].help, commands[i].usage);
1867 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1870 if (i == comsize && ua->argc == 2) {
1871 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1873 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1877 int qhelp_cmd(UAContext *ua, const char *cmd)
1880 /* Want to display only commands */
1881 j = find_arg(ua, NT_("all"));
1883 for (i=0; i<comsize; i++) {
1884 ua->send_msg("%s\n", commands[i].key);
1888 /* Want to display a specific help section */
1889 j = find_arg_with_value(ua, NT_("item"));
1890 if (j >= 0 && ua->argk[j]) {
1891 for (i=0; i<comsize; i++) {
1892 if (bstrcmp(commands[i].key, ua->argv[j])) {
1893 ua->send_msg("%s\n", commands[i].usage);
1899 /* Want to display everything */
1900 for (i=0; i<comsize; i++) {
1901 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1907 static int version_cmd(UAContext *ua, const char *cmd)
1909 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1910 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1915 * Test code -- turned on only for debug testing
1917 static int version_cmd(UAContext *ua, const char *cmd)
1920 POOL_MEM query(PM_MESSAGE);
1922 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1923 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1924 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1925 for (int i=0; i < ids.num_ids; i++) {
1926 ua->send_msg("id=%d\n", ids.DBId[i]);
1934 * This call uses open_client_db() and force a
1935 * new dedicated connection to the catalog
1937 bool open_new_client_db(UAContext *ua)
1941 /* Force a new dedicated connection */
1943 ua->force_mult_db_connections = true;
1944 ret = open_client_db(ua);
1945 ua->force_mult_db_connections = false;
1950 * This call explicitly checks for a catalog=xxx and
1951 * if given, opens that catalog. It also checks for
1952 * client=xxx and if found, opens the catalog
1953 * corresponding to that client. If we still don't
1954 * have a catalog, look for a Job keyword and get the
1955 * catalog from its client record.
1957 bool open_client_db(UAContext *ua)
1964 /* Try for catalog keyword */
1965 i = find_arg_with_value(ua, NT_("catalog"));
1967 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1968 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1971 catalog = GetCatalogResWithName(ua->argv[i]);
1973 if (ua->catalog && ua->catalog != catalog) {
1976 ua->catalog = catalog;
1981 /* Try for client keyword */
1982 i = find_arg_with_value(ua, NT_("client"));
1984 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1985 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1988 client = GetClientResWithName(ua->argv[i]);
1990 catalog = client->catalog;
1991 if (ua->catalog && ua->catalog != catalog) {
1994 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1995 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1998 ua->catalog = catalog;
2003 /* Try for Job keyword */
2004 i = find_arg_with_value(ua, NT_("job"));
2006 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2007 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2010 job = GetJobResWithName(ua->argv[i]);
2012 catalog = job->client->catalog;
2013 if (ua->catalog && ua->catalog != catalog) {
2016 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2017 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2020 ua->catalog = catalog;
2030 * Open the catalog database.
2032 bool open_db(UAContext *ua)
2040 ua->catalog = get_catalog_resource(ua);
2042 ua->error_msg( _("Could not find a Catalog resource\n"));
2047 /* Some modules like bvfs need their own catalog connection */
2048 mult_db_conn = ua->catalog->mult_db_connections;
2049 if (ua->force_mult_db_connections) {
2050 mult_db_conn = true;
2053 ua->jcr->catalog = ua->catalog;
2055 Dmsg0(100, "UA Open database\n");
2056 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2057 ua->catalog->db_user,
2058 ua->catalog->db_password, ua->catalog->db_address,
2059 ua->catalog->db_port, ua->catalog->db_socket,
2060 mult_db_conn, ua->catalog->disable_batch_insert);
2061 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2062 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2063 ua->catalog->db_name);
2065 ua->error_msg("%s", db_strerror(ua->db));
2070 ua->jcr->db = ua->db;
2072 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2074 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2078 void close_db(UAContext *ua)
2081 db_close_database(ua->jcr, ua->db);