/*
- Bacula® - The Network Backup Solution
-
- Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
-
- The main author of Bacula is Kern Sibbald, with contributions from
- many others, a complete list can be found in the file AUTHORS.
- This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
- License as published by the Free Software Foundation and included
- in the file LICENSE.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
- Bacula® is a registered trademark of Kern Sibbald.
- The licensor of Bacula is the Free Software Foundation Europe
- (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
- Switzerland, email:ftf@fsfeurope.org.
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2015 Kern Sibbald
+ Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
*/
/*
*
*
* Kern Sibbald, September MM
*
- * Version $Id$
*/
-
+
#include "bacula.h"
#include "dird.h"
-#ifdef HAVE_PYTHON
-
-#undef _POSIX_C_SOURCE
-#include <Python.h>
-
-#include "lib/pythonlib.h"
-
-/* Imported Functions */
-extern PyObject *job_getattr(PyObject *self, char *attrname);
-extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
-
-#endif /* HAVE_PYTHON */
-
/* Imported subroutines */
/* Imported variables */
extern int llist_cmd(UAContext *ua, const char *cmd);
extern int messagescmd(UAContext *ua, const char *cmd);
extern int prunecmd(UAContext *ua, const char *cmd);
-extern int purgecmd(UAContext *ua, const char *cmd);
+extern int purge_cmd(UAContext *ua, const char *cmd);
+extern int truncate_cmd(UAContext *ua, const char *cmd); /* in ua_purge.c */
extern int querycmd(UAContext *ua, const char *cmd);
extern int relabel_cmd(UAContext *ua, const char *cmd);
extern int restore_cmd(UAContext *ua, const char *cmd);
static int help_cmd(UAContext *ua, const char *cmd);
static int memory_cmd(UAContext *ua, const char *cmd);
static int mount_cmd(UAContext *ua, const char *cmd);
-static int python_cmd(UAContext *ua, const char *cmd);
static int release_cmd(UAContext *ua, const char *cmd);
static int reload_cmd(UAContext *ua, const char *cmd);
static int setdebug_cmd(UAContext *ua, const char *cmd);
+static int setbwlimit_cmd(UAContext *ua, const char *cmd);
static int setip_cmd(UAContext *ua, const char *cmd);
static int time_cmd(UAContext *ua, const char *cmd);
static int trace_cmd(UAContext *ua, const char *cmd);
static int wait_cmd(UAContext *ua, const char *cmd);
static void do_job_delete(UAContext *ua, JobId_t JobId);
-static void delete_job_id_range(UAContext *ua, char *tok);
static int delete_volume(UAContext *ua);
static int delete_pool(UAContext *ua);
static void delete_job(UAContext *ua);
+static void do_storage_cmd(UAContext *ua, const char *command);
int qhelp_cmd(UAContext *ua, const char *cmd);
int quit_cmd(UAContext *ua, const char *cmd);
/* not all in alphabetical order. New commands are added after existing commands with similar letters
to prevent breakage of existing user scripts. */
-struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; const bool use_in_rs;};
+struct cmdstruct {
+ const char *key; /* command */
+ int (*func)(UAContext *ua, const char *cmd); /* handler */
+ const char *help; /* main purpose */
+ const char *usage; /* all arguments to build usage */
+ const bool use_in_rs; /* Can use it in Console RunScript */
+};
static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
- { NT_("add"), add_cmd, _("add [pool=<pool-name> storage=<storage> jobid=<JobId>] -- add media to a pool"), false},
- { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages"),false},
- { NT_("automount"), automount_cmd, _("automount [on|off] -- after label"), false},
- { NT_("cancel"), cancel_cmd, _("cancel [jobid=<number> job=<job-name> ujobid=<unique-jobid>] -- cancel a job"), false},
- { NT_("create"), create_cmd, _("create [pool=<pool-name>] -- create DB Pool from resource"), false},
- { NT_("delete"), delete_cmd, _("delete [volume=<vol-name> pool=<pool-name> job jobid=<id>]"), true},
- { NT_("disable"), disable_cmd, _("disable <job=name> -- disable a job"), true},
- { NT_("enable"), enable_cmd, _("enable <job=name> -- enable a job"), true},
- { NT_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing"), true},
- { NT_("exit"), quit_cmd, _("exit = quit"), false},
- { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode"), false},
- { NT_("help"), help_cmd, _("print this command"), false},
- { NT_("label"), label_cmd, _("label a tape"), false},
- { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn> | copies <jobid=nn>]; from catalog"), true},
- { NT_("llist"), llist_cmd, _("full or long list like list command"), true},
- { NT_("messages"), messagescmd, _("messages"), false},
- { NT_("memory"), memory_cmd, _("print current memory usage"), true},
- { NT_("mount"), mount_cmd, _("mount storage=<storage-name> [ slot=<num> ] [ drive=<num> ] or mount [ jobid=<id> | job=<job-name> ]"), false},
- { NT_("prune"), prunecmd, _("prune files|jobs|volume client=<client-name> volume=<volume-name> prune expired records from catalog"), true},
- { NT_("purge"), purgecmd, _("purge records from catalog"), true},
- { NT_("python"), python_cmd, _("python control commands"), false},
- { NT_("quit"), quit_cmd, _("quit"), false},
- { NT_("query"), querycmd, _("query catalog"), false},
- { NT_("restore"), restore_cmd, _("restore files"), false},
- { NT_("relabel"), relabel_cmd, _("relabel storage=<storage-name> oldvolume=<old-volume-name> volume=<newvolume-name> -- relabel a tape"), false},
- { NT_("release"), release_cmd, _("release <storage-name>"), false},
- { NT_("reload"), reload_cmd, _("reload conf file"), true},
- { NT_("run"), run_cmd, _("run job=<job-name> client=<client-name> fileset=<FileSet-name> level=<level-keyword> storage=<storage-name> where=<directory-prefix> when=<universal-time-specification> yes"), false}, /* need to be check */
- { NT_("status"), status_cmd, _("status [all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> | days=nnn]"), true},
- { NT_("setdebug"), setdebug_cmd, _("setdebug level=nn [trace=0/1 client=<client-name> | dir | director | storage=<storage-name> | all] -- sets debug level"), true},
- { NT_("setip"), setip_cmd, _("sets new client address -- if authorized"), false},
- { NT_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]"), true},
- { NT_("sqlquery"), sqlquerycmd, _("use SQL to query catalog"), false},
- { NT_("time"), time_cmd, _("print current time"), true},
- { NT_("trace"), trace_cmd, _("turn on/off trace to file"), true},
- { NT_("unmount"), unmount_cmd, _("unmount storage=<storage-name> [ drive=<num> ] or unmount [ jobid=<id> | job=<job-name> ]"), false},
- { NT_("umount"), unmount_cmd, _("umount - for old-time Unix guys, see unmount"),false},
- { NT_("update"), update_cmd, _("update Volume, Pool or slots"), true},
- { NT_("use"), use_cmd, _("use <database-name> -- catalog xxx"), false},
- { NT_("var"), var_cmd, _("does variable expansion"), false},
- { NT_("version"), version_cmd, _("print Director version"), true},
- { NT_("wait"), wait_cmd, _("wait [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>] -- wait until no jobs are running"), false},
- };
-#define comsize (sizeof(commands)/sizeof(struct cmdstruct))
+ { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
+ { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
+ { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
+ { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid=<number-list> | job=<job-name> | ujobid=<unique-jobid> | inactive client=<client-name> storage=<storage-name> | all"), false},
+ { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
+ { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume=<vol-name> | pool=<pool-name> | jobid=<id> | snapshot"), true},
+ { NT_("disable"), disable_cmd, _("Disable a job, attributes batch process"), NT_("job=<name> | batch"), true},
+ { NT_("enable"), enable_cmd, _("Enable a job, attributes batch process"), NT_("job=<name> | batch"), true},
+ { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
+ NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
+
+ { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
+ { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
+ { NT_("help"), help_cmd, _("Print help on specific command"),
+ NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
+ "\n\tmessages memory mount prune purge quit query\n\trestore relabel release reload run status"
+ "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"
+ "\n\tsnapshot"), false},
+
+ { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> barcodes"), false},
+ { NT_("list"), list_cmd, _("List objects from catalog"),
+ NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [limit=<n>]|\n"
+ "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
+ "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot"), false},
+
+ { NT_("llist"), llist_cmd, _("Full or long list like list command"),
+ NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [limit=<n>]|\n"
+ "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
+ "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot"), false},
+
+ { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
+ { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
+ { NT_("mount"), mount_cmd, _("Mount storage"),
+ NT_("storage=<storage-name> slot=<num> drive=<num> [ device=<device-name> ] [ jobid=<id> | job=<job-name> ]"), false},
+
+ { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
+ NT_("files | jobs | pool=<pool> | snapshot [client=<client-name>] | client=<client-name> | [ expired ] volume=<volume-name> "), true},
+
+ { NT_("purge"), purge_cmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
+ { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
+ { NT_("query"), querycmd, _("Query catalog"), NT_(""), false},
+ { NT_("restore"), restore_cmd, _("Restore files"),
+ NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
+ "restorejob=<job>"
+ "\n\tcomment=<text> jobid=<jobid> copies done select all"), false},
+
+ { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
+ NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
+
+ { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name> [ device=<device-name> ] "), false},
+ { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
+ { NT_("run"), run_cmd, _("Run a job"),
+ NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
+ " where=<directory-prefix>\n\twhen=<universal-time-specification> pool=<pool-name>\n\t"
+ " nextpool=<next-pool-name> comment=<text> accurate=<bool> spooldata=<bool> yes"), false},
+
+ { NT_("restart"), restart_cmd, _("Restart a job"),
+ NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
+ "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
+
+ { NT_("resume"), restart_cmd, _("Resume a job"),
+ NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
+ "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
+
+ { NT_("status"), status_cmd, _("Report status"),
+ NT_("all | dir=<dir-name> | director | client=<client-name> |\n"
+ "\tstorage=<storage-name> slots |\n"
+ "\tschedule [job=<job-name>] [days=<nn>] [limit=<nn>]\n"
+ "\t\t[time=<universal-time-specification>]"), true},
+
+ { NT_("stop"), cancel_cmd, _("Stop a job"), NT_("jobid=<number-list> job=<job-name> ujobid=<unique-jobid> all"), false},
+ { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
+ NT_("level=<nn> tags=<tags> trace=0/1 options=<0tTc> tags=<tags> | client=<client-name> | dir | storage=<storage-name> | all"), true},
+
+ { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"),
+ NT_("limit=<nn-kbs> client=<client-name> jobid=<number> job=<job-name> ujobid=<unique-jobid>"), true},
+
+ { NT_("snapshot"), snapshot_cmd, _("Handle snapshots"),
+ NT_("[client=<client-name> | job=<job-name> | jobid=<jobid>] [delete | list | listclient | prune | sync | update]"), true},
+
+ { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
+ { NT_("show"), show_cmd, _("Show resource records"),
+ NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> | schedule=<sss> | client=<zzz> | storage=<sss> | disabled | all"), true},
+
+ { NT_("sqlquery"), sqlquerycmd, _("Use SQL to query catalog"), NT_(""), false},
+ { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
+ { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
+ { NT_("truncate"), truncate_cmd, _("Truncate one or more Volumes"), NT_("volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
+ { NT_("unmount"), unmount_cmd, _("Unmount storage"),
+ NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
+
+ { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
+ NT_("storage=<storage-name> [ drive=<num> ] [ device=<dev-name> ]| jobid=<id> | job=<job-name>"), false},
+
+ { NT_("update"), update_cmd, _("Update volume, pool or stats"),
+ NT_("stats\n\tsnapshot\n\tpool=<poolname>\n\tslots storage=<storage> scan"
+ "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
+ "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
+ "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
+ "\n\t enabled=<yes/no> recyclepool=<pool> actiononpurge=<action>"
+ "\n\t allfrompool=<pool> fromallpools frompool"),true},
+ { NT_("use"), use_cmd, _("Use catalog xxx"), NT_("catalog=<catalog>"), false},
+ { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
+ { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
+ { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
+ NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
+};
+
+#define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
+
+const char *get_command(int index) {
+ return commands[index].key;
+}
/*
* Execute a command from the UA
*/
bool do_a_command(UAContext *ua)
{
- unsigned int i;
+ int i;
int len;
bool ok = false;
bool found = false;
return false;
}
- while (ua->jcr->wstorage->size()) {
- ua->jcr->wstorage->remove(0);
+ if (ua->jcr->wstorage) {
+ while (ua->jcr->wstorage->size()) {
+ ua->jcr->wstorage->remove(0);
+ }
}
len = strlen(ua->argk[0]);
for (i=0; i<comsize; i++) { /* search for command */
if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
+ ua->cmd_index = i;
/* Check if command permitted, but "quit" is always OK */
if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
!acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
}
if (ua->api) user->signal(BNET_CMD_BEGIN);
ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
- found = true;
+ if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
+ found = (user && user->is_stop()) ? false : true;
break;
}
}
ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
ok = false;
}
- if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
return ok;
}
mr->Recycle = pr->Recycle;
mr->VolRetention = pr->VolRetention;
mr->VolUseDuration = pr->VolUseDuration;
+ mr->ActionOnPurge = pr->ActionOnPurge;
mr->RecyclePoolId = pr->RecyclePoolId;
mr->MaxVolJobs = pr->MaxVolJobs;
mr->MaxVolFiles = pr->MaxVolFiles;
POOL_DBR pr;
MEDIA_DBR mr;
int num, i, max, startnum;
- int first_id = 0;
char name[MAX_NAME_LENGTH];
STORE *store;
int Slot = 0, InChanger = 0;
}
memset(&pr, 0, sizeof(pr));
- memset(&mr, 0, sizeof(mr));
if (!get_pool_dbr(ua, &pr)) {
return 1;
bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
mr.Slot = Slot++;
mr.InChanger = InChanger;
- mr.StorageId = store->StorageId;
mr.Enabled = 1;
+ set_storageid_in_mr(store, &mr);
Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
ua->error_msg("%s", db_strerror(ua->db));
return 1;
}
- if (i == startnum) {
- first_id = mr.PoolId;
- }
+// if (i == startnum) {
+// first_id = mr.PoolId;
+// }
}
pr.NumVols += num;
Dmsg0(200, "Update pool record.\n");
return 1;
}
-
/*
- * Cancel a job
+ * Cancel/Stop a job -- Stop marks it as Incomplete
+ * so that it can be restarted.
*/
static int cancel_cmd(UAContext *ua, const char *cmd)
{
- int i, ret;
- int njobs = 0;
- JCR *jcr = NULL;
- char JobName[MAX_NAME_LENGTH];
-
- for (i=1; i<ua->argc; i++) {
- if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
- uint32_t JobId;
- if (!ua->argv[i]) {
- break;
- }
- JobId = str_to_int64(ua->argv[i]);
- if (!(jcr=get_jcr_by_id(JobId))) {
- ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
- return 1;
- }
- break;
- } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
- if (!ua->argv[i]) {
- break;
- }
- if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
- ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
- jcr = new_jcr(sizeof(JCR), dird_free_jcr);
- bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
- }
- break;
- } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
- if (!ua->argv[i]) {
- break;
- }
- if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
- ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
- jcr = new_jcr(sizeof(JCR), dird_free_jcr);
- bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
- }
- break;
- }
-
- }
- if (jcr) {
- if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
- ua->error_msg(_("Unauthorized command from this console.\n"));
- return 1;
- }
- } else {
- /*
- * If we still do not have a jcr,
- * throw up a list and ask the user to select one.
- */
- char buf[1000];
- int tjobs = 0; /* total # number jobs */
- /* Count Jobs running */
- foreach_jcr(jcr) {
- if (jcr->JobId == 0) { /* this is us */
- continue;
- }
- tjobs++; /* count of all jobs */
- if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
- continue; /* skip not authorized */
- }
- njobs++; /* count of authorized jobs */
- }
- endeach_jcr(jcr);
+ JCR *jcr;
+ bool ret = true;
+ int nb;
+ bool cancel = strcasecmp(commands[ua->cmd_index].key, "cancel") == 0;
+ alist *jcrs = New(alist(5, not_owned_by_alist));
- if (njobs == 0) { /* no authorized */
- if (tjobs == 0) {
- ua->send_msg(_("No Jobs running.\n"));
- } else {
- ua->send_msg(_("None of your jobs are running.\n"));
- }
- return 1;
- }
+ nb = select_running_jobs(ua, jcrs, commands[ua->cmd_index].key);
- start_prompt(ua, _("Select Job:\n"));
- foreach_jcr(jcr) {
- char ed1[50];
- if (jcr->JobId == 0) { /* this is us */
- continue;
- }
- if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
- continue; /* skip not authorized */
- }
- bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
- add_prompt(ua, buf);
- }
- endeach_jcr(jcr);
-
- if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
- return 1;
- }
- if (ua->api && njobs == 1) {
- char nbuf[1000];
- bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
- _("Confirm cancel?"));
- if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
- return 1;
- }
- } else {
- if (njobs == 1) {
- if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
- return 1;
- }
- }
- }
- sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
- jcr = get_jcr_by_full_name(JobName);
- if (!jcr) {
- ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
- return 1;
+ foreach_alist(jcr, jcrs) {
+ /* Execute the cancel command only if we don't have an error */
+ if (nb != -1) {
+ ret &= cancel_job(ua, jcr, cancel);
}
+ free_jcr(jcr);
}
- ret = cancel_job(ua, jcr);
- free_jcr(jcr);
+ delete jcrs;
return ret;
}
pr->MaxVolFiles = pool->MaxVolFiles;
pr->MaxVolBytes = pool->MaxVolBytes;
pr->AutoPrune = pool->AutoPrune;
+ pr->ActionOnPurge = pool->action_on_purge;
pr->Recycle = pool->Recycle;
if (pool->label_format) {
bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
}
}
-/* set/update Pool.RecyclePoolId in Catalog */
-int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
+/* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
+int update_pool_references(JCR *jcr, BDB *db, POOL *pool)
{
POOL_DBR pr;
- if (!pool->RecyclePool) {
+ if (pool->ScratchPool == pool) {
+ Jmsg(NULL, M_WARNING, 0,
+ _("The ScratchPool directive for Pool \"%s\" is incorrect. Using default Scratch pool instead.\n"),
+ pool->name());
+ pool->ScratchPool = NULL;
+ }
+
+ if (!pool->RecyclePool && !pool->ScratchPool) {
return 1;
}
memset(&pr, 0, sizeof(POOL_DBR));
bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
+ /* Don't compute NumVols here */
if (!db_get_pool_record(jcr, db, &pr)) {
return -1; /* not exists in database */
}
set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
- if (!set_pooldbr_recyclepoolid(jcr, db, &pr, pool)) {
+ if (!set_pooldbr_references(jcr, db, &pr, pool)) {
return -1; /* error */
}
+ /* NumVols is updated here */
if (!db_update_pool_record(jcr, db, &pr)) {
return -1; /* error */
}
return 1;
}
-/* set POOL_DBR.RecyclePoolId from Pool resource
+/* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
* works with set_pooldbr_from_poolres
*/
-bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
+bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool)
{
POOL_DBR rpool;
bool ret = true;
} else { /* no RecyclePool used, set it to 0 */
pr->RecyclePoolId = 0;
}
+
+ if (pool->ScratchPool) {
+ memset(&rpool, 0, sizeof(POOL_DBR));
+
+ bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
+ if (db_get_pool_record(jcr, db, &rpool)) {
+ pr->ScratchPoolId = rpool.PoolId;
+ } else {
+ Jmsg(jcr, M_WARNING, 0,
+ _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
+ "Try to update it with 'update pool=%s'\n"),
+ pool->name(), rpool.Name, rpool.Name,pool->name());
+ ret = false;
+ }
+ } else { /* no ScratchPool used, set it to 0 */
+ pr->ScratchPoolId = 0;
+ }
+
return ret;
}
* 1 record created
*/
-int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
+int create_pool(JCR *jcr, BDB *db, POOL *pool, e_pool_op op)
{
POOL_DBR pr;
-
memset(&pr, 0, sizeof(POOL_DBR));
-
bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
if (db_get_pool_record(jcr, db, &pr)) {
/* Pool Exists */
if (op == POOL_OP_UPDATE) { /* update request */
set_pooldbr_from_poolres(&pr, pool, op);
+ set_pooldbr_references(jcr, db, &pr, pool);
db_update_pool_record(jcr, db, &pr);
}
return 0; /* exists */
}
set_pooldbr_from_poolres(&pr, pool, op);
+ set_pooldbr_references(jcr, db, &pr, pool);
if (!db_create_pool_record(jcr, db, &pr)) {
return -1; /* error */
extern DIRRES *director;
extern char *configfile;
-/*
- * Python control command
- * python restart (restarts interpreter)
- */
-static int python_cmd(UAContext *ua, const char *cmd)
+static int setbwlimit_client(UAContext *ua, CLIENT *client, char *Job, int64_t limit)
{
-#ifdef HAVE_PYTHON
- init_python_interpreter_args python_args;
+ CLIENT *old_client;
- if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
- term_python_interpreter();
+ if (!client) {
+ return 1;
+ }
- python_args.progname = director->name();
- python_args.scriptdir = director->scripts_directory;
- python_args.modulename = "DirStartUp";
- python_args.configfile = configfile;
- python_args.workingdir = director->working_directory;
- python_args.job_getattr = job_getattr;
- python_args.job_setattr = job_setattr;
+ /* Connect to File daemon */
+ old_client = ua->jcr->client;
+ ua->jcr->client = client;
+ ua->jcr->max_bandwidth = limit;
+
+ /* Try to connect for 15 seconds */
+ ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
+ client->name(), client->address, client->FDport);
+ if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
+ ua->error_msg(_("Failed to connect to Client.\n"));
+ goto bail_out;
+ }
+ Dmsg0(120, "Connected to file daemon\n");
- init_python_interpreter(&python_args);
+ if (!send_bwlimit(ua->jcr, Job)) {
+ ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
- ua->send_msg(_("Python interpreter restarted.\n"));
} else {
-#endif /* HAVE_PYTHON */
- ua->warning_msg(_("Nothing done.\n"));
-#ifdef HAVE_PYTHON
+ /* Note, we add 2000 OK that was sent by FD to us to message */
+ ua->info_msg(_("2000 OK Limiting bandwidth to %lldkb/s %s\n"),
+ limit/1024, *Job?Job:_("on running and future jobs"));
}
-#endif /* HAVE_PYTHON */
+
+ ua->jcr->file_bsock->signal(BNET_TERMINATE);
+ free_bsock(ua->jcr->file_bsock);
+ ua->jcr->max_bandwidth = 0;
+
+bail_out:
+ ua->jcr->client = old_client;
return 1;
}
+static int setbwlimit_cmd(UAContext *ua, const char *cmd)
+{
+ int action = -1;
+ CLIENT *client = NULL;
+ char Job[MAX_NAME_LENGTH];
+ *Job=0;
+ int64_t limit = -1;
+ JCR *jcr = NULL;
+ int i;
+
+ const char *lst_all[] = { "job", "jobid", "jobname", "client", NULL };
+ if (find_arg_keyword(ua, lst_all) < 0) {
+ start_prompt(ua, _("Set Bandwidth choice:\n"));
+ add_prompt(ua, _("Running Job")); /* 0 */
+ add_prompt(ua, _("Running and future Jobs for a Client")); /* 1 */
+ action = do_prompt(ua, "item", _("Choose where to limit the bandwidth"),
+ NULL, 0);
+ if (action < 0) {
+ return 1;
+ }
+ }
+
+ i = find_arg_with_value(ua, "limit");
+ if (i >= 0) {
+ limit = atoi(ua->argv[i]) * 1024LL;
+ }
+ if (limit < 0) {
+ if (!get_pint(ua, _("Enter new bandwidth limit kb/s: "))) {
+ return 1;
+ }
+ limit = ua->pint32_val * 1024LL; /* kb/s */
+ }
+
+ const char *lst[] = { "job", "jobid", "jobname", NULL };
+ if (action == 0 || find_arg_keyword(ua, lst) > 0) {
+ alist *jcrs = New(alist(10, not_owned_by_alist));
+ select_running_jobs(ua, jcrs, "limit");
+ foreach_alist(jcr, jcrs) {
+ jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
+ bstrncpy(Job, jcr->Job, sizeof(Job));
+ client = jcr->client;
+ setbwlimit_client(ua, client, Job, limit);
+ free_jcr(jcr);
+ }
+
+ } else {
+ client = get_client_resource(ua);
+ if (client) {
+ setbwlimit_client(ua, client, Job, limit);
+ }
+ }
+ return 1;
+}
/*
* Set a new address in a Client resource. We do this only
free(client->address);
}
/* MA Bug 6 remove ifdef */
- sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
+ sockaddr_to_ascii(&(ua->UA_sock->client_addr),
+ sizeof(ua->UA_sock->client_addr), buf, sizeof(buf));
client->address = bstrdup(buf);
ua->send_msg(_("Client \"%s\" address set to %s\n"),
client->name(), client->address);
return 1;
}
-
-static void do_en_disable_cmd(UAContext *ua, bool setting)
+/*
+ * Does all sorts of enable/disable commands: batch, scheduler (not implemented)
+ * job, client, schedule, storage
+ */
+static void do_enable_disable_cmd(UAContext *ua, bool setting)
{
- JOB *job;
+ JOB *job = NULL;
+ CLIENT *client = NULL;
+ SCHED *sched = NULL;
int i;
- i = find_arg_with_value(ua, NT_("job"));
- if (i < 0) {
- job = select_job_resource(ua);
- if (!job) {
+ if (find_arg(ua, NT_("batch")) > 0) {
+ ua->send_msg(_("Job Attributes Insertion %sabled\n"), setting?"en":"dis");
+ db_disable_batch_insert(setting);
+ return;
+ }
+
+ /*
+ * if (find_arg(ua, NT_("scheduler")) > 0) {
+ * ua->send_msg(_("Job Scheduler %sabled\n"), setting?"en":"dis");
+ * return;
+ * }
+ */
+
+ i = find_arg(ua, NT_("job"));
+ if (i >= 0) {
+ if (ua->argv[i]) {
+ LockRes();
+ job = GetJobResWithName(ua->argv[i]);
+ UnlockRes();
+ } else {
+ job = select_enable_disable_job_resource(ua, setting);
+ if (!job) {
+ return;
+ }
+ }
+ }
+ if (job) {
+ if (!acl_access_ok(ua, Job_ACL, job->name())) {
+ ua->error_msg(_("Unauthorized command from this console.\n"));
return;
}
- } else {
- LockRes();
- job = GetJobResWithName(ua->argv[i]);
- UnlockRes();
- }
- if (!job) {
- ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
- return;
+ job->enabled = setting;
+ ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
}
- if (!acl_access_ok(ua, Job_ACL, job->name())) {
- ua->error_msg(_("Unauthorized command from this console.\n"));
- return;
+ i = find_arg(ua, NT_("client"));
+ if (i >= 0) {
+ if (ua->argv[i]) {
+ LockRes();
+ client = GetClientResWithName(ua->argv[i]);
+ UnlockRes();
+ } else {
+ client = select_enable_disable_client_resource(ua, setting);
+ if (!client) {
+ return;
+ }
+ }
+ }
+ if (client) {
+ if (!acl_access_ok(ua, Client_ACL, client->name())) {
+ ua->error_msg(_("Unauthorized command from this console.\n"));
+ return;
+ }
+ client->enabled = setting;
+ ua->send_msg(_("Client \"%s\" %sabled\n"), client->name(), setting?"en":"dis");
+ }
+
+ i = find_arg(ua, NT_("schedule"));
+ if (i >= 0) {
+ if (ua->argv[i]) {
+ LockRes();
+ sched = (SCHED *)GetResWithName(R_SCHEDULE, ua->argv[i]);
+ UnlockRes();
+ } else {
+ sched = select_enable_disable_schedule_resource(ua, setting);
+ if (!sched) {
+ return;
+ }
+ }
+ }
+ if (sched) {
+ if (!acl_access_ok(ua, Schedule_ACL, sched->name())) {
+ ua->error_msg(_("Unauthorized command from this console.\n"));
+ return;
+ }
+ sched->enabled = setting;
+ ua->send_msg(_("Schedule \"%s\" %sabled\n"), sched->name(), setting?"en":"dis");
+ }
+
+ i = find_arg(ua, NT_("storage"));
+ if (i >= 0) {
+ do_storage_cmd(ua, setting?"enable":"disable");
}
- job->enabled = setting;
- ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
+
+ if (i < 0 && !sched && !client && !job) {
+ ua->error_msg(_("You must enter one of the following keywords: job, client, schedule, or storage.\n"));
+ }
+
return;
}
static int enable_cmd(UAContext *ua, const char *cmd)
{
- do_en_disable_cmd(ua, true);
+ do_enable_disable_cmd(ua, true);
return 1;
}
static int disable_cmd(UAContext *ua, const char *cmd)
{
- do_en_disable_cmd(ua, false);
+ do_enable_disable_cmd(ua, false);
return 1;
}
+static void do_dir_setdebug(UAContext *ua, int64_t level, int trace_flag, char *options, int64_t tags)
+{
+ debug_level = level;
+ debug_level_tags = tags;
+ set_trace(trace_flag);
+ set_debug_flags(options);
+}
-static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
+static void do_storage_setdebug(UAContext *ua, STORE *store,
+ int64_t level, int trace_flag, int hangup, int blowup,
+ char *options, char *tags)
{
BSOCK *sd;
- JCR *jcr = ua->jcr;
USTORE lstore;
-
+
lstore.store = store;
pm_strcpy(lstore.store_source, _("unknown source"));
- set_wstorage(jcr, &lstore);
+ set_wstorage(ua->jcr, &lstore);
/* Try connecting for up to 15 seconds */
ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
store->name(), store->address, store->SDport);
- if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
+ if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
ua->error_msg(_("Failed to connect to Storage daemon.\n"));
return;
}
Dmsg0(120, _("Connected to storage daemon\n"));
- sd = jcr->store_bsock;
- sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
+ sd = ua->jcr->store_bsock;
+ sd->fsend("setdebug=%ld trace=%ld hangup=%ld blowup=%ld options=%s tags=%s\n",
+ (int32_t)level, trace_flag, hangup, blowup, options, NPRTB(tags));
if (sd->recv() >= 0) {
ua->send_msg("%s", sd->msg);
}
sd->signal(BNET_TERMINATE);
- sd->close();
- jcr->store_bsock = NULL;
+ free_bsock(ua->jcr->store_bsock);
return;
}
-static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
+/*
+ * For the client, we have the following values that can be set
+ * level = debug level
+ * trace = send debug output to a file
+ * options = various options for debug or specific FD behavior
+ * hangup = how many records to send to FD before hanging up
+ * obviously this is most useful for testing restarting
+ * failed jobs.
+ * blowup = how many records to send to FD before blowing up the FD.
+ */
+static void do_client_setdebug(UAContext *ua, CLIENT *client,
+ int64_t level, int trace, int hangup, int blowup,
+ char *options, char *tags)
{
+ CLIENT *old_client;
BSOCK *fd;
/* Connect to File daemon */
+ old_client = ua->jcr->client;
ua->jcr->client = client;
/* Try to connect for 15 seconds */
ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
client->name(), client->address, client->FDport);
if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
ua->error_msg(_("Failed to connect to Client.\n"));
+ ua->jcr->client = old_client;
return;
}
Dmsg0(120, "Connected to file daemon\n");
+
fd = ua->jcr->file_bsock;
- fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
+ if (ua->jcr->FDVersion <= 10) {
+ fd->fsend("setdebug=%ld trace=%d hangup=%d\n",
+ (int32_t)level, trace, hangup);
+ } else {
+ fd->fsend("setdebug=%ld trace=%d hangup=%d blowup=%d options=%s tags=%s\n",
+ (int32_t)level, trace, hangup, blowup, options, NPRTB(tags));
+ }
if (fd->recv() >= 0) {
ua->send_msg("%s", fd->msg);
}
fd->signal(BNET_TERMINATE);
- fd->close();
- ua->jcr->file_bsock = NULL;
+ free_bsock(ua->jcr->file_bsock);
+ ua->jcr->client = old_client;
return;
}
-static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
+static void do_all_setdebug(UAContext *ua, int64_t level,
+ int trace_flag, int hangup, int blowup,
+ char *options, char *tags)
{
STORE *store, **unique_store;
CLIENT *client, **unique_client;
int i, j, found;
+ int64_t t=0;
/* Director */
- debug_level = level;
+ debug_parse_tags(tags, &t);
+ do_dir_setdebug(ua, level, trace_flag, options, t);
/* Count Storage items */
LockRes();
/* Call each unique Storage daemon */
for (j=0; j<i; j++) {
- do_storage_setdebug(ua, unique_store[j], level, trace_flag);
+ do_storage_setdebug(ua, unique_store[j], level, trace_flag,
+ hangup, blowup, options, tags);
}
free(unique_store);
/* Call each unique File daemon */
for (j=0; j<i; j++) {
- do_client_setdebug(ua, unique_client[j], level, trace_flag);
+ do_client_setdebug(ua, unique_client[j], level, trace_flag,
+ hangup, blowup, options, tags);
}
free(unique_client);
}
{
STORE *store;
CLIENT *client;
- int level;
+ int64_t level=0, tags=0;
int trace_flag = -1;
+ int hangup = -1;
+ int blowup = -1;
int i;
+ char *tags_str=NULL;
+ char options[60];
Dmsg1(120, "setdebug:%s:\n", cmd);
+ *options = 0;
+ i = find_arg_with_value(ua, "options");
+ if (i >= 0) {
+ bstrncpy(options, ua->argv[i], sizeof(options) - 1);
+ }
level = -1;
i = find_arg_with_value(ua, "level");
if (i >= 0) {
- level = atoi(ua->argv[i]);
+ level = str_to_int64(ua->argv[i]);
}
if (level < 0) {
if (!get_pint(ua, _("Enter new debug level: "))) {
level = ua->pint32_val;
}
+ /* Better to send the tag string instead of tweaking the level
+ * in case where we extend the tag or change the representation
+ */
+ i = find_arg_with_value(ua, "tags");
+ if (i > 0) {
+ tags_str = ua->argv[i];
+ if (!debug_parse_tags(tags_str, &tags)) {
+ ua->error_msg(_("Incorrect tags found on command line %s\n"), tags_str);
+ return 1;
+ }
+ }
+
/* Look for trace flag. -1 => not change */
i = find_arg_with_value(ua, "trace");
if (i >= 0) {
}
}
+ /* Look for hangup (debug only) flag. -1 => not change */
+ i = find_arg_with_value(ua, "hangup");
+ if (i >= 0) {
+ hangup = atoi(ua->argv[i]);
+ }
+
+ /* Look for blowup (debug only) flag. -1 => not change */
+ i = find_arg_with_value(ua, "blowup");
+ if (i >= 0) {
+ blowup = atoi(ua->argv[i]);
+ }
+
/* General debug? */
for (i=1; i<ua->argc; i++) {
if (strcasecmp(ua->argk[i], "all") == 0) {
- do_all_setdebug(ua, level, trace_flag);
+ do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
return 1;
}
if (strcasecmp(ua->argk[i], "dir") == 0 ||
strcasecmp(ua->argk[i], "director") == 0) {
- debug_level = level;
- set_trace(trace_flag);
+ do_dir_setdebug(ua, level, trace_flag, options, tags);
return 1;
}
if (strcasecmp(ua->argk[i], "client") == 0 ||
if (ua->argv[i]) {
client = GetClientResWithName(ua->argv[i]);
if (client) {
- do_client_setdebug(ua, client, level, trace_flag);
+ do_client_setdebug(ua, client, level, trace_flag,
+ hangup, blowup, options, tags_str);
return 1;
}
}
client = select_client_resource(ua);
if (client) {
- do_client_setdebug(ua, client, level, trace_flag);
+ do_client_setdebug(ua, client, level, trace_flag,
+ hangup, blowup, options, tags_str);
return 1;
}
}
if (ua->argv[i]) {
store = GetStoreResWithName(ua->argv[i]);
if (store) {
- do_storage_setdebug(ua, store, level, trace_flag);
+ do_storage_setdebug(ua, store, level, trace_flag,
+ hangup, blowup, options, tags_str);
return 1;
}
}
- store = get_storage_resource(ua, false/*no default*/);
+ store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
if (store) {
- do_storage_setdebug(ua, store, level, trace_flag);
+ do_storage_setdebug(ua, store, level, trace_flag,
+ hangup, blowup, options, tags_str);
return 1;
}
}
add_prompt(ua, _("All"));
switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
case 0: /* Director */
- debug_level = level;
- set_trace(trace_flag);
+ do_dir_setdebug(ua, level, trace_flag, options, tags);
break;
case 1:
- store = get_storage_resource(ua, false/*no default*/);
+ store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
if (store) {
- do_storage_setdebug(ua, store, level, trace_flag);
+ do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
+ options, tags_str);
}
break;
case 2:
client = select_client_resource(ua);
if (client) {
- do_client_setdebug(ua, client, level, trace_flag);
+ do_client_setdebug(ua, client, level, trace_flag, hangup, blowup,
+ options, tags_str);
}
break;
case 3:
- do_all_setdebug(ua, level, trace_flag);
+ do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
break;
default:
break;
if (ua->argc != 2) {
if (!get_cmd(ua, _("Turn on or off? "))) {
- return 1;
+ return 1;
}
onoff = ua->cmd;
} else {
set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
return 1;
-
}
static int var_cmd(UAContext *ua, const char *cmd)
int listing = 0;
char since[MAXSTRING];
JCR *jcr = ua->jcr;
+ int accurate=-1;
- jcr->set_JobLevel(L_FULL);
+ jcr->setJobLevel(L_FULL);
for (int i=1; i<ua->argc; i++) {
if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
strcasecmp(ua->argk[i], NT_("fd")) == 0) {
ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
return 1;
}
+ if (!acl_access_ok(ua, Client_ACL, client->name())) {
+ ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
+ return 1;
+ }
continue;
} else {
ua->error_msg(_("Client name missing.\n"));
if (ua->argv[i]) {
if (!get_level_from_name(ua->jcr, ua->argv[i])) {
ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
+ return 1;
+ }
+ continue;
+ } else {
+ ua->error_msg(_("Level value missing.\n"));
+ return 1;
+ }
+ }
+ if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
+ if (ua->argv[i]) {
+ if (!is_yesno(ua->argv[i], &accurate)) {
+ ua->error_msg(_("Invalid value for accurate. "
+ "It must be yes or no.\n"));
+ return 1;
}
continue;
} else {
- ua->error_msg(_("Level value missing.\n"));
- return 1;
+ ua->error_msg(_("Accurate value missing.\n"));
+ return 1;
}
}
}
}
jcr->job = job;
- jcr->set_JobType(JT_BACKUP);
+ jcr->setJobType(JT_BACKUP);
+ jcr->start_time = time(NULL);
init_jcr_job_record(jcr);
if (!get_or_create_client_record(jcr)) {
return 1;
}
+ /* The level string change if accurate mode is enabled */
+ if (accurate >= 0) {
+ jcr->accurate = accurate;
+ } else {
+ jcr->accurate = job->accurate;
+ }
+
+ if (!send_level_command(jcr)) {
+ goto bail_out;
+ }
+
if (!send_include_list(jcr)) {
ua->error_msg(_("Error sending include list.\n"));
goto bail_out;
goto bail_out;
}
- if (!send_level_command(jcr)) {
+ /*
+ * If the job is in accurate mode, we send the list of
+ * all files to FD.
+ */
+ Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
+ if (!send_accurate_current_files(jcr)) {
goto bail_out;
}
- bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
- while (bnet_recv(jcr->file_bsock) >= 0) {
+ jcr->file_bsock->fsend("estimate listing=%d\n", listing);
+ while (jcr->file_bsock->recv() >= 0) {
ua->send_msg("%s", jcr->file_bsock->msg);
}
bail_out:
if (jcr->file_bsock) {
- bnet_sig(jcr->file_bsock, BNET_TERMINATE);
- bnet_close(jcr->file_bsock);
- jcr->file_bsock = NULL;
+ jcr->file_bsock->signal(BNET_TERMINATE);
+ free_bsock(ua->jcr->file_bsock);
}
return 1;
}
-
/*
* print time
*/
time_t ttime = time(NULL);
struct tm tm;
(void)localtime_r(&ttime, &tm);
- strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
+ strftime(sdt, sizeof(sdt), "%a %d-%b-%Y %H:%M:%S", &tm);
ua->send_msg("%s\n", sdt);
return 1;
}
NT_("volume"),
NT_("pool"),
NT_("jobid"),
+ NT_("snapshot"),
NULL};
- if (!open_client_db(ua)) {
+ /* Deleting large jobs can take time! */
+ if (!open_new_client_db(ua)) {
return 1;
}
*ua->argk[i] = 0; /* zap keyword already visited */
}
return 1;
+ case 3:
+ delete_snapshot(ua);
+ return 1;
default:
break;
}
case 2:
delete_job(ua);
return 1;
+ case 3:
+ delete_snapshot(ua);
+ return 1;
default:
ua->warning_msg(_("Nothing done.\n"));
break;
return 1;
}
-
/*
* delete_job has been modified to parse JobID lists like the
* following:
*
* Thanks to Phil Stracchino for the above addition.
*/
-
static void delete_job(UAContext *ua)
{
- JobId_t JobId;
- char *s,*sep,*tok;
+ int JobId; /* not JobId_t because it's unsigned and not compatible with sellist */
+ char buf[256];
+ sellist sl;
int i = find_arg_with_value(ua, NT_("jobid"));
if (i >= 0) {
- if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
- s = bstrdup(ua->argv[i]);
- tok = s;
- /*
- * We could use strtok() here. But we're not going to, because:
- * (a) strtok() is deprecated, having been replaced by strsep();
- * (b) strtok() is broken in significant ways.
- * we could use strsep() instead, but it's not universally available.
- * so we grow our own using strchr().
- */
- sep = strchr(tok, ',');
- while (sep != NULL) {
- *sep = '\0';
- if (strchr(tok, '-')) {
- delete_job_id_range(ua, tok);
- } else {
- JobId = str_to_int64(tok);
- do_job_delete(ua, JobId);
- }
- tok = ++sep;
- sep = strchr(tok, ',');
- }
- /* pick up the last token */
- if (strchr(tok, '-')) {
- delete_job_id_range(ua, tok);
- } else {
- JobId = str_to_int64(tok);
- do_job_delete(ua, JobId);
- }
-
- free(s);
- } else {
- JobId = str_to_int64(ua->argv[i]);
- do_job_delete(ua, JobId);
+ if (!sl.set_string(ua->argv[i], true)) {
+ ua->warning_msg("%s", sl.get_errmsg());
+ return;
+ }
+
+ if (sl.size() > 25 && (find_arg(ua, "yes") < 0)) {
+ bsnprintf(buf, sizeof(buf),
+ _("Are you sure you want to delete %d JobIds ? (yes/no): "), sl.size());
+ if (!get_yesno(ua, buf)) {
+ return;
+ }
}
+
+ foreach_sellist(JobId, &sl) {
+ do_job_delete(ua, JobId);
+ }
+
} else if (!get_pint(ua, _("Enter JobId to delete: "))) {
return;
+
} else {
JobId = ua->int64_val;
do_job_delete(ua, JobId);
}
}
-/*
- * we call delete_job_id_range to parse range tokens and iterate over ranges
- */
-static void delete_job_id_range(UAContext *ua, char *tok)
-{
- char *tok2;
- JobId_t j,j1,j2;
-
- tok2 = strchr(tok, '-');
- *tok2 = '\0';
- tok2++;
- j1 = str_to_int64(tok);
- j2 = str_to_int64(tok2);
- for (j=j1; j<=j2; j++) {
- do_job_delete(ua, j);
- }
-}
-
/*
* do_job_delete now performs the actual delete operation atomically
*/
edit_int64(JobId, ed1);
purge_jobs_from_catalog(ua, ed1);
- ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
+ ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1);
}
/*
{
MEDIA_DBR mr;
char buf[1000];
+ db_list_ctx lst;
if (!select_media_dbr(ua, &mr)) {
return 1;
return 1;
}
}
- if (ua->pint32_val) {
- db_delete_media_record(ua->jcr, ua->db, &mr);
+ if (!ua->pint32_val) {
+ return 1;
+ }
+
+ /* If not purged, do it */
+ if (strcmp(mr.VolStatus, "Purged") != 0) {
+ if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
+ ua->error_msg(_("Can't list jobs on this volume\n"));
+ return 1;
+ }
+ if (lst.count) {
+ purge_jobs_from_catalog(ua, lst.list);
+ }
}
+
+ db_delete_media_record(ua->jcr, ua->db, &mr);
return 1;
}
int memory_cmd(UAContext *ua, const char *cmd)
{
+ garbage_collect_memory();
list_dir_status_header(ua);
sm_dump(false, true);
return 1;
}
-static void do_mount_cmd(UAContext *ua, const char *command)
+static void do_storage_cmd(UAContext *ua, const char *command)
{
USTORE store;
BSOCK *sd;
JCR *jcr = ua->jcr;
char dev_name[MAX_NAME_LENGTH];
- int drive;
- int slot = -1;
+ int drive, i;
+ int slot;
if (!open_client_db(ua)) {
return;
pm_strcpy(store.store_source, _("unknown source"));
set_wstorage(jcr, &store);
drive = get_storage_drive(ua, store.store);
- if (strcmp(command, "mount") == 0) {
- slot = get_storage_slot(ua, store.store);
+ slot = get_storage_slot(ua, store.store);
+
+ /* Users may set a device name directly on the command line */
+ if ((i = find_arg_with_value(ua, "device")) > 0) {
+ POOLMEM *errmsg = get_pool_memory(PM_NAME);
+ if (!is_name_valid(ua->argv[i], &errmsg)) {
+ ua->error_msg(_("Invalid device name. %s"), errmsg);
+ free_pool_memory(errmsg);
+ return;
+ }
+ free_pool_memory(errmsg);
+ bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
+
+ } else { /* We take the default device name */
+ bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
}
Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
store.store->media_type, store.store->dev_name(), drive);
+ Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
ua->error_msg(_("Failed to connect to Storage daemon.\n"));
return;
}
sd = jcr->store_bsock;
- bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
bash_spaces(dev_name);
- if (slot > 0) {
- bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
- } else {
- bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
- }
- while (bnet_recv(sd) >= 0) {
+ sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
+ while (sd->recv() >= 0) {
ua->send_msg("%s", sd->msg);
}
- bnet_sig(sd, BNET_TERMINATE);
- bnet_close(sd);
- jcr->store_bsock = NULL;
+ sd->signal(BNET_TERMINATE);
+ free_bsock(ua->jcr->store_bsock);
}
/*
*/
static int mount_cmd(UAContext *ua, const char *cmd)
{
- do_mount_cmd(ua, "mount"); /* mount */
+ do_storage_cmd(ua, "mount") ; /* mount */
return 1;
}
*/
static int unmount_cmd(UAContext *ua, const char *cmd)
{
- do_mount_cmd(ua, "unmount"); /* unmount */
+ do_storage_cmd(ua, "unmount"); /* unmount */
return 1;
}
*/
static int release_cmd(UAContext *ua, const char *cmd)
{
- do_mount_cmd(ua, "release"); /* release */
+ do_storage_cmd(ua, "release"); /* release */
return 1;
}
for (bool running=true; running; ) {
running = false;
foreach_jcr(jcr) {
- if (jcr->JobId != 0) {
+ if (!jcr->is_internal_job()) {
running = true;
break;
}
return 1;
}
- i = find_arg_with_value(ua, NT_("timeout"));
+ i = find_arg_with_value(ua, NT_("timeout"));
if (i > 0 && ua->argv[i]) {
stop_time = time(NULL) + str_to_int64(ua->argv[i]);
}
} else if (strcasecmp(ua->argk[i], "mount") == 0) {
for (bool waiting=false; !waiting; ) {
foreach_jcr(jcr) {
- if (jcr->JobId != 0 &&
+ if (!jcr->is_internal_job() &&
(jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
waiting = true;
break;
"SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
- db_sql_query(ua->db, buf,
- status_handler, (void *)&jobstatus);
+ db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
switch (jobstatus) {
case JS_Error:
status = 1 ; /* Warning */
break;
+ case JS_Incomplete:
case JS_FatalError:
case JS_ErrorTerminated:
case JS_Canceled:
status = 2 ; /* Critical */
break;
+ case JS_Warnings:
case JS_Terminated:
status = 0 ; /* Ok */
break;
}
ua->send_msg("JobId=%i\n", jobid) ;
- ua->send_msg("JobStatus=%s (%c)\n",
- job_status_to_str(jobstatus),
- jobstatus) ;
+ ua->send_msg("JobStatus=%s (%c)\n",
+ job_status_to_str(jobstatus, 0),
+ jobstatus) ;
if (ua->gui || ua->api) {
ua->send_msg("ExitStatus=%i\n", status) ;
static int help_cmd(UAContext *ua, const char *cmd)
{
- unsigned int i;
-
- ua->send_msg(_(" Command Description\n ======= ===========\n"));
+ int i;
+ ua->send_msg(_(" Command Description\n ======= ===========\n"));
for (i=0; i<comsize; i++) {
- ua->send_msg(_(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
+ if (ua->argc == 2) {
+ if (!strcasecmp(ua->argk[1], commands[i].key)) {
+ ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
+ commands[i].help, commands[i].usage);
+ break;
+ }
+ } else {
+ ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
+ }
+ }
+ if (i == comsize && ua->argc == 2) {
+ ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
}
ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
return 1;
int qhelp_cmd(UAContext *ua, const char *cmd)
{
- unsigned int i;
-
+ int i,j;
+ /* Want to display only commands */
+ j = find_arg(ua, NT_("all"));
+ if (j >= 0) {
+ for (i=0; i<comsize; i++) {
+ ua->send_msg("%s\n", commands[i].key);
+ }
+ return 1;
+ }
+ /* Want to display a specific help section */
+ j = find_arg_with_value(ua, NT_("item"));
+ if (j >= 0 && ua->argk[j]) {
+ for (i=0; i<comsize; i++) {
+ if (bstrcmp(commands[i].key, ua->argv[j])) {
+ ua->send_msg("%s\n", commands[i].usage);
+ break;
+ }
+ }
+ return 1;
+ }
+ /* Want to display everything */
for (i=0; i<comsize; i++) {
- ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
+ ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
}
return 1;
}
-#if 1
+#if 1
static int version_cmd(UAContext *ua, const char *cmd)
{
ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
- NPRTB(director->verid), HOST_OS, DISTNAME, DISTVER);
+ HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
return 1;
}
#else
/*
- * Test code -- turned on only for debug testing
+ * Test code -- turned on only for debug testing
*/
static int version_cmd(UAContext *ua, const char *cmd)
{
}
#endif
-/*
+/*
+ * This call uses open_client_db() and force a
+ * new dedicated connection to the catalog
+ */
+bool open_new_client_db(UAContext *ua)
+{
+ bool ret;
+
+ /* Force a new dedicated connection */
+ ua->force_mult_db_connections = true;
+ ret = open_client_db(ua);
+ ua->force_mult_db_connections = false;
+
+ return ret;
+}
+
+/*
* This call explicitly checks for a catalog=xxx and
* if given, opens that catalog. It also checks for
- * client=xxx and if found, opens the catalog
- * corresponding to that client. If we still don't
+ * client=xxx and if found, opens the catalog
+ * corresponding to that client. If we still don't
* have a catalog, look for a Job keyword and get the
* catalog from its client record.
*/
*/
bool open_db(UAContext *ua)
{
+ bool mult_db_conn;
+
+ /* The force_mult_db_connections is telling us if we modify the
+ * private or the shared link
+ */
+ if (ua->force_mult_db_connections) {
+ ua->db = ua->private_db;
+
+ } else {
+ ua->db = ua->shared_db;
+ }
+
if (ua->db) {
return true;
}
+
if (!ua->catalog) {
ua->catalog = get_catalog_resource(ua);
if (!ua->catalog) {
}
}
+ /* Some modules like bvfs need their own catalog connection */
+ mult_db_conn = ua->catalog->mult_db_connections;
+ if (ua->force_mult_db_connections) {
+ mult_db_conn = true;
+ }
+
ua->jcr->catalog = ua->catalog;
Dmsg0(100, "UA Open database\n");
- ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
+ ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
+ ua->catalog->db_name,
ua->catalog->db_user,
ua->catalog->db_password, ua->catalog->db_address,
ua->catalog->db_port, ua->catalog->db_socket,
- ua->catalog->mult_db_connections);
+ ua->catalog->db_ssl_key, ua->catalog->db_ssl_cert,
+ ua->catalog->db_ssl_ca, ua->catalog->db_ssl_capath,
+ ua->catalog->db_ssl_cipher,
+ mult_db_conn, ua->catalog->disable_batch_insert);
if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
ua->error_msg(_("Could not open catalog database \"%s\".\n"),
ua->catalog->db_name);
return false;
}
ua->jcr->db = ua->db;
+
+ /* Depending on the type of connection, we set the right variable */
+ if (ua->force_mult_db_connections) {
+ ua->private_db = ua->db;
+
+ } else {
+ ua->shared_db = ua->db;
+ }
+
if (!ua->api) {
- ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
+ ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
}
Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
return true;
void close_db(UAContext *ua)
{
- if (ua->db) {
- db_close_database(ua->jcr, ua->db);
- ua->db = NULL;
- if (ua->jcr) {
- ua->jcr->db = NULL;
- }
+ if (ua->jcr) {
+ ua->jcr->db = NULL;
}
+
+ if (ua->shared_db) {
+ db_close_database(ua->jcr, ua->shared_db);
+ ua->shared_db = NULL;
+ }
+
+ if (ua->private_db) {
+ db_close_database(ua->jcr, ua->private_db);
+ ua->private_db = NULL;
+ }
+
+ ua->db = NULL;
}