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 setip_cmd(UAContext *ua, const char *cmd);
93 static int time_cmd(UAContext *ua, const char *cmd);
94 static int trace_cmd(UAContext *ua, const char *cmd);
95 static int unmount_cmd(UAContext *ua, const char *cmd);
96 static int use_cmd(UAContext *ua, const char *cmd);
97 static int var_cmd(UAContext *ua, const char *cmd);
98 static int version_cmd(UAContext *ua, const char *cmd);
99 static int wait_cmd(UAContext *ua, const char *cmd);
101 static void do_job_delete(UAContext *ua, JobId_t JobId);
102 static bool delete_job_id_range(UAContext *ua, char *tok);
103 static int delete_volume(UAContext *ua);
104 static int delete_pool(UAContext *ua);
105 static void delete_job(UAContext *ua);
107 int qhelp_cmd(UAContext *ua, const char *cmd);
108 int quit_cmd(UAContext *ua, const char *cmd);
110 /* not all in alphabetical order. New commands are added after existing commands with similar letters
111 to prevent breakage of existing user scripts. */
113 const char *key; /* command */
114 int (*func)(UAContext *ua, const char *cmd); /* handler */
115 const char *help; /* main purpose */
116 const char *usage; /* all arguments to build usage */
117 const bool use_in_rs; /* Can use it in Console RunScript */
119 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
120 { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
121 { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
122 { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
123 { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid=<number> job=<job-name> ujobid=<unique-jobid>"), false},
124 { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
125 { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume=<vol-name> pool=<pool-name> jobid=<id>"), true},
126 { NT_("disable"), disable_cmd, _("Disable a job"), NT_("job=<name>"), true},
127 { NT_("enable"), enable_cmd, _("Enable a job"), NT_("job=<name>"), true},
128 { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
129 NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
131 { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
132 { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
133 { NT_("help"), help_cmd, _("Print help on specific command"),
134 NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
135 "\n\tmessages memory mount prune purge python quit query\n\trestore relabel release reload run status"
136 "\n\tsetdebug setip show sqlquery time trace unmount umount\n\tupdate use var version wait"), false},
138 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> barcodes"), false},
139 { NT_("list"), list_cmd, _("List objects from catalog"),
140 NT_("pools | jobs | jobtotals | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
142 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
143 NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
145 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
146 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
147 { NT_("mount"), mount_cmd, _("Mount storage"),
148 NT_("storage=<storage-name> slot=<num> drive=<num> [ jobid=<id> | job=<job-name> ]"), false},
150 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
151 NT_("files | jobs | pool=<pool> | client=<client-name> | volume=<volume-name> "), true},
153 { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [action=<action> devicetype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
154 { NT_("python"), python_cmd, _("Python control commands"), NT_(""), false},
155 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
156 { NT_("query"), querycmd, _("Query catalog"), NT_(""), false},
157 { NT_("restore"), restore_cmd, _("Restore files"),
158 NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
160 "\n\tcomment=<text> jobid=<jobid> done select all"), false},
162 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
163 NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
165 { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name>"), false},
166 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
167 { NT_("run"), run_cmd, _("Run a job"),
168 NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
169 "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tcomment=<text> yes"), false},
171 { NT_("status"), status_cmd, _("Report status"),
172 NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
174 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
175 NT_("level=<nn> trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
177 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
178 { NT_("show"), show_cmd, _("Show resource records"),
179 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | disabled | all"), true},
181 { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
182 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
183 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
184 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
185 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
187 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
188 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
190 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
191 NT_("stats\n\tpool=<poolname>\n\tslots storage=<storage> scan"
192 "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
193 "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
194 "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
195 "\n\t enable=<yes/no> recyclepool=<pool> actiononpurge=<action>"),true},
196 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_(""), false},
197 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
198 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
199 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
200 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
203 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
206 * Execute a command from the UA
208 bool do_a_command(UAContext *ua)
214 BSOCK *user = ua->UA_sock;
217 Dmsg1(900, "Command: %s\n", ua->argk[0]);
222 while (ua->jcr->wstorage->size()) {
223 ua->jcr->wstorage->remove(0);
226 len = strlen(ua->argk[0]);
227 for (i=0; i<comsize; i++) { /* search for command */
228 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
229 /* Check if command permitted, but "quit" is always OK */
230 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
231 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
234 /* Check if this command is authorized in RunScript */
235 if (ua->runscript && !commands[i].use_in_rs) {
236 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
239 if (ua->api) user->signal(BNET_CMD_BEGIN);
240 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
241 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
247 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
254 * This is a common routine used to stuff the Pool DB record defaults
255 * into the Media DB record just before creating a media (Volume)
258 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
260 mr->PoolId = pr->PoolId;
261 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
262 mr->Recycle = pr->Recycle;
263 mr->VolRetention = pr->VolRetention;
264 mr->VolUseDuration = pr->VolUseDuration;
265 mr->ActionOnPurge = pr->ActionOnPurge;
266 mr->RecyclePoolId = pr->RecyclePoolId;
267 mr->MaxVolJobs = pr->MaxVolJobs;
268 mr->MaxVolFiles = pr->MaxVolFiles;
269 mr->MaxVolBytes = pr->MaxVolBytes;
270 mr->LabelType = pr->LabelType;
276 * Add Volumes to an existing Pool
278 static int add_cmd(UAContext *ua, const char *cmd)
282 int num, i, max, startnum;
284 char name[MAX_NAME_LENGTH];
286 int Slot = 0, InChanger = 0;
289 "You probably don't want to be using this command since it\n"
290 "creates database records without labeling the Volumes.\n"
291 "You probably want to use the \"label\" command.\n\n"));
293 if (!open_client_db(ua)) {
297 memset(&pr, 0, sizeof(pr));
298 memset(&mr, 0, sizeof(mr));
300 if (!get_pool_dbr(ua, &pr)) {
304 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
305 pr.MaxVols, pr.PoolType);
307 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
308 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
309 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
312 pr.MaxVols = ua->pint32_val;
316 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
317 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
318 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
322 if (pr.MaxVols == 0) {
325 max = pr.MaxVols - pr.NumVols;
329 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
330 if (!get_pint(ua, buf)) {
333 num = ua->pint32_val;
334 if (num < 0 || num > max) {
335 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
343 if (!get_cmd(ua, _("Enter Volume name: "))) {
347 if (!get_cmd(ua, _("Enter base volume name: "))) {
351 /* Don't allow | in Volume name because it is the volume separator character */
352 if (!is_volume_name_legal(ua, ua->cmd)) {
355 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
356 ua->warning_msg(_("Volume name too long.\n"));
359 if (strlen(ua->cmd) == 0) {
360 ua->warning_msg(_("Volume name must be at least one character long.\n"));
366 bstrncpy(name, ua->cmd, sizeof(name));
368 bstrncat(name, "%04d", sizeof(name));
371 if (!get_pint(ua, _("Enter the starting number: "))) {
374 startnum = ua->pint32_val;
376 ua->warning_msg(_("Start number must be greater than zero.\n"));
386 if (store && store->autochanger) {
387 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
390 Slot = ua->pint32_val;
391 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
394 InChanger = ua->pint32_val;
397 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
398 for (i=startnum; i < num+startnum; i++) {
399 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
401 mr.InChanger = InChanger;
402 mr.StorageId = store->StorageId;
404 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
405 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
406 ua->error_msg("%s", db_strerror(ua->db));
410 first_id = mr.PoolId;
414 Dmsg0(200, "Update pool record.\n");
415 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
416 ua->warning_msg("%s", db_strerror(ua->db));
419 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
425 * Turn auto mount on/off
430 int automount_cmd(UAContext *ua, const char *cmd)
435 if (!get_cmd(ua, _("Turn on or off? "))) {
443 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
451 static int cancel_cmd(UAContext *ua, const char *cmd)
456 char JobName[MAX_NAME_LENGTH];
458 for (i=1; i<ua->argc; i++) {
459 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
461 JobId = str_to_int64(ua->argv[i]);
465 if (!(jcr=get_jcr_by_id(JobId))) {
466 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
470 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
474 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
475 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
476 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
477 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
480 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
484 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
485 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
486 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
487 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
494 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
495 ua->error_msg(_("Unauthorized command from this console.\n"));
500 * If we still do not have a jcr,
501 * throw up a list and ask the user to select one.
504 int tjobs = 0; /* total # number jobs */
505 /* Count Jobs running */
507 if (jcr->JobId == 0) { /* this is us */
510 tjobs++; /* count of all jobs */
511 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
512 continue; /* skip not authorized */
514 njobs++; /* count of authorized jobs */
518 if (njobs == 0) { /* no authorized */
520 ua->send_msg(_("No Jobs running.\n"));
522 ua->send_msg(_("None of your jobs are running.\n"));
527 start_prompt(ua, _("Select Job:\n"));
530 if (jcr->JobId == 0) { /* this is us */
533 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
534 continue; /* skip not authorized */
536 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
541 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
544 if (ua->api && njobs == 1) {
546 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
547 _("Confirm cancel?"));
548 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
553 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
558 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
559 jcr = get_jcr_by_full_name(JobName);
561 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
566 ret = cancel_job(ua, jcr);
572 * This is a common routine to create or update a
573 * Pool DB base record from a Pool Resource. We handle
574 * the setting of MaxVols and NumVols slightly differently
575 * depending on if we are creating the Pool or we are
576 * simply bringing it into agreement with the resource (updage).
578 * Caution : RecyclePoolId isn't setup in this function.
579 * You can use set_pooldbr_recyclepoolid();
582 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
584 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
585 if (op == POOL_OP_CREATE) {
586 pr->MaxVols = pool->max_volumes;
588 } else { /* update pool */
589 if (pr->MaxVols != pool->max_volumes) {
590 pr->MaxVols = pool->max_volumes;
592 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
593 pr->MaxVols = pr->NumVols;
596 pr->LabelType = pool->LabelType;
597 pr->UseOnce = pool->use_volume_once;
598 pr->UseCatalog = pool->use_catalog;
599 pr->Recycle = pool->Recycle;
600 pr->VolRetention = pool->VolRetention;
601 pr->VolUseDuration = pool->VolUseDuration;
602 pr->MaxVolJobs = pool->MaxVolJobs;
603 pr->MaxVolFiles = pool->MaxVolFiles;
604 pr->MaxVolBytes = pool->MaxVolBytes;
605 pr->AutoPrune = pool->AutoPrune;
606 pr->ActionOnPurge = pool->action_on_purge;
607 pr->Recycle = pool->Recycle;
608 if (pool->label_format) {
609 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
611 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
615 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
616 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
620 if (!pool->RecyclePool && !pool->ScratchPool) {
624 memset(&pr, 0, sizeof(POOL_DBR));
625 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
627 if (!db_get_pool_record(jcr, db, &pr)) {
628 return -1; /* not exists in database */
631 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
633 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
634 return -1; /* error */
637 if (!db_update_pool_record(jcr, db, &pr)) {
638 return -1; /* error */
643 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
644 * works with set_pooldbr_from_poolres
646 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
651 if (pool->RecyclePool) {
652 memset(&rpool, 0, sizeof(POOL_DBR));
654 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
655 if (db_get_pool_record(jcr, db, &rpool)) {
656 pr->RecyclePoolId = rpool.PoolId;
658 Jmsg(jcr, M_WARNING, 0,
659 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
660 "Try to update it with 'update pool=%s'\n"),
661 pool->name(), rpool.Name, rpool.Name,pool->name());
665 } else { /* no RecyclePool used, set it to 0 */
666 pr->RecyclePoolId = 0;
669 if (pool->ScratchPool) {
670 memset(&rpool, 0, sizeof(POOL_DBR));
672 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
673 if (db_get_pool_record(jcr, db, &rpool)) {
674 pr->ScratchPoolId = rpool.PoolId;
676 Jmsg(jcr, M_WARNING, 0,
677 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
678 "Try to update it with 'update pool=%s'\n"),
679 pool->name(), rpool.Name, rpool.Name,pool->name());
682 } else { /* no ScratchPool used, set it to 0 */
683 pr->ScratchPoolId = 0;
691 * Create a pool record from a given Pool resource
692 * Also called from backup.c
693 * Returns: -1 on error
694 * 0 record already exists
698 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
702 memset(&pr, 0, sizeof(POOL_DBR));
704 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
706 if (db_get_pool_record(jcr, db, &pr)) {
708 if (op == POOL_OP_UPDATE) { /* update request */
709 set_pooldbr_from_poolres(&pr, pool, op);
710 set_pooldbr_references(jcr, db, &pr, pool);
711 db_update_pool_record(jcr, db, &pr);
713 return 0; /* exists */
716 set_pooldbr_from_poolres(&pr, pool, op);
717 set_pooldbr_references(jcr, db, &pr, pool);
719 if (!db_create_pool_record(jcr, db, &pr)) {
720 return -1; /* error */
728 * Create a Pool Record in the database.
729 * It is always created from the Resource record.
731 static int create_cmd(UAContext *ua, const char *cmd)
735 if (!open_client_db(ua)) {
739 pool = get_pool_resource(ua);
744 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
746 ua->error_msg(_("Error: Pool %s already exists.\n"
747 "Use update to change it.\n"), pool->name());
751 ua->error_msg("%s", db_strerror(ua->db));
757 ua->send_msg(_("Pool %s created.\n"), pool->name());
762 extern DIRRES *director;
763 extern char *configfile;
766 * Python control command
767 * python restart (restarts interpreter)
769 static int python_cmd(UAContext *ua, const char *cmd)
772 init_python_interpreter_args python_args;
774 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
775 term_python_interpreter();
777 python_args.progname = director->name();
778 python_args.scriptdir = director->scripts_directory;
779 python_args.modulename = "DirStartUp";
780 python_args.configfile = configfile;
781 python_args.workingdir = director->working_directory;
782 python_args.job_getattr = job_getattr;
783 python_args.job_setattr = job_setattr;
785 init_python_interpreter(&python_args);
787 ua->send_msg(_("Python interpreter restarted.\n"));
789 #endif /* HAVE_PYTHON */
790 ua->warning_msg(_("Nothing done.\n"));
793 #endif /* HAVE_PYTHON */
799 * Set a new address in a Client resource. We do this only
800 * if the Console name is the same as the Client name
801 * and the Console can access the client.
803 static int setip_cmd(UAContext *ua, const char *cmd)
807 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
808 ua->error_msg(_("Unauthorized command from this console.\n"));
812 client = GetClientResWithName(ua->cons->name());
815 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
818 if (client->address) {
819 free(client->address);
821 /* MA Bug 6 remove ifdef */
822 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
823 client->address = bstrdup(buf);
824 ua->send_msg(_("Client \"%s\" address set to %s\n"),
825 client->name(), client->address);
832 static void do_en_disable_cmd(UAContext *ua, bool setting)
837 i = find_arg_with_value(ua, NT_("job"));
839 job = select_enable_disable_job_resource(ua, setting);
845 job = GetJobResWithName(ua->argv[i]);
849 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
853 if (!acl_access_ok(ua, Job_ACL, job->name())) {
854 ua->error_msg(_("Unauthorized command from this console.\n"));
857 job->enabled = setting;
858 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
862 static int enable_cmd(UAContext *ua, const char *cmd)
864 do_en_disable_cmd(ua, true);
868 static int disable_cmd(UAContext *ua, const char *cmd)
870 do_en_disable_cmd(ua, false);
874 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
880 lstore.store = store;
881 pm_strcpy(lstore.store_source, _("unknown source"));
882 set_wstorage(jcr, &lstore);
883 /* Try connecting for up to 15 seconds */
884 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
885 store->name(), store->address, store->SDport);
886 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
887 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
890 Dmsg0(120, _("Connected to storage daemon\n"));
891 sd = jcr->store_bsock;
892 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
893 if (sd->recv() >= 0) {
894 ua->send_msg("%s", sd->msg);
896 sd->signal(BNET_TERMINATE);
898 jcr->store_bsock = NULL;
903 * For the client, we have the following values that can be set
904 * level = debug level
905 * trace = send debug output to a file
906 * hangup = how many records to send to SD before hanging up
907 * obviously this is most useful for testing restarting
910 static void do_client_setdebug(UAContext *ua, CLIENT *client,
911 int level, int trace, int hangup)
915 /* Connect to File daemon */
917 ua->jcr->client = client;
918 /* Try to connect for 15 seconds */
919 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
920 client->name(), client->address, client->FDport);
921 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
922 ua->error_msg(_("Failed to connect to Client.\n"));
925 Dmsg0(120, "Connected to file daemon\n");
926 fd = ua->jcr->file_bsock;
927 fd->fsend("setdebug=%d trace=%d hangup=%d\n", level, trace, hangup);
928 if (fd->recv() >= 0) {
929 ua->send_msg("%s", fd->msg);
931 fd->signal(BNET_TERMINATE);
933 ua->jcr->file_bsock = NULL;
938 static void do_all_setdebug(UAContext *ua, int level, int trace_flag, int hangup)
940 STORE *store, **unique_store;
941 CLIENT *client, **unique_client;
947 /* Count Storage items */
951 foreach_res(store, R_STORAGE) {
954 unique_store = (STORE **) malloc(i * sizeof(STORE));
955 /* Find Unique Storage address/port */
956 store = (STORE *)GetNextRes(R_STORAGE, NULL);
958 unique_store[i++] = store;
959 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
961 for (j=0; j<i; j++) {
962 if (strcmp(unique_store[j]->address, store->address) == 0 &&
963 unique_store[j]->SDport == store->SDport) {
969 unique_store[i++] = store;
970 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
975 /* Call each unique Storage daemon */
976 for (j=0; j<i; j++) {
977 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
981 /* Count Client items */
985 foreach_res(client, R_CLIENT) {
988 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
989 /* Find Unique Client address/port */
990 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
992 unique_client[i++] = client;
993 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
995 for (j=0; j<i; j++) {
996 if (strcmp(unique_client[j]->address, client->address) == 0 &&
997 unique_client[j]->FDport == client->FDport) {
1003 unique_client[i++] = client;
1004 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1009 /* Call each unique File daemon */
1010 for (j=0; j<i; j++) {
1011 do_client_setdebug(ua, unique_client[j], level, trace_flag, hangup);
1013 free(unique_client);
1017 * setdebug level=nn all trace=1/0
1019 static int setdebug_cmd(UAContext *ua, const char *cmd)
1024 int trace_flag = -1;
1028 Dmsg1(120, "setdebug:%s:\n", cmd);
1031 i = find_arg_with_value(ua, "level");
1033 level = atoi(ua->argv[i]);
1036 if (!get_pint(ua, _("Enter new debug level: "))) {
1039 level = ua->pint32_val;
1042 /* Look for trace flag. -1 => not change */
1043 i = find_arg_with_value(ua, "trace");
1045 trace_flag = atoi(ua->argv[i]);
1046 if (trace_flag > 0) {
1051 /* Look for hangup (debug only)flag. -1 => not change */
1052 i = find_arg_with_value(ua, "hangup");
1054 hangup = atoi(ua->argv[i]);
1058 /* General debug? */
1059 for (i=1; i<ua->argc; i++) {
1060 if (strcasecmp(ua->argk[i], "all") == 0) {
1061 do_all_setdebug(ua, level, trace_flag, hangup);
1064 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1065 strcasecmp(ua->argk[i], "director") == 0) {
1066 debug_level = level;
1067 set_trace(trace_flag);
1070 if (strcasecmp(ua->argk[i], "client") == 0 ||
1071 strcasecmp(ua->argk[i], "fd") == 0) {
1074 client = GetClientResWithName(ua->argv[i]);
1076 do_client_setdebug(ua, client, level, trace_flag, hangup);
1080 client = select_client_resource(ua);
1082 do_client_setdebug(ua, client, level, trace_flag, hangup);
1087 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1088 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1089 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1092 store = GetStoreResWithName(ua->argv[i]);
1094 do_storage_setdebug(ua, store, level, trace_flag);
1098 store = get_storage_resource(ua, false/*no default*/);
1100 do_storage_setdebug(ua, store, level, trace_flag);
1106 * We didn't find an appropriate keyword above, so
1109 start_prompt(ua, _("Available daemons are: \n"));
1110 add_prompt(ua, _("Director"));
1111 add_prompt(ua, _("Storage"));
1112 add_prompt(ua, _("Client"));
1113 add_prompt(ua, _("All"));
1114 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1115 case 0: /* Director */
1116 debug_level = level;
1117 set_trace(trace_flag);
1120 store = get_storage_resource(ua, false/*no default*/);
1122 do_storage_setdebug(ua, store, level, trace_flag);
1126 client = select_client_resource(ua);
1128 do_client_setdebug(ua, client, level, trace_flag, hangup);
1132 do_all_setdebug(ua, level, trace_flag, hangup);
1141 * Turn debug tracing to file on/off
1143 static int trace_cmd(UAContext *ua, const char *cmd)
1147 if (ua->argc != 2) {
1148 if (!get_cmd(ua, _("Turn on or off? "))) {
1153 onoff = ua->argk[1];
1156 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1161 static int var_cmd(UAContext *ua, const char *cmd)
1163 POOLMEM *val = get_pool_memory(PM_FNAME);
1166 if (!open_client_db(ua)) {
1169 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1172 while (*var == ' ') { /* skip spaces */
1175 Dmsg1(100, "Var=%s:\n", var);
1176 variable_expansion(ua->jcr, var, &val);
1177 ua->send_msg("%s\n", val);
1178 free_pool_memory(val);
1182 static int estimate_cmd(UAContext *ua, const char *cmd)
1185 CLIENT *client = NULL;
1186 FILESET *fileset = NULL;
1188 char since[MAXSTRING];
1192 jcr->setJobLevel(L_FULL);
1193 for (int i=1; i<ua->argc; i++) {
1194 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1195 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1197 client = GetClientResWithName(ua->argv[i]);
1199 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1202 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1203 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1208 ua->error_msg(_("Client name missing.\n"));
1212 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1214 job = GetJobResWithName(ua->argv[i]);
1216 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1219 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1220 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1225 ua->error_msg(_("Job name missing.\n"));
1230 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1232 fileset = GetFileSetResWithName(ua->argv[i]);
1234 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1237 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1238 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1243 ua->error_msg(_("Fileset name missing.\n"));
1247 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1251 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1253 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1254 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1258 ua->error_msg(_("Level value missing.\n"));
1262 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1263 if (!is_yesno(ua->argv[i], &accurate)) {
1264 ua->error_msg(_("Invalid value for accurate. "
1265 "It must be yes or no.\n"));
1269 if (!job && !(client && fileset)) {
1270 if (!(job = select_job_resource(ua))) {
1275 job = GetJobResWithName(ua->argk[1]);
1277 ua->error_msg(_("No job specified.\n"));
1280 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1281 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1286 client = job->client;
1289 fileset = job->fileset;
1291 jcr->client = client;
1292 jcr->fileset = fileset;
1294 if (job->pool->catalog) {
1295 ua->catalog = job->pool->catalog;
1297 ua->catalog = client->catalog;
1305 jcr->setJobType(JT_BACKUP);
1306 init_jcr_job_record(jcr);
1308 if (!get_or_create_client_record(jcr)) {
1311 if (!get_or_create_fileset_record(jcr)) {
1315 get_level_since_time(ua->jcr, since, sizeof(since));
1317 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1318 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1319 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1320 ua->error_msg(_("Failed to connect to Client.\n"));
1324 if (!send_include_list(jcr)) {
1325 ua->error_msg(_("Error sending include list.\n"));
1329 if (!send_exclude_list(jcr)) {
1330 ua->error_msg(_("Error sending exclude list.\n"));
1334 /* The level string change if accurate mode is enabled */
1335 if (accurate >= 0) {
1336 jcr->accurate = accurate;
1338 jcr->accurate = job->accurate;
1341 if (!send_level_command(jcr)) {
1346 * If the job is in accurate mode, we send the list of
1349 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1350 if (!send_accurate_current_files(jcr)) {
1354 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1355 while (jcr->file_bsock->recv() >= 0) {
1356 ua->send_msg("%s", jcr->file_bsock->msg);
1360 if (jcr->file_bsock) {
1361 jcr->file_bsock->signal(BNET_TERMINATE);
1362 jcr->file_bsock->close();
1363 jcr->file_bsock = NULL;
1372 static int time_cmd(UAContext *ua, const char *cmd)
1375 time_t ttime = time(NULL);
1377 (void)localtime_r(&ttime, &tm);
1378 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1379 ua->send_msg("%s\n", sdt);
1384 * reload the conf file
1386 extern "C" void reload_config(int sig);
1388 static int reload_cmd(UAContext *ua, const char *cmd)
1395 * Delete Pool records (should purge Media with it).
1397 * delete pool=<pool-name>
1398 * delete volume pool=<pool-name> volume=<name>
1401 static int delete_cmd(UAContext *ua, const char *cmd)
1403 static const char *keywords[] = {
1409 if (!open_client_db(ua)) {
1413 switch (find_arg_keyword(ua, keywords)) {
1422 while ((i=find_arg(ua, "jobid")) > 0) {
1424 *ua->argk[i] = 0; /* zap keyword already visited */
1432 "In general it is not a good idea to delete either a\n"
1433 "Pool or a Volume since they may contain data.\n\n"));
1435 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1446 ua->warning_msg(_("Nothing done.\n"));
1454 * delete_job has been modified to parse JobID lists like the
1456 * delete JobID=3,4,6,7-11,14
1458 * Thanks to Phil Stracchino for the above addition.
1461 static void delete_job(UAContext *ua)
1466 int i = find_arg_with_value(ua, NT_("jobid"));
1468 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1469 s = bstrdup(ua->argv[i]);
1472 * We could use strtok() here. But we're not going to, because:
1473 * (a) strtok() is deprecated, having been replaced by strsep();
1474 * (b) strtok() is broken in significant ways.
1475 * we could use strsep() instead, but it's not universally available.
1476 * so we grow our own using strchr().
1478 sep = strchr(tok, ',');
1479 while (sep != NULL) {
1481 if (!delete_job_id_range(ua, tok)) {
1482 JobId = str_to_int64(tok);
1483 do_job_delete(ua, JobId);
1486 sep = strchr(tok, ',');
1488 /* pick up the last token */
1489 if (!delete_job_id_range(ua, tok)) {
1490 JobId = str_to_int64(tok);
1491 do_job_delete(ua, JobId);
1496 JobId = str_to_int64(ua->argv[i]);
1497 do_job_delete(ua, JobId);
1499 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1502 JobId = ua->int64_val;
1503 do_job_delete(ua, JobId);
1508 * we call delete_job_id_range to parse range tokens and iterate over ranges
1510 static bool delete_job_id_range(UAContext *ua, char *tok)
1515 tok2 = strchr(tok, '-');
1521 j1 = str_to_int64(tok);
1522 j2 = str_to_int64(tok2);
1523 for (j=j1; j<=j2; j++) {
1524 do_job_delete(ua, j);
1530 * do_job_delete now performs the actual delete operation atomically
1532 static void do_job_delete(UAContext *ua, JobId_t JobId)
1536 edit_int64(JobId, ed1);
1537 purge_jobs_from_catalog(ua, ed1);
1538 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1542 * Delete media records from database -- dangerous
1544 static int delete_volume(UAContext *ua)
1550 if (!select_media_dbr(ua, &mr)) {
1553 ua->warning_msg(_("\nThis command will delete volume %s\n"
1554 "and all Jobs saved on that volume from the Catalog\n"),
1557 if (find_arg(ua, "yes") >= 0) {
1558 ua->pint32_val = 1; /* Have "yes" on command line already" */
1560 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1562 if (!get_yesno(ua, buf)) {
1566 if (!ua->pint32_val) {
1570 /* If not purged, do it */
1571 if (strcmp(mr.VolStatus, "Purged") != 0) {
1572 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1573 ua->error_msg(_("Can't list jobs on this volume\n"));
1577 purge_jobs_from_catalog(ua, lst.list);
1581 db_delete_media_record(ua->jcr, ua->db, &mr);
1586 * Delete a pool record from the database -- dangerous
1588 static int delete_pool(UAContext *ua)
1593 memset(&pr, 0, sizeof(pr));
1595 if (!get_pool_dbr(ua, &pr)) {
1598 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1600 if (!get_yesno(ua, buf)) {
1603 if (ua->pint32_val) {
1604 db_delete_pool_record(ua->jcr, ua->db, &pr);
1609 int memory_cmd(UAContext *ua, const char *cmd)
1611 garbage_collect_memory();
1612 list_dir_status_header(ua);
1613 sm_dump(false, true);
1617 static void do_mount_cmd(UAContext *ua, const char *command)
1622 char dev_name[MAX_NAME_LENGTH];
1626 if (!open_client_db(ua)) {
1629 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1631 store.store = get_storage_resource(ua, true/*arg is storage*/);
1635 pm_strcpy(store.store_source, _("unknown source"));
1636 set_wstorage(jcr, &store);
1637 drive = get_storage_drive(ua, store.store);
1638 if (strcmp(command, "mount") == 0) {
1639 slot = get_storage_slot(ua, store.store);
1642 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1643 store.store->media_type, store.store->dev_name(), drive);
1645 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1646 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1649 sd = jcr->store_bsock;
1650 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1651 bash_spaces(dev_name);
1653 sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1655 sd->fsend("%s %s drive=%d", command, dev_name, drive);
1657 while (sd->recv() >= 0) {
1658 ua->send_msg("%s", sd->msg);
1660 sd->signal(BNET_TERMINATE);
1662 jcr->store_bsock = NULL;
1666 * mount [storage=<name>] [drive=nn] [slot=mm]
1668 static int mount_cmd(UAContext *ua, const char *cmd)
1670 do_mount_cmd(ua, "mount"); /* mount */
1676 * unmount [storage=<name>] [drive=nn]
1678 static int unmount_cmd(UAContext *ua, const char *cmd)
1680 do_mount_cmd(ua, "unmount"); /* unmount */
1686 * release [storage=<name>] [drive=nn]
1688 static int release_cmd(UAContext *ua, const char *cmd)
1690 do_mount_cmd(ua, "release"); /* release */
1697 * use catalog=<name>
1699 static int use_cmd(UAContext *ua, const char *cmd)
1701 CAT *oldcatalog, *catalog;
1704 close_db(ua); /* close any previously open db */
1705 oldcatalog = ua->catalog;
1707 if (!(catalog = get_catalog_resource(ua))) {
1708 ua->catalog = oldcatalog;
1710 ua->catalog = catalog;
1713 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1714 ua->catalog->name(), ua->catalog->db_name);
1719 int quit_cmd(UAContext *ua, const char *cmd)
1725 /* Handler to get job status */
1726 static int status_handler(void *ctx, int num_fields, char **row)
1728 char *val = (char *)ctx;
1733 *val = '?'; /* Unknown by default */
1740 * Wait until no job is running
1742 int wait_cmd(UAContext *ua, const char *cmd)
1746 time_t stop_time = 0;
1750 * Wait until no job is running
1752 if (ua->argc == 1) {
1753 bmicrosleep(0, 200000); /* let job actually start */
1754 for (bool running=true; running; ) {
1757 if (jcr->JobId != 0) {
1771 i = find_arg_with_value(ua, NT_("timeout"));
1772 if (i > 0 && ua->argv[i]) {
1773 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1776 /* we have jobid, jobname or ujobid argument */
1778 uint32_t jobid = 0 ;
1780 if (!open_client_db(ua)) {
1781 ua->error_msg(_("ERR: Can't open db\n")) ;
1785 for (int i=1; i<ua->argc; i++) {
1786 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1790 jobid = str_to_int64(ua->argv[i]);
1792 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1793 strcasecmp(ua->argk[i], "job") == 0) {
1797 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1799 jobid = jcr->JobId ;
1803 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1807 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1809 jobid = jcr->JobId ;
1813 /* Wait for a mount request */
1814 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1815 for (bool waiting=false; !waiting; ) {
1817 if (jcr->JobId != 0 &&
1818 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1827 if (stop_time && (time(NULL) >= stop_time)) {
1828 ua->warning_msg(_("Wait on mount timed out\n"));
1838 ua->error_msg(_("ERR: Job was not found\n"));
1843 * We wait the end of a specific job
1846 bmicrosleep(0, 200000); /* let job actually start */
1847 for (bool running=true; running; ) {
1850 jcr=get_jcr_by_id(jobid) ;
1863 * We have to get JobStatus
1867 char jobstatus = '?'; /* Unknown by default */
1870 bsnprintf(buf, sizeof(buf),
1871 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1874 db_sql_query(ua->db, buf,
1875 status_handler, (void *)&jobstatus);
1877 switch (jobstatus) {
1879 status = 1 ; /* Warning */
1883 case JS_ErrorTerminated:
1885 status = 2 ; /* Critical */
1890 status = 0 ; /* Ok */
1894 status = 3 ; /* Unknown */
1898 ua->send_msg("JobId=%i\n", jobid) ;
1899 ua->send_msg("JobStatus=%s (%c)\n",
1900 job_status_to_str(jobstatus),
1903 if (ua->gui || ua->api) {
1904 ua->send_msg("ExitStatus=%i\n", status) ;
1911 static int help_cmd(UAContext *ua, const char *cmd)
1914 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1915 for (i=0; i<comsize; i++) {
1916 if (ua->argc == 2) {
1917 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1918 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1919 commands[i].help, commands[i].usage);
1923 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1926 if (i == comsize && ua->argc == 2) {
1927 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1929 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1933 int qhelp_cmd(UAContext *ua, const char *cmd)
1936 /* Want to display only commands */
1937 j = find_arg(ua, NT_("all"));
1939 for (i=0; i<comsize; i++) {
1940 ua->send_msg("%s\n", commands[i].key);
1944 /* Want to display a specific help section */
1945 j = find_arg_with_value(ua, NT_("item"));
1946 if (j >= 0 && ua->argk[j]) {
1947 for (i=0; i<comsize; i++) {
1948 if (bstrcmp(commands[i].key, ua->argv[j])) {
1949 ua->send_msg("%s\n", commands[i].usage);
1955 /* Want to display everything */
1956 for (i=0; i<comsize; i++) {
1957 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1963 static int version_cmd(UAContext *ua, const char *cmd)
1965 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1966 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1971 * Test code -- turned on only for debug testing
1973 static int version_cmd(UAContext *ua, const char *cmd)
1976 POOL_MEM query(PM_MESSAGE);
1978 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1979 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1980 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1981 for (int i=0; i < ids.num_ids; i++) {
1982 ua->send_msg("id=%d\n", ids.DBId[i]);
1990 * This call uses open_client_db() and force a
1991 * new dedicated connection to the catalog
1993 bool open_new_client_db(UAContext *ua)
1997 /* Force a new dedicated connection */
1999 ua->force_mult_db_connections = true;
2000 ret = open_client_db(ua);
2001 ua->force_mult_db_connections = false;
2006 * This call explicitly checks for a catalog=xxx and
2007 * if given, opens that catalog. It also checks for
2008 * client=xxx and if found, opens the catalog
2009 * corresponding to that client. If we still don't
2010 * have a catalog, look for a Job keyword and get the
2011 * catalog from its client record.
2013 bool open_client_db(UAContext *ua)
2020 /* Try for catalog keyword */
2021 i = find_arg_with_value(ua, NT_("catalog"));
2023 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2024 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2027 catalog = GetCatalogResWithName(ua->argv[i]);
2029 if (ua->catalog && ua->catalog != catalog) {
2032 ua->catalog = catalog;
2037 /* Try for client keyword */
2038 i = find_arg_with_value(ua, NT_("client"));
2040 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2041 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2044 client = GetClientResWithName(ua->argv[i]);
2046 catalog = client->catalog;
2047 if (ua->catalog && ua->catalog != catalog) {
2050 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2051 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2054 ua->catalog = catalog;
2059 /* Try for Job keyword */
2060 i = find_arg_with_value(ua, NT_("job"));
2062 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2063 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2066 job = GetJobResWithName(ua->argv[i]);
2068 catalog = job->client->catalog;
2069 if (ua->catalog && ua->catalog != catalog) {
2072 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2073 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2076 ua->catalog = catalog;
2086 * Open the catalog database.
2088 bool open_db(UAContext *ua)
2096 ua->catalog = get_catalog_resource(ua);
2098 ua->error_msg( _("Could not find a Catalog resource\n"));
2103 /* Some modules like bvfs need their own catalog connection */
2104 mult_db_conn = ua->catalog->mult_db_connections;
2105 if (ua->force_mult_db_connections) {
2106 mult_db_conn = true;
2109 ua->jcr->catalog = ua->catalog;
2111 Dmsg0(100, "UA Open database\n");
2112 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2113 ua->catalog->db_user,
2114 ua->catalog->db_password, ua->catalog->db_address,
2115 ua->catalog->db_port, ua->catalog->db_socket,
2116 mult_db_conn, ua->catalog->disable_batch_insert);
2117 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2118 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2119 ua->catalog->db_name);
2121 ua->error_msg("%s", db_strerror(ua->db));
2126 ua->jcr->db = ua->db;
2128 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2130 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2134 void close_db(UAContext *ua)
2137 db_close_database(ua->jcr, ua->db);