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>"
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_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"),
179 NT_("limit=<nn-kbs> client=<client-name> jobid=<number> job=<job-name> ujobid=<unique-jobid>"), true},
181 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
182 { NT_("show"), show_cmd, _("Show resource records"),
183 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | disabled | all"), true},
185 { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
186 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
187 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
188 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
189 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
191 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
192 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
194 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
195 NT_("stats\n\tpool=<poolname>\n\tslots storage=<storage> scan"
196 "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
197 "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
198 "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
199 "\n\t enable=<yes/no> recyclepool=<pool> actiononpurge=<action>"),true},
200 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_(""), false},
201 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
202 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
203 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
204 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
207 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
210 * Execute a command from the UA
212 bool do_a_command(UAContext *ua)
218 BSOCK *user = ua->UA_sock;
221 Dmsg1(900, "Command: %s\n", ua->argk[0]);
226 while (ua->jcr->wstorage->size()) {
227 ua->jcr->wstorage->remove(0);
230 len = strlen(ua->argk[0]);
231 for (i=0; i<comsize; i++) { /* search for command */
232 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
233 /* Check if command permitted, but "quit" is always OK */
234 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
235 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
238 /* Check if this command is authorized in RunScript */
239 if (ua->runscript && !commands[i].use_in_rs) {
240 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
243 if (ua->api) user->signal(BNET_CMD_BEGIN);
244 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
245 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
251 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
258 * This is a common routine used to stuff the Pool DB record defaults
259 * into the Media DB record just before creating a media (Volume)
262 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
264 mr->PoolId = pr->PoolId;
265 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
266 mr->Recycle = pr->Recycle;
267 mr->VolRetention = pr->VolRetention;
268 mr->VolUseDuration = pr->VolUseDuration;
269 mr->ActionOnPurge = pr->ActionOnPurge;
270 mr->RecyclePoolId = pr->RecyclePoolId;
271 mr->MaxVolJobs = pr->MaxVolJobs;
272 mr->MaxVolFiles = pr->MaxVolFiles;
273 mr->MaxVolBytes = pr->MaxVolBytes;
274 mr->LabelType = pr->LabelType;
280 * Add Volumes to an existing Pool
282 static int add_cmd(UAContext *ua, const char *cmd)
286 int num, i, max, startnum;
288 char name[MAX_NAME_LENGTH];
290 int Slot = 0, InChanger = 0;
293 "You probably don't want to be using this command since it\n"
294 "creates database records without labeling the Volumes.\n"
295 "You probably want to use the \"label\" command.\n\n"));
297 if (!open_client_db(ua)) {
301 memset(&pr, 0, sizeof(pr));
302 memset(&mr, 0, sizeof(mr));
304 if (!get_pool_dbr(ua, &pr)) {
308 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
309 pr.MaxVols, pr.PoolType);
311 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
312 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
313 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
316 pr.MaxVols = ua->pint32_val;
320 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
321 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
322 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
326 if (pr.MaxVols == 0) {
329 max = pr.MaxVols - pr.NumVols;
333 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
334 if (!get_pint(ua, buf)) {
337 num = ua->pint32_val;
338 if (num < 0 || num > max) {
339 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
347 if (!get_cmd(ua, _("Enter Volume name: "))) {
351 if (!get_cmd(ua, _("Enter base volume name: "))) {
355 /* Don't allow | in Volume name because it is the volume separator character */
356 if (!is_volume_name_legal(ua, ua->cmd)) {
359 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
360 ua->warning_msg(_("Volume name too long.\n"));
363 if (strlen(ua->cmd) == 0) {
364 ua->warning_msg(_("Volume name must be at least one character long.\n"));
370 bstrncpy(name, ua->cmd, sizeof(name));
372 bstrncat(name, "%04d", sizeof(name));
375 if (!get_pint(ua, _("Enter the starting number: "))) {
378 startnum = ua->pint32_val;
380 ua->warning_msg(_("Start number must be greater than zero.\n"));
390 if (store && store->autochanger) {
391 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
394 Slot = ua->pint32_val;
395 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
398 InChanger = ua->pint32_val;
401 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
402 for (i=startnum; i < num+startnum; i++) {
403 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
405 mr.InChanger = InChanger;
406 mr.StorageId = store->StorageId;
408 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
409 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
410 ua->error_msg("%s", db_strerror(ua->db));
414 first_id = mr.PoolId;
418 Dmsg0(200, "Update pool record.\n");
419 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
420 ua->warning_msg("%s", db_strerror(ua->db));
423 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
429 * Turn auto mount on/off
434 int automount_cmd(UAContext *ua, const char *cmd)
439 if (!get_cmd(ua, _("Turn on or off? "))) {
447 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
454 static int cancel_cmd(UAContext *ua, const char *cmd)
456 JCR *jcr = select_running_job(ua, "cancel");
460 int ret = cancel_job(ua, jcr);
466 * This is a common routine to create or update a
467 * Pool DB base record from a Pool Resource. We handle
468 * the setting of MaxVols and NumVols slightly differently
469 * depending on if we are creating the Pool or we are
470 * simply bringing it into agreement with the resource (updage).
472 * Caution : RecyclePoolId isn't setup in this function.
473 * You can use set_pooldbr_recyclepoolid();
476 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
478 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
479 if (op == POOL_OP_CREATE) {
480 pr->MaxVols = pool->max_volumes;
482 } else { /* update pool */
483 if (pr->MaxVols != pool->max_volumes) {
484 pr->MaxVols = pool->max_volumes;
486 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
487 pr->MaxVols = pr->NumVols;
490 pr->LabelType = pool->LabelType;
491 pr->UseOnce = pool->use_volume_once;
492 pr->UseCatalog = pool->use_catalog;
493 pr->Recycle = pool->Recycle;
494 pr->VolRetention = pool->VolRetention;
495 pr->VolUseDuration = pool->VolUseDuration;
496 pr->MaxVolJobs = pool->MaxVolJobs;
497 pr->MaxVolFiles = pool->MaxVolFiles;
498 pr->MaxVolBytes = pool->MaxVolBytes;
499 pr->AutoPrune = pool->AutoPrune;
500 pr->ActionOnPurge = pool->action_on_purge;
501 pr->Recycle = pool->Recycle;
502 if (pool->label_format) {
503 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
505 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
509 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
510 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
514 if (!pool->RecyclePool && !pool->ScratchPool) {
518 memset(&pr, 0, sizeof(POOL_DBR));
519 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
521 if (!db_get_pool_record(jcr, db, &pr)) {
522 return -1; /* not exists in database */
525 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
527 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
528 return -1; /* error */
531 if (!db_update_pool_record(jcr, db, &pr)) {
532 return -1; /* error */
537 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
538 * works with set_pooldbr_from_poolres
540 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
545 if (pool->RecyclePool) {
546 memset(&rpool, 0, sizeof(POOL_DBR));
548 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
549 if (db_get_pool_record(jcr, db, &rpool)) {
550 pr->RecyclePoolId = rpool.PoolId;
552 Jmsg(jcr, M_WARNING, 0,
553 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
554 "Try to update it with 'update pool=%s'\n"),
555 pool->name(), rpool.Name, rpool.Name,pool->name());
559 } else { /* no RecyclePool used, set it to 0 */
560 pr->RecyclePoolId = 0;
563 if (pool->ScratchPool) {
564 memset(&rpool, 0, sizeof(POOL_DBR));
566 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
567 if (db_get_pool_record(jcr, db, &rpool)) {
568 pr->ScratchPoolId = rpool.PoolId;
570 Jmsg(jcr, M_WARNING, 0,
571 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
572 "Try to update it with 'update pool=%s'\n"),
573 pool->name(), rpool.Name, rpool.Name,pool->name());
576 } else { /* no ScratchPool used, set it to 0 */
577 pr->ScratchPoolId = 0;
585 * Create a pool record from a given Pool resource
586 * Also called from backup.c
587 * Returns: -1 on error
588 * 0 record already exists
592 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
596 memset(&pr, 0, sizeof(POOL_DBR));
598 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
600 if (db_get_pool_record(jcr, db, &pr)) {
602 if (op == POOL_OP_UPDATE) { /* update request */
603 set_pooldbr_from_poolres(&pr, pool, op);
604 set_pooldbr_references(jcr, db, &pr, pool);
605 db_update_pool_record(jcr, db, &pr);
607 return 0; /* exists */
610 set_pooldbr_from_poolres(&pr, pool, op);
611 set_pooldbr_references(jcr, db, &pr, pool);
613 if (!db_create_pool_record(jcr, db, &pr)) {
614 return -1; /* error */
622 * Create a Pool Record in the database.
623 * It is always created from the Resource record.
625 static int create_cmd(UAContext *ua, const char *cmd)
629 if (!open_client_db(ua)) {
633 pool = get_pool_resource(ua);
638 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
640 ua->error_msg(_("Error: Pool %s already exists.\n"
641 "Use update to change it.\n"), pool->name());
645 ua->error_msg("%s", db_strerror(ua->db));
651 ua->send_msg(_("Pool %s created.\n"), pool->name());
656 extern DIRRES *director;
657 extern char *configfile;
660 * Python control command
661 * python restart (restarts interpreter)
663 static int python_cmd(UAContext *ua, const char *cmd)
666 init_python_interpreter_args python_args;
668 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
669 term_python_interpreter();
671 python_args.progname = director->name();
672 python_args.scriptdir = director->scripts_directory;
673 python_args.modulename = "DirStartUp";
674 python_args.configfile = configfile;
675 python_args.workingdir = director->working_directory;
676 python_args.job_getattr = job_getattr;
677 python_args.job_setattr = job_setattr;
679 init_python_interpreter(&python_args);
681 ua->send_msg(_("Python interpreter restarted.\n"));
683 #endif /* HAVE_PYTHON */
684 ua->warning_msg(_("Nothing done.\n"));
687 #endif /* HAVE_PYTHON */
691 static int setbwlimit_cmd(UAContext *ua, const char *cmd)
694 char Job[MAX_NAME_LENGTH];
699 i = find_arg_with_value(ua, "limit");
701 limit = atoi(ua->argv[i]) * 1024;
704 if (!get_pint(ua, _("Enter new bandwidth limit kb/s: "))) {
707 limit = ua->pint32_val * 1024; /* kb/s */
710 const char *lst[] = { "job", "jobid", "jobname", NULL };
711 if (find_arg_keyword(ua, lst) > 0) {
712 JCR *jcr = select_running_job(ua, "limit");
714 jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
715 bstrncpy(Job, jcr->Job, sizeof(Job));
716 client = jcr->client;
723 client = get_client_resource(ua);
730 /* Connect to File daemon */
731 ua->jcr->client = client;
732 ua->jcr->max_bandwidth = limit;
734 /* Try to connect for 15 seconds */
735 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
736 client->name(), client->address, client->FDport);
737 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
738 ua->error_msg(_("Failed to connect to Client.\n"));
741 Dmsg0(120, "Connected to file daemon\n");
742 if (!send_bwlimit(ua->jcr, Job)) {
743 ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
746 ua->info_msg(_("OK Limiting bandwidth to %lldkb/s %s\n"),
750 ua->jcr->file_bsock->signal(BNET_TERMINATE);
751 ua->jcr->file_bsock->close();
752 ua->jcr->file_bsock = NULL;
753 ua->jcr->client = NULL;
754 ua->jcr->max_bandwidth = 0;
759 * Set a new address in a Client resource. We do this only
760 * if the Console name is the same as the Client name
761 * and the Console can access the client.
763 static int setip_cmd(UAContext *ua, const char *cmd)
767 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
768 ua->error_msg(_("Unauthorized command from this console.\n"));
772 client = GetClientResWithName(ua->cons->name());
775 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
778 if (client->address) {
779 free(client->address);
781 /* MA Bug 6 remove ifdef */
782 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
783 client->address = bstrdup(buf);
784 ua->send_msg(_("Client \"%s\" address set to %s\n"),
785 client->name(), client->address);
792 static void do_en_disable_cmd(UAContext *ua, bool setting)
797 i = find_arg_with_value(ua, NT_("job"));
799 job = select_enable_disable_job_resource(ua, setting);
805 job = GetJobResWithName(ua->argv[i]);
809 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
813 if (!acl_access_ok(ua, Job_ACL, job->name())) {
814 ua->error_msg(_("Unauthorized command from this console.\n"));
817 job->enabled = setting;
818 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
822 static int enable_cmd(UAContext *ua, const char *cmd)
824 do_en_disable_cmd(ua, true);
828 static int disable_cmd(UAContext *ua, const char *cmd)
830 do_en_disable_cmd(ua, false);
835 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
841 lstore.store = store;
842 pm_strcpy(lstore.store_source, _("unknown source"));
843 set_wstorage(jcr, &lstore);
844 /* Try connecting for up to 15 seconds */
845 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
846 store->name(), store->address, store->SDport);
847 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
848 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
851 Dmsg0(120, _("Connected to storage daemon\n"));
852 sd = jcr->store_bsock;
853 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
854 if (sd->recv() >= 0) {
855 ua->send_msg("%s", sd->msg);
857 sd->signal(BNET_TERMINATE);
859 jcr->store_bsock = NULL;
863 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
867 /* Connect to File daemon */
869 ua->jcr->client = client;
870 /* Try to connect for 15 seconds */
871 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
872 client->name(), client->address, client->FDport);
873 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
874 ua->error_msg(_("Failed to connect to Client.\n"));
877 Dmsg0(120, "Connected to file daemon\n");
878 fd = ua->jcr->file_bsock;
879 fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
880 if (fd->recv() >= 0) {
881 ua->send_msg("%s", fd->msg);
883 fd->signal(BNET_TERMINATE);
885 ua->jcr->file_bsock = NULL;
890 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
892 STORE *store, **unique_store;
893 CLIENT *client, **unique_client;
899 /* Count Storage items */
903 foreach_res(store, R_STORAGE) {
906 unique_store = (STORE **) malloc(i * sizeof(STORE));
907 /* Find Unique Storage address/port */
908 store = (STORE *)GetNextRes(R_STORAGE, NULL);
910 unique_store[i++] = store;
911 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
913 for (j=0; j<i; j++) {
914 if (strcmp(unique_store[j]->address, store->address) == 0 &&
915 unique_store[j]->SDport == store->SDport) {
921 unique_store[i++] = store;
922 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
927 /* Call each unique Storage daemon */
928 for (j=0; j<i; j++) {
929 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
933 /* Count Client items */
937 foreach_res(client, R_CLIENT) {
940 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
941 /* Find Unique Client address/port */
942 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
944 unique_client[i++] = client;
945 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
947 for (j=0; j<i; j++) {
948 if (strcmp(unique_client[j]->address, client->address) == 0 &&
949 unique_client[j]->FDport == client->FDport) {
955 unique_client[i++] = client;
956 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
961 /* Call each unique File daemon */
962 for (j=0; j<i; j++) {
963 do_client_setdebug(ua, unique_client[j], level, trace_flag);
969 * setdebug level=nn all trace=1/0
971 static int setdebug_cmd(UAContext *ua, const char *cmd)
979 Dmsg1(120, "setdebug:%s:\n", cmd);
982 i = find_arg_with_value(ua, "level");
984 level = atoi(ua->argv[i]);
987 if (!get_pint(ua, _("Enter new debug level: "))) {
990 level = ua->pint32_val;
993 /* Look for trace flag. -1 => not change */
994 i = find_arg_with_value(ua, "trace");
996 trace_flag = atoi(ua->argv[i]);
997 if (trace_flag > 0) {
1002 /* General debug? */
1003 for (i=1; i<ua->argc; i++) {
1004 if (strcasecmp(ua->argk[i], "all") == 0) {
1005 do_all_setdebug(ua, level, trace_flag);
1008 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1009 strcasecmp(ua->argk[i], "director") == 0) {
1010 debug_level = level;
1011 set_trace(trace_flag);
1014 if (strcasecmp(ua->argk[i], "client") == 0 ||
1015 strcasecmp(ua->argk[i], "fd") == 0) {
1018 client = GetClientResWithName(ua->argv[i]);
1020 do_client_setdebug(ua, client, level, trace_flag);
1024 client = select_client_resource(ua);
1026 do_client_setdebug(ua, client, level, trace_flag);
1031 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1032 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1033 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1036 store = GetStoreResWithName(ua->argv[i]);
1038 do_storage_setdebug(ua, store, level, trace_flag);
1042 store = get_storage_resource(ua, false/*no default*/);
1044 do_storage_setdebug(ua, store, level, trace_flag);
1050 * We didn't find an appropriate keyword above, so
1053 start_prompt(ua, _("Available daemons are: \n"));
1054 add_prompt(ua, _("Director"));
1055 add_prompt(ua, _("Storage"));
1056 add_prompt(ua, _("Client"));
1057 add_prompt(ua, _("All"));
1058 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1059 case 0: /* Director */
1060 debug_level = level;
1061 set_trace(trace_flag);
1064 store = get_storage_resource(ua, false/*no default*/);
1066 do_storage_setdebug(ua, store, level, trace_flag);
1070 client = select_client_resource(ua);
1072 do_client_setdebug(ua, client, level, trace_flag);
1076 do_all_setdebug(ua, level, trace_flag);
1085 * Turn debug tracing to file on/off
1087 static int trace_cmd(UAContext *ua, const char *cmd)
1091 if (ua->argc != 2) {
1092 if (!get_cmd(ua, _("Turn on or off? "))) {
1097 onoff = ua->argk[1];
1100 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1105 static int var_cmd(UAContext *ua, const char *cmd)
1107 POOLMEM *val = get_pool_memory(PM_FNAME);
1110 if (!open_client_db(ua)) {
1113 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1116 while (*var == ' ') { /* skip spaces */
1119 Dmsg1(100, "Var=%s:\n", var);
1120 variable_expansion(ua->jcr, var, &val);
1121 ua->send_msg("%s\n", val);
1122 free_pool_memory(val);
1126 static int estimate_cmd(UAContext *ua, const char *cmd)
1129 CLIENT *client = NULL;
1130 FILESET *fileset = NULL;
1132 char since[MAXSTRING];
1136 jcr->set_JobLevel(L_FULL);
1137 for (int i=1; i<ua->argc; i++) {
1138 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1139 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1141 client = GetClientResWithName(ua->argv[i]);
1143 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1146 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1147 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1152 ua->error_msg(_("Client name missing.\n"));
1156 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1158 job = GetJobResWithName(ua->argv[i]);
1160 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1163 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1164 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1169 ua->error_msg(_("Job name missing.\n"));
1174 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1176 fileset = GetFileSetResWithName(ua->argv[i]);
1178 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1181 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1182 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1187 ua->error_msg(_("Fileset name missing.\n"));
1191 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1195 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1197 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1198 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1202 ua->error_msg(_("Level value missing.\n"));
1206 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1207 if (!is_yesno(ua->argv[i], &accurate)) {
1208 ua->error_msg(_("Invalid value for accurate. "
1209 "It must be yes or no.\n"));
1213 if (!job && !(client && fileset)) {
1214 if (!(job = select_job_resource(ua))) {
1219 job = GetJobResWithName(ua->argk[1]);
1221 ua->error_msg(_("No job specified.\n"));
1224 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1225 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1230 client = job->client;
1233 fileset = job->fileset;
1235 jcr->client = client;
1236 jcr->fileset = fileset;
1238 if (job->pool->catalog) {
1239 ua->catalog = job->pool->catalog;
1241 ua->catalog = client->catalog;
1249 jcr->set_JobType(JT_BACKUP);
1250 init_jcr_job_record(jcr);
1252 if (!get_or_create_client_record(jcr)) {
1255 if (!get_or_create_fileset_record(jcr)) {
1259 get_level_since_time(ua->jcr, since, sizeof(since));
1261 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1262 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1263 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1264 ua->error_msg(_("Failed to connect to Client.\n"));
1268 if (!send_include_list(jcr)) {
1269 ua->error_msg(_("Error sending include list.\n"));
1273 if (!send_exclude_list(jcr)) {
1274 ua->error_msg(_("Error sending exclude list.\n"));
1278 /* The level string change if accurate mode is enabled */
1279 if (accurate >= 0) {
1280 jcr->accurate = accurate;
1282 jcr->accurate = job->accurate;
1285 if (!send_level_command(jcr)) {
1290 * If the job is in accurate mode, we send the list of
1293 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1294 if (!send_accurate_current_files(jcr)) {
1298 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1299 while (bnet_recv(jcr->file_bsock) >= 0) {
1300 ua->send_msg("%s", jcr->file_bsock->msg);
1304 if (jcr->file_bsock) {
1305 jcr->file_bsock->signal(BNET_TERMINATE);
1306 jcr->file_bsock->close();
1307 jcr->file_bsock = NULL;
1316 static int time_cmd(UAContext *ua, const char *cmd)
1319 time_t ttime = time(NULL);
1321 (void)localtime_r(&ttime, &tm);
1322 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1323 ua->send_msg("%s\n", sdt);
1328 * reload the conf file
1330 extern "C" void reload_config(int sig);
1332 static int reload_cmd(UAContext *ua, const char *cmd)
1339 * Delete Pool records (should purge Media with it).
1341 * delete pool=<pool-name>
1342 * delete volume pool=<pool-name> volume=<name>
1345 static int delete_cmd(UAContext *ua, const char *cmd)
1347 static const char *keywords[] = {
1353 if (!open_client_db(ua)) {
1357 switch (find_arg_keyword(ua, keywords)) {
1366 while ((i=find_arg(ua, "jobid")) > 0) {
1368 *ua->argk[i] = 0; /* zap keyword already visited */
1376 "In general it is not a good idea to delete either a\n"
1377 "Pool or a Volume since they may contain data.\n\n"));
1379 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1390 ua->warning_msg(_("Nothing done.\n"));
1398 * delete_job has been modified to parse JobID lists like the
1400 * delete JobID=3,4,6,7-11,14
1402 * Thanks to Phil Stracchino for the above addition.
1405 static void delete_job(UAContext *ua)
1410 int i = find_arg_with_value(ua, NT_("jobid"));
1412 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1413 s = bstrdup(ua->argv[i]);
1416 * We could use strtok() here. But we're not going to, because:
1417 * (a) strtok() is deprecated, having been replaced by strsep();
1418 * (b) strtok() is broken in significant ways.
1419 * we could use strsep() instead, but it's not universally available.
1420 * so we grow our own using strchr().
1422 sep = strchr(tok, ',');
1423 while (sep != NULL) {
1425 if (!delete_job_id_range(ua, tok)) {
1426 JobId = str_to_int64(tok);
1427 do_job_delete(ua, JobId);
1430 sep = strchr(tok, ',');
1432 /* pick up the last token */
1433 if (!delete_job_id_range(ua, tok)) {
1434 JobId = str_to_int64(tok);
1435 do_job_delete(ua, JobId);
1440 JobId = str_to_int64(ua->argv[i]);
1441 do_job_delete(ua, JobId);
1443 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1446 JobId = ua->int64_val;
1447 do_job_delete(ua, JobId);
1452 * we call delete_job_id_range to parse range tokens and iterate over ranges
1454 static bool delete_job_id_range(UAContext *ua, char *tok)
1459 tok2 = strchr(tok, '-');
1465 j1 = str_to_int64(tok);
1466 j2 = str_to_int64(tok2);
1467 for (j=j1; j<=j2; j++) {
1468 do_job_delete(ua, j);
1474 * do_job_delete now performs the actual delete operation atomically
1476 static void do_job_delete(UAContext *ua, JobId_t JobId)
1480 edit_int64(JobId, ed1);
1481 purge_jobs_from_catalog(ua, ed1);
1482 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1486 * Delete media records from database -- dangerous
1488 static int delete_volume(UAContext *ua)
1494 if (!select_media_dbr(ua, &mr)) {
1497 ua->warning_msg(_("\nThis command will delete volume %s\n"
1498 "and all Jobs saved on that volume from the Catalog\n"),
1501 if (find_arg(ua, "yes") >= 0) {
1502 ua->pint32_val = 1; /* Have "yes" on command line already" */
1504 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1506 if (!get_yesno(ua, buf)) {
1510 if (!ua->pint32_val) {
1514 /* If not purged, do it */
1515 if (strcmp(mr.VolStatus, "Purged") != 0) {
1516 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1517 ua->error_msg(_("Can't list jobs on this volume\n"));
1521 purge_jobs_from_catalog(ua, lst.list);
1525 db_delete_media_record(ua->jcr, ua->db, &mr);
1530 * Delete a pool record from the database -- dangerous
1532 static int delete_pool(UAContext *ua)
1537 memset(&pr, 0, sizeof(pr));
1539 if (!get_pool_dbr(ua, &pr)) {
1542 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1544 if (!get_yesno(ua, buf)) {
1547 if (ua->pint32_val) {
1548 db_delete_pool_record(ua->jcr, ua->db, &pr);
1553 int memory_cmd(UAContext *ua, const char *cmd)
1555 list_dir_status_header(ua);
1556 sm_dump(false, true);
1560 static void do_mount_cmd(UAContext *ua, const char *command)
1565 char dev_name[MAX_NAME_LENGTH];
1569 if (!open_client_db(ua)) {
1572 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1574 store.store = get_storage_resource(ua, true/*arg is storage*/);
1578 pm_strcpy(store.store_source, _("unknown source"));
1579 set_wstorage(jcr, &store);
1580 drive = get_storage_drive(ua, store.store);
1581 if (strcmp(command, "mount") == 0) {
1582 slot = get_storage_slot(ua, store.store);
1585 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1586 store.store->media_type, store.store->dev_name(), drive);
1588 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1589 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1592 sd = jcr->store_bsock;
1593 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1594 bash_spaces(dev_name);
1596 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1598 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1600 while (bnet_recv(sd) >= 0) {
1601 ua->send_msg("%s", sd->msg);
1603 bnet_sig(sd, BNET_TERMINATE);
1605 jcr->store_bsock = NULL;
1609 * mount [storage=<name>] [drive=nn] [slot=mm]
1611 static int mount_cmd(UAContext *ua, const char *cmd)
1613 do_mount_cmd(ua, "mount"); /* mount */
1619 * unmount [storage=<name>] [drive=nn]
1621 static int unmount_cmd(UAContext *ua, const char *cmd)
1623 do_mount_cmd(ua, "unmount"); /* unmount */
1629 * release [storage=<name>] [drive=nn]
1631 static int release_cmd(UAContext *ua, const char *cmd)
1633 do_mount_cmd(ua, "release"); /* release */
1640 * use catalog=<name>
1642 static int use_cmd(UAContext *ua, const char *cmd)
1644 CAT *oldcatalog, *catalog;
1647 close_db(ua); /* close any previously open db */
1648 oldcatalog = ua->catalog;
1650 if (!(catalog = get_catalog_resource(ua))) {
1651 ua->catalog = oldcatalog;
1653 ua->catalog = catalog;
1656 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1657 ua->catalog->name(), ua->catalog->db_name);
1662 int quit_cmd(UAContext *ua, const char *cmd)
1668 /* Handler to get job status */
1669 static int status_handler(void *ctx, int num_fields, char **row)
1671 char *val = (char *)ctx;
1676 *val = '?'; /* Unknown by default */
1683 * Wait until no job is running
1685 int wait_cmd(UAContext *ua, const char *cmd)
1689 time_t stop_time = 0;
1693 * Wait until no job is running
1695 if (ua->argc == 1) {
1696 bmicrosleep(0, 200000); /* let job actually start */
1697 for (bool running=true; running; ) {
1700 if (jcr->JobId != 0) {
1714 i = find_arg_with_value(ua, NT_("timeout"));
1715 if (i > 0 && ua->argv[i]) {
1716 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1719 /* we have jobid, jobname or ujobid argument */
1721 uint32_t jobid = 0 ;
1723 if (!open_client_db(ua)) {
1724 ua->error_msg(_("ERR: Can't open db\n")) ;
1728 for (int i=1; i<ua->argc; i++) {
1729 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1733 jobid = str_to_int64(ua->argv[i]);
1735 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1736 strcasecmp(ua->argk[i], "job") == 0) {
1740 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1742 jobid = jcr->JobId ;
1746 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1750 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1752 jobid = jcr->JobId ;
1756 /* Wait for a mount request */
1757 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1758 for (bool waiting=false; !waiting; ) {
1760 if (jcr->JobId != 0 &&
1761 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1770 if (stop_time && (time(NULL) >= stop_time)) {
1771 ua->warning_msg(_("Wait on mount timed out\n"));
1781 ua->error_msg(_("ERR: Job was not found\n"));
1786 * We wait the end of a specific job
1789 bmicrosleep(0, 200000); /* let job actually start */
1790 for (bool running=true; running; ) {
1793 jcr=get_jcr_by_id(jobid) ;
1806 * We have to get JobStatus
1810 char jobstatus = '?'; /* Unknown by default */
1813 bsnprintf(buf, sizeof(buf),
1814 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1817 db_sql_query(ua->db, buf,
1818 status_handler, (void *)&jobstatus);
1820 switch (jobstatus) {
1822 status = 1 ; /* Warning */
1826 case JS_ErrorTerminated:
1828 status = 2 ; /* Critical */
1833 status = 0 ; /* Ok */
1837 status = 3 ; /* Unknown */
1841 ua->send_msg("JobId=%i\n", jobid) ;
1842 ua->send_msg("JobStatus=%s (%c)\n",
1843 job_status_to_str(jobstatus),
1846 if (ua->gui || ua->api) {
1847 ua->send_msg("ExitStatus=%i\n", status) ;
1854 static int help_cmd(UAContext *ua, const char *cmd)
1857 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1858 for (i=0; i<comsize; i++) {
1859 if (ua->argc == 2) {
1860 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1861 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1862 commands[i].help, commands[i].usage);
1866 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1869 if (i == comsize && ua->argc == 2) {
1870 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1872 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1876 int qhelp_cmd(UAContext *ua, const char *cmd)
1879 /* Want to display only commands */
1880 j = find_arg(ua, NT_("all"));
1882 for (i=0; i<comsize; i++) {
1883 ua->send_msg("%s\n", commands[i].key);
1887 /* Want to display a specific help section */
1888 j = find_arg_with_value(ua, NT_("item"));
1889 if (j >= 0 && ua->argk[j]) {
1890 for (i=0; i<comsize; i++) {
1891 if (bstrcmp(commands[i].key, ua->argv[j])) {
1892 ua->send_msg("%s\n", commands[i].usage);
1898 /* Want to display everything */
1899 for (i=0; i<comsize; i++) {
1900 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1906 static int version_cmd(UAContext *ua, const char *cmd)
1908 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1909 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1914 * Test code -- turned on only for debug testing
1916 static int version_cmd(UAContext *ua, const char *cmd)
1919 POOL_MEM query(PM_MESSAGE);
1921 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1922 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1923 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1924 for (int i=0; i < ids.num_ids; i++) {
1925 ua->send_msg("id=%d\n", ids.DBId[i]);
1933 * This call uses open_client_db() and force a
1934 * new dedicated connection to the catalog
1936 bool open_new_client_db(UAContext *ua)
1940 /* Force a new dedicated connection */
1942 ua->force_mult_db_connections = true;
1943 ret = open_client_db(ua);
1944 ua->force_mult_db_connections = false;
1949 * This call explicitly checks for a catalog=xxx and
1950 * if given, opens that catalog. It also checks for
1951 * client=xxx and if found, opens the catalog
1952 * corresponding to that client. If we still don't
1953 * have a catalog, look for a Job keyword and get the
1954 * catalog from its client record.
1956 bool open_client_db(UAContext *ua)
1963 /* Try for catalog keyword */
1964 i = find_arg_with_value(ua, NT_("catalog"));
1966 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1967 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1970 catalog = GetCatalogResWithName(ua->argv[i]);
1972 if (ua->catalog && ua->catalog != catalog) {
1975 ua->catalog = catalog;
1980 /* Try for client keyword */
1981 i = find_arg_with_value(ua, NT_("client"));
1983 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1984 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1987 client = GetClientResWithName(ua->argv[i]);
1989 catalog = client->catalog;
1990 if (ua->catalog && ua->catalog != catalog) {
1993 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1994 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1997 ua->catalog = catalog;
2002 /* Try for Job keyword */
2003 i = find_arg_with_value(ua, NT_("job"));
2005 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2006 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2009 job = GetJobResWithName(ua->argv[i]);
2011 catalog = job->client->catalog;
2012 if (ua->catalog && ua->catalog != catalog) {
2015 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2016 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2019 ua->catalog = catalog;
2029 * Open the catalog database.
2031 bool open_db(UAContext *ua)
2039 ua->catalog = get_catalog_resource(ua);
2041 ua->error_msg( _("Could not find a Catalog resource\n"));
2046 /* Some modules like bvfs need their own catalog connection */
2047 mult_db_conn = ua->catalog->mult_db_connections;
2048 if (ua->force_mult_db_connections) {
2049 mult_db_conn = true;
2052 ua->jcr->catalog = ua->catalog;
2054 Dmsg0(100, "UA Open database\n");
2055 ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2056 ua->catalog->db_user,
2057 ua->catalog->db_password, ua->catalog->db_address,
2058 ua->catalog->db_port, ua->catalog->db_socket,
2060 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2061 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2062 ua->catalog->db_name);
2064 ua->error_msg("%s", db_strerror(ua->db));
2069 ua->jcr->db = ua->db;
2071 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2073 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2077 void close_db(UAContext *ua)
2080 db_close_database(ua->jcr, ua->db);