2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2012 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\n\tumount update 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_("catalog=<catalog>"), 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;
283 char name[MAX_NAME_LENGTH];
285 int Slot = 0, InChanger = 0;
288 "You probably don't want to be using this command since it\n"
289 "creates database records without labeling the Volumes.\n"
290 "You probably want to use the \"label\" command.\n\n"));
292 if (!open_client_db(ua)) {
296 memset(&pr, 0, sizeof(pr));
298 if (!get_pool_dbr(ua, &pr)) {
302 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
303 pr.MaxVols, pr.PoolType);
305 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
306 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
307 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
310 pr.MaxVols = ua->pint32_val;
314 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
315 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
316 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
320 if (pr.MaxVols == 0) {
323 max = pr.MaxVols - pr.NumVols;
327 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
328 if (!get_pint(ua, buf)) {
331 num = ua->pint32_val;
332 if (num < 0 || num > max) {
333 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
341 if (!get_cmd(ua, _("Enter Volume name: "))) {
345 if (!get_cmd(ua, _("Enter base volume name: "))) {
349 /* Don't allow | in Volume name because it is the volume separator character */
350 if (!is_volume_name_legal(ua, ua->cmd)) {
353 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
354 ua->warning_msg(_("Volume name too long.\n"));
357 if (strlen(ua->cmd) == 0) {
358 ua->warning_msg(_("Volume name must be at least one character long.\n"));
364 bstrncpy(name, ua->cmd, sizeof(name));
366 bstrncat(name, "%04d", sizeof(name));
369 if (!get_pint(ua, _("Enter the starting number: "))) {
372 startnum = ua->pint32_val;
374 ua->warning_msg(_("Start number must be greater than zero.\n"));
384 if (store && store->autochanger) {
385 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
388 Slot = ua->pint32_val;
389 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
392 InChanger = ua->pint32_val;
395 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
396 for (i=startnum; i < num+startnum; i++) {
397 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
399 mr.InChanger = InChanger;
401 set_storageid_in_mr(store, &mr);
402 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
403 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
404 ua->error_msg("%s", db_strerror(ua->db));
409 Dmsg0(200, "Update pool record.\n");
410 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
411 ua->warning_msg("%s", db_strerror(ua->db));
414 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
420 * Turn auto mount on/off
425 int automount_cmd(UAContext *ua, const char *cmd)
430 if (!get_cmd(ua, _("Turn on or off? "))) {
438 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
446 static int cancel_cmd(UAContext *ua, const char *cmd)
451 char JobName[MAX_NAME_LENGTH];
453 for (i=1; i<ua->argc; i++) {
454 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
456 JobId = str_to_int64(ua->argv[i]);
460 if (!(jcr=get_jcr_by_id(JobId))) {
461 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
465 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
469 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
470 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
471 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
472 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
475 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
479 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
480 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
481 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
482 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
489 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
490 ua->error_msg(_("Unauthorized command from this console.\n"));
495 * If we still do not have a jcr,
496 * throw up a list and ask the user to select one.
499 int tjobs = 0; /* total # number jobs */
500 /* Count Jobs running */
502 if (jcr->JobId == 0) { /* this is us */
505 tjobs++; /* count of all jobs */
506 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
507 continue; /* skip not authorized */
509 njobs++; /* count of authorized jobs */
513 if (njobs == 0) { /* no authorized */
515 ua->send_msg(_("No Jobs running.\n"));
517 ua->send_msg(_("None of your jobs are running.\n"));
522 start_prompt(ua, _("Select Job:\n"));
525 if (jcr->JobId == 0) { /* this is us */
528 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
529 continue; /* skip not authorized */
531 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
536 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
539 if (ua->api && njobs == 1) {
541 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
542 _("Confirm cancel?"));
543 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
548 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
553 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
554 jcr = get_jcr_by_full_name(JobName);
556 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
561 ret = cancel_job(ua, jcr);
567 * This is a common routine to create or update a
568 * Pool DB base record from a Pool Resource. We handle
569 * the setting of MaxVols and NumVols slightly differently
570 * depending on if we are creating the Pool or we are
571 * simply bringing it into agreement with the resource (updage).
573 * Caution : RecyclePoolId isn't setup in this function.
574 * You can use set_pooldbr_recyclepoolid();
577 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
579 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
580 if (op == POOL_OP_CREATE) {
581 pr->MaxVols = pool->max_volumes;
583 } else { /* update pool */
584 if (pr->MaxVols != pool->max_volumes) {
585 pr->MaxVols = pool->max_volumes;
587 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
588 pr->MaxVols = pr->NumVols;
591 pr->LabelType = pool->LabelType;
592 pr->UseOnce = pool->use_volume_once;
593 pr->UseCatalog = pool->use_catalog;
594 pr->Recycle = pool->Recycle;
595 pr->VolRetention = pool->VolRetention;
596 pr->VolUseDuration = pool->VolUseDuration;
597 pr->MaxVolJobs = pool->MaxVolJobs;
598 pr->MaxVolFiles = pool->MaxVolFiles;
599 pr->MaxVolBytes = pool->MaxVolBytes;
600 pr->AutoPrune = pool->AutoPrune;
601 pr->ActionOnPurge = pool->action_on_purge;
602 pr->Recycle = pool->Recycle;
603 if (pool->label_format) {
604 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
606 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
610 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
611 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
615 if (!pool->RecyclePool && !pool->ScratchPool) {
619 memset(&pr, 0, sizeof(POOL_DBR));
620 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
622 if (!db_get_pool_record(jcr, db, &pr)) {
623 return -1; /* not exists in database */
626 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
628 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
629 return -1; /* error */
632 if (!db_update_pool_record(jcr, db, &pr)) {
633 return -1; /* error */
638 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
639 * works with set_pooldbr_from_poolres
641 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
646 if (pool->RecyclePool) {
647 memset(&rpool, 0, sizeof(POOL_DBR));
649 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
650 if (db_get_pool_record(jcr, db, &rpool)) {
651 pr->RecyclePoolId = rpool.PoolId;
653 Jmsg(jcr, M_WARNING, 0,
654 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
655 "Try to update it with 'update pool=%s'\n"),
656 pool->name(), rpool.Name, rpool.Name,pool->name());
660 } else { /* no RecyclePool used, set it to 0 */
661 pr->RecyclePoolId = 0;
664 if (pool->ScratchPool) {
665 memset(&rpool, 0, sizeof(POOL_DBR));
667 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
668 if (db_get_pool_record(jcr, db, &rpool)) {
669 pr->ScratchPoolId = rpool.PoolId;
671 Jmsg(jcr, M_WARNING, 0,
672 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
673 "Try to update it with 'update pool=%s'\n"),
674 pool->name(), rpool.Name, rpool.Name,pool->name());
677 } else { /* no ScratchPool used, set it to 0 */
678 pr->ScratchPoolId = 0;
686 * Create a pool record from a given Pool resource
687 * Also called from backup.c
688 * Returns: -1 on error
689 * 0 record already exists
693 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
697 memset(&pr, 0, sizeof(POOL_DBR));
699 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
701 if (db_get_pool_record(jcr, db, &pr)) {
703 if (op == POOL_OP_UPDATE) { /* update request */
704 set_pooldbr_from_poolres(&pr, pool, op);
705 set_pooldbr_references(jcr, db, &pr, pool);
706 db_update_pool_record(jcr, db, &pr);
708 return 0; /* exists */
711 set_pooldbr_from_poolres(&pr, pool, op);
712 set_pooldbr_references(jcr, db, &pr, pool);
714 if (!db_create_pool_record(jcr, db, &pr)) {
715 return -1; /* error */
723 * Create a Pool Record in the database.
724 * It is always created from the Resource record.
726 static int create_cmd(UAContext *ua, const char *cmd)
730 if (!open_client_db(ua)) {
734 pool = get_pool_resource(ua);
739 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
741 ua->error_msg(_("Error: Pool %s already exists.\n"
742 "Use update to change it.\n"), pool->name());
746 ua->error_msg("%s", db_strerror(ua->db));
752 ua->send_msg(_("Pool %s created.\n"), pool->name());
757 extern DIRRES *director;
758 extern char *configfile;
761 * Python control command
762 * python restart (restarts interpreter)
764 static int python_cmd(UAContext *ua, const char *cmd)
767 init_python_interpreter_args python_args;
769 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
770 term_python_interpreter();
772 python_args.progname = director->name();
773 python_args.scriptdir = director->scripts_directory;
774 python_args.modulename = "DirStartUp";
775 python_args.configfile = configfile;
776 python_args.workingdir = director->working_directory;
777 python_args.job_getattr = job_getattr;
778 python_args.job_setattr = job_setattr;
780 init_python_interpreter(&python_args);
782 ua->send_msg(_("Python interpreter restarted.\n"));
784 #endif /* HAVE_PYTHON */
785 ua->warning_msg(_("Nothing done.\n"));
788 #endif /* HAVE_PYTHON */
793 * Set a new address in a Client resource. We do this only
794 * if the Console name is the same as the Client name
795 * and the Console can access the client.
797 static int setip_cmd(UAContext *ua, const char *cmd)
801 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
802 ua->error_msg(_("Unauthorized command from this console.\n"));
806 client = GetClientResWithName(ua->cons->name());
809 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
812 if (client->address) {
813 free(client->address);
815 /* MA Bug 6 remove ifdef */
816 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
817 client->address = bstrdup(buf);
818 ua->send_msg(_("Client \"%s\" address set to %s\n"),
819 client->name(), client->address);
826 static void do_en_disable_cmd(UAContext *ua, bool setting)
831 i = find_arg_with_value(ua, NT_("job"));
833 job = select_enable_disable_job_resource(ua, setting);
839 job = GetJobResWithName(ua->argv[i]);
843 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
847 if (!acl_access_ok(ua, Job_ACL, job->name())) {
848 ua->error_msg(_("Unauthorized command from this console.\n"));
851 job->enabled = setting;
852 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
856 static int enable_cmd(UAContext *ua, const char *cmd)
858 do_en_disable_cmd(ua, true);
862 static int disable_cmd(UAContext *ua, const char *cmd)
864 do_en_disable_cmd(ua, false);
868 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
874 lstore.store = store;
875 pm_strcpy(lstore.store_source, _("unknown source"));
876 set_wstorage(jcr, &lstore);
877 /* Try connecting for up to 15 seconds */
878 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
879 store->name(), store->address, store->SDport);
880 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
881 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
884 Dmsg0(120, _("Connected to storage daemon\n"));
885 sd = jcr->store_bsock;
886 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
887 if (sd->recv() >= 0) {
888 ua->send_msg("%s", sd->msg);
890 sd->signal(BNET_TERMINATE);
892 jcr->store_bsock = NULL;
897 * For the client, we have the following values that can be set
898 * level = debug level
899 * trace = send debug output to a file
900 * hangup = how many records to send to SD before hanging up
901 * obviously this is most useful for testing restarting
904 static void do_client_setdebug(UAContext *ua, CLIENT *client,
905 int level, int trace, int hangup)
909 /* Connect to File daemon */
911 ua->jcr->client = client;
912 /* Try to connect for 15 seconds */
913 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
914 client->name(), client->address, client->FDport);
915 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
916 ua->error_msg(_("Failed to connect to Client.\n"));
919 Dmsg0(120, "Connected to file daemon\n");
920 fd = ua->jcr->file_bsock;
921 fd->fsend("setdebug=%d trace=%d hangup=%d\n", level, trace, hangup);
922 if (fd->recv() >= 0) {
923 ua->send_msg("%s", fd->msg);
925 fd->signal(BNET_TERMINATE);
927 ua->jcr->file_bsock = NULL;
932 static void do_all_setdebug(UAContext *ua, int level, int trace_flag, int hangup)
934 STORE *store, **unique_store;
935 CLIENT *client, **unique_client;
941 /* Count Storage items */
945 foreach_res(store, R_STORAGE) {
948 unique_store = (STORE **) malloc(i * sizeof(STORE));
949 /* Find Unique Storage address/port */
950 store = (STORE *)GetNextRes(R_STORAGE, NULL);
952 unique_store[i++] = store;
953 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
955 for (j=0; j<i; j++) {
956 if (strcmp(unique_store[j]->address, store->address) == 0 &&
957 unique_store[j]->SDport == store->SDport) {
963 unique_store[i++] = store;
964 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
969 /* Call each unique Storage daemon */
970 for (j=0; j<i; j++) {
971 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
975 /* Count Client items */
979 foreach_res(client, R_CLIENT) {
982 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
983 /* Find Unique Client address/port */
984 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
986 unique_client[i++] = client;
987 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
989 for (j=0; j<i; j++) {
990 if (strcmp(unique_client[j]->address, client->address) == 0 &&
991 unique_client[j]->FDport == client->FDport) {
997 unique_client[i++] = client;
998 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1003 /* Call each unique File daemon */
1004 for (j=0; j<i; j++) {
1005 do_client_setdebug(ua, unique_client[j], level, trace_flag, hangup);
1007 free(unique_client);
1011 * setdebug level=nn all trace=1/0
1013 static int setdebug_cmd(UAContext *ua, const char *cmd)
1018 int trace_flag = -1;
1022 Dmsg1(120, "setdebug:%s:\n", cmd);
1025 i = find_arg_with_value(ua, "level");
1027 level = atoi(ua->argv[i]);
1030 if (!get_pint(ua, _("Enter new debug level: "))) {
1033 level = ua->pint32_val;
1036 /* Look for trace flag. -1 => not change */
1037 i = find_arg_with_value(ua, "trace");
1039 trace_flag = atoi(ua->argv[i]);
1040 if (trace_flag > 0) {
1045 /* Look for hangup (debug only)flag. -1 => not change */
1046 i = find_arg_with_value(ua, "hangup");
1048 hangup = atoi(ua->argv[i]);
1052 /* General debug? */
1053 for (i=1; i<ua->argc; i++) {
1054 if (strcasecmp(ua->argk[i], "all") == 0) {
1055 do_all_setdebug(ua, level, trace_flag, hangup);
1058 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1059 strcasecmp(ua->argk[i], "director") == 0) {
1060 debug_level = level;
1061 set_trace(trace_flag);
1064 if (strcasecmp(ua->argk[i], "client") == 0 ||
1065 strcasecmp(ua->argk[i], "fd") == 0) {
1068 client = GetClientResWithName(ua->argv[i]);
1070 do_client_setdebug(ua, client, level, trace_flag, hangup);
1074 client = select_client_resource(ua);
1076 do_client_setdebug(ua, client, level, trace_flag, hangup);
1081 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1082 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1083 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1086 store = GetStoreResWithName(ua->argv[i]);
1088 do_storage_setdebug(ua, store, level, trace_flag);
1092 store = get_storage_resource(ua, false/*no default*/);
1094 do_storage_setdebug(ua, store, level, trace_flag);
1100 * We didn't find an appropriate keyword above, so
1103 start_prompt(ua, _("Available daemons are: \n"));
1104 add_prompt(ua, _("Director"));
1105 add_prompt(ua, _("Storage"));
1106 add_prompt(ua, _("Client"));
1107 add_prompt(ua, _("All"));
1108 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1109 case 0: /* Director */
1110 debug_level = level;
1111 set_trace(trace_flag);
1114 store = get_storage_resource(ua, false/*no default*/);
1116 do_storage_setdebug(ua, store, level, trace_flag);
1120 client = select_client_resource(ua);
1122 do_client_setdebug(ua, client, level, trace_flag, hangup);
1126 do_all_setdebug(ua, level, trace_flag, hangup);
1135 * Turn debug tracing to file on/off
1137 static int trace_cmd(UAContext *ua, const char *cmd)
1141 if (ua->argc != 2) {
1142 if (!get_cmd(ua, _("Turn on or off? "))) {
1147 onoff = ua->argk[1];
1150 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1155 static int var_cmd(UAContext *ua, const char *cmd)
1157 POOLMEM *val = get_pool_memory(PM_FNAME);
1160 if (!open_client_db(ua)) {
1163 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1166 while (*var == ' ') { /* skip spaces */
1169 Dmsg1(100, "Var=%s:\n", var);
1170 variable_expansion(ua->jcr, var, &val);
1171 ua->send_msg("%s\n", val);
1172 free_pool_memory(val);
1176 static int estimate_cmd(UAContext *ua, const char *cmd)
1179 CLIENT *client = NULL;
1180 FILESET *fileset = NULL;
1182 char since[MAXSTRING];
1186 jcr->setJobLevel(L_FULL);
1187 for (int i=1; i<ua->argc; i++) {
1188 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1189 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1191 client = GetClientResWithName(ua->argv[i]);
1193 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1196 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1197 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1202 ua->error_msg(_("Client name missing.\n"));
1206 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1208 job = GetJobResWithName(ua->argv[i]);
1210 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1213 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1214 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1219 ua->error_msg(_("Job name missing.\n"));
1224 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1226 fileset = GetFileSetResWithName(ua->argv[i]);
1228 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1231 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1232 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1237 ua->error_msg(_("Fileset name missing.\n"));
1241 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1245 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1247 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1248 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1252 ua->error_msg(_("Level value missing.\n"));
1256 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1258 if (!is_yesno(ua->argv[i], &accurate)) {
1259 ua->error_msg(_("Invalid value for accurate. "
1260 "It must be yes or no.\n"));
1264 ua->error_msg(_("Accurate value missing.\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 jcr->start_time = time(NULL);
1307 init_jcr_job_record(jcr);
1309 if (!get_or_create_client_record(jcr)) {
1312 if (!get_or_create_fileset_record(jcr)) {
1316 get_level_since_time(ua->jcr, since, sizeof(since));
1318 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1319 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1320 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1321 ua->error_msg(_("Failed to connect to Client.\n"));
1325 /* The level string change if accurate mode is enabled */
1326 if (accurate >= 0) {
1327 jcr->accurate = accurate;
1329 jcr->accurate = job->accurate;
1332 if (!send_level_command(jcr)) {
1336 if (!send_include_list(jcr)) {
1337 ua->error_msg(_("Error sending include list.\n"));
1341 if (!send_exclude_list(jcr)) {
1342 ua->error_msg(_("Error sending exclude list.\n"));
1347 * If the job is in accurate mode, we send the list of
1350 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1351 if (!send_accurate_current_files(jcr)) {
1355 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1356 while (jcr->file_bsock->recv() >= 0) {
1357 ua->send_msg("%s", jcr->file_bsock->msg);
1361 if (jcr->file_bsock) {
1362 jcr->file_bsock->signal(BNET_TERMINATE);
1363 jcr->file_bsock->close();
1364 jcr->file_bsock = NULL;
1373 static int time_cmd(UAContext *ua, const char *cmd)
1376 time_t ttime = time(NULL);
1378 (void)localtime_r(&ttime, &tm);
1379 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1380 ua->send_msg("%s\n", sdt);
1385 * reload the conf file
1387 extern "C" void reload_config(int sig);
1389 static int reload_cmd(UAContext *ua, const char *cmd)
1396 * Delete Pool records (should purge Media with it).
1398 * delete pool=<pool-name>
1399 * delete volume pool=<pool-name> volume=<name>
1402 static int delete_cmd(UAContext *ua, const char *cmd)
1404 static const char *keywords[] = {
1410 if (!open_client_db(ua)) {
1414 switch (find_arg_keyword(ua, keywords)) {
1423 while ((i = find_arg(ua, "jobid")) > 0) {
1425 *ua->argk[i] = 0; /* zap keyword already visited */
1433 "In general it is not a good idea to delete either a\n"
1434 "Pool or a Volume since they may contain data.\n\n"));
1436 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1447 ua->warning_msg(_("Nothing done.\n"));
1454 * delete_job has been modified to parse JobID lists like the following:
1455 * delete JobID=3,4,6,7-11,14
1457 * Thanks to Phil Stracchino for the above addition.
1459 static void delete_job(UAContext *ua)
1463 char *s, *sep, *tok;
1465 i = find_arg_with_value(ua, NT_("jobid"));
1467 if (strchr(ua->argv[i], ',') || strchr(ua->argv[i], '-')) {
1468 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 if (is_a_number(tok)) {
1483 JobId = (JobId_t)str_to_uint64(tok);
1484 do_job_delete(ua, JobId);
1486 ua->warning_msg(_("Illegal JobId %s ignored\n"), tok);
1490 sep = strchr(tok, ',');
1494 * Pick up the last token
1496 if (!delete_job_id_range(ua, tok)) {
1497 if (is_a_number(tok)) {
1498 JobId = (JobId_t)str_to_uint64(tok);
1499 do_job_delete(ua, JobId);
1501 ua->warning_msg(_("Illegal JobId %s ignored\n"), tok);
1507 if (is_a_number(ua->argv[i])) {
1508 JobId = (JobId_t)str_to_uint64(ua->argv[i]);
1509 do_job_delete(ua, JobId);
1511 ua->warning_msg(_("Illegal JobId %s ignored\n"), ua->argv[i]);
1514 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1517 JobId = ua->int64_val;
1518 do_job_delete(ua, JobId);
1523 * We call delete_job_id_range to parse range tokens and iterate over ranges
1525 static bool delete_job_id_range(UAContext *ua, char *tok)
1531 tok2 = strchr(tok, '-');
1539 if (is_a_number(tok) && is_a_number(tok2)) {
1540 j1 = (JobId_t)str_to_uint64(tok);
1541 j2 = (JobId_t)str_to_uint64(tok2);
1545 * See if the range is big if more then 25 Jobs are deleted
1546 * ask the user for confirmation.
1548 if ((j2 - j1) > 25) {
1549 bsnprintf(buf, sizeof(buf),
1550 _("Are you sure you want to delete %d JobIds ? (yes/no): "),
1552 if (!get_yesno(ua, buf)) {
1556 for (j = j1; j <= j2; j++) {
1557 do_job_delete(ua, j);
1560 ua->warning_msg(_("Illegal JobId range %s - %s should define increasing JobIds, ignored\n"),
1564 ua->warning_msg(_("Illegal JobId range %s - %s, ignored\n"), tok, tok2);
1571 * do_job_delete now performs the actual delete operation atomically
1573 static void do_job_delete(UAContext *ua, JobId_t JobId)
1577 edit_int64(JobId, ed1);
1578 purge_jobs_from_catalog(ua, ed1);
1579 ua->send_msg(_("Jobid %s and associated records deleted from the catalog.\n"), ed1);
1583 * Delete media records from database -- dangerous
1585 static int delete_volume(UAContext *ua)
1591 if (!select_media_dbr(ua, &mr)) {
1594 ua->warning_msg(_("\nThis command will delete volume %s\n"
1595 "and all Jobs saved on that volume from the Catalog\n"),
1598 if (find_arg(ua, "yes") >= 0) {
1599 ua->pint32_val = 1; /* Have "yes" on command line already" */
1601 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1603 if (!get_yesno(ua, buf)) {
1607 if (!ua->pint32_val) {
1611 /* If not purged, do it */
1612 if (strcmp(mr.VolStatus, "Purged") != 0) {
1613 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1614 ua->error_msg(_("Can't list jobs on this volume\n"));
1618 purge_jobs_from_catalog(ua, lst.list);
1622 db_delete_media_record(ua->jcr, ua->db, &mr);
1627 * Delete a pool record from the database -- dangerous
1629 static int delete_pool(UAContext *ua)
1634 memset(&pr, 0, sizeof(pr));
1636 if (!get_pool_dbr(ua, &pr)) {
1639 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1641 if (!get_yesno(ua, buf)) {
1644 if (ua->pint32_val) {
1645 db_delete_pool_record(ua->jcr, ua->db, &pr);
1650 int memory_cmd(UAContext *ua, const char *cmd)
1652 garbage_collect_memory();
1653 list_dir_status_header(ua);
1654 sm_dump(false, true);
1658 static void do_mount_cmd(UAContext *ua, const char *command)
1663 char dev_name[MAX_NAME_LENGTH];
1667 if (!open_client_db(ua)) {
1670 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1672 store.store = get_storage_resource(ua, true/*arg is storage*/);
1676 pm_strcpy(store.store_source, _("unknown source"));
1677 set_wstorage(jcr, &store);
1678 drive = get_storage_drive(ua, store.store);
1679 if (strcmp(command, "mount") == 0) {
1680 slot = get_storage_slot(ua, store.store);
1683 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1684 store.store->media_type, store.store->dev_name(), drive);
1686 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1687 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1690 sd = jcr->store_bsock;
1691 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1692 bash_spaces(dev_name);
1694 sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1696 sd->fsend("%s %s drive=%d", command, dev_name, drive);
1698 while (sd->recv() >= 0) {
1699 ua->send_msg("%s", sd->msg);
1701 sd->signal(BNET_TERMINATE);
1703 jcr->store_bsock = NULL;
1707 * mount [storage=<name>] [drive=nn] [slot=mm]
1709 static int mount_cmd(UAContext *ua, const char *cmd)
1711 do_mount_cmd(ua, "mount"); /* mount */
1717 * unmount [storage=<name>] [drive=nn]
1719 static int unmount_cmd(UAContext *ua, const char *cmd)
1721 do_mount_cmd(ua, "unmount"); /* unmount */
1727 * release [storage=<name>] [drive=nn]
1729 static int release_cmd(UAContext *ua, const char *cmd)
1731 do_mount_cmd(ua, "release"); /* release */
1738 * use catalog=<name>
1740 static int use_cmd(UAContext *ua, const char *cmd)
1742 CAT *oldcatalog, *catalog;
1745 close_db(ua); /* close any previously open db */
1746 oldcatalog = ua->catalog;
1748 if (!(catalog = get_catalog_resource(ua))) {
1749 ua->catalog = oldcatalog;
1751 ua->catalog = catalog;
1754 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1755 ua->catalog->name(), ua->catalog->db_name);
1760 int quit_cmd(UAContext *ua, const char *cmd)
1766 /* Handler to get job status */
1767 static int status_handler(void *ctx, int num_fields, char **row)
1769 char *val = (char *)ctx;
1774 *val = '?'; /* Unknown by default */
1781 * Wait until no job is running
1783 int wait_cmd(UAContext *ua, const char *cmd)
1787 time_t stop_time = 0;
1791 * Wait until no job is running
1793 if (ua->argc == 1) {
1794 bmicrosleep(0, 200000); /* let job actually start */
1795 for (bool running=true; running; ) {
1798 if (jcr->JobId != 0) {
1812 i = find_arg_with_value(ua, NT_("timeout"));
1813 if (i > 0 && ua->argv[i]) {
1814 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1817 /* we have jobid, jobname or ujobid argument */
1819 uint32_t jobid = 0 ;
1821 if (!open_client_db(ua)) {
1822 ua->error_msg(_("ERR: Can't open db\n")) ;
1826 for (int i=1; i<ua->argc; i++) {
1827 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1831 jobid = str_to_int64(ua->argv[i]);
1833 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1834 strcasecmp(ua->argk[i], "job") == 0) {
1838 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1840 jobid = jcr->JobId ;
1844 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1848 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1850 jobid = jcr->JobId ;
1854 /* Wait for a mount request */
1855 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1856 for (bool waiting=false; !waiting; ) {
1858 if (jcr->JobId != 0 &&
1859 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1868 if (stop_time && (time(NULL) >= stop_time)) {
1869 ua->warning_msg(_("Wait on mount timed out\n"));
1879 ua->error_msg(_("ERR: Job was not found\n"));
1884 * We wait the end of a specific job
1887 bmicrosleep(0, 200000); /* let job actually start */
1888 for (bool running=true; running; ) {
1891 jcr=get_jcr_by_id(jobid) ;
1904 * We have to get JobStatus
1908 char jobstatus = '?'; /* Unknown by default */
1911 bsnprintf(buf, sizeof(buf),
1912 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1915 db_sql_query(ua->db, buf,
1916 status_handler, (void *)&jobstatus);
1918 switch (jobstatus) {
1920 status = 1 ; /* Warning */
1924 case JS_ErrorTerminated:
1926 status = 2 ; /* Critical */
1931 status = 0 ; /* Ok */
1935 status = 3 ; /* Unknown */
1939 ua->send_msg("JobId=%i\n", jobid) ;
1940 ua->send_msg("JobStatus=%s (%c)\n",
1941 job_status_to_str(jobstatus),
1944 if (ua->gui || ua->api) {
1945 ua->send_msg("ExitStatus=%i\n", status) ;
1952 static int help_cmd(UAContext *ua, const char *cmd)
1955 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1956 for (i=0; i<comsize; i++) {
1957 if (ua->argc == 2) {
1958 if (!strcasecmp(ua->argk[1], commands[i].key)) {
1959 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
1960 commands[i].help, commands[i].usage);
1964 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
1967 if (i == comsize && ua->argc == 2) {
1968 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1970 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1974 int qhelp_cmd(UAContext *ua, const char *cmd)
1977 /* Want to display only commands */
1978 j = find_arg(ua, NT_("all"));
1980 for (i=0; i<comsize; i++) {
1981 ua->send_msg("%s\n", commands[i].key);
1985 /* Want to display a specific help section */
1986 j = find_arg_with_value(ua, NT_("item"));
1987 if (j >= 0 && ua->argk[j]) {
1988 for (i=0; i<comsize; i++) {
1989 if (bstrcmp(commands[i].key, ua->argv[j])) {
1990 ua->send_msg("%s\n", commands[i].usage);
1996 /* Want to display everything */
1997 for (i=0; i<comsize; i++) {
1998 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2004 static int version_cmd(UAContext *ua, const char *cmd)
2006 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2007 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2012 * Test code -- turned on only for debug testing
2014 static int version_cmd(UAContext *ua, const char *cmd)
2017 POOL_MEM query(PM_MESSAGE);
2019 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2020 db_get_query_dbids(ua->jcr, ua->db, query, ids);
2021 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2022 for (int i=0; i < ids.num_ids; i++) {
2023 ua->send_msg("id=%d\n", ids.DBId[i]);
2031 * This call uses open_client_db() and force a
2032 * new dedicated connection to the catalog
2034 bool open_new_client_db(UAContext *ua)
2038 /* Force a new dedicated connection */
2040 ua->force_mult_db_connections = true;
2041 ret = open_client_db(ua);
2042 ua->force_mult_db_connections = false;
2047 * This call explicitly checks for a catalog=xxx and
2048 * if given, opens that catalog. It also checks for
2049 * client=xxx and if found, opens the catalog
2050 * corresponding to that client. If we still don't
2051 * have a catalog, look for a Job keyword and get the
2052 * catalog from its client record.
2054 bool open_client_db(UAContext *ua)
2061 /* Try for catalog keyword */
2062 i = find_arg_with_value(ua, NT_("catalog"));
2064 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2065 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2068 catalog = GetCatalogResWithName(ua->argv[i]);
2070 if (ua->catalog && ua->catalog != catalog) {
2073 ua->catalog = catalog;
2078 /* Try for client keyword */
2079 i = find_arg_with_value(ua, NT_("client"));
2081 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2082 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2085 client = GetClientResWithName(ua->argv[i]);
2087 catalog = client->catalog;
2088 if (ua->catalog && ua->catalog != catalog) {
2091 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2092 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2095 ua->catalog = catalog;
2100 /* Try for Job keyword */
2101 i = find_arg_with_value(ua, NT_("job"));
2103 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2104 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2107 job = GetJobResWithName(ua->argv[i]);
2109 catalog = job->client->catalog;
2110 if (ua->catalog && ua->catalog != catalog) {
2113 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2114 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2117 ua->catalog = catalog;
2127 * Open the catalog database.
2129 bool open_db(UAContext *ua)
2137 ua->catalog = get_catalog_resource(ua);
2139 ua->error_msg( _("Could not find a Catalog resource\n"));
2144 /* Some modules like bvfs need their own catalog connection */
2145 mult_db_conn = ua->catalog->mult_db_connections;
2146 if (ua->force_mult_db_connections) {
2147 mult_db_conn = true;
2150 ua->jcr->catalog = ua->catalog;
2152 Dmsg0(100, "UA Open database\n");
2153 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2154 ua->catalog->db_user,
2155 ua->catalog->db_password, ua->catalog->db_address,
2156 ua->catalog->db_port, ua->catalog->db_socket,
2157 mult_db_conn, ua->catalog->disable_batch_insert);
2158 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2159 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2160 ua->catalog->db_name);
2162 ua->error_msg("%s", db_strerror(ua->db));
2167 ua->jcr->db = ua->db;
2169 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2171 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2175 void close_db(UAContext *ua)
2178 db_close_database(ua->jcr, ua->db);