2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU 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 General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Commands
32 * Kern Sibbald, September MM
42 #undef _POSIX_C_SOURCE
45 #include "lib/pythonlib.h"
47 /* Imported Functions */
48 extern PyObject *job_getattr(PyObject *self, char *attrname);
49 extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
51 #endif /* HAVE_PYTHON */
53 /* Imported subroutines */
55 /* Imported variables */
56 extern jobq_t job_queue; /* job queue */
59 /* Imported functions */
60 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
61 extern int gui_cmd(UAContext *ua, const char *cmd);
62 extern int label_cmd(UAContext *ua, const char *cmd);
63 extern int list_cmd(UAContext *ua, const char *cmd);
64 extern int llist_cmd(UAContext *ua, const char *cmd);
65 extern int messagescmd(UAContext *ua, const char *cmd);
66 extern int prunecmd(UAContext *ua, const char *cmd);
67 extern int purgecmd(UAContext *ua, const char *cmd);
68 extern int querycmd(UAContext *ua, const char *cmd);
69 extern int relabel_cmd(UAContext *ua, const char *cmd);
70 extern int restore_cmd(UAContext *ua, const char *cmd);
71 extern int retentioncmd(UAContext *ua, const char *cmd);
72 extern int show_cmd(UAContext *ua, const char *cmd);
73 extern int sqlquerycmd(UAContext *ua, const char *cmd);
74 extern int status_cmd(UAContext *ua, const char *cmd);
75 extern int update_cmd(UAContext *ua, const char *cmd);
77 /* Forward referenced functions */
78 static int add_cmd(UAContext *ua, const char *cmd);
79 static int automount_cmd(UAContext *ua, const char *cmd);
80 static int cancel_cmd(UAContext *ua, const char *cmd);
81 static int create_cmd(UAContext *ua, const char *cmd);
82 static int delete_cmd(UAContext *ua, const char *cmd);
83 static int disable_cmd(UAContext *ua, const char *cmd);
84 static int enable_cmd(UAContext *ua, const char *cmd);
85 static int estimate_cmd(UAContext *ua, const char *cmd);
86 static int help_cmd(UAContext *ua, const char *cmd);
87 static int memory_cmd(UAContext *ua, const char *cmd);
88 static int mount_cmd(UAContext *ua, const char *cmd);
89 static int python_cmd(UAContext *ua, const char *cmd);
90 static int release_cmd(UAContext *ua, const char *cmd);
91 static int reload_cmd(UAContext *ua, const char *cmd);
92 static int setdebug_cmd(UAContext *ua, const char *cmd);
93 static int setip_cmd(UAContext *ua, const char *cmd);
94 static int time_cmd(UAContext *ua, const char *cmd);
95 static int trace_cmd(UAContext *ua, const char *cmd);
96 static int unmount_cmd(UAContext *ua, const char *cmd);
97 static int use_cmd(UAContext *ua, const char *cmd);
98 static int var_cmd(UAContext *ua, const char *cmd);
99 static int version_cmd(UAContext *ua, const char *cmd);
100 static int wait_cmd(UAContext *ua, const char *cmd);
102 static void do_job_delete(UAContext *ua, JobId_t JobId);
103 static void delete_job_id_range(UAContext *ua, char *tok);
104 static int delete_volume(UAContext *ua);
105 static int delete_pool(UAContext *ua);
106 static void delete_job(UAContext *ua);
108 int qhelp_cmd(UAContext *ua, const char *cmd);
109 int quit_cmd(UAContext *ua, const char *cmd);
111 /* not all in alphabetical order. New commands are added after existing commands with similar letters
112 to prevent breakage of existing user scripts. */
113 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; const bool use_in_rs;};
114 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
115 { NT_("add"), add_cmd, _("add [pool=<pool-name> storage=<storage> jobid=<JobId>] -- "
116 "\n add media to a pool"), false},
117 { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages"),false},
118 { NT_("automount"), automount_cmd, _("automount [on|off] -- after label"), false},
119 { NT_("cancel"), cancel_cmd, _("cancel [jobid=<number> job=<job-name> ujobid=<unique-jobid>] -- "
120 "\n cancel a job"), false},
121 { NT_("create"), create_cmd, _("create [pool=<pool-name>] -- create DB Pool from resource"), false},
122 { NT_("delete"), delete_cmd, _("delete [volume=<vol-name> pool=<pool-name> job jobid=<id>]"), true},
123 { NT_("disable"), disable_cmd, _("disable <job=name> -- disable a job"), true},
124 { NT_("enable"), enable_cmd, _("enable <job=name> -- enable a job"), true},
125 { NT_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing"
126 "\n you can make it more accurate with accurate=yes/no"), true},
127 { NT_("exit"), quit_cmd, _("exit = quit"), false},
128 { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode"), false},
129 { NT_("help"), help_cmd, _("print this command"), false},
130 { NT_("label"), label_cmd, _("label a tape"), false},
131 { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | "
132 "\n files <jobid=nn> | copies <jobid=nn>]; from catalog"), true},
133 { NT_("llist"), llist_cmd, _("full or long list like list command"), true},
134 { NT_("messages"), messagescmd, _("messages"), false},
135 { NT_("memory"), memory_cmd, _("print current memory usage"), true},
136 { NT_("mount"), mount_cmd, _("mount storage=<storage-name> [ slot=<num> ] [ drive=<num> ] "
137 "\n or mount [ jobid=<id> | job=<job-name> ]"), false},
138 { NT_("prune"), prunecmd, _("prune files|jobs|volume client=<client-name> volume=<volume-name> "
139 "\n prune expired records from catalog"), true},
140 { NT_("purge"), purgecmd, _("purge records from catalog"), true},
141 { NT_("python"), python_cmd, _("python control commands"), false},
142 { NT_("quit"), quit_cmd, _("quit"), false},
143 { NT_("query"), querycmd, _("query catalog"), false},
144 { NT_("restore"), restore_cmd, _("restore files"), false},
145 { NT_("relabel"), relabel_cmd, _("relabel storage=<storage-name> oldvolume=<old-volume-name> "
146 "\n volume=<newvolume-name> -- relabel a tape"), false},
147 { NT_("release"), release_cmd, _("release <storage-name>"), false},
148 { NT_("reload"), reload_cmd, _("reload conf file"), true},
149 { NT_("run"), run_cmd, _("run job=<job-name> client=<client-name> fileset=<FileSet-name> "
150 "\n level=<level-keyword> storage=<storage-name> where=<directory-prefix> "
151 "\n when=<universal-time-specification> yes"), false}, /* need to be check */
152 { NT_("status"), status_cmd, _("status [all | dir=<dir-name> | director | client=<client-name> |"
153 "\n storage=<storage-name> | days=nnn]"), true},
154 { NT_("setdebug"), setdebug_cmd, _("setdebug level=nn [trace=0/1 client=<client-name> |"
155 "\n dir | director | storage=<storage-name> | all] -- sets debug level"), true},
156 { NT_("setip"), setip_cmd, _("sets new client address -- if authorized"), false},
157 { NT_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]"), true},
158 { NT_("sqlquery"), sqlquerycmd, _("use SQL to query catalog"), false},
159 { NT_("time"), time_cmd, _("print current time"), true},
160 { NT_("trace"), trace_cmd, _("turn on/off trace to file"), true},
161 { NT_("unmount"), unmount_cmd, _("unmount storage=<storage-name> [ drive=<num> ] "
162 "\n or unmount [ jobid=<id> | job=<job-name> ]"), false},
163 { NT_("umount"), unmount_cmd, _("umount - for old-time Unix guys, see unmount"),false},
164 { NT_("update"), update_cmd, _("update Volume, Pool or slots"), true},
165 { NT_("use"), use_cmd, _("use <database-name> -- catalog xxx"), false},
166 { NT_("var"), var_cmd, _("does variable expansion"), false},
167 { NT_("version"), version_cmd, _("print Director version"), true},
168 { NT_("wait"), wait_cmd, _("wait [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>] -- "
169 "\n wait until no jobs are running"), false}
172 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
175 * Execute a command from the UA
177 bool do_a_command(UAContext *ua)
183 BSOCK *user = ua->UA_sock;
186 Dmsg1(900, "Command: %s\n", ua->argk[0]);
191 while (ua->jcr->wstorage->size()) {
192 ua->jcr->wstorage->remove(0);
195 len = strlen(ua->argk[0]);
196 for (i=0; i<comsize; i++) { /* search for command */
197 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
198 /* Check if command permitted, but "quit" is always OK */
199 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
200 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
203 /* Check if this command is authorized in RunScript */
204 if (ua->runscript && !commands[i].use_in_rs) {
205 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
208 if (ua->api) user->signal(BNET_CMD_BEGIN);
209 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
210 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
216 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
223 * This is a common routine used to stuff the Pool DB record defaults
224 * into the Media DB record just before creating a media (Volume)
227 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
229 mr->PoolId = pr->PoolId;
230 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
231 mr->Recycle = pr->Recycle;
232 mr->VolRetention = pr->VolRetention;
233 mr->VolUseDuration = pr->VolUseDuration;
234 mr->RecyclePoolId = pr->RecyclePoolId;
235 mr->MaxVolJobs = pr->MaxVolJobs;
236 mr->MaxVolFiles = pr->MaxVolFiles;
237 mr->MaxVolBytes = pr->MaxVolBytes;
238 mr->LabelType = pr->LabelType;
244 * Add Volumes to an existing Pool
246 static int add_cmd(UAContext *ua, const char *cmd)
250 int num, i, max, startnum;
252 char name[MAX_NAME_LENGTH];
254 int Slot = 0, InChanger = 0;
257 "You probably don't want to be using this command since it\n"
258 "creates database records without labeling the Volumes.\n"
259 "You probably want to use the \"label\" command.\n\n"));
261 if (!open_client_db(ua)) {
265 memset(&pr, 0, sizeof(pr));
266 memset(&mr, 0, sizeof(mr));
268 if (!get_pool_dbr(ua, &pr)) {
272 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
273 pr.MaxVols, pr.PoolType);
275 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
276 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
277 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
280 pr.MaxVols = ua->pint32_val;
284 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
285 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
286 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
290 if (pr.MaxVols == 0) {
293 max = pr.MaxVols - pr.NumVols;
297 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
298 if (!get_pint(ua, buf)) {
301 num = ua->pint32_val;
302 if (num < 0 || num > max) {
303 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
311 if (!get_cmd(ua, _("Enter Volume name: "))) {
315 if (!get_cmd(ua, _("Enter base volume name: "))) {
319 /* Don't allow | in Volume name because it is the volume separator character */
320 if (!is_volume_name_legal(ua, ua->cmd)) {
323 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
324 ua->warning_msg(_("Volume name too long.\n"));
327 if (strlen(ua->cmd) == 0) {
328 ua->warning_msg(_("Volume name must be at least one character long.\n"));
334 bstrncpy(name, ua->cmd, sizeof(name));
336 bstrncat(name, "%04d", sizeof(name));
339 if (!get_pint(ua, _("Enter the starting number: "))) {
342 startnum = ua->pint32_val;
344 ua->warning_msg(_("Start number must be greater than zero.\n"));
354 if (store && store->autochanger) {
355 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
358 Slot = ua->pint32_val;
359 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
362 InChanger = ua->pint32_val;
365 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
366 for (i=startnum; i < num+startnum; i++) {
367 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
369 mr.InChanger = InChanger;
370 mr.StorageId = store->StorageId;
372 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
373 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
374 ua->error_msg("%s", db_strerror(ua->db));
378 first_id = mr.PoolId;
382 Dmsg0(200, "Update pool record.\n");
383 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
384 ua->warning_msg("%s", db_strerror(ua->db));
387 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
393 * Turn auto mount on/off
398 int automount_cmd(UAContext *ua, const char *cmd)
403 if (!get_cmd(ua, _("Turn on or off? "))) {
411 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
419 static int cancel_cmd(UAContext *ua, const char *cmd)
424 char JobName[MAX_NAME_LENGTH];
426 for (i=1; i<ua->argc; i++) {
427 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
432 JobId = str_to_int64(ua->argv[i]);
433 if (!(jcr=get_jcr_by_id(JobId))) {
434 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
438 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
442 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
443 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
444 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
445 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
448 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
452 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
453 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
454 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
455 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
462 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
463 ua->error_msg(_("Unauthorized command from this console.\n"));
468 * If we still do not have a jcr,
469 * throw up a list and ask the user to select one.
472 int tjobs = 0; /* total # number jobs */
473 /* Count Jobs running */
475 if (jcr->JobId == 0) { /* this is us */
478 tjobs++; /* count of all jobs */
479 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
480 continue; /* skip not authorized */
482 njobs++; /* count of authorized jobs */
486 if (njobs == 0) { /* no authorized */
488 ua->send_msg(_("No Jobs running.\n"));
490 ua->send_msg(_("None of your jobs are running.\n"));
495 start_prompt(ua, _("Select Job:\n"));
498 if (jcr->JobId == 0) { /* this is us */
501 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
502 continue; /* skip not authorized */
504 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
509 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
512 if (ua->api && njobs == 1) {
514 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
515 _("Confirm cancel?"));
516 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
521 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
526 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
527 jcr = get_jcr_by_full_name(JobName);
529 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
534 ret = cancel_job(ua, jcr);
540 * This is a common routine to create or update a
541 * Pool DB base record from a Pool Resource. We handle
542 * the setting of MaxVols and NumVols slightly differently
543 * depending on if we are creating the Pool or we are
544 * simply bringing it into agreement with the resource (updage).
546 * Caution : RecyclePoolId isn't setup in this function.
547 * You can use set_pooldbr_recyclepoolid();
550 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
552 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
553 if (op == POOL_OP_CREATE) {
554 pr->MaxVols = pool->max_volumes;
556 } else { /* update pool */
557 if (pr->MaxVols != pool->max_volumes) {
558 pr->MaxVols = pool->max_volumes;
560 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
561 pr->MaxVols = pr->NumVols;
564 pr->LabelType = pool->LabelType;
565 pr->UseOnce = pool->use_volume_once;
566 pr->UseCatalog = pool->use_catalog;
567 pr->Recycle = pool->Recycle;
568 pr->VolRetention = pool->VolRetention;
569 pr->VolUseDuration = pool->VolUseDuration;
570 pr->MaxVolJobs = pool->MaxVolJobs;
571 pr->MaxVolFiles = pool->MaxVolFiles;
572 pr->MaxVolBytes = pool->MaxVolBytes;
573 pr->AutoPrune = pool->AutoPrune;
574 pr->Recycle = pool->Recycle;
575 if (pool->label_format) {
576 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
578 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
582 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
583 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
587 if (!pool->RecyclePool && !pool->ScratchPool) {
591 memset(&pr, 0, sizeof(POOL_DBR));
592 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
594 if (!db_get_pool_record(jcr, db, &pr)) {
595 return -1; /* not exists in database */
598 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
600 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
601 return -1; /* error */
604 if (!db_update_pool_record(jcr, db, &pr)) {
605 return -1; /* error */
610 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
611 * works with set_pooldbr_from_poolres
613 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
618 if (pool->RecyclePool) {
619 memset(&rpool, 0, sizeof(POOL_DBR));
621 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
622 if (db_get_pool_record(jcr, db, &rpool)) {
623 pr->RecyclePoolId = rpool.PoolId;
625 Jmsg(jcr, M_WARNING, 0,
626 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
627 "Try to update it with 'update pool=%s'\n"),
628 pool->name(), rpool.Name, rpool.Name,pool->name());
632 } else { /* no RecyclePool used, set it to 0 */
633 pr->RecyclePoolId = 0;
636 if (pool->ScratchPool) {
637 memset(&rpool, 0, sizeof(POOL_DBR));
639 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
640 if (db_get_pool_record(jcr, db, &rpool)) {
641 pr->ScratchPoolId = rpool.PoolId;
643 Jmsg(jcr, M_WARNING, 0,
644 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
645 "Try to update it with 'update pool=%s'\n"),
646 pool->name(), rpool.Name, rpool.Name,pool->name());
649 } else { /* no ScratchPool used, set it to 0 */
650 pr->ScratchPoolId = 0;
658 * Create a pool record from a given Pool resource
659 * Also called from backup.c
660 * Returns: -1 on error
661 * 0 record already exists
665 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
669 memset(&pr, 0, sizeof(POOL_DBR));
671 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
673 if (db_get_pool_record(jcr, db, &pr)) {
675 if (op == POOL_OP_UPDATE) { /* update request */
676 set_pooldbr_from_poolres(&pr, pool, op);
677 set_pooldbr_references(jcr, db, &pr, pool);
678 db_update_pool_record(jcr, db, &pr);
680 return 0; /* exists */
683 set_pooldbr_from_poolres(&pr, pool, op);
684 set_pooldbr_references(jcr, db, &pr, pool);
686 if (!db_create_pool_record(jcr, db, &pr)) {
687 return -1; /* error */
695 * Create a Pool Record in the database.
696 * It is always created from the Resource record.
698 static int create_cmd(UAContext *ua, const char *cmd)
702 if (!open_client_db(ua)) {
706 pool = get_pool_resource(ua);
711 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
713 ua->error_msg(_("Error: Pool %s already exists.\n"
714 "Use update to change it.\n"), pool->name());
718 ua->error_msg("%s", db_strerror(ua->db));
724 ua->send_msg(_("Pool %s created.\n"), pool->name());
729 extern DIRRES *director;
730 extern char *configfile;
733 * Python control command
734 * python restart (restarts interpreter)
736 static int python_cmd(UAContext *ua, const char *cmd)
739 init_python_interpreter_args python_args;
741 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
742 term_python_interpreter();
744 python_args.progname = director->name();
745 python_args.scriptdir = director->scripts_directory;
746 python_args.modulename = "DirStartUp";
747 python_args.configfile = configfile;
748 python_args.workingdir = director->working_directory;
749 python_args.job_getattr = job_getattr;
750 python_args.job_setattr = job_setattr;
752 init_python_interpreter(&python_args);
754 ua->send_msg(_("Python interpreter restarted.\n"));
756 #endif /* HAVE_PYTHON */
757 ua->warning_msg(_("Nothing done.\n"));
760 #endif /* HAVE_PYTHON */
766 * Set a new address in a Client resource. We do this only
767 * if the Console name is the same as the Client name
768 * and the Console can access the client.
770 static int setip_cmd(UAContext *ua, const char *cmd)
774 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
775 ua->error_msg(_("Unauthorized command from this console.\n"));
779 client = GetClientResWithName(ua->cons->name());
782 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
785 if (client->address) {
786 free(client->address);
788 /* MA Bug 6 remove ifdef */
789 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
790 client->address = bstrdup(buf);
791 ua->send_msg(_("Client \"%s\" address set to %s\n"),
792 client->name(), client->address);
799 static void do_en_disable_cmd(UAContext *ua, bool setting)
804 i = find_arg_with_value(ua, NT_("job"));
806 job = select_enable_disable_job_resource(ua, setting);
812 job = GetJobResWithName(ua->argv[i]);
816 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
820 if (!acl_access_ok(ua, Job_ACL, job->name())) {
821 ua->error_msg(_("Unauthorized command from this console.\n"));
824 job->enabled = setting;
825 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
829 static int enable_cmd(UAContext *ua, const char *cmd)
831 do_en_disable_cmd(ua, true);
835 static int disable_cmd(UAContext *ua, const char *cmd)
837 do_en_disable_cmd(ua, false);
842 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
848 lstore.store = store;
849 pm_strcpy(lstore.store_source, _("unknown source"));
850 set_wstorage(jcr, &lstore);
851 /* Try connecting for up to 15 seconds */
852 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
853 store->name(), store->address, store->SDport);
854 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
855 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
858 Dmsg0(120, _("Connected to storage daemon\n"));
859 sd = jcr->store_bsock;
860 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
861 if (sd->recv() >= 0) {
862 ua->send_msg("%s", sd->msg);
864 sd->signal(BNET_TERMINATE);
866 jcr->store_bsock = NULL;
870 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
874 /* Connect to File daemon */
876 ua->jcr->client = client;
877 /* Try to connect for 15 seconds */
878 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
879 client->name(), client->address, client->FDport);
880 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
881 ua->error_msg(_("Failed to connect to Client.\n"));
884 Dmsg0(120, "Connected to file daemon\n");
885 fd = ua->jcr->file_bsock;
886 fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
887 if (fd->recv() >= 0) {
888 ua->send_msg("%s", fd->msg);
890 fd->signal(BNET_TERMINATE);
892 ua->jcr->file_bsock = NULL;
897 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
899 STORE *store, **unique_store;
900 CLIENT *client, **unique_client;
906 /* Count Storage items */
910 foreach_res(store, R_STORAGE) {
913 unique_store = (STORE **) malloc(i * sizeof(STORE));
914 /* Find Unique Storage address/port */
915 store = (STORE *)GetNextRes(R_STORAGE, NULL);
917 unique_store[i++] = store;
918 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
920 for (j=0; j<i; j++) {
921 if (strcmp(unique_store[j]->address, store->address) == 0 &&
922 unique_store[j]->SDport == store->SDport) {
928 unique_store[i++] = store;
929 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
934 /* Call each unique Storage daemon */
935 for (j=0; j<i; j++) {
936 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
940 /* Count Client items */
944 foreach_res(client, R_CLIENT) {
947 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
948 /* Find Unique Client address/port */
949 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
951 unique_client[i++] = client;
952 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
954 for (j=0; j<i; j++) {
955 if (strcmp(unique_client[j]->address, client->address) == 0 &&
956 unique_client[j]->FDport == client->FDport) {
962 unique_client[i++] = client;
963 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
968 /* Call each unique File daemon */
969 for (j=0; j<i; j++) {
970 do_client_setdebug(ua, unique_client[j], level, trace_flag);
976 * setdebug level=nn all trace=1/0
978 static int setdebug_cmd(UAContext *ua, const char *cmd)
986 Dmsg1(120, "setdebug:%s:\n", cmd);
989 i = find_arg_with_value(ua, "level");
991 level = atoi(ua->argv[i]);
994 if (!get_pint(ua, _("Enter new debug level: "))) {
997 level = ua->pint32_val;
1000 /* Look for trace flag. -1 => not change */
1001 i = find_arg_with_value(ua, "trace");
1003 trace_flag = atoi(ua->argv[i]);
1004 if (trace_flag > 0) {
1009 /* General debug? */
1010 for (i=1; i<ua->argc; i++) {
1011 if (strcasecmp(ua->argk[i], "all") == 0) {
1012 do_all_setdebug(ua, level, trace_flag);
1015 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1016 strcasecmp(ua->argk[i], "director") == 0) {
1017 debug_level = level;
1018 set_trace(trace_flag);
1021 if (strcasecmp(ua->argk[i], "client") == 0 ||
1022 strcasecmp(ua->argk[i], "fd") == 0) {
1025 client = GetClientResWithName(ua->argv[i]);
1027 do_client_setdebug(ua, client, level, trace_flag);
1031 client = select_client_resource(ua);
1033 do_client_setdebug(ua, client, level, trace_flag);
1038 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1039 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1040 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1043 store = GetStoreResWithName(ua->argv[i]);
1045 do_storage_setdebug(ua, store, level, trace_flag);
1049 store = get_storage_resource(ua, false/*no default*/);
1051 do_storage_setdebug(ua, store, level, trace_flag);
1057 * We didn't find an appropriate keyword above, so
1060 start_prompt(ua, _("Available daemons are: \n"));
1061 add_prompt(ua, _("Director"));
1062 add_prompt(ua, _("Storage"));
1063 add_prompt(ua, _("Client"));
1064 add_prompt(ua, _("All"));
1065 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1066 case 0: /* Director */
1067 debug_level = level;
1068 set_trace(trace_flag);
1071 store = get_storage_resource(ua, false/*no default*/);
1073 do_storage_setdebug(ua, store, level, trace_flag);
1077 client = select_client_resource(ua);
1079 do_client_setdebug(ua, client, level, trace_flag);
1083 do_all_setdebug(ua, level, trace_flag);
1092 * Turn debug tracing to file on/off
1094 static int trace_cmd(UAContext *ua, const char *cmd)
1098 if (ua->argc != 2) {
1099 if (!get_cmd(ua, _("Turn on or off? "))) {
1104 onoff = ua->argk[1];
1107 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1112 static int var_cmd(UAContext *ua, const char *cmd)
1114 POOLMEM *val = get_pool_memory(PM_FNAME);
1117 if (!open_client_db(ua)) {
1120 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1123 while (*var == ' ') { /* skip spaces */
1126 Dmsg1(100, "Var=%s:\n", var);
1127 variable_expansion(ua->jcr, var, &val);
1128 ua->send_msg("%s\n", val);
1129 free_pool_memory(val);
1133 static int estimate_cmd(UAContext *ua, const char *cmd)
1136 CLIENT *client = NULL;
1137 FILESET *fileset = NULL;
1139 char since[MAXSTRING];
1143 jcr->set_JobLevel(L_FULL);
1144 for (int i=1; i<ua->argc; i++) {
1145 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1146 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1148 client = GetClientResWithName(ua->argv[i]);
1150 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1153 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1154 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1159 ua->error_msg(_("Client name missing.\n"));
1163 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1165 job = GetJobResWithName(ua->argv[i]);
1167 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1170 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1171 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1176 ua->error_msg(_("Job name missing.\n"));
1181 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1183 fileset = GetFileSetResWithName(ua->argv[i]);
1185 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1188 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1189 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1194 ua->error_msg(_("Fileset name missing.\n"));
1198 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1202 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1204 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1205 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1209 ua->error_msg(_("Level value missing.\n"));
1213 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1214 if (!is_yesno(ua->argv[i], &accurate)) {
1215 ua->error_msg(_("Invalid value for accurate. "
1216 "It must be yes or no.\n"));
1220 if (!job && !(client && fileset)) {
1221 if (!(job = select_job_resource(ua))) {
1226 job = GetJobResWithName(ua->argk[1]);
1228 ua->error_msg(_("No job specified.\n"));
1231 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1232 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1237 client = job->client;
1240 fileset = job->fileset;
1242 jcr->client = client;
1243 jcr->fileset = fileset;
1245 if (job->pool->catalog) {
1246 ua->catalog = job->pool->catalog;
1248 ua->catalog = client->catalog;
1256 jcr->set_JobType(JT_BACKUP);
1257 init_jcr_job_record(jcr);
1259 if (!get_or_create_client_record(jcr)) {
1262 if (!get_or_create_fileset_record(jcr)) {
1266 get_level_since_time(ua->jcr, since, sizeof(since));
1268 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1269 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1270 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1271 ua->error_msg(_("Failed to connect to Client.\n"));
1275 if (!send_include_list(jcr)) {
1276 ua->error_msg(_("Error sending include list.\n"));
1280 if (!send_exclude_list(jcr)) {
1281 ua->error_msg(_("Error sending exclude list.\n"));
1285 /* The level string change if accurate mode is enabled */
1286 if (accurate >= 0) {
1287 jcr->accurate = accurate;
1289 jcr->accurate = job->accurate;
1292 if (!send_level_command(jcr)) {
1297 * If the job is in accurate mode, we send the list of
1300 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1301 if (!send_accurate_current_files(jcr)) {
1305 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1306 while (bnet_recv(jcr->file_bsock) >= 0) {
1307 ua->send_msg("%s", jcr->file_bsock->msg);
1311 if (jcr->file_bsock) {
1312 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1313 bnet_close(jcr->file_bsock);
1314 jcr->file_bsock = NULL;
1323 static int time_cmd(UAContext *ua, const char *cmd)
1326 time_t ttime = time(NULL);
1328 (void)localtime_r(&ttime, &tm);
1329 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1330 ua->send_msg("%s\n", sdt);
1335 * reload the conf file
1337 extern "C" void reload_config(int sig);
1339 static int reload_cmd(UAContext *ua, const char *cmd)
1346 * Delete Pool records (should purge Media with it).
1348 * delete pool=<pool-name>
1349 * delete volume pool=<pool-name> volume=<name>
1352 static int delete_cmd(UAContext *ua, const char *cmd)
1354 static const char *keywords[] = {
1360 if (!open_client_db(ua)) {
1364 switch (find_arg_keyword(ua, keywords)) {
1373 while ((i=find_arg(ua, "jobid")) > 0) {
1375 *ua->argk[i] = 0; /* zap keyword already visited */
1383 "In general it is not a good idea to delete either a\n"
1384 "Pool or a Volume since they may contain data.\n\n"));
1386 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1397 ua->warning_msg(_("Nothing done.\n"));
1405 * delete_job has been modified to parse JobID lists like the
1407 * delete JobID=3,4,6,7-11,14
1409 * Thanks to Phil Stracchino for the above addition.
1412 static void delete_job(UAContext *ua)
1417 int i = find_arg_with_value(ua, NT_("jobid"));
1419 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1420 s = bstrdup(ua->argv[i]);
1423 * We could use strtok() here. But we're not going to, because:
1424 * (a) strtok() is deprecated, having been replaced by strsep();
1425 * (b) strtok() is broken in significant ways.
1426 * we could use strsep() instead, but it's not universally available.
1427 * so we grow our own using strchr().
1429 sep = strchr(tok, ',');
1430 while (sep != NULL) {
1432 if (strchr(tok, '-')) {
1433 delete_job_id_range(ua, tok);
1435 JobId = str_to_int64(tok);
1436 do_job_delete(ua, JobId);
1439 sep = strchr(tok, ',');
1441 /* pick up the last token */
1442 if (strchr(tok, '-')) {
1443 delete_job_id_range(ua, tok);
1445 JobId = str_to_int64(tok);
1446 do_job_delete(ua, JobId);
1451 JobId = str_to_int64(ua->argv[i]);
1452 do_job_delete(ua, JobId);
1454 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1457 JobId = ua->int64_val;
1458 do_job_delete(ua, JobId);
1463 * we call delete_job_id_range to parse range tokens and iterate over ranges
1465 static void delete_job_id_range(UAContext *ua, char *tok)
1470 tok2 = strchr(tok, '-');
1473 j1 = str_to_int64(tok);
1474 j2 = str_to_int64(tok2);
1475 for (j=j1; j<=j2; j++) {
1476 do_job_delete(ua, j);
1481 * do_job_delete now performs the actual delete operation atomically
1483 static void do_job_delete(UAContext *ua, JobId_t JobId)
1487 edit_int64(JobId, ed1);
1488 purge_jobs_from_catalog(ua, ed1);
1489 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1493 * Delete media records from database -- dangerous
1495 static int delete_volume(UAContext *ua)
1500 if (!select_media_dbr(ua, &mr)) {
1503 ua->warning_msg(_("\nThis command will delete volume %s\n"
1504 "and all Jobs saved on that volume from the Catalog\n"),
1507 if (find_arg(ua, "yes") >= 0) {
1508 ua->pint32_val = 1; /* Have "yes" on command line already" */
1510 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1512 if (!get_yesno(ua, buf)) {
1516 if (ua->pint32_val) {
1517 db_delete_media_record(ua->jcr, ua->db, &mr);
1523 * Delete a pool record from the database -- dangerous
1525 static int delete_pool(UAContext *ua)
1530 memset(&pr, 0, sizeof(pr));
1532 if (!get_pool_dbr(ua, &pr)) {
1535 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1537 if (!get_yesno(ua, buf)) {
1540 if (ua->pint32_val) {
1541 db_delete_pool_record(ua->jcr, ua->db, &pr);
1546 int memory_cmd(UAContext *ua, const char *cmd)
1548 list_dir_status_header(ua);
1549 sm_dump(false, true);
1553 static void do_mount_cmd(UAContext *ua, const char *command)
1558 char dev_name[MAX_NAME_LENGTH];
1562 if (!open_client_db(ua)) {
1565 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1567 store.store = get_storage_resource(ua, true/*arg is storage*/);
1571 pm_strcpy(store.store_source, _("unknown source"));
1572 set_wstorage(jcr, &store);
1573 drive = get_storage_drive(ua, store.store);
1574 if (strcmp(command, "mount") == 0) {
1575 slot = get_storage_slot(ua, store.store);
1578 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1579 store.store->media_type, store.store->dev_name(), drive);
1581 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1582 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1585 sd = jcr->store_bsock;
1586 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1587 bash_spaces(dev_name);
1589 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1591 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1593 while (bnet_recv(sd) >= 0) {
1594 ua->send_msg("%s", sd->msg);
1596 bnet_sig(sd, BNET_TERMINATE);
1598 jcr->store_bsock = NULL;
1602 * mount [storage=<name>] [drive=nn] [slot=mm]
1604 static int mount_cmd(UAContext *ua, const char *cmd)
1606 do_mount_cmd(ua, "mount"); /* mount */
1612 * unmount [storage=<name>] [drive=nn]
1614 static int unmount_cmd(UAContext *ua, const char *cmd)
1616 do_mount_cmd(ua, "unmount"); /* unmount */
1622 * release [storage=<name>] [drive=nn]
1624 static int release_cmd(UAContext *ua, const char *cmd)
1626 do_mount_cmd(ua, "release"); /* release */
1633 * use catalog=<name>
1635 static int use_cmd(UAContext *ua, const char *cmd)
1637 CAT *oldcatalog, *catalog;
1640 close_db(ua); /* close any previously open db */
1641 oldcatalog = ua->catalog;
1643 if (!(catalog = get_catalog_resource(ua))) {
1644 ua->catalog = oldcatalog;
1646 ua->catalog = catalog;
1649 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1650 ua->catalog->name(), ua->catalog->db_name);
1655 int quit_cmd(UAContext *ua, const char *cmd)
1661 /* Handler to get job status */
1662 static int status_handler(void *ctx, int num_fields, char **row)
1664 char *val = (char *)ctx;
1669 *val = '?'; /* Unknown by default */
1676 * Wait until no job is running
1678 int wait_cmd(UAContext *ua, const char *cmd)
1682 time_t stop_time = 0;
1686 * Wait until no job is running
1688 if (ua->argc == 1) {
1689 bmicrosleep(0, 200000); /* let job actually start */
1690 for (bool running=true; running; ) {
1693 if (jcr->JobId != 0) {
1707 i = find_arg_with_value(ua, NT_("timeout"));
1708 if (i > 0 && ua->argv[i]) {
1709 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1712 /* we have jobid, jobname or ujobid argument */
1714 uint32_t jobid = 0 ;
1716 if (!open_client_db(ua)) {
1717 ua->error_msg(_("ERR: Can't open db\n")) ;
1721 for (int i=1; i<ua->argc; i++) {
1722 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1726 jobid = str_to_int64(ua->argv[i]);
1728 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1729 strcasecmp(ua->argk[i], "job") == 0) {
1733 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1735 jobid = jcr->JobId ;
1739 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1743 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1745 jobid = jcr->JobId ;
1749 /* Wait for a mount request */
1750 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1751 for (bool waiting=false; !waiting; ) {
1753 if (jcr->JobId != 0 &&
1754 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1763 if (stop_time && (time(NULL) >= stop_time)) {
1764 ua->warning_msg(_("Wait on mount timed out\n"));
1774 ua->error_msg(_("ERR: Job was not found\n"));
1779 * We wait the end of a specific job
1782 bmicrosleep(0, 200000); /* let job actually start */
1783 for (bool running=true; running; ) {
1786 jcr=get_jcr_by_id(jobid) ;
1799 * We have to get JobStatus
1803 char jobstatus = '?'; /* Unknown by default */
1806 bsnprintf(buf, sizeof(buf),
1807 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1810 db_sql_query(ua->db, buf,
1811 status_handler, (void *)&jobstatus);
1813 switch (jobstatus) {
1815 status = 1 ; /* Warning */
1819 case JS_ErrorTerminated:
1821 status = 2 ; /* Critical */
1826 status = 0 ; /* Ok */
1830 status = 3 ; /* Unknown */
1834 ua->send_msg("JobId=%i\n", jobid) ;
1835 ua->send_msg("JobStatus=%s (%c)\n",
1836 job_status_to_str(jobstatus),
1839 if (ua->gui || ua->api) {
1840 ua->send_msg("ExitStatus=%i\n", status) ;
1847 static int help_cmd(UAContext *ua, const char *cmd)
1851 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1852 for (i=0; i<comsize; i++) {
1853 ua->send_msg(_(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1855 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1859 int qhelp_cmd(UAContext *ua, const char *cmd)
1863 for (i=0; i<comsize; i++) {
1864 ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1870 static int version_cmd(UAContext *ua, const char *cmd)
1872 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1873 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1878 * Test code -- turned on only for debug testing
1880 static int version_cmd(UAContext *ua, const char *cmd)
1883 POOL_MEM query(PM_MESSAGE);
1885 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1886 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1887 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1888 for (int i=0; i < ids.num_ids; i++) {
1889 ua->send_msg("id=%d\n", ids.DBId[i]);
1897 * This call explicitly checks for a catalog=xxx and
1898 * if given, opens that catalog. It also checks for
1899 * client=xxx and if found, opens the catalog
1900 * corresponding to that client. If we still don't
1901 * have a catalog, look for a Job keyword and get the
1902 * catalog from its client record.
1904 bool open_client_db(UAContext *ua)
1911 /* Try for catalog keyword */
1912 i = find_arg_with_value(ua, NT_("catalog"));
1914 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1915 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1918 catalog = GetCatalogResWithName(ua->argv[i]);
1920 if (ua->catalog && ua->catalog != catalog) {
1923 ua->catalog = catalog;
1928 /* Try for client keyword */
1929 i = find_arg_with_value(ua, NT_("client"));
1931 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1932 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1935 client = GetClientResWithName(ua->argv[i]);
1937 catalog = client->catalog;
1938 if (ua->catalog && ua->catalog != catalog) {
1941 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1942 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1945 ua->catalog = catalog;
1950 /* Try for Job keyword */
1951 i = find_arg_with_value(ua, NT_("job"));
1953 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1954 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1957 job = GetJobResWithName(ua->argv[i]);
1959 catalog = job->client->catalog;
1960 if (ua->catalog && ua->catalog != catalog) {
1963 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1964 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1967 ua->catalog = catalog;
1977 * Open the catalog database.
1979 bool open_db(UAContext *ua)
1985 ua->catalog = get_catalog_resource(ua);
1987 ua->error_msg( _("Could not find a Catalog resource\n"));
1992 ua->jcr->catalog = ua->catalog;
1994 Dmsg0(100, "UA Open database\n");
1995 ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
1996 ua->catalog->db_user,
1997 ua->catalog->db_password, ua->catalog->db_address,
1998 ua->catalog->db_port, ua->catalog->db_socket,
1999 ua->catalog->mult_db_connections);
2000 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2001 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2002 ua->catalog->db_name);
2004 ua->error_msg("%s", db_strerror(ua->db));
2009 ua->jcr->db = ua->db;
2011 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2013 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2017 void close_db(UAContext *ua)
2020 db_close_database(ua->jcr, ua->db);