2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Commands
32 * Kern Sibbald, September MM
42 #undef _POSIX_C_SOURCE
45 #include "lib/pythonlib.h"
47 /* Imported Functions */
48 extern PyObject *job_getattr(PyObject *self, char *attrname);
49 extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
51 #endif /* HAVE_PYTHON */
53 /* Imported subroutines */
55 /* Imported variables */
56 extern jobq_t job_queue; /* job queue */
59 /* Imported functions */
60 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
61 extern int gui_cmd(UAContext *ua, const char *cmd);
62 extern int label_cmd(UAContext *ua, const char *cmd);
63 extern int list_cmd(UAContext *ua, const char *cmd);
64 extern int llist_cmd(UAContext *ua, const char *cmd);
65 extern int messagescmd(UAContext *ua, const char *cmd);
66 extern int prunecmd(UAContext *ua, const char *cmd);
67 extern int purgecmd(UAContext *ua, const char *cmd);
68 extern int querycmd(UAContext *ua, const char *cmd);
69 extern int relabel_cmd(UAContext *ua, const char *cmd);
70 extern int restore_cmd(UAContext *ua, const char *cmd);
71 extern int retentioncmd(UAContext *ua, const char *cmd);
72 extern int show_cmd(UAContext *ua, const char *cmd);
73 extern int sqlquerycmd(UAContext *ua, const char *cmd);
74 extern int status_cmd(UAContext *ua, const char *cmd);
75 extern int update_cmd(UAContext *ua, const char *cmd);
77 /* Forward referenced functions */
78 static int add_cmd(UAContext *ua, const char *cmd);
79 static int automount_cmd(UAContext *ua, const char *cmd);
80 static int cancel_cmd(UAContext *ua, const char *cmd);
81 static int create_cmd(UAContext *ua, const char *cmd);
82 static int delete_cmd(UAContext *ua, const char *cmd);
83 static int disable_cmd(UAContext *ua, const char *cmd);
84 static int enable_cmd(UAContext *ua, const char *cmd);
85 static int estimate_cmd(UAContext *ua, const char *cmd);
86 static int help_cmd(UAContext *ua, const char *cmd);
87 static int memory_cmd(UAContext *ua, const char *cmd);
88 static int mount_cmd(UAContext *ua, const char *cmd);
89 static int python_cmd(UAContext *ua, const char *cmd);
90 static int release_cmd(UAContext *ua, const char *cmd);
91 static int reload_cmd(UAContext *ua, const char *cmd);
92 static int setdebug_cmd(UAContext *ua, const char *cmd);
93 static int setbwlimit_cmd(UAContext *ua, const char *cmd);
94 static int setip_cmd(UAContext *ua, const char *cmd);
95 static int time_cmd(UAContext *ua, const char *cmd);
96 static int trace_cmd(UAContext *ua, const char *cmd);
97 static int unmount_cmd(UAContext *ua, const char *cmd);
98 static int use_cmd(UAContext *ua, const char *cmd);
99 static int var_cmd(UAContext *ua, const char *cmd);
100 static int version_cmd(UAContext *ua, const char *cmd);
101 static int wait_cmd(UAContext *ua, const char *cmd);
103 static void do_job_delete(UAContext *ua, JobId_t JobId);
104 static bool delete_job_id_range(UAContext *ua, char *tok);
105 static int delete_volume(UAContext *ua);
106 static int delete_pool(UAContext *ua);
107 static void delete_job(UAContext *ua);
109 int qhelp_cmd(UAContext *ua, const char *cmd);
110 int quit_cmd(UAContext *ua, const char *cmd);
112 /* not all in alphabetical order. New commands are added after existing commands with similar letters
113 to prevent breakage of existing user scripts. */
115 const char *key; /* command */
116 int (*func)(UAContext *ua, const char *cmd); /* handler */
117 const char *help; /* main purpose */
118 const char *usage; /* all arguments to build usage */
119 const bool use_in_rs; /* Can use it in Console RunScript */
121 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
122 { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
123 { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
124 { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
125 { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid=<number> job=<job-name> ujobid=<unique-jobid>"), false},
126 { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
127 { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume=<vol-name> pool=<pool-name> jobid=<id>"), true},
128 { NT_("disable"), disable_cmd, _("Disable a job"), NT_("job=<name>"), true},
129 { NT_("enable"), enable_cmd, _("Enable a job"), NT_("job=<name>"), true},
130 { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
131 NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
133 { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
134 { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
135 { NT_("help"), help_cmd, _("Print help on specific command"),
136 NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
137 "\n\tmessages memory mount prune purge python quit query\n\trestore relabel release reload run status"
138 "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"), false},
140 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool>"), false},
141 { NT_("list"), list_cmd, _("List objects from catalog"),
142 NT_("pools | jobs | jobtotals | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
144 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
145 NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
147 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
148 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
149 { NT_("mount"), mount_cmd, _("Mount storage"),
150 NT_("storage=<storage-name> slot=<num> drive=<num> [ jobid=<id> | job=<job-name> ]"), false},
152 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
153 NT_("files | jobs | pool=<pool> | client=<client-name> | volume=<volume-name> "), true},
155 { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [action=<action> devicetype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
156 { NT_("python"), python_cmd, _("Python control commands"), NT_(""), false},
157 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
158 { NT_("query"), querycmd, _("Query catalog"), NT_(""), false},
159 { NT_("restore"), restore_cmd, _("Restore files"),
160 NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
162 "\n\tcomment=<text> jobid=<jobid> done select all"), false},
164 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
165 NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
167 { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name>"), false},
168 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
169 { NT_("run"), run_cmd, _("Run a job"),
170 NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
171 "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tcomment=<text> yes"), false},
173 { NT_("status"), status_cmd, _("Report status"),
174 NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
176 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
177 NT_("level=<nn> trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
179 { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"),
180 NT_("limit=<nn-kbs> client=<client-name> jobid=<number> job=<job-name> ujobid=<unique-jobid>"), true},
182 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
183 { NT_("show"), show_cmd, _("Show resource records"),
184 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | disabled | all"), true},
186 { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
187 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
188 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
189 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
190 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
192 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
193 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
195 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
196 NT_("stats\n\tpool=<poolname>\n\tslots storage=<storage> scan"
197 "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
198 "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
199 "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
200 "\n\t enable=<yes/no> recyclepool=<pool> actiononpurge=<action>"),true},
201 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_(""), false},
202 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
203 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
204 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
205 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
208 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
211 * Execute a command from the UA
213 bool do_a_command(UAContext *ua)
219 BSOCK *user = ua->UA_sock;
222 Dmsg1(900, "Command: %s\n", ua->argk[0]);
227 while (ua->jcr->wstorage->size()) {
228 ua->jcr->wstorage->remove(0);
231 len = strlen(ua->argk[0]);
232 for (i=0; i<comsize; i++) { /* search for command */
233 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
234 /* Check if command permitted, but "quit" is always OK */
235 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
236 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
239 /* Check if this command is authorized in RunScript */
240 if (ua->runscript && !commands[i].use_in_rs) {
241 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
244 if (ua->api) user->signal(BNET_CMD_BEGIN);
245 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
246 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
252 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
259 * This is a common routine used to stuff the Pool DB record defaults
260 * into the Media DB record just before creating a media (Volume)
263 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
265 mr->PoolId = pr->PoolId;
266 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
267 mr->Recycle = pr->Recycle;
268 mr->VolRetention = pr->VolRetention;
269 mr->VolUseDuration = pr->VolUseDuration;
270 mr->ActionOnPurge = pr->ActionOnPurge;
271 mr->RecyclePoolId = pr->RecyclePoolId;
272 mr->MaxVolJobs = pr->MaxVolJobs;
273 mr->MaxVolFiles = pr->MaxVolFiles;
274 mr->MaxVolBytes = pr->MaxVolBytes;
275 mr->LabelType = pr->LabelType;
281 * Add Volumes to an existing Pool
283 static int add_cmd(UAContext *ua, const char *cmd)
287 int num, i, max, startnum;
289 char name[MAX_NAME_LENGTH];
291 int Slot = 0, InChanger = 0;
294 "You probably don't want to be using this command since it\n"
295 "creates database records without labeling the Volumes.\n"
296 "You probably want to use the \"label\" command.\n\n"));
298 if (!open_client_db(ua)) {
302 memset(&pr, 0, sizeof(pr));
303 memset(&mr, 0, sizeof(mr));
305 if (!get_pool_dbr(ua, &pr)) {
309 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
310 pr.MaxVols, pr.PoolType);
312 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
313 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
314 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
317 pr.MaxVols = ua->pint32_val;
321 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
322 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
323 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
327 if (pr.MaxVols == 0) {
330 max = pr.MaxVols - pr.NumVols;
334 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
335 if (!get_pint(ua, buf)) {
338 num = ua->pint32_val;
339 if (num < 0 || num > max) {
340 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
348 if (!get_cmd(ua, _("Enter Volume name: "))) {
352 if (!get_cmd(ua, _("Enter base volume name: "))) {
356 /* Don't allow | in Volume name because it is the volume separator character */
357 if (!is_volume_name_legal(ua, ua->cmd)) {
360 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
361 ua->warning_msg(_("Volume name too long.\n"));
364 if (strlen(ua->cmd) == 0) {
365 ua->warning_msg(_("Volume name must be at least one character long.\n"));
371 bstrncpy(name, ua->cmd, sizeof(name));
373 bstrncat(name, "%04d", sizeof(name));
376 if (!get_pint(ua, _("Enter the starting number: "))) {
379 startnum = ua->pint32_val;
381 ua->warning_msg(_("Start number must be greater than zero.\n"));
391 if (store && store->autochanger) {
392 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
395 Slot = ua->pint32_val;
396 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
399 InChanger = ua->pint32_val;
402 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
403 for (i=startnum; i < num+startnum; i++) {
404 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
406 mr.InChanger = InChanger;
407 mr.StorageId = store->StorageId;
409 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
410 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
411 ua->error_msg("%s", db_strerror(ua->db));
415 first_id = mr.PoolId;
419 Dmsg0(200, "Update pool record.\n");
420 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
421 ua->warning_msg("%s", db_strerror(ua->db));
424 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
430 * Turn auto mount on/off
435 int automount_cmd(UAContext *ua, const char *cmd)
440 if (!get_cmd(ua, _("Turn on or off? "))) {
448 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
455 static int cancel_cmd(UAContext *ua, const char *cmd)
457 JCR *jcr = select_running_job(ua, "cancel");
461 int ret = cancel_job(ua, jcr);
467 * This is a common routine to create or update a
468 * Pool DB base record from a Pool Resource. We handle
469 * the setting of MaxVols and NumVols slightly differently
470 * depending on if we are creating the Pool or we are
471 * simply bringing it into agreement with the resource (updage).
473 * Caution : RecyclePoolId isn't setup in this function.
474 * You can use set_pooldbr_recyclepoolid();
477 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
479 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
480 if (op == POOL_OP_CREATE) {
481 pr->MaxVols = pool->max_volumes;
483 } else { /* update pool */
484 if (pr->MaxVols != pool->max_volumes) {
485 pr->MaxVols = pool->max_volumes;
487 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
488 pr->MaxVols = pr->NumVols;
491 pr->LabelType = pool->LabelType;
492 pr->UseOnce = pool->use_volume_once;
493 pr->UseCatalog = pool->use_catalog;
494 pr->Recycle = pool->Recycle;
495 pr->VolRetention = pool->VolRetention;
496 pr->VolUseDuration = pool->VolUseDuration;
497 pr->MaxVolJobs = pool->MaxVolJobs;
498 pr->MaxVolFiles = pool->MaxVolFiles;
499 pr->MaxVolBytes = pool->MaxVolBytes;
500 pr->AutoPrune = pool->AutoPrune;
501 pr->ActionOnPurge = pool->action_on_purge;
502 pr->Recycle = pool->Recycle;
503 if (pool->label_format) {
504 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
506 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
510 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
511 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
515 if (!pool->RecyclePool && !pool->ScratchPool) {
519 memset(&pr, 0, sizeof(POOL_DBR));
520 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
522 if (!db_get_pool_record(jcr, db, &pr)) {
523 return -1; /* not exists in database */
526 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
528 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
529 return -1; /* error */
532 if (!db_update_pool_record(jcr, db, &pr)) {
533 return -1; /* error */
538 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
539 * works with set_pooldbr_from_poolres
541 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
546 if (pool->RecyclePool) {
547 memset(&rpool, 0, sizeof(POOL_DBR));
549 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
550 if (db_get_pool_record(jcr, db, &rpool)) {
551 pr->RecyclePoolId = rpool.PoolId;
553 Jmsg(jcr, M_WARNING, 0,
554 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
555 "Try to update it with 'update pool=%s'\n"),
556 pool->name(), rpool.Name, rpool.Name,pool->name());
560 } else { /* no RecyclePool used, set it to 0 */
561 pr->RecyclePoolId = 0;
564 if (pool->ScratchPool) {
565 memset(&rpool, 0, sizeof(POOL_DBR));
567 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
568 if (db_get_pool_record(jcr, db, &rpool)) {
569 pr->ScratchPoolId = rpool.PoolId;
571 Jmsg(jcr, M_WARNING, 0,
572 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
573 "Try to update it with 'update pool=%s'\n"),
574 pool->name(), rpool.Name, rpool.Name,pool->name());
577 } else { /* no ScratchPool used, set it to 0 */
578 pr->ScratchPoolId = 0;
586 * Create a pool record from a given Pool resource
587 * Also called from backup.c
588 * Returns: -1 on error
589 * 0 record already exists
593 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
597 memset(&pr, 0, sizeof(POOL_DBR));
599 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
601 if (db_get_pool_record(jcr, db, &pr)) {
603 if (op == POOL_OP_UPDATE) { /* update request */
604 set_pooldbr_from_poolres(&pr, pool, op);
605 set_pooldbr_references(jcr, db, &pr, pool);
606 db_update_pool_record(jcr, db, &pr);
608 return 0; /* exists */
611 set_pooldbr_from_poolres(&pr, pool, op);
612 set_pooldbr_references(jcr, db, &pr, pool);
614 if (!db_create_pool_record(jcr, db, &pr)) {
615 return -1; /* error */
623 * Create a Pool Record in the database.
624 * It is always created from the Resource record.
626 static int create_cmd(UAContext *ua, const char *cmd)
630 if (!open_client_db(ua)) {
634 pool = get_pool_resource(ua);
639 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
641 ua->error_msg(_("Error: Pool %s already exists.\n"
642 "Use update to change it.\n"), pool->name());
646 ua->error_msg("%s", db_strerror(ua->db));
652 ua->send_msg(_("Pool %s created.\n"), pool->name());
657 extern DIRRES *director;
658 extern char *configfile;
661 * Python control command
662 * python restart (restarts interpreter)
664 static int python_cmd(UAContext *ua, const char *cmd)
667 init_python_interpreter_args python_args;
669 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
670 term_python_interpreter();
672 python_args.progname = director->name();
673 python_args.scriptdir = director->scripts_directory;
674 python_args.modulename = "DirStartUp";
675 python_args.configfile = configfile;
676 python_args.workingdir = director->working_directory;
677 python_args.job_getattr = job_getattr;
678 python_args.job_setattr = job_setattr;
680 init_python_interpreter(&python_args);
682 ua->send_msg(_("Python interpreter restarted.\n"));
684 #endif /* HAVE_PYTHON */
685 ua->warning_msg(_("Nothing done.\n"));
688 #endif /* HAVE_PYTHON */
692 static int setbwlimit_cmd(UAContext *ua, const char *cmd)
695 char Job[MAX_NAME_LENGTH];
700 i = find_arg_with_value(ua, "limit");
702 limit = atoi(ua->argv[i]) * 1024;
705 if (!get_pint(ua, _("Enter new bandwidth limit kb/s: "))) {
708 limit = ua->pint32_val * 1024; /* kb/s */
711 const char *lst[] = { "job", "jobid", "jobname", NULL };
712 if (find_arg_keyword(ua, lst) > 0) {
713 JCR *jcr = select_running_job(ua, "limit");
715 jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
716 bstrncpy(Job, jcr->Job, sizeof(Job));
717 client = jcr->client;
724 client = get_client_resource(ua);
731 /* Connect to File daemon */
732 ua->jcr->client = client;
733 ua->jcr->max_bandwidth = limit;
735 /* Try to connect for 15 seconds */
736 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
737 client->name(), client->address, client->FDport);
738 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
739 ua->error_msg(_("Failed to connect to Client.\n"));
742 Dmsg0(120, "Connected to file daemon\n");
743 if (!send_bwlimit(ua->jcr, Job)) {
744 ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
747 ua->info_msg(_("OK Limiting bandwidth to %lldkb/s %s\n"),
751 ua->jcr->file_bsock->signal(BNET_TERMINATE);
752 ua->jcr->file_bsock->close();
753 ua->jcr->file_bsock = NULL;
754 ua->jcr->client = NULL;
755 ua->jcr->max_bandwidth = 0;
760 * Set a new address in a Client resource. We do this only
761 * if the Console name is the same as the Client name
762 * and the Console can access the client.
764 static int setip_cmd(UAContext *ua, const char *cmd)
768 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
769 ua->error_msg(_("Unauthorized command from this console.\n"));
773 client = GetClientResWithName(ua->cons->name());
776 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
779 if (client->address) {
780 free(client->address);
782 /* MA Bug 6 remove ifdef */
783 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
784 client->address = bstrdup(buf);
785 ua->send_msg(_("Client \"%s\" address set to %s\n"),
786 client->name(), client->address);
793 static void do_en_disable_cmd(UAContext *ua, bool setting)
798 i = find_arg_with_value(ua, NT_("job"));
800 job = select_enable_disable_job_resource(ua, setting);
806 job = GetJobResWithName(ua->argv[i]);
810 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
814 if (!acl_access_ok(ua, Job_ACL, job->name())) {
815 ua->error_msg(_("Unauthorized command from this console.\n"));
818 job->enabled = setting;
819 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
823 static int enable_cmd(UAContext *ua, const char *cmd)
825 do_en_disable_cmd(ua, true);
829 static int disable_cmd(UAContext *ua, const char *cmd)
831 do_en_disable_cmd(ua, false);
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;
864 * For the client, we have the following values that can be set
865 * level = debug level
866 * trace = send debug output to a file
867 * hangup = how many records to send to SD before hanging up
868 * obviously this is most useful for testing restarting
871 static void do_client_setdebug(UAContext *ua, CLIENT *client,
872 int level, int trace, int hangup)
876 /* Connect to File daemon */
878 ua->jcr->client = client;
879 /* Try to connect for 15 seconds */
880 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
881 client->name(), client->address, client->FDport);
882 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
883 ua->error_msg(_("Failed to connect to Client.\n"));
886 Dmsg0(120, "Connected to file daemon\n");
887 fd = ua->jcr->file_bsock;
888 fd->fsend("setdebug=%d trace=%d hangup=%d\n", level, trace, hangup);
889 if (fd->recv() >= 0) {
890 ua->send_msg("%s", fd->msg);
892 fd->signal(BNET_TERMINATE);
894 ua->jcr->file_bsock = NULL;
899 static void do_all_setdebug(UAContext *ua, int level, int trace_flag, int hangup)
901 STORE *store, **unique_store;
902 CLIENT *client, **unique_client;
908 /* Count Storage items */
912 foreach_res(store, R_STORAGE) {
915 unique_store = (STORE **) malloc(i * sizeof(STORE));
916 /* Find Unique Storage address/port */
917 store = (STORE *)GetNextRes(R_STORAGE, NULL);
919 unique_store[i++] = store;
920 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
922 for (j=0; j<i; j++) {
923 if (strcmp(unique_store[j]->address, store->address) == 0 &&
924 unique_store[j]->SDport == store->SDport) {
930 unique_store[i++] = store;
931 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
936 /* Call each unique Storage daemon */
937 for (j=0; j<i; j++) {
938 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
942 /* Count Client items */
946 foreach_res(client, R_CLIENT) {
949 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
950 /* Find Unique Client address/port */
951 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
953 unique_client[i++] = client;
954 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
956 for (j=0; j<i; j++) {
957 if (strcmp(unique_client[j]->address, client->address) == 0 &&
958 unique_client[j]->FDport == client->FDport) {
964 unique_client[i++] = client;
965 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
970 /* Call each unique File daemon */
971 for (j=0; j<i; j++) {
972 do_client_setdebug(ua, unique_client[j], level, trace_flag, hangup);
978 * setdebug level=nn all trace=1/0
980 static int setdebug_cmd(UAContext *ua, const char *cmd)
989 Dmsg1(120, "setdebug:%s:\n", cmd);
992 i = find_arg_with_value(ua, "level");
994 level = atoi(ua->argv[i]);
997 if (!get_pint(ua, _("Enter new debug level: "))) {
1000 level = ua->pint32_val;
1003 /* Look for trace flag. -1 => not change */
1004 i = find_arg_with_value(ua, "trace");
1006 trace_flag = atoi(ua->argv[i]);
1007 if (trace_flag > 0) {
1012 /* Look for hangup (debug only)flag. -1 => not change */
1013 i = find_arg_with_value(ua, "hangup");
1015 hangup = atoi(ua->argv[i]);
1019 /* General debug? */
1020 for (i=1; i<ua->argc; i++) {
1021 if (strcasecmp(ua->argk[i], "all") == 0) {
1022 do_all_setdebug(ua, level, trace_flag, hangup);
1025 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1026 strcasecmp(ua->argk[i], "director") == 0) {
1027 debug_level = level;
1028 set_trace(trace_flag);
1031 if (strcasecmp(ua->argk[i], "client") == 0 ||
1032 strcasecmp(ua->argk[i], "fd") == 0) {
1035 client = GetClientResWithName(ua->argv[i]);
1037 do_client_setdebug(ua, client, level, trace_flag, hangup);
1041 client = select_client_resource(ua);
1043 do_client_setdebug(ua, client, level, trace_flag, hangup);
1048 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1049 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1050 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1053 store = GetStoreResWithName(ua->argv[i]);
1055 do_storage_setdebug(ua, store, level, trace_flag);
1059 store = get_storage_resource(ua, false/*no default*/);
1061 do_storage_setdebug(ua, store, level, trace_flag);
1067 * We didn't find an appropriate keyword above, so
1070 start_prompt(ua, _("Available daemons are: \n"));
1071 add_prompt(ua, _("Director"));
1072 add_prompt(ua, _("Storage"));
1073 add_prompt(ua, _("Client"));
1074 add_prompt(ua, _("All"));
1075 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1076 case 0: /* Director */
1077 debug_level = level;
1078 set_trace(trace_flag);
1081 store = get_storage_resource(ua, false/*no default*/);
1083 do_storage_setdebug(ua, store, level, trace_flag);
1087 client = select_client_resource(ua);
1089 do_client_setdebug(ua, client, level, trace_flag, hangup);
1093 do_all_setdebug(ua, level, trace_flag, hangup);
1102 * Turn debug tracing to file on/off
1104 static int trace_cmd(UAContext *ua, const char *cmd)
1108 if (ua->argc != 2) {
1109 if (!get_cmd(ua, _("Turn on or off? "))) {
1114 onoff = ua->argk[1];
1117 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1122 static int var_cmd(UAContext *ua, const char *cmd)
1124 POOLMEM *val = get_pool_memory(PM_FNAME);
1127 if (!open_client_db(ua)) {
1130 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1133 while (*var == ' ') { /* skip spaces */
1136 Dmsg1(100, "Var=%s:\n", var);
1137 variable_expansion(ua->jcr, var, &val);
1138 ua->send_msg("%s\n", val);
1139 free_pool_memory(val);
1143 static int estimate_cmd(UAContext *ua, const char *cmd)
1146 CLIENT *client = NULL;
1147 FILESET *fileset = NULL;
1149 char since[MAXSTRING];
1153 jcr->set_JobLevel(L_FULL);
1154 for (int i=1; i<ua->argc; i++) {
1155 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1156 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1158 client = GetClientResWithName(ua->argv[i]);
1160 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1163 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1164 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1169 ua->error_msg(_("Client name missing.\n"));
1173 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1175 job = GetJobResWithName(ua->argv[i]);
1177 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1180 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1181 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1186 ua->error_msg(_("Job name missing.\n"));
1191 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1193 fileset = GetFileSetResWithName(ua->argv[i]);
1195 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1198 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1199 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1204 ua->error_msg(_("Fileset name missing.\n"));
1208 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1212 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1214 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1215 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1219 ua->error_msg(_("Level value missing.\n"));
1223 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1224 if (!is_yesno(ua->argv[i], &accurate)) {
1225 ua->error_msg(_("Invalid value for accurate. "
1226 "It must be yes or no.\n"));
1230 if (!job && !(client && fileset)) {
1231 if (!(job = select_job_resource(ua))) {
1236 job = GetJobResWithName(ua->argk[1]);
1238 ua->error_msg(_("No job specified.\n"));
1241 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1242 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1247 client = job->client;
1250 fileset = job->fileset;
1252 jcr->client = client;
1253 jcr->fileset = fileset;
1255 if (job->pool->catalog) {
1256 ua->catalog = job->pool->catalog;
1258 ua->catalog = client->catalog;
1266 jcr->set_JobType(JT_BACKUP);
1267 init_jcr_job_record(jcr);
1269 if (!get_or_create_client_record(jcr)) {
1272 if (!get_or_create_fileset_record(jcr)) {
1276 get_level_since_time(ua->jcr, since, sizeof(since));
1278 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1279 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1280 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1281 ua->error_msg(_("Failed to connect to Client.\n"));
1285 if (!send_include_list(jcr)) {
1286 ua->error_msg(_("Error sending include list.\n"));
1290 if (!send_exclude_list(jcr)) {
1291 ua->error_msg(_("Error sending exclude list.\n"));
1295 /* The level string change if accurate mode is enabled */
1296 if (accurate >= 0) {
1297 jcr->accurate = accurate;
1299 jcr->accurate = job->accurate;
1302 if (!send_level_command(jcr)) {
1307 * If the job is in accurate mode, we send the list of
1310 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1311 if (!send_accurate_current_files(jcr)) {
1315 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1316 while (jcr->file_bsock->recv() >= 0) {
1317 ua->send_msg("%s", jcr->file_bsock->msg);
1321 if (jcr->file_bsock) {
1322 jcr->file_bsock->signal(BNET_TERMINATE);
1323 jcr->file_bsock->close();
1324 jcr->file_bsock = NULL;
1333 static int time_cmd(UAContext *ua, const char *cmd)
1336 time_t ttime = time(NULL);
1338 (void)localtime_r(&ttime, &tm);
1339 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1340 ua->send_msg("%s\n", sdt);
1345 * reload the conf file
1347 extern "C" void reload_config(int sig);
1349 static int reload_cmd(UAContext *ua, const char *cmd)
1356 * Delete Pool records (should purge Media with it).
1358 * delete pool=<pool-name>
1359 * delete volume pool=<pool-name> volume=<name>
1362 static int delete_cmd(UAContext *ua, const char *cmd)
1364 static const char *keywords[] = {
1370 if (!open_client_db(ua)) {
1374 switch (find_arg_keyword(ua, keywords)) {
1383 while ((i=find_arg(ua, "jobid")) > 0) {
1385 *ua->argk[i] = 0; /* zap keyword already visited */
1393 "In general it is not a good idea to delete either a\n"
1394 "Pool or a Volume since they may contain data.\n\n"));
1396 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1407 ua->warning_msg(_("Nothing done.\n"));
1415 * delete_job has been modified to parse JobID lists like the
1417 * delete JobID=3,4,6,7-11,14
1419 * Thanks to Phil Stracchino for the above addition.
1422 static void delete_job(UAContext *ua)
1427 int i = find_arg_with_value(ua, NT_("jobid"));
1429 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1430 s = bstrdup(ua->argv[i]);
1433 * We could use strtok() here. But we're not going to, because:
1434 * (a) strtok() is deprecated, having been replaced by strsep();
1435 * (b) strtok() is broken in significant ways.
1436 * we could use strsep() instead, but it's not universally available.
1437 * so we grow our own using strchr().
1439 sep = strchr(tok, ',');
1440 while (sep != NULL) {
1442 if (!delete_job_id_range(ua, tok)) {
1443 JobId = str_to_int64(tok);
1444 do_job_delete(ua, JobId);
1447 sep = strchr(tok, ',');
1449 /* pick up the last token */
1450 if (!delete_job_id_range(ua, tok)) {
1451 JobId = str_to_int64(tok);
1452 do_job_delete(ua, JobId);
1457 JobId = str_to_int64(ua->argv[i]);
1458 do_job_delete(ua, JobId);
1460 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1463 JobId = ua->int64_val;
1464 do_job_delete(ua, JobId);
1469 * we call delete_job_id_range to parse range tokens and iterate over ranges
1471 static bool delete_job_id_range(UAContext *ua, char *tok)
1476 tok2 = strchr(tok, '-');
1482 j1 = str_to_int64(tok);
1483 j2 = str_to_int64(tok2);
1484 for (j=j1; j<=j2; j++) {
1485 do_job_delete(ua, j);
1491 * do_job_delete now performs the actual delete operation atomically
1493 static void do_job_delete(UAContext *ua, JobId_t JobId)
1497 edit_int64(JobId, ed1);
1498 purge_jobs_from_catalog(ua, ed1);
1499 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1503 * Delete media records from database -- dangerous
1505 static int delete_volume(UAContext *ua)
1511 if (!select_media_dbr(ua, &mr)) {
1514 ua->warning_msg(_("\nThis command will delete volume %s\n"
1515 "and all Jobs saved on that volume from the Catalog\n"),
1518 if (find_arg(ua, "yes") >= 0) {
1519 ua->pint32_val = 1; /* Have "yes" on command line already" */
1521 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1523 if (!get_yesno(ua, buf)) {
1527 if (!ua->pint32_val) {
1531 /* If not purged, do it */
1532 if (strcmp(mr.VolStatus, "Purged") != 0) {
1533 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1534 ua->error_msg(_("Can't list jobs on this volume\n"));
1538 purge_jobs_from_catalog(ua, lst.list);
1542 db_delete_media_record(ua->jcr, ua->db, &mr);
1547 * Delete a pool record from the database -- dangerous
1549 static int delete_pool(UAContext *ua)
1554 memset(&pr, 0, sizeof(pr));
1556 if (!get_pool_dbr(ua, &pr)) {
1559 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1561 if (!get_yesno(ua, buf)) {
1564 if (ua->pint32_val) {
1565 db_delete_pool_record(ua->jcr, ua->db, &pr);
1570 int memory_cmd(UAContext *ua, const char *cmd)
1572 garbage_collect_memory();
1573 list_dir_status_header(ua);
1574 sm_dump(false, true);
1578 static void do_mount_cmd(UAContext *ua, const char *command)
1583 char dev_name[MAX_NAME_LENGTH];
1587 if (!open_client_db(ua)) {
1590 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1592 store.store = get_storage_resource(ua, true/*arg is storage*/);
1596 pm_strcpy(store.store_source, _("unknown source"));
1597 set_wstorage(jcr, &store);
1598 drive = get_storage_drive(ua, store.store);
1599 if (strcmp(command, "mount") == 0) {
1600 slot = get_storage_slot(ua, store.store);
1603 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1604 store.store->media_type, store.store->dev_name(), drive);
1606 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1607 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1610 sd = jcr->store_bsock;
1611 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1612 bash_spaces(dev_name);
1614 sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1616 sd->fsend("%s %s drive=%d", command, dev_name, drive);
1618 while (sd->recv() >= 0) {
1619 ua->send_msg("%s", sd->msg);
1621 sd->signal(BNET_TERMINATE);
1623 jcr->store_bsock = NULL;
1627 * mount [storage=<name>] [drive=nn] [slot=mm]
1629 static int mount_cmd(UAContext *ua, const char *cmd)
1631 do_mount_cmd(ua, "mount"); /* mount */
1637 * unmount [storage=<name>] [drive=nn]
1639 static int unmount_cmd(UAContext *ua, const char *cmd)
1641 do_mount_cmd(ua, "unmount"); /* unmount */
1647 * release [storage=<name>] [drive=nn]
1649 static int release_cmd(UAContext *ua, const char *cmd)
1651 do_mount_cmd(ua, "release"); /* release */
1658 * use catalog=<name>
1660 static int use_cmd(UAContext *ua, const char *cmd)
1662 CAT *oldcatalog, *catalog;
1665 close_db(ua); /* close any previously open db */
1666 oldcatalog = ua->catalog;
1668 if (!(catalog = get_catalog_resource(ua))) {
1669 ua->catalog = oldcatalog;
1671 ua->catalog = catalog;
1674 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1675 ua->catalog->name(), ua->catalog->db_name);
1680 int quit_cmd(UAContext *ua, const char *cmd)
1686 /* Handler to get job status */
1687 static int status_handler(void *ctx, int num_fields, char **row)
1689 char *val = (char *)ctx;
1694 *val = '?'; /* Unknown by default */
1701 * Wait until no job is running
1703 int wait_cmd(UAContext *ua, const char *cmd)
1707 time_t stop_time = 0;
1711 * Wait until no job is running
1713 if (ua->argc == 1) {
1714 bmicrosleep(0, 200000); /* let job actually start */
1715 for (bool running=true; running; ) {
1718 if (jcr->JobId != 0) {
1732 i = find_arg_with_value(ua, NT_("timeout"));
1733 if (i > 0 && ua->argv[i]) {
1734 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1737 /* we have jobid, jobname or ujobid argument */
1739 uint32_t jobid = 0 ;
1741 if (!open_client_db(ua)) {
1742 ua->error_msg(_("ERR: Can't open db\n")) ;
1746 for (int i=1; i<ua->argc; i++) {
1747 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1751 jobid = str_to_int64(ua->argv[i]);
1753 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1754 strcasecmp(ua->argk[i], "job") == 0) {
1758 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1760 jobid = jcr->JobId ;
1764 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1768 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1770 jobid = jcr->JobId ;
1774 /* Wait for a mount request */
1775 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1776 for (bool waiting=false; !waiting; ) {
1778 if (jcr->JobId != 0 &&
1779 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1788 if (stop_time && (time(NULL) >= stop_time)) {
1789 ua->warning_msg(_("Wait on mount timed out\n"));
1799 ua->error_msg(_("ERR: Job was not found\n"));
1804 * We wait the end of a specific job
1807 bmicrosleep(0, 200000); /* let job actually start */
1808 for (bool running=true; running; ) {
1811 jcr=get_jcr_by_id(jobid) ;
1824 * We have to get JobStatus
1828 char jobstatus = '?'; /* Unknown by default */
1831 bsnprintf(buf, sizeof(buf),
1832 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1835 db_sql_query(ua->db, buf,
1836 status_handler, (void *)&jobstatus);
1838 switch (jobstatus) {
1840 status = 1 ; /* Warning */
1844 case JS_ErrorTerminated:
1846 status = 2 ; /* Critical */
1851 status = 0 ; /* Ok */
1855 status = 3 ; /* Unknown */
1859 ua->send_msg("JobId=%i\n", jobid) ;
1860 ua->send_msg("JobStatus=%s (%c)\n",
1861 job_status_to_str(jobstatus),
1864 if (ua->gui || ua->api) {
1865 ua->send_msg("ExitStatus=%i\n", status) ;
1872 static int help_cmd(UAContext *ua, const char *cmd)
1875 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1876 for (i=0; i<comsize; i++) {
1877 if (ua->argc == 2) {
1878 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1879 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1880 commands[i].help, commands[i].usage);
1884 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1887 if (i == comsize && ua->argc == 2) {
1888 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1890 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1894 int qhelp_cmd(UAContext *ua, const char *cmd)
1897 /* Want to display only commands */
1898 j = find_arg(ua, NT_("all"));
1900 for (i=0; i<comsize; i++) {
1901 ua->send_msg("%s\n", commands[i].key);
1905 /* Want to display a specific help section */
1906 j = find_arg_with_value(ua, NT_("item"));
1907 if (j >= 0 && ua->argk[j]) {
1908 for (i=0; i<comsize; i++) {
1909 if (bstrcmp(commands[i].key, ua->argv[j])) {
1910 ua->send_msg("%s\n", commands[i].usage);
1916 /* Want to display everything */
1917 for (i=0; i<comsize; i++) {
1918 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1924 static int version_cmd(UAContext *ua, const char *cmd)
1926 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1927 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1932 * Test code -- turned on only for debug testing
1934 static int version_cmd(UAContext *ua, const char *cmd)
1937 POOL_MEM query(PM_MESSAGE);
1939 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1940 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1941 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1942 for (int i=0; i < ids.num_ids; i++) {
1943 ua->send_msg("id=%d\n", ids.DBId[i]);
1951 * This call uses open_client_db() and force a
1952 * new dedicated connection to the catalog
1954 bool open_new_client_db(UAContext *ua)
1958 /* Force a new dedicated connection */
1960 ua->force_mult_db_connections = true;
1961 ret = open_client_db(ua);
1962 ua->force_mult_db_connections = false;
1967 * This call explicitly checks for a catalog=xxx and
1968 * if given, opens that catalog. It also checks for
1969 * client=xxx and if found, opens the catalog
1970 * corresponding to that client. If we still don't
1971 * have a catalog, look for a Job keyword and get the
1972 * catalog from its client record.
1974 bool open_client_db(UAContext *ua)
1981 /* Try for catalog keyword */
1982 i = find_arg_with_value(ua, NT_("catalog"));
1984 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1985 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1988 catalog = GetCatalogResWithName(ua->argv[i]);
1990 if (ua->catalog && ua->catalog != catalog) {
1993 ua->catalog = catalog;
1998 /* Try for client keyword */
1999 i = find_arg_with_value(ua, NT_("client"));
2001 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2002 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2005 client = GetClientResWithName(ua->argv[i]);
2007 catalog = client->catalog;
2008 if (ua->catalog && ua->catalog != catalog) {
2011 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2012 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2015 ua->catalog = catalog;
2020 /* Try for Job keyword */
2021 i = find_arg_with_value(ua, NT_("job"));
2023 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2024 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2027 job = GetJobResWithName(ua->argv[i]);
2029 catalog = job->client->catalog;
2030 if (ua->catalog && ua->catalog != catalog) {
2033 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2034 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2037 ua->catalog = catalog;
2047 * Open the catalog database.
2049 bool open_db(UAContext *ua)
2057 ua->catalog = get_catalog_resource(ua);
2059 ua->error_msg( _("Could not find a Catalog resource\n"));
2064 /* Some modules like bvfs need their own catalog connection */
2065 mult_db_conn = ua->catalog->mult_db_connections;
2066 if (ua->force_mult_db_connections) {
2067 mult_db_conn = true;
2070 ua->jcr->catalog = ua->catalog;
2072 Dmsg0(100, "UA Open database\n");
2073 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2074 ua->catalog->db_user,
2075 ua->catalog->db_password, ua->catalog->db_address,
2076 ua->catalog->db_port, ua->catalog->db_socket,
2077 mult_db_conn, ua->catalog->disable_batch_insert);
2078 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2079 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2080 ua->catalog->db_name);
2082 ua->error_msg("%s", db_strerror(ua->db));
2087 ua->jcr->db = ua->db;
2089 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2091 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2095 void close_db(UAContext *ua)
2098 db_close_database(ua->jcr, ua->db);