2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2011 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
41 #undef _POSIX_C_SOURCE
44 #include "lib/pythonlib.h"
46 /* Imported Functions */
47 extern PyObject *job_getattr(PyObject *self, char *attrname);
48 extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
50 #endif /* HAVE_PYTHON */
52 /* Imported subroutines */
54 /* Imported variables */
55 extern jobq_t job_queue; /* job queue */
58 /* Imported functions */
59 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
60 extern int gui_cmd(UAContext *ua, const char *cmd);
61 extern int label_cmd(UAContext *ua, const char *cmd);
62 extern int list_cmd(UAContext *ua, const char *cmd);
63 extern int llist_cmd(UAContext *ua, const char *cmd);
64 extern int messagescmd(UAContext *ua, const char *cmd);
65 extern int prunecmd(UAContext *ua, const char *cmd);
66 extern int purgecmd(UAContext *ua, const char *cmd);
67 extern int querycmd(UAContext *ua, const char *cmd);
68 extern int relabel_cmd(UAContext *ua, const char *cmd);
69 extern int restore_cmd(UAContext *ua, const char *cmd);
70 extern int retentioncmd(UAContext *ua, const char *cmd);
71 extern int show_cmd(UAContext *ua, const char *cmd);
72 extern int sqlquerycmd(UAContext *ua, const char *cmd);
73 extern int status_cmd(UAContext *ua, const char *cmd);
74 extern int update_cmd(UAContext *ua, const char *cmd);
76 /* Forward referenced functions */
77 static int add_cmd(UAContext *ua, const char *cmd);
78 static int automount_cmd(UAContext *ua, const char *cmd);
79 static int cancel_cmd(UAContext *ua, const char *cmd);
80 static int create_cmd(UAContext *ua, const char *cmd);
81 static int delete_cmd(UAContext *ua, const char *cmd);
82 static int disable_cmd(UAContext *ua, const char *cmd);
83 static int enable_cmd(UAContext *ua, const char *cmd);
84 static int estimate_cmd(UAContext *ua, const char *cmd);
85 static int help_cmd(UAContext *ua, const char *cmd);
86 static int memory_cmd(UAContext *ua, const char *cmd);
87 static int mount_cmd(UAContext *ua, const char *cmd);
88 static int python_cmd(UAContext *ua, const char *cmd);
89 static int release_cmd(UAContext *ua, const char *cmd);
90 static int reload_cmd(UAContext *ua, const char *cmd);
91 static int setdebug_cmd(UAContext *ua, const char *cmd);
92 static int setbwlimit_cmd(UAContext *ua, const char *cmd);
93 static int setip_cmd(UAContext *ua, const char *cmd);
94 static int time_cmd(UAContext *ua, const char *cmd);
95 static int trace_cmd(UAContext *ua, const char *cmd);
96 static int unmount_cmd(UAContext *ua, const char *cmd);
97 static int use_cmd(UAContext *ua, const char *cmd);
98 static int var_cmd(UAContext *ua, const char *cmd);
99 static int version_cmd(UAContext *ua, const char *cmd);
100 static int wait_cmd(UAContext *ua, const char *cmd);
102 static void do_job_delete(UAContext *ua, JobId_t JobId);
103 static bool delete_job_id_range(UAContext *ua, char *tok);
104 static int delete_volume(UAContext *ua);
105 static int delete_pool(UAContext *ua);
106 static void delete_job(UAContext *ua);
108 int qhelp_cmd(UAContext *ua, const char *cmd);
109 int quit_cmd(UAContext *ua, const char *cmd);
111 /* not all in alphabetical order. New commands are added after existing commands with similar letters
112 to prevent breakage of existing user scripts. */
114 const char *key; /* command */
115 int (*func)(UAContext *ua, const char *cmd); /* handler */
116 const char *help; /* main purpose */
117 const char *usage; /* all arguments to build usage */
118 const bool use_in_rs; /* Can use it in Console RunScript */
120 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
121 { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
122 { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
123 { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
124 { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid=<number> job=<job-name> ujobid=<unique-jobid>"), false},
125 { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
126 { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume=<vol-name> pool=<pool-name> jobid=<id>"), true},
127 { NT_("disable"), disable_cmd, _("Disable a job"), NT_("job=<name>"), true},
128 { NT_("enable"), enable_cmd, _("Enable a job"), NT_("job=<name>"), true},
129 { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
130 NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
132 { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
133 { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
134 { NT_("help"), help_cmd, _("Print help on specific command"),
135 NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
136 "\n\tmessages memory mount prune purge python quit query\n\trestore relabel release reload run status"
137 "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"), false},
139 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> barcodes"), false},
140 { NT_("list"), list_cmd, _("List objects from catalog"),
141 NT_("pools | jobs | jobtotals | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
143 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
144 NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
146 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
147 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
148 { NT_("mount"), mount_cmd, _("Mount storage"),
149 NT_("storage=<storage-name> slot=<num> drive=<num> [ jobid=<id> | job=<job-name> ]"), false},
151 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
152 NT_("files | jobs | pool=<pool> | client=<client-name> | volume=<volume-name> "), true},
154 { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [action=<action> devicetype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
155 { NT_("python"), python_cmd, _("Python control commands"), NT_(""), false},
156 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
157 { NT_("query"), querycmd, _("Query catalog"), NT_(""), false},
158 { NT_("restore"), restore_cmd, _("Restore files"),
159 NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
161 "\n\tcomment=<text> jobid=<jobid> done select all"), false},
163 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
164 NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
166 { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name>"), false},
167 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
168 { NT_("run"), run_cmd, _("Run a job"),
169 NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
170 "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tcomment=<text> yes"), false},
172 { NT_("status"), status_cmd, _("Report status"),
173 NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
175 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
176 NT_("level=<nn> trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
178 { NT_("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);
834 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
840 lstore.store = store;
841 pm_strcpy(lstore.store_source, _("unknown source"));
842 set_wstorage(jcr, &lstore);
843 /* Try connecting for up to 15 seconds */
844 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
845 store->name(), store->address, store->SDport);
846 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
847 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
850 Dmsg0(120, _("Connected to storage daemon\n"));
851 sd = jcr->store_bsock;
852 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
853 if (sd->recv() >= 0) {
854 ua->send_msg("%s", sd->msg);
856 sd->signal(BNET_TERMINATE);
858 jcr->store_bsock = NULL;
863 * For the client, we have the following values that can be set
864 * level = debug level
865 * trace = send debug output to a file
866 * hangup = how many records to send to SD before hanging up
867 * obviously this is most useful for testing restarting
870 static void do_client_setdebug(UAContext *ua, CLIENT *client,
871 int level, int trace, int hangup)
875 /* Connect to File daemon */
877 ua->jcr->client = client;
878 /* Try to connect for 15 seconds */
879 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
880 client->name(), client->address, client->FDport);
881 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
882 ua->error_msg(_("Failed to connect to Client.\n"));
885 Dmsg0(120, "Connected to file daemon\n");
886 fd = ua->jcr->file_bsock;
887 fd->fsend("setdebug=%d trace=%d hangup=%d\n", level, trace, hangup);
888 if (fd->recv() >= 0) {
889 ua->send_msg("%s", fd->msg);
891 fd->signal(BNET_TERMINATE);
893 ua->jcr->file_bsock = NULL;
898 static void do_all_setdebug(UAContext *ua, int level, int trace_flag, int hangup)
900 STORE *store, **unique_store;
901 CLIENT *client, **unique_client;
907 /* Count Storage items */
911 foreach_res(store, R_STORAGE) {
914 unique_store = (STORE **) malloc(i * sizeof(STORE));
915 /* Find Unique Storage address/port */
916 store = (STORE *)GetNextRes(R_STORAGE, NULL);
918 unique_store[i++] = store;
919 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
921 for (j=0; j<i; j++) {
922 if (strcmp(unique_store[j]->address, store->address) == 0 &&
923 unique_store[j]->SDport == store->SDport) {
929 unique_store[i++] = store;
930 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
935 /* Call each unique Storage daemon */
936 for (j=0; j<i; j++) {
937 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
941 /* Count Client items */
945 foreach_res(client, R_CLIENT) {
948 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
949 /* Find Unique Client address/port */
950 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
952 unique_client[i++] = client;
953 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
955 for (j=0; j<i; j++) {
956 if (strcmp(unique_client[j]->address, client->address) == 0 &&
957 unique_client[j]->FDport == client->FDport) {
963 unique_client[i++] = client;
964 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
969 /* Call each unique File daemon */
970 for (j=0; j<i; j++) {
971 do_client_setdebug(ua, unique_client[j], level, trace_flag, hangup);
977 * setdebug level=nn all trace=1/0
979 static int setdebug_cmd(UAContext *ua, const char *cmd)
988 Dmsg1(120, "setdebug:%s:\n", cmd);
991 i = find_arg_with_value(ua, "level");
993 level = atoi(ua->argv[i]);
996 if (!get_pint(ua, _("Enter new debug level: "))) {
999 level = ua->pint32_val;
1002 /* Look for trace flag. -1 => not change */
1003 i = find_arg_with_value(ua, "trace");
1005 trace_flag = atoi(ua->argv[i]);
1006 if (trace_flag > 0) {
1011 /* Look for hangup (debug only)flag. -1 => not change */
1012 i = find_arg_with_value(ua, "hangup");
1014 hangup = atoi(ua->argv[i]);
1018 /* General debug? */
1019 for (i=1; i<ua->argc; i++) {
1020 if (strcasecmp(ua->argk[i], "all") == 0) {
1021 do_all_setdebug(ua, level, trace_flag, hangup);
1024 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1025 strcasecmp(ua->argk[i], "director") == 0) {
1026 debug_level = level;
1027 set_trace(trace_flag);
1030 if (strcasecmp(ua->argk[i], "client") == 0 ||
1031 strcasecmp(ua->argk[i], "fd") == 0) {
1034 client = GetClientResWithName(ua->argv[i]);
1036 do_client_setdebug(ua, client, level, trace_flag, hangup);
1040 client = select_client_resource(ua);
1042 do_client_setdebug(ua, client, level, trace_flag, hangup);
1047 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1048 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1049 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1052 store = GetStoreResWithName(ua->argv[i]);
1054 do_storage_setdebug(ua, store, level, trace_flag);
1058 store = get_storage_resource(ua, false/*no default*/);
1060 do_storage_setdebug(ua, store, level, trace_flag);
1066 * We didn't find an appropriate keyword above, so
1069 start_prompt(ua, _("Available daemons are: \n"));
1070 add_prompt(ua, _("Director"));
1071 add_prompt(ua, _("Storage"));
1072 add_prompt(ua, _("Client"));
1073 add_prompt(ua, _("All"));
1074 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1075 case 0: /* Director */
1076 debug_level = level;
1077 set_trace(trace_flag);
1080 store = get_storage_resource(ua, false/*no default*/);
1082 do_storage_setdebug(ua, store, level, trace_flag);
1086 client = select_client_resource(ua);
1088 do_client_setdebug(ua, client, level, trace_flag, hangup);
1092 do_all_setdebug(ua, level, trace_flag, hangup);
1101 * Turn debug tracing to file on/off
1103 static int trace_cmd(UAContext *ua, const char *cmd)
1107 if (ua->argc != 2) {
1108 if (!get_cmd(ua, _("Turn on or off? "))) {
1113 onoff = ua->argk[1];
1116 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1121 static int var_cmd(UAContext *ua, const char *cmd)
1123 POOLMEM *val = get_pool_memory(PM_FNAME);
1126 if (!open_client_db(ua)) {
1129 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1132 while (*var == ' ') { /* skip spaces */
1135 Dmsg1(100, "Var=%s:\n", var);
1136 variable_expansion(ua->jcr, var, &val);
1137 ua->send_msg("%s\n", val);
1138 free_pool_memory(val);
1142 static int estimate_cmd(UAContext *ua, const char *cmd)
1145 CLIENT *client = NULL;
1146 FILESET *fileset = NULL;
1148 char since[MAXSTRING];
1152 jcr->setJobLevel(L_FULL);
1153 for (int i=1; i<ua->argc; i++) {
1154 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1155 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1157 client = GetClientResWithName(ua->argv[i]);
1159 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1162 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1163 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1168 ua->error_msg(_("Client name missing.\n"));
1172 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1174 job = GetJobResWithName(ua->argv[i]);
1176 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1179 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1180 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1185 ua->error_msg(_("Job name missing.\n"));
1190 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1192 fileset = GetFileSetResWithName(ua->argv[i]);
1194 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1197 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1198 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1203 ua->error_msg(_("Fileset name missing.\n"));
1207 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1211 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1213 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1214 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1218 ua->error_msg(_("Level value missing.\n"));
1222 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1223 if (!is_yesno(ua->argv[i], &accurate)) {
1224 ua->error_msg(_("Invalid value for accurate. "
1225 "It must be yes or no.\n"));
1229 if (!job && !(client && fileset)) {
1230 if (!(job = select_job_resource(ua))) {
1235 job = GetJobResWithName(ua->argk[1]);
1237 ua->error_msg(_("No job specified.\n"));
1240 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1241 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1246 client = job->client;
1249 fileset = job->fileset;
1251 jcr->client = client;
1252 jcr->fileset = fileset;
1254 if (job->pool->catalog) {
1255 ua->catalog = job->pool->catalog;
1257 ua->catalog = client->catalog;
1265 jcr->setJobType(JT_BACKUP);
1266 init_jcr_job_record(jcr);
1268 if (!get_or_create_client_record(jcr)) {
1271 if (!get_or_create_fileset_record(jcr)) {
1275 get_level_since_time(ua->jcr, since, sizeof(since));
1277 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1278 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1279 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1280 ua->error_msg(_("Failed to connect to Client.\n"));
1284 if (!send_include_list(jcr)) {
1285 ua->error_msg(_("Error sending include list.\n"));
1289 if (!send_exclude_list(jcr)) {
1290 ua->error_msg(_("Error sending exclude list.\n"));
1294 /* The level string change if accurate mode is enabled */
1295 if (accurate >= 0) {
1296 jcr->accurate = accurate;
1298 jcr->accurate = job->accurate;
1301 if (!send_level_command(jcr)) {
1306 * If the job is in accurate mode, we send the list of
1309 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1310 if (!send_accurate_current_files(jcr)) {
1314 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1315 while (jcr->file_bsock->recv() >= 0) {
1316 ua->send_msg("%s", jcr->file_bsock->msg);
1320 if (jcr->file_bsock) {
1321 jcr->file_bsock->signal(BNET_TERMINATE);
1322 jcr->file_bsock->close();
1323 jcr->file_bsock = NULL;
1332 static int time_cmd(UAContext *ua, const char *cmd)
1335 time_t ttime = time(NULL);
1337 (void)localtime_r(&ttime, &tm);
1338 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1339 ua->send_msg("%s\n", sdt);
1344 * reload the conf file
1346 extern "C" void reload_config(int sig);
1348 static int reload_cmd(UAContext *ua, const char *cmd)
1355 * Delete Pool records (should purge Media with it).
1357 * delete pool=<pool-name>
1358 * delete volume pool=<pool-name> volume=<name>
1361 static int delete_cmd(UAContext *ua, const char *cmd)
1363 static const char *keywords[] = {
1369 if (!open_client_db(ua)) {
1373 switch (find_arg_keyword(ua, keywords)) {
1382 while ((i=find_arg(ua, "jobid")) > 0) {
1384 *ua->argk[i] = 0; /* zap keyword already visited */
1392 "In general it is not a good idea to delete either a\n"
1393 "Pool or a Volume since they may contain data.\n\n"));
1395 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1406 ua->warning_msg(_("Nothing done.\n"));
1414 * delete_job has been modified to parse JobID lists like the
1416 * delete JobID=3,4,6,7-11,14
1418 * Thanks to Phil Stracchino for the above addition.
1421 static void delete_job(UAContext *ua)
1426 int i = find_arg_with_value(ua, NT_("jobid"));
1428 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1429 s = bstrdup(ua->argv[i]);
1432 * We could use strtok() here. But we're not going to, because:
1433 * (a) strtok() is deprecated, having been replaced by strsep();
1434 * (b) strtok() is broken in significant ways.
1435 * we could use strsep() instead, but it's not universally available.
1436 * so we grow our own using strchr().
1438 sep = strchr(tok, ',');
1439 while (sep != NULL) {
1441 if (!delete_job_id_range(ua, tok)) {
1442 JobId = str_to_int64(tok);
1443 do_job_delete(ua, JobId);
1446 sep = strchr(tok, ',');
1448 /* pick up the last token */
1449 if (!delete_job_id_range(ua, tok)) {
1450 JobId = str_to_int64(tok);
1451 do_job_delete(ua, JobId);
1456 JobId = str_to_int64(ua->argv[i]);
1457 do_job_delete(ua, JobId);
1459 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1462 JobId = ua->int64_val;
1463 do_job_delete(ua, JobId);
1468 * we call delete_job_id_range to parse range tokens and iterate over ranges
1470 static bool delete_job_id_range(UAContext *ua, char *tok)
1475 tok2 = strchr(tok, '-');
1481 j1 = str_to_int64(tok);
1482 j2 = str_to_int64(tok2);
1483 for (j=j1; j<=j2; j++) {
1484 do_job_delete(ua, j);
1490 * do_job_delete now performs the actual delete operation atomically
1492 static void do_job_delete(UAContext *ua, JobId_t JobId)
1496 edit_int64(JobId, ed1);
1497 purge_jobs_from_catalog(ua, ed1);
1498 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1502 * Delete media records from database -- dangerous
1504 static int delete_volume(UAContext *ua)
1510 if (!select_media_dbr(ua, &mr)) {
1513 ua->warning_msg(_("\nThis command will delete volume %s\n"
1514 "and all Jobs saved on that volume from the Catalog\n"),
1517 if (find_arg(ua, "yes") >= 0) {
1518 ua->pint32_val = 1; /* Have "yes" on command line already" */
1520 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1522 if (!get_yesno(ua, buf)) {
1526 if (!ua->pint32_val) {
1530 /* If not purged, do it */
1531 if (strcmp(mr.VolStatus, "Purged") != 0) {
1532 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1533 ua->error_msg(_("Can't list jobs on this volume\n"));
1537 purge_jobs_from_catalog(ua, lst.list);
1541 db_delete_media_record(ua->jcr, ua->db, &mr);
1546 * Delete a pool record from the database -- dangerous
1548 static int delete_pool(UAContext *ua)
1553 memset(&pr, 0, sizeof(pr));
1555 if (!get_pool_dbr(ua, &pr)) {
1558 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1560 if (!get_yesno(ua, buf)) {
1563 if (ua->pint32_val) {
1564 db_delete_pool_record(ua->jcr, ua->db, &pr);
1569 int memory_cmd(UAContext *ua, const char *cmd)
1571 garbage_collect_memory();
1572 list_dir_status_header(ua);
1573 sm_dump(false, true);
1577 static void do_mount_cmd(UAContext *ua, const char *command)
1582 char dev_name[MAX_NAME_LENGTH];
1586 if (!open_client_db(ua)) {
1589 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1591 store.store = get_storage_resource(ua, true/*arg is storage*/);
1595 pm_strcpy(store.store_source, _("unknown source"));
1596 set_wstorage(jcr, &store);
1597 drive = get_storage_drive(ua, store.store);
1598 if (strcmp(command, "mount") == 0) {
1599 slot = get_storage_slot(ua, store.store);
1602 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1603 store.store->media_type, store.store->dev_name(), drive);
1605 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1606 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1609 sd = jcr->store_bsock;
1610 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1611 bash_spaces(dev_name);
1613 sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1615 sd->fsend("%s %s drive=%d", command, dev_name, drive);
1617 while (sd->recv() >= 0) {
1618 ua->send_msg("%s", sd->msg);
1620 sd->signal(BNET_TERMINATE);
1622 jcr->store_bsock = NULL;
1626 * mount [storage=<name>] [drive=nn] [slot=mm]
1628 static int mount_cmd(UAContext *ua, const char *cmd)
1630 do_mount_cmd(ua, "mount"); /* mount */
1636 * unmount [storage=<name>] [drive=nn]
1638 static int unmount_cmd(UAContext *ua, const char *cmd)
1640 do_mount_cmd(ua, "unmount"); /* unmount */
1646 * release [storage=<name>] [drive=nn]
1648 static int release_cmd(UAContext *ua, const char *cmd)
1650 do_mount_cmd(ua, "release"); /* release */
1657 * use catalog=<name>
1659 static int use_cmd(UAContext *ua, const char *cmd)
1661 CAT *oldcatalog, *catalog;
1664 close_db(ua); /* close any previously open db */
1665 oldcatalog = ua->catalog;
1667 if (!(catalog = get_catalog_resource(ua))) {
1668 ua->catalog = oldcatalog;
1670 ua->catalog = catalog;
1673 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1674 ua->catalog->name(), ua->catalog->db_name);
1679 int quit_cmd(UAContext *ua, const char *cmd)
1685 /* Handler to get job status */
1686 static int status_handler(void *ctx, int num_fields, char **row)
1688 char *val = (char *)ctx;
1693 *val = '?'; /* Unknown by default */
1700 * Wait until no job is running
1702 int wait_cmd(UAContext *ua, const char *cmd)
1706 time_t stop_time = 0;
1710 * Wait until no job is running
1712 if (ua->argc == 1) {
1713 bmicrosleep(0, 200000); /* let job actually start */
1714 for (bool running=true; running; ) {
1717 if (jcr->JobId != 0) {
1731 i = find_arg_with_value(ua, NT_("timeout"));
1732 if (i > 0 && ua->argv[i]) {
1733 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1736 /* we have jobid, jobname or ujobid argument */
1738 uint32_t jobid = 0 ;
1740 if (!open_client_db(ua)) {
1741 ua->error_msg(_("ERR: Can't open db\n")) ;
1745 for (int i=1; i<ua->argc; i++) {
1746 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1750 jobid = str_to_int64(ua->argv[i]);
1752 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1753 strcasecmp(ua->argk[i], "job") == 0) {
1757 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1759 jobid = jcr->JobId ;
1763 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1767 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1769 jobid = jcr->JobId ;
1773 /* Wait for a mount request */
1774 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1775 for (bool waiting=false; !waiting; ) {
1777 if (jcr->JobId != 0 &&
1778 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1787 if (stop_time && (time(NULL) >= stop_time)) {
1788 ua->warning_msg(_("Wait on mount timed out\n"));
1798 ua->error_msg(_("ERR: Job was not found\n"));
1803 * We wait the end of a specific job
1806 bmicrosleep(0, 200000); /* let job actually start */
1807 for (bool running=true; running; ) {
1810 jcr=get_jcr_by_id(jobid) ;
1823 * We have to get JobStatus
1827 char jobstatus = '?'; /* Unknown by default */
1830 bsnprintf(buf, sizeof(buf),
1831 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1834 db_sql_query(ua->db, buf,
1835 status_handler, (void *)&jobstatus);
1837 switch (jobstatus) {
1839 status = 1 ; /* Warning */
1843 case JS_ErrorTerminated:
1845 status = 2 ; /* Critical */
1850 status = 0 ; /* Ok */
1854 status = 3 ; /* Unknown */
1858 ua->send_msg("JobId=%i\n", jobid) ;
1859 ua->send_msg("JobStatus=%s (%c)\n",
1860 job_status_to_str(jobstatus),
1863 if (ua->gui || ua->api) {
1864 ua->send_msg("ExitStatus=%i\n", status) ;
1871 static int help_cmd(UAContext *ua, const char *cmd)
1874 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1875 for (i=0; i<comsize; i++) {
1876 if (ua->argc == 2) {
1877 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1878 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1879 commands[i].help, commands[i].usage);
1883 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1886 if (i == comsize && ua->argc == 2) {
1887 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1889 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1893 int qhelp_cmd(UAContext *ua, const char *cmd)
1896 /* Want to display only commands */
1897 j = find_arg(ua, NT_("all"));
1899 for (i=0; i<comsize; i++) {
1900 ua->send_msg("%s\n", commands[i].key);
1904 /* Want to display a specific help section */
1905 j = find_arg_with_value(ua, NT_("item"));
1906 if (j >= 0 && ua->argk[j]) {
1907 for (i=0; i<comsize; i++) {
1908 if (bstrcmp(commands[i].key, ua->argv[j])) {
1909 ua->send_msg("%s\n", commands[i].usage);
1915 /* Want to display everything */
1916 for (i=0; i<comsize; i++) {
1917 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1923 static int version_cmd(UAContext *ua, const char *cmd)
1925 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1926 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1931 * Test code -- turned on only for debug testing
1933 static int version_cmd(UAContext *ua, const char *cmd)
1936 POOL_MEM query(PM_MESSAGE);
1938 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1939 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1940 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1941 for (int i=0; i < ids.num_ids; i++) {
1942 ua->send_msg("id=%d\n", ids.DBId[i]);
1950 * This call uses open_client_db() and force a
1951 * new dedicated connection to the catalog
1953 bool open_new_client_db(UAContext *ua)
1957 /* Force a new dedicated connection */
1959 ua->force_mult_db_connections = true;
1960 ret = open_client_db(ua);
1961 ua->force_mult_db_connections = false;
1966 * This call explicitly checks for a catalog=xxx and
1967 * if given, opens that catalog. It also checks for
1968 * client=xxx and if found, opens the catalog
1969 * corresponding to that client. If we still don't
1970 * have a catalog, look for a Job keyword and get the
1971 * catalog from its client record.
1973 bool open_client_db(UAContext *ua)
1980 /* Try for catalog keyword */
1981 i = find_arg_with_value(ua, NT_("catalog"));
1983 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1984 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1987 catalog = GetCatalogResWithName(ua->argv[i]);
1989 if (ua->catalog && ua->catalog != catalog) {
1992 ua->catalog = catalog;
1997 /* Try for client keyword */
1998 i = find_arg_with_value(ua, NT_("client"));
2000 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2001 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2004 client = GetClientResWithName(ua->argv[i]);
2006 catalog = client->catalog;
2007 if (ua->catalog && ua->catalog != catalog) {
2010 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2011 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2014 ua->catalog = catalog;
2019 /* Try for Job keyword */
2020 i = find_arg_with_value(ua, NT_("job"));
2022 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2023 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2026 job = GetJobResWithName(ua->argv[i]);
2028 catalog = job->client->catalog;
2029 if (ua->catalog && ua->catalog != catalog) {
2032 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2033 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2036 ua->catalog = catalog;
2046 * Open the catalog database.
2048 bool open_db(UAContext *ua)
2056 ua->catalog = get_catalog_resource(ua);
2058 ua->error_msg( _("Could not find a Catalog resource\n"));
2063 /* Some modules like bvfs need their own catalog connection */
2064 mult_db_conn = ua->catalog->mult_db_connections;
2065 if (ua->force_mult_db_connections) {
2066 mult_db_conn = true;
2069 ua->jcr->catalog = ua->catalog;
2071 Dmsg0(100, "UA Open database\n");
2072 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2073 ua->catalog->db_user,
2074 ua->catalog->db_password, ua->catalog->db_address,
2075 ua->catalog->db_port, ua->catalog->db_socket,
2076 mult_db_conn, ua->catalog->disable_batch_insert);
2077 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2078 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2079 ua->catalog->db_name);
2081 ua->error_msg("%s", db_strerror(ua->db));
2086 ua->jcr->db = ua->db;
2088 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2090 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2094 void close_db(UAContext *ua)
2097 db_close_database(ua->jcr, ua->db);