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"
127 "\n estimate fileset=<fs> client=<cli> accurate=<yes/no> job=<job> listing"), true},
128 { NT_("exit"), quit_cmd, _("exit = quit"), false},
129 { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode"), false},
130 { NT_("help"), help_cmd, _("print this command"), false},
131 { NT_("label"), label_cmd, _("label a tape -- "
132 "\n label storage=<storage> volume=<vol> pool=<pool>"), false},
133 { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | "
134 "\n files <jobid=nn> | copies <jobid=nn>]; from catalog"), true},
135 { NT_("llist"), llist_cmd, _("full or long list like list command"), true},
136 { NT_("messages"), messagescmd, _("messages"), false},
137 { NT_("memory"), memory_cmd, _("print current memory usage"), true},
138 { NT_("mount"), mount_cmd, _("mount storage=<storage-name> [ slot=<num> ] [ drive=<num> ] "
139 "\n or mount [ jobid=<id> | job=<job-name> ]"), false},
140 { NT_("prune"), prunecmd, _("prune files|jobs|volume client=<client-name> volume=<volume-name> "
141 "\n prune expired records from catalog"), true},
142 { NT_("purge"), purgecmd, _("purge records from catalog - purge [volume=<vol>]"), true},
143 { NT_("python"), python_cmd, _("python control commands"), false},
144 { NT_("quit"), quit_cmd, _("quit"), false},
145 { NT_("query"), querycmd, _("query catalog"), false},
146 { NT_("restore"), restore_cmd, _("restore files - "
147 "\n restore [where=/path client=<client> storage=<storage> bootstrap=<file>"
148 "\n jobid=<jobid> done select all]"), false},
149 { NT_("relabel"), relabel_cmd, _("relabel storage=<storage-name> oldvolume=<old-volume-name> "
150 "\n volume=<newvolume-name> -- relabel a tape"), false},
151 { NT_("release"), release_cmd, _("release <storage-name>"), false},
152 { NT_("reload"), reload_cmd, _("reload conf file"), true},
153 { NT_("run"), run_cmd, _("run job=<job-name> client=<client-name> fileset=<FileSet-name> "
154 "\n level=<level-keyword> storage=<storage-name> where=<directory-prefix> "
155 "\n when=<universal-time-specification> yes"), false}, /* need to be check */
156 { NT_("status"), status_cmd, _("status [all | dir=<dir-name> | director | client=<client-name> |"
157 "\n storage=<storage-name> | days=nnn]"), true},
158 { NT_("setdebug"), setdebug_cmd, _("setdebug level=nn [trace=0/1 client=<client-name> |"
159 "\n dir | director | storage=<storage-name> | all] -- sets debug level"), true},
160 { NT_("setip"), setip_cmd, _("sets new client address -- if authorized"), false},
161 { NT_("show"), show_cmd, _("show (resource records) [job=xxx | pool=yyy | fileset=aaa "
162 "\n schedule=sss | client=zzz | fileset... | all]"), true},
163 { NT_("sqlquery"), sqlquerycmd, _("use SQL to query catalog"), false},
164 { NT_("time"), time_cmd, _("print current time"), true},
165 { NT_("trace"), trace_cmd, _("turn on/off trace to file"), true},
166 { NT_("unmount"), unmount_cmd, _("unmount storage=<storage-name> [ drive=<num> ] "
167 "\n or unmount [ jobid=<id> | job=<job-name> ]"), false},
168 { NT_("umount"), unmount_cmd, _("umount - for old-time Unix guys, see unmount"),false},
169 { NT_("update"), update_cmd, _("update [pool=<poolname> | slots | stats | "
170 "\n volume=<volname> volstatus=<status> volretention=<time-def>"
171 "\n pool=<pool> recycle=<yes/no> slot=<number> inchanger=<yes/no>]"),true},
172 { NT_("use"), use_cmd, _("use <database-name> -- catalog xxx"), false},
173 { NT_("var"), var_cmd, _("does variable expansion"), false},
174 { NT_("version"), version_cmd, _("print Director version"), true},
175 { NT_("wait"), wait_cmd, _("wait [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>] -- "
176 "\n wait until no jobs are running"), false}
179 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
182 * Execute a command from the UA
184 bool do_a_command(UAContext *ua)
190 BSOCK *user = ua->UA_sock;
193 Dmsg1(900, "Command: %s\n", ua->argk[0]);
198 while (ua->jcr->wstorage->size()) {
199 ua->jcr->wstorage->remove(0);
202 len = strlen(ua->argk[0]);
203 for (i=0; i<comsize; i++) { /* search for command */
204 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
205 /* Check if command permitted, but "quit" is always OK */
206 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
207 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
210 /* Check if this command is authorized in RunScript */
211 if (ua->runscript && !commands[i].use_in_rs) {
212 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
215 if (ua->api) user->signal(BNET_CMD_BEGIN);
216 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
217 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
223 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
230 * This is a common routine used to stuff the Pool DB record defaults
231 * into the Media DB record just before creating a media (Volume)
234 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
236 mr->PoolId = pr->PoolId;
237 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
238 mr->Recycle = pr->Recycle;
239 mr->VolRetention = pr->VolRetention;
240 mr->VolUseDuration = pr->VolUseDuration;
241 mr->ActionOnPurge = pr->ActionOnPurge;
242 mr->RecyclePoolId = pr->RecyclePoolId;
243 mr->MaxVolJobs = pr->MaxVolJobs;
244 mr->MaxVolFiles = pr->MaxVolFiles;
245 mr->MaxVolBytes = pr->MaxVolBytes;
246 mr->LabelType = pr->LabelType;
252 * Add Volumes to an existing Pool
254 static int add_cmd(UAContext *ua, const char *cmd)
258 int num, i, max, startnum;
260 char name[MAX_NAME_LENGTH];
262 int Slot = 0, InChanger = 0;
265 "You probably don't want to be using this command since it\n"
266 "creates database records without labeling the Volumes.\n"
267 "You probably want to use the \"label\" command.\n\n"));
269 if (!open_client_db(ua)) {
273 memset(&pr, 0, sizeof(pr));
274 memset(&mr, 0, sizeof(mr));
276 if (!get_pool_dbr(ua, &pr)) {
280 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
281 pr.MaxVols, pr.PoolType);
283 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
284 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
285 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
288 pr.MaxVols = ua->pint32_val;
292 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
293 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
294 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
298 if (pr.MaxVols == 0) {
301 max = pr.MaxVols - pr.NumVols;
305 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
306 if (!get_pint(ua, buf)) {
309 num = ua->pint32_val;
310 if (num < 0 || num > max) {
311 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
319 if (!get_cmd(ua, _("Enter Volume name: "))) {
323 if (!get_cmd(ua, _("Enter base volume name: "))) {
327 /* Don't allow | in Volume name because it is the volume separator character */
328 if (!is_volume_name_legal(ua, ua->cmd)) {
331 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
332 ua->warning_msg(_("Volume name too long.\n"));
335 if (strlen(ua->cmd) == 0) {
336 ua->warning_msg(_("Volume name must be at least one character long.\n"));
342 bstrncpy(name, ua->cmd, sizeof(name));
344 bstrncat(name, "%04d", sizeof(name));
347 if (!get_pint(ua, _("Enter the starting number: "))) {
350 startnum = ua->pint32_val;
352 ua->warning_msg(_("Start number must be greater than zero.\n"));
362 if (store && store->autochanger) {
363 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
366 Slot = ua->pint32_val;
367 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
370 InChanger = ua->pint32_val;
373 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
374 for (i=startnum; i < num+startnum; i++) {
375 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
377 mr.InChanger = InChanger;
378 mr.StorageId = store->StorageId;
380 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
381 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
382 ua->error_msg("%s", db_strerror(ua->db));
386 first_id = mr.PoolId;
390 Dmsg0(200, "Update pool record.\n");
391 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
392 ua->warning_msg("%s", db_strerror(ua->db));
395 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
401 * Turn auto mount on/off
406 int automount_cmd(UAContext *ua, const char *cmd)
411 if (!get_cmd(ua, _("Turn on or off? "))) {
419 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
427 static int cancel_cmd(UAContext *ua, const char *cmd)
432 char JobName[MAX_NAME_LENGTH];
434 for (i=1; i<ua->argc; i++) {
435 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
440 JobId = str_to_int64(ua->argv[i]);
441 if (!(jcr=get_jcr_by_id(JobId))) {
442 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
446 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
450 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
451 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
452 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
453 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
456 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
460 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
461 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
462 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
463 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
470 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
471 ua->error_msg(_("Unauthorized command from this console.\n"));
476 * If we still do not have a jcr,
477 * throw up a list and ask the user to select one.
480 int tjobs = 0; /* total # number jobs */
481 /* Count Jobs running */
483 if (jcr->JobId == 0) { /* this is us */
486 tjobs++; /* count of all jobs */
487 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
488 continue; /* skip not authorized */
490 njobs++; /* count of authorized jobs */
494 if (njobs == 0) { /* no authorized */
496 ua->send_msg(_("No Jobs running.\n"));
498 ua->send_msg(_("None of your jobs are running.\n"));
503 start_prompt(ua, _("Select Job:\n"));
506 if (jcr->JobId == 0) { /* this is us */
509 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
510 continue; /* skip not authorized */
512 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
517 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
520 if (ua->api && njobs == 1) {
522 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
523 _("Confirm cancel?"));
524 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
529 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
534 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
535 jcr = get_jcr_by_full_name(JobName);
537 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
542 ret = cancel_job(ua, jcr);
548 * This is a common routine to create or update a
549 * Pool DB base record from a Pool Resource. We handle
550 * the setting of MaxVols and NumVols slightly differently
551 * depending on if we are creating the Pool or we are
552 * simply bringing it into agreement with the resource (updage).
554 * Caution : RecyclePoolId isn't setup in this function.
555 * You can use set_pooldbr_recyclepoolid();
558 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
560 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
561 if (op == POOL_OP_CREATE) {
562 pr->MaxVols = pool->max_volumes;
564 } else { /* update pool */
565 if (pr->MaxVols != pool->max_volumes) {
566 pr->MaxVols = pool->max_volumes;
568 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
569 pr->MaxVols = pr->NumVols;
572 pr->LabelType = pool->LabelType;
573 pr->UseOnce = pool->use_volume_once;
574 pr->UseCatalog = pool->use_catalog;
575 pr->Recycle = pool->Recycle;
576 pr->VolRetention = pool->VolRetention;
577 pr->VolUseDuration = pool->VolUseDuration;
578 pr->MaxVolJobs = pool->MaxVolJobs;
579 pr->MaxVolFiles = pool->MaxVolFiles;
580 pr->MaxVolBytes = pool->MaxVolBytes;
581 pr->AutoPrune = pool->AutoPrune;
582 pr->ActionOnPurge = pool->action_on_purge;
583 pr->Recycle = pool->Recycle;
584 if (pool->label_format) {
585 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
587 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
591 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
592 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
596 if (!pool->RecyclePool && !pool->ScratchPool) {
600 memset(&pr, 0, sizeof(POOL_DBR));
601 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
603 if (!db_get_pool_record(jcr, db, &pr)) {
604 return -1; /* not exists in database */
607 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
609 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
610 return -1; /* error */
613 if (!db_update_pool_record(jcr, db, &pr)) {
614 return -1; /* error */
619 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
620 * works with set_pooldbr_from_poolres
622 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
627 if (pool->RecyclePool) {
628 memset(&rpool, 0, sizeof(POOL_DBR));
630 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
631 if (db_get_pool_record(jcr, db, &rpool)) {
632 pr->RecyclePoolId = rpool.PoolId;
634 Jmsg(jcr, M_WARNING, 0,
635 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
636 "Try to update it with 'update pool=%s'\n"),
637 pool->name(), rpool.Name, rpool.Name,pool->name());
641 } else { /* no RecyclePool used, set it to 0 */
642 pr->RecyclePoolId = 0;
645 if (pool->ScratchPool) {
646 memset(&rpool, 0, sizeof(POOL_DBR));
648 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
649 if (db_get_pool_record(jcr, db, &rpool)) {
650 pr->ScratchPoolId = rpool.PoolId;
652 Jmsg(jcr, M_WARNING, 0,
653 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
654 "Try to update it with 'update pool=%s'\n"),
655 pool->name(), rpool.Name, rpool.Name,pool->name());
658 } else { /* no ScratchPool used, set it to 0 */
659 pr->ScratchPoolId = 0;
667 * Create a pool record from a given Pool resource
668 * Also called from backup.c
669 * Returns: -1 on error
670 * 0 record already exists
674 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
678 memset(&pr, 0, sizeof(POOL_DBR));
680 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
682 if (db_get_pool_record(jcr, db, &pr)) {
684 if (op == POOL_OP_UPDATE) { /* update request */
685 set_pooldbr_from_poolres(&pr, pool, op);
686 set_pooldbr_references(jcr, db, &pr, pool);
687 db_update_pool_record(jcr, db, &pr);
689 return 0; /* exists */
692 set_pooldbr_from_poolres(&pr, pool, op);
693 set_pooldbr_references(jcr, db, &pr, pool);
695 if (!db_create_pool_record(jcr, db, &pr)) {
696 return -1; /* error */
704 * Create a Pool Record in the database.
705 * It is always created from the Resource record.
707 static int create_cmd(UAContext *ua, const char *cmd)
711 if (!open_client_db(ua)) {
715 pool = get_pool_resource(ua);
720 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
722 ua->error_msg(_("Error: Pool %s already exists.\n"
723 "Use update to change it.\n"), pool->name());
727 ua->error_msg("%s", db_strerror(ua->db));
733 ua->send_msg(_("Pool %s created.\n"), pool->name());
738 extern DIRRES *director;
739 extern char *configfile;
742 * Python control command
743 * python restart (restarts interpreter)
745 static int python_cmd(UAContext *ua, const char *cmd)
748 init_python_interpreter_args python_args;
750 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
751 term_python_interpreter();
753 python_args.progname = director->name();
754 python_args.scriptdir = director->scripts_directory;
755 python_args.modulename = "DirStartUp";
756 python_args.configfile = configfile;
757 python_args.workingdir = director->working_directory;
758 python_args.job_getattr = job_getattr;
759 python_args.job_setattr = job_setattr;
761 init_python_interpreter(&python_args);
763 ua->send_msg(_("Python interpreter restarted.\n"));
765 #endif /* HAVE_PYTHON */
766 ua->warning_msg(_("Nothing done.\n"));
769 #endif /* HAVE_PYTHON */
775 * Set a new address in a Client resource. We do this only
776 * if the Console name is the same as the Client name
777 * and the Console can access the client.
779 static int setip_cmd(UAContext *ua, const char *cmd)
783 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
784 ua->error_msg(_("Unauthorized command from this console.\n"));
788 client = GetClientResWithName(ua->cons->name());
791 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
794 if (client->address) {
795 free(client->address);
797 /* MA Bug 6 remove ifdef */
798 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
799 client->address = bstrdup(buf);
800 ua->send_msg(_("Client \"%s\" address set to %s\n"),
801 client->name(), client->address);
808 static void do_en_disable_cmd(UAContext *ua, bool setting)
813 i = find_arg_with_value(ua, NT_("job"));
815 job = select_enable_disable_job_resource(ua, setting);
821 job = GetJobResWithName(ua->argv[i]);
825 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
829 if (!acl_access_ok(ua, Job_ACL, job->name())) {
830 ua->error_msg(_("Unauthorized command from this console.\n"));
833 job->enabled = setting;
834 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
838 static int enable_cmd(UAContext *ua, const char *cmd)
840 do_en_disable_cmd(ua, true);
844 static int disable_cmd(UAContext *ua, const char *cmd)
846 do_en_disable_cmd(ua, false);
851 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
857 lstore.store = store;
858 pm_strcpy(lstore.store_source, _("unknown source"));
859 set_wstorage(jcr, &lstore);
860 /* Try connecting for up to 15 seconds */
861 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
862 store->name(), store->address, store->SDport);
863 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
864 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
867 Dmsg0(120, _("Connected to storage daemon\n"));
868 sd = jcr->store_bsock;
869 sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
870 if (sd->recv() >= 0) {
871 ua->send_msg("%s", sd->msg);
873 sd->signal(BNET_TERMINATE);
875 jcr->store_bsock = NULL;
879 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
883 /* Connect to File daemon */
885 ua->jcr->client = client;
886 /* Try to connect for 15 seconds */
887 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
888 client->name(), client->address, client->FDport);
889 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
890 ua->error_msg(_("Failed to connect to Client.\n"));
893 Dmsg0(120, "Connected to file daemon\n");
894 fd = ua->jcr->file_bsock;
895 fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
896 if (fd->recv() >= 0) {
897 ua->send_msg("%s", fd->msg);
899 fd->signal(BNET_TERMINATE);
901 ua->jcr->file_bsock = NULL;
906 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
908 STORE *store, **unique_store;
909 CLIENT *client, **unique_client;
915 /* Count Storage items */
919 foreach_res(store, R_STORAGE) {
922 unique_store = (STORE **) malloc(i * sizeof(STORE));
923 /* Find Unique Storage address/port */
924 store = (STORE *)GetNextRes(R_STORAGE, NULL);
926 unique_store[i++] = store;
927 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
929 for (j=0; j<i; j++) {
930 if (strcmp(unique_store[j]->address, store->address) == 0 &&
931 unique_store[j]->SDport == store->SDport) {
937 unique_store[i++] = store;
938 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
943 /* Call each unique Storage daemon */
944 for (j=0; j<i; j++) {
945 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
949 /* Count Client items */
953 foreach_res(client, R_CLIENT) {
956 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
957 /* Find Unique Client address/port */
958 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
960 unique_client[i++] = client;
961 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
963 for (j=0; j<i; j++) {
964 if (strcmp(unique_client[j]->address, client->address) == 0 &&
965 unique_client[j]->FDport == client->FDport) {
971 unique_client[i++] = client;
972 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
977 /* Call each unique File daemon */
978 for (j=0; j<i; j++) {
979 do_client_setdebug(ua, unique_client[j], level, trace_flag);
985 * setdebug level=nn all trace=1/0
987 static int setdebug_cmd(UAContext *ua, const char *cmd)
995 Dmsg1(120, "setdebug:%s:\n", cmd);
998 i = find_arg_with_value(ua, "level");
1000 level = atoi(ua->argv[i]);
1003 if (!get_pint(ua, _("Enter new debug level: "))) {
1006 level = ua->pint32_val;
1009 /* Look for trace flag. -1 => not change */
1010 i = find_arg_with_value(ua, "trace");
1012 trace_flag = atoi(ua->argv[i]);
1013 if (trace_flag > 0) {
1018 /* General debug? */
1019 for (i=1; i<ua->argc; i++) {
1020 if (strcasecmp(ua->argk[i], "all") == 0) {
1021 do_all_setdebug(ua, level, trace_flag);
1024 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1025 strcasecmp(ua->argk[i], "director") == 0) {
1026 debug_level = level;
1027 set_trace(trace_flag);
1030 if (strcasecmp(ua->argk[i], "client") == 0 ||
1031 strcasecmp(ua->argk[i], "fd") == 0) {
1034 client = GetClientResWithName(ua->argv[i]);
1036 do_client_setdebug(ua, client, level, trace_flag);
1040 client = select_client_resource(ua);
1042 do_client_setdebug(ua, client, level, trace_flag);
1047 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1048 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1049 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1052 store = GetStoreResWithName(ua->argv[i]);
1054 do_storage_setdebug(ua, store, level, trace_flag);
1058 store = get_storage_resource(ua, false/*no default*/);
1060 do_storage_setdebug(ua, store, level, trace_flag);
1066 * We didn't find an appropriate keyword above, so
1069 start_prompt(ua, _("Available daemons are: \n"));
1070 add_prompt(ua, _("Director"));
1071 add_prompt(ua, _("Storage"));
1072 add_prompt(ua, _("Client"));
1073 add_prompt(ua, _("All"));
1074 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1075 case 0: /* Director */
1076 debug_level = level;
1077 set_trace(trace_flag);
1080 store = get_storage_resource(ua, false/*no default*/);
1082 do_storage_setdebug(ua, store, level, trace_flag);
1086 client = select_client_resource(ua);
1088 do_client_setdebug(ua, client, level, trace_flag);
1092 do_all_setdebug(ua, level, trace_flag);
1101 * Turn debug tracing to file on/off
1103 static int trace_cmd(UAContext *ua, const char *cmd)
1107 if (ua->argc != 2) {
1108 if (!get_cmd(ua, _("Turn on or off? "))) {
1113 onoff = ua->argk[1];
1116 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1121 static int var_cmd(UAContext *ua, const char *cmd)
1123 POOLMEM *val = get_pool_memory(PM_FNAME);
1126 if (!open_client_db(ua)) {
1129 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1132 while (*var == ' ') { /* skip spaces */
1135 Dmsg1(100, "Var=%s:\n", var);
1136 variable_expansion(ua->jcr, var, &val);
1137 ua->send_msg("%s\n", val);
1138 free_pool_memory(val);
1142 static int estimate_cmd(UAContext *ua, const char *cmd)
1145 CLIENT *client = NULL;
1146 FILESET *fileset = NULL;
1148 char since[MAXSTRING];
1152 jcr->set_JobLevel(L_FULL);
1153 for (int i=1; i<ua->argc; i++) {
1154 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1155 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1157 client = GetClientResWithName(ua->argv[i]);
1159 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1162 if (!acl_access_ok(ua, Client_ACL, client->name())) {
1163 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1168 ua->error_msg(_("Client name missing.\n"));
1172 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1174 job = GetJobResWithName(ua->argv[i]);
1176 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1179 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1180 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1185 ua->error_msg(_("Job name missing.\n"));
1190 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1192 fileset = GetFileSetResWithName(ua->argv[i]);
1194 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1197 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1198 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1203 ua->error_msg(_("Fileset name missing.\n"));
1207 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1211 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1213 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1214 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1218 ua->error_msg(_("Level value missing.\n"));
1222 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1223 if (!is_yesno(ua->argv[i], &accurate)) {
1224 ua->error_msg(_("Invalid value for accurate. "
1225 "It must be yes or no.\n"));
1229 if (!job && !(client && fileset)) {
1230 if (!(job = select_job_resource(ua))) {
1235 job = GetJobResWithName(ua->argk[1]);
1237 ua->error_msg(_("No job specified.\n"));
1240 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1241 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1246 client = job->client;
1249 fileset = job->fileset;
1251 jcr->client = client;
1252 jcr->fileset = fileset;
1254 if (job->pool->catalog) {
1255 ua->catalog = job->pool->catalog;
1257 ua->catalog = client->catalog;
1265 jcr->set_JobType(JT_BACKUP);
1266 init_jcr_job_record(jcr);
1268 if (!get_or_create_client_record(jcr)) {
1271 if (!get_or_create_fileset_record(jcr)) {
1275 get_level_since_time(ua->jcr, since, sizeof(since));
1277 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1278 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1279 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1280 ua->error_msg(_("Failed to connect to Client.\n"));
1284 if (!send_include_list(jcr)) {
1285 ua->error_msg(_("Error sending include list.\n"));
1289 if (!send_exclude_list(jcr)) {
1290 ua->error_msg(_("Error sending exclude list.\n"));
1294 /* The level string change if accurate mode is enabled */
1295 if (accurate >= 0) {
1296 jcr->accurate = accurate;
1298 jcr->accurate = job->accurate;
1301 if (!send_level_command(jcr)) {
1306 * If the job is in accurate mode, we send the list of
1309 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1310 if (!send_accurate_current_files(jcr)) {
1314 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1315 while (bnet_recv(jcr->file_bsock) >= 0) {
1316 ua->send_msg("%s", jcr->file_bsock->msg);
1320 if (jcr->file_bsock) {
1321 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1322 bnet_close(jcr->file_bsock);
1323 jcr->file_bsock = NULL;
1332 static int time_cmd(UAContext *ua, const char *cmd)
1335 time_t ttime = time(NULL);
1337 (void)localtime_r(&ttime, &tm);
1338 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1339 ua->send_msg("%s\n", sdt);
1344 * reload the conf file
1346 extern "C" void reload_config(int sig);
1348 static int reload_cmd(UAContext *ua, const char *cmd)
1355 * Delete Pool records (should purge Media with it).
1357 * delete pool=<pool-name>
1358 * delete volume pool=<pool-name> volume=<name>
1361 static int delete_cmd(UAContext *ua, const char *cmd)
1363 static const char *keywords[] = {
1369 if (!open_client_db(ua)) {
1373 switch (find_arg_keyword(ua, keywords)) {
1382 while ((i=find_arg(ua, "jobid")) > 0) {
1384 *ua->argk[i] = 0; /* zap keyword already visited */
1392 "In general it is not a good idea to delete either a\n"
1393 "Pool or a Volume since they may contain data.\n\n"));
1395 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1406 ua->warning_msg(_("Nothing done.\n"));
1414 * delete_job has been modified to parse JobID lists like the
1416 * delete JobID=3,4,6,7-11,14
1418 * Thanks to Phil Stracchino for the above addition.
1421 static void delete_job(UAContext *ua)
1426 int i = find_arg_with_value(ua, NT_("jobid"));
1428 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1429 s = bstrdup(ua->argv[i]);
1432 * We could use strtok() here. But we're not going to, because:
1433 * (a) strtok() is deprecated, having been replaced by strsep();
1434 * (b) strtok() is broken in significant ways.
1435 * we could use strsep() instead, but it's not universally available.
1436 * so we grow our own using strchr().
1438 sep = strchr(tok, ',');
1439 while (sep != NULL) {
1441 if (strchr(tok, '-')) {
1442 delete_job_id_range(ua, tok);
1444 JobId = str_to_int64(tok);
1445 do_job_delete(ua, JobId);
1448 sep = strchr(tok, ',');
1450 /* pick up the last token */
1451 if (strchr(tok, '-')) {
1452 delete_job_id_range(ua, tok);
1454 JobId = str_to_int64(tok);
1455 do_job_delete(ua, JobId);
1460 JobId = str_to_int64(ua->argv[i]);
1461 do_job_delete(ua, JobId);
1463 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1466 JobId = ua->int64_val;
1467 do_job_delete(ua, JobId);
1472 * we call delete_job_id_range to parse range tokens and iterate over ranges
1474 static void delete_job_id_range(UAContext *ua, char *tok)
1479 tok2 = strchr(tok, '-');
1482 j1 = str_to_int64(tok);
1483 j2 = str_to_int64(tok2);
1484 for (j=j1; j<=j2; j++) {
1485 do_job_delete(ua, j);
1490 * do_job_delete now performs the actual delete operation atomically
1492 static void do_job_delete(UAContext *ua, JobId_t JobId)
1496 edit_int64(JobId, ed1);
1497 purge_jobs_from_catalog(ua, ed1);
1498 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1502 * Delete media records from database -- dangerous
1504 static int delete_volume(UAContext *ua)
1509 if (!select_media_dbr(ua, &mr)) {
1512 ua->warning_msg(_("\nThis command will delete volume %s\n"
1513 "and all Jobs saved on that volume from the Catalog\n"),
1516 if (find_arg(ua, "yes") >= 0) {
1517 ua->pint32_val = 1; /* Have "yes" on command line already" */
1519 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1521 if (!get_yesno(ua, buf)) {
1525 if (ua->pint32_val) {
1526 db_delete_media_record(ua->jcr, ua->db, &mr);
1532 * Delete a pool record from the database -- dangerous
1534 static int delete_pool(UAContext *ua)
1539 memset(&pr, 0, sizeof(pr));
1541 if (!get_pool_dbr(ua, &pr)) {
1544 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1546 if (!get_yesno(ua, buf)) {
1549 if (ua->pint32_val) {
1550 db_delete_pool_record(ua->jcr, ua->db, &pr);
1555 int memory_cmd(UAContext *ua, const char *cmd)
1557 list_dir_status_header(ua);
1558 sm_dump(false, true);
1562 static void do_mount_cmd(UAContext *ua, const char *command)
1567 char dev_name[MAX_NAME_LENGTH];
1571 if (!open_client_db(ua)) {
1574 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1576 store.store = get_storage_resource(ua, true/*arg is storage*/);
1580 pm_strcpy(store.store_source, _("unknown source"));
1581 set_wstorage(jcr, &store);
1582 drive = get_storage_drive(ua, store.store);
1583 if (strcmp(command, "mount") == 0) {
1584 slot = get_storage_slot(ua, store.store);
1587 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1588 store.store->media_type, store.store->dev_name(), drive);
1590 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1591 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1594 sd = jcr->store_bsock;
1595 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1596 bash_spaces(dev_name);
1598 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1600 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1602 while (bnet_recv(sd) >= 0) {
1603 ua->send_msg("%s", sd->msg);
1605 bnet_sig(sd, BNET_TERMINATE);
1607 jcr->store_bsock = NULL;
1611 * mount [storage=<name>] [drive=nn] [slot=mm]
1613 static int mount_cmd(UAContext *ua, const char *cmd)
1615 do_mount_cmd(ua, "mount"); /* mount */
1621 * unmount [storage=<name>] [drive=nn]
1623 static int unmount_cmd(UAContext *ua, const char *cmd)
1625 do_mount_cmd(ua, "unmount"); /* unmount */
1631 * release [storage=<name>] [drive=nn]
1633 static int release_cmd(UAContext *ua, const char *cmd)
1635 do_mount_cmd(ua, "release"); /* release */
1642 * use catalog=<name>
1644 static int use_cmd(UAContext *ua, const char *cmd)
1646 CAT *oldcatalog, *catalog;
1649 close_db(ua); /* close any previously open db */
1650 oldcatalog = ua->catalog;
1652 if (!(catalog = get_catalog_resource(ua))) {
1653 ua->catalog = oldcatalog;
1655 ua->catalog = catalog;
1658 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1659 ua->catalog->name(), ua->catalog->db_name);
1664 int quit_cmd(UAContext *ua, const char *cmd)
1670 /* Handler to get job status */
1671 static int status_handler(void *ctx, int num_fields, char **row)
1673 char *val = (char *)ctx;
1678 *val = '?'; /* Unknown by default */
1685 * Wait until no job is running
1687 int wait_cmd(UAContext *ua, const char *cmd)
1691 time_t stop_time = 0;
1695 * Wait until no job is running
1697 if (ua->argc == 1) {
1698 bmicrosleep(0, 200000); /* let job actually start */
1699 for (bool running=true; running; ) {
1702 if (jcr->JobId != 0) {
1716 i = find_arg_with_value(ua, NT_("timeout"));
1717 if (i > 0 && ua->argv[i]) {
1718 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1721 /* we have jobid, jobname or ujobid argument */
1723 uint32_t jobid = 0 ;
1725 if (!open_client_db(ua)) {
1726 ua->error_msg(_("ERR: Can't open db\n")) ;
1730 for (int i=1; i<ua->argc; i++) {
1731 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1735 jobid = str_to_int64(ua->argv[i]);
1737 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1738 strcasecmp(ua->argk[i], "job") == 0) {
1742 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1744 jobid = jcr->JobId ;
1748 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1752 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1754 jobid = jcr->JobId ;
1758 /* Wait for a mount request */
1759 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1760 for (bool waiting=false; !waiting; ) {
1762 if (jcr->JobId != 0 &&
1763 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1772 if (stop_time && (time(NULL) >= stop_time)) {
1773 ua->warning_msg(_("Wait on mount timed out\n"));
1783 ua->error_msg(_("ERR: Job was not found\n"));
1788 * We wait the end of a specific job
1791 bmicrosleep(0, 200000); /* let job actually start */
1792 for (bool running=true; running; ) {
1795 jcr=get_jcr_by_id(jobid) ;
1808 * We have to get JobStatus
1812 char jobstatus = '?'; /* Unknown by default */
1815 bsnprintf(buf, sizeof(buf),
1816 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1819 db_sql_query(ua->db, buf,
1820 status_handler, (void *)&jobstatus);
1822 switch (jobstatus) {
1824 status = 1 ; /* Warning */
1828 case JS_ErrorTerminated:
1830 status = 2 ; /* Critical */
1835 status = 0 ; /* Ok */
1839 status = 3 ; /* Unknown */
1843 ua->send_msg("JobId=%i\n", jobid) ;
1844 ua->send_msg("JobStatus=%s (%c)\n",
1845 job_status_to_str(jobstatus),
1848 if (ua->gui || ua->api) {
1849 ua->send_msg("ExitStatus=%i\n", status) ;
1856 static int help_cmd(UAContext *ua, const char *cmd)
1860 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1861 for (i=0; i<comsize; i++) {
1862 ua->send_msg(_(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1864 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1868 int qhelp_cmd(UAContext *ua, const char *cmd)
1871 /* Want to display only commands */
1872 j = find_arg(ua, NT_("all"));
1874 for (i=0; i<comsize; i++) {
1875 ua->send_msg("%s\n", commands[i].key);
1879 /* Want to display a specific help section */
1880 j = find_arg_with_value(ua, NT_("item"));
1881 if (j >= 0 && ua->argk[j]) {
1882 for (i=0; i<comsize; i++) {
1883 if (bstrcmp(commands[i].key, ua->argv[j])) {
1884 ua->send_msg("%s\n", _(commands[i].help));
1890 /* Want to display everything */
1891 for (i=0; i<comsize; i++) {
1892 ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1898 static int version_cmd(UAContext *ua, const char *cmd)
1900 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1901 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1906 * Test code -- turned on only for debug testing
1908 static int version_cmd(UAContext *ua, const char *cmd)
1911 POOL_MEM query(PM_MESSAGE);
1913 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1914 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1915 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1916 for (int i=0; i < ids.num_ids; i++) {
1917 ua->send_msg("id=%d\n", ids.DBId[i]);
1925 * This call explicitly checks for a catalog=xxx and
1926 * if given, opens that catalog. It also checks for
1927 * client=xxx and if found, opens the catalog
1928 * corresponding to that client. If we still don't
1929 * have a catalog, look for a Job keyword and get the
1930 * catalog from its client record.
1932 bool open_client_db(UAContext *ua)
1939 /* Try for catalog keyword */
1940 i = find_arg_with_value(ua, NT_("catalog"));
1942 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1943 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1946 catalog = GetCatalogResWithName(ua->argv[i]);
1948 if (ua->catalog && ua->catalog != catalog) {
1951 ua->catalog = catalog;
1956 /* Try for client keyword */
1957 i = find_arg_with_value(ua, NT_("client"));
1959 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1960 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1963 client = GetClientResWithName(ua->argv[i]);
1965 catalog = client->catalog;
1966 if (ua->catalog && ua->catalog != catalog) {
1969 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1970 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1973 ua->catalog = catalog;
1978 /* Try for Job keyword */
1979 i = find_arg_with_value(ua, NT_("job"));
1981 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1982 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1985 job = GetJobResWithName(ua->argv[i]);
1987 catalog = job->client->catalog;
1988 if (ua->catalog && ua->catalog != catalog) {
1991 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1992 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1995 ua->catalog = catalog;
2005 * Open the catalog database.
2007 bool open_db(UAContext *ua)
2013 ua->catalog = get_catalog_resource(ua);
2015 ua->error_msg( _("Could not find a Catalog resource\n"));
2020 ua->jcr->catalog = ua->catalog;
2022 Dmsg0(100, "UA Open database\n");
2023 ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
2024 ua->catalog->db_user,
2025 ua->catalog->db_password, ua->catalog->db_address,
2026 ua->catalog->db_port, ua->catalog->db_socket,
2027 ua->catalog->mult_db_connections);
2028 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2029 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2030 ua->catalog->db_name);
2032 ua->error_msg("%s", db_strerror(ua->db));
2037 ua->jcr->db = ua->db;
2039 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2041 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2045 void close_db(UAContext *ua)
2048 db_close_database(ua->jcr, ua->db);