X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;ds=sidebyside;f=bacula%2Fsrc%2Fdird%2Fua_cmds.c;h=d2e007b320126351d1d72a6215b5a15fce33df65;hb=302b2d5b5d44060a790bcb826f6396135acad13b;hp=9ac584c70c820a4a781ed1ff4e753a1b730320eb;hpb=2eea5f97c21d91f83edb972e50088bca6ffd2666;p=bacula%2Fbacula diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index 9ac584c70c..d2e007b320 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -1,29 +1,21 @@ /* - Bacula® - The Network Backup Solution - - Copyright (C) 2000-2011 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 three of the GNU Affero 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 Affero 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. */ /* * @@ -32,23 +24,10 @@ * Kern Sibbald, September MM * */ - + #include "bacula.h" #include "dird.h" -#ifdef HAVE_PYTHON - -#undef _POSIX_C_SOURCE -#include - -#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 */ @@ -63,7 +42,8 @@ extern int list_cmd(UAContext *ua, const char *cmd); 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); @@ -85,10 +65,10 @@ static int estimate_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); @@ -99,17 +79,17 @@ static int version_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 bool 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 { +struct cmdstruct { const char *key; /* command */ int (*func)(UAContext *ua, const char *cmd); /* handler */ const char *help; /* main purpose */ @@ -120,88 +100,117 @@ static struct cmdstruct commands[] = { /* C { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool= storage= 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= job= ujobid="), false}, + { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid= | job= | ujobid= | inactive client= storage= | all"), false}, { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool="), false}, - { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume= pool= jobid="), true}, - { NT_("disable"), disable_cmd, _("Disable a job"), NT_("job="), true}, - { NT_("enable"), enable_cmd, _("Enable a job"), NT_("job="), true}, - { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"), + { NT_("delete"), delete_cmd, _("Delete volume, pool or job"), NT_("volume= | pool= | jobid= | snapshot"), true}, + { NT_("disable"), disable_cmd, _("Disable a job, attributes batch process"), NT_("job= | batch"), true}, + { NT_("enable"), enable_cmd, _("Enable a job, attributes batch process"), NT_("job= | batch"), true}, + { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"), NT_("fileset= client= level= accurate= 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_("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 python quit query\n\trestore relabel release reload run status" - "\n\tsetdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"), false}, + "\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= volume= pool= slot= barcodes"), false}, - { NT_("list"), list_cmd, _("List objects from catalog"), - NT_("pools | jobs | jobtotals | volume | media | files jobid= | copies jobid="), true}, + { NT_("list"), list_cmd, _("List objects from catalog"), + NT_("jobs [client=] [jobid=] [ujobid=] [job=] [joberrors] [jobstatus=] [limit=]|\n" + "\tjobtotals | pools | volume | media | files jobid= | copies jobid= |\n" + "\tjoblog jobid= | pluginrestoreconf jobid= restoreobjectid= | snapshot"), false}, { NT_("llist"), llist_cmd, _("Full or long list like list command"), - NT_("pools | jobs | jobtotals | media | files jobid= | copies jobid="), true}, + NT_("jobs [client=] [jobid=] [ujobid=] [job=] [joberrors] [jobstatus=] [limit=]|\n" + "\tjobtotals | pools | volume | media | files jobid= | copies jobid= |\n" + "\tjoblog jobid= | pluginrestoreconf jobid= restoreobjectid= | 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= slot= drive= [ jobid= | job= ]"), false}, + { NT_("mount"), mount_cmd, _("Mount storage"), + NT_("storage= slot= drive= [ device= ] [ jobid= | job= ]"), false}, - { NT_("prune"), prunecmd, _("Prune expired records from catalog"), - NT_("files | jobs | pool= | client= | volume= "), true}, + { NT_("prune"), prunecmd, _("Prune expired records from catalog"), + NT_("files | jobs | pool= | snapshot [client=] | client= | [ expired ] volume= "), true}, - { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("files jobs volume= [action= devicetype= pool= allpools storage= drive=]"), true}, - { NT_("python"), python_cmd, _("Python control commands"), NT_(""), false}, + { NT_("purge"), purge_cmd, _("Purge records from catalog"), NT_("files jobs volume= [mediatype= pool= allpools storage= drive=]"), true}, { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false}, { NT_("query"), querycmd, _("Query catalog"), NT_(""), false}, - { NT_("restore"), restore_cmd, _("Restore files"), + { NT_("restore"), restore_cmd, _("Restore files"), NT_("where= client= storage= bootstrap= " - "restore_job=" - "\n\tcomment= jobid= done select all"), false}, + "restorejob=" + "\n\tcomment= jobid= copies done select all"), false}, - { NT_("relabel"), relabel_cmd, _("Relabel a tape"), + { NT_("relabel"), relabel_cmd, _("Relabel a tape"), NT_("storage= oldvolume=\n\tvolume= pool="), false}, - { NT_("release"), release_cmd, _("Release storage"), NT_("storage="), false}, + { NT_("release"), release_cmd, _("Release storage"), NT_("storage= [ device= ] "), false}, { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true}, - { NT_("run"), run_cmd, _("Run a job"), + { NT_("run"), run_cmd, _("Run a job"), NT_("job= client=\n\tfileset= level=\n\tstorage=" - "where=\n\twhen=\n\tcomment= yes"), false}, + " where=\n\twhen= pool=\n\t" + " nextpool= comment= accurate= spooldata= yes"), false}, + + { NT_("restart"), restart_cmd, _("Restart a job"), + NT_("incomplete job= client=\n\tfileset= level=\n\tstorage=" + "when=\n\tcomment= spooldata= jobid="), false}, - { NT_("status"), status_cmd, _("Report status"), - NT_("all | dir= | director | client= | storage= slots | days=nnn"), true}, + { NT_("resume"), restart_cmd, _("Resume a job"), + NT_("incomplete job= client=\n\tfileset= level=\n\tstorage=" + "when=\n\tcomment= spooldata= jobid="), false}, - { NT_("setdebug"), setdebug_cmd, _("Sets debug level"), - NT_("level= trace=0/1 client= | dir | storage= | all"), true}, + { NT_("status"), status_cmd, _("Report status"), + NT_("all | dir= | director | client= |\n" + "\tstorage= slots |\n" + "\tschedule [job=] [days=] [limit=]\n" + "\t\t[time=]"), true}, + + { NT_("stop"), cancel_cmd, _("Stop a job"), NT_("jobid= job= ujobid= all"), false}, + { NT_("setdebug"), setdebug_cmd, _("Sets debug level"), + NT_("level= tags= trace=0/1 options=<0tTc> tags= | client= | dir | storage= | all"), true}, + + { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"), + NT_("limit= client= jobid= job= ujobid="), true}, + + { NT_("snapshot"), snapshot_cmd, _("Handle snapshots"), + NT_("[client= | job= | 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= | pool= | fileset= schedule= | client= | disabled | all"), true}, + { NT_("show"), show_cmd, _("Show resource records"), + NT_("job= | pool= | fileset= | schedule= | client= | storage= | 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_("unmount"), unmount_cmd, _("Unmount storage"), + { NT_("truncate"), truncate_cmd, _("Truncate one or more Volumes"), NT_("volume= [mediatype= pool= allpools storage= drive=]"), true}, + { NT_("unmount"), unmount_cmd, _("Unmount storage"), NT_("storage= [ drive= ] | jobid= | job="), false}, - { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"), - NT_("storage= [ drive= ] | jobid= | job="), false}, + { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"), + NT_("storage= [ drive= ] [ device= ]| jobid= | job="), false}, - { NT_("update"), update_cmd, _("Update volume, pool or stats"), - NT_("stats\n\tpool=\n\tslots storage= scan" + { NT_("update"), update_cmd, _("Update volume, pool or stats"), + NT_("stats\n\tsnapshot\n\tpool=\n\tslots storage= scan" "\n\tvolume= volstatus= volretention=" "\n\t pool= recycle= slot=\n\t inchanger=" "\n\t maxvolbytes= maxvolfiles= maxvoljobs=" - "\n\t enable= recyclepool= actiononpurge="),true}, + "\n\t enabled= recyclepool= actiononpurge=" + "\n\t allfrompool= fromallpools frompool"),true}, { NT_("use"), use_cmd, _("Use catalog xxx"), NT_("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_("wait"), wait_cmd, _("Wait until no jobs are running"), NT_("jobname= | jobid= | ujobid="), 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 */ @@ -219,13 +228,16 @@ bool do_a_command(UAContext *ua) 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; iargk[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)) { @@ -239,7 +251,7 @@ bool do_a_command(UAContext *ua) if (ua->api) user->signal(BNET_CMD_BEGIN); ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */ if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED); - found = true; + found = (user && user->is_stop()) ? false : true; break; } } @@ -294,7 +306,6 @@ static int add_cmd(UAContext *ua, const char *cmd) } memset(&pr, 0, sizeof(pr)); - memset(&mr, 0, sizeof(mr)); if (!get_pool_dbr(ua, &pr)) { return 1; @@ -398,13 +409,16 @@ static int add_cmd(UAContext *ua, const char *cmd) 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; +// } } pr.NumVols += num; Dmsg0(200, "Update pool record.\n"); @@ -440,127 +454,29 @@ int automount_cmd(UAContext *ua, const char *cmd) 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; iargc; i++) { - if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) { - uint32_t JobId; - JobId = str_to_int64(ua->argv[i]); - if (!JobId) { - break; - } - 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); - - 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; - } + 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)); - 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); + nb = select_running_jobs(ua, jcrs, commands[ua->cmd_index].key); - 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; } @@ -609,10 +525,17 @@ void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op) } /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */ -int update_pool_references(JCR *jcr, B_DB *db, POOL *pool) +int update_pool_references(JCR *jcr, BDB *db, POOL *pool) { POOL_DBR pr; + 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; } @@ -620,6 +543,7 @@ int update_pool_references(JCR *jcr, B_DB *db, POOL *pool) 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 */ } @@ -630,16 +554,17 @@ int update_pool_references(JCR *jcr, B_DB *db, POOL *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 and POOL_DBR.ScratchPoolId from Pool resource +/* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource * works with set_pooldbr_from_poolres */ -bool set_pooldbr_references(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; @@ -678,7 +603,7 @@ bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool) } else { /* no ScratchPool used, set it to 0 */ pr->ScratchPoolId = 0; } - + return ret; } @@ -691,12 +616,10 @@ bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool) * 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)) { @@ -758,35 +681,97 @@ static int create_cmd(UAContext *ua, const char *cmd) 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; + } + + /* Connect to File daemon */ + old_client = ua->jcr->client; + ua->jcr->client = client; + ua->jcr->max_bandwidth = limit; - 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; + /* 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")); + } + + 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); + } } -#endif /* HAVE_PYTHON */ return 1; } @@ -814,7 +799,8 @@ static int setip_cmd(UAContext *ua, const char *cmd) 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); @@ -823,74 +809,154 @@ get_out: 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_enable_disable_job_resource(ua, setting); - 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"); } - job->enabled = setting; - ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis"); + + i = find_arg(ua, NT_("storage")); + if (i >= 0) { + do_storage_cmd(ua, setting?"enable":"disable"); + } + + 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_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag) +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, + 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; } @@ -898,46 +964,63 @@ static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trac * For the client, we have the following values that can be set * level = debug level * trace = send debug output to a file - * hangup = how many records to send to SD before hanging up + * 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, - int level, int trace, int hangup) +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 hangup=%d\n", level, trace, hangup); + 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, int hangup) +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(); @@ -969,7 +1052,8 @@ static void do_all_setdebug(UAContext *ua, int level, int trace_flag, int hangup /* Call each unique Storage daemon */ for (j=0; j= 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: "))) { @@ -1034,6 +1127,18 @@ static int setdebug_cmd(UAContext *ua, const char *cmd) 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) { @@ -1043,23 +1148,27 @@ static int setdebug_cmd(UAContext *ua, const char *cmd) } } - /* Look for hangup (debug only)flag. -1 => not change */ + /* 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; iargc; i++) { if (strcasecmp(ua->argk[i], "all") == 0) { - do_all_setdebug(ua, level, trace_flag, hangup); + 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 || @@ -1068,13 +1177,15 @@ static int setdebug_cmd(UAContext *ua, const char *cmd) if (ua->argv[i]) { client = GetClientResWithName(ua->argv[i]); if (client) { - do_client_setdebug(ua, client, level, trace_flag, hangup); + 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, hangup); + do_client_setdebug(ua, client, level, trace_flag, + hangup, blowup, options, tags_str); return 1; } } @@ -1086,13 +1197,15 @@ static int setdebug_cmd(UAContext *ua, const char *cmd) 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; } } @@ -1108,23 +1221,24 @@ static int setdebug_cmd(UAContext *ua, const char *cmd) 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, hangup); + do_client_setdebug(ua, client, level, trace_flag, hangup, blowup, + options, tags_str); } break; case 3: - do_all_setdebug(ua, level, trace_flag, hangup); + do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str); break; default: break; @@ -1141,7 +1255,7 @@ static int trace_cmd(UAContext *ua, const char *cmd) if (ua->argc != 2) { if (!get_cmd(ua, _("Turn on or off? "))) { - return 1; + return 1; } onoff = ua->cmd; } else { @@ -1150,7 +1264,6 @@ static int trace_cmd(UAContext *ua, const char *cmd) set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true); return 1; - } static int var_cmd(UAContext *ua, const char *cmd) @@ -1247,17 +1360,25 @@ static int estimate_cmd(UAContext *ua, const char *cmd) 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; + ua->error_msg(_("Level value missing.\n")); + return 1; } } if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) { - if (!is_yesno(ua->argv[i], &accurate)) { - ua->error_msg(_("Invalid value for accurate. " - "It must be yes or no.\n")); + 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(_("Accurate value missing.\n")); + return 1; } } } @@ -1317,16 +1438,6 @@ static int estimate_cmd(UAContext *ua, const char *cmd) return 1; } - if (!send_include_list(jcr)) { - ua->error_msg(_("Error sending include list.\n")); - goto bail_out; - } - - if (!send_exclude_list(jcr)) { - ua->error_msg(_("Error sending exclude list.\n")); - goto bail_out; - } - /* The level string change if accurate mode is enabled */ if (accurate >= 0) { jcr->accurate = accurate; @@ -1338,6 +1449,16 @@ static int estimate_cmd(UAContext *ua, const char *cmd) goto bail_out; } + if (!send_include_list(jcr)) { + ua->error_msg(_("Error sending include list.\n")); + goto bail_out; + } + + if (!send_exclude_list(jcr)) { + ua->error_msg(_("Error sending exclude list.\n")); + goto bail_out; + } + /* * If the job is in accurate mode, we send the list of * all files to FD. @@ -1355,13 +1476,11 @@ static int estimate_cmd(UAContext *ua, const char *cmd) bail_out: if (jcr->file_bsock) { jcr->file_bsock->signal(BNET_TERMINATE); - jcr->file_bsock->close(); - jcr->file_bsock = NULL; + free_bsock(ua->jcr->file_bsock); } return 1; } - /* * print time */ @@ -1371,7 +1490,7 @@ static int time_cmd(UAContext *ua, const char *cmd) 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; } @@ -1400,9 +1519,11 @@ static int delete_cmd(UAContext *ua, const char *cmd) 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; } @@ -1420,6 +1541,9 @@ static int delete_cmd(UAContext *ua, const char *cmd) *ua->argk[i] = 0; /* zap keyword already visited */ } return 1; + case 3: + delete_snapshot(ua); + return 1; default: break; } @@ -1438,6 +1562,9 @@ static int delete_cmd(UAContext *ua, const char *cmd) case 2: delete_job(ua); return 1; + case 3: + delete_snapshot(ua); + return 1; default: ua->warning_msg(_("Nothing done.\n")); break; @@ -1445,7 +1572,6 @@ static int delete_cmd(UAContext *ua, const char *cmd) return 1; } - /* * delete_job has been modified to parse JobID lists like the * following: @@ -1453,75 +1579,40 @@ static int delete_cmd(UAContext *ua, const char *cmd) * * 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 (!delete_job_id_range(ua, tok)) { - JobId = str_to_int64(tok); - do_job_delete(ua, JobId); - } - tok = ++sep; - sep = strchr(tok, ','); - } - /* pick up the last token */ - if (!delete_job_id_range(ua, tok)) { - 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 bool delete_job_id_range(UAContext *ua, char *tok) -{ - char *tok2; - JobId_t j,j1,j2; - - tok2 = strchr(tok, '-'); - if (!tok2) { - return false; - } - *tok2 = '\0'; - tok2++; - j1 = str_to_int64(tok); - j2 = str_to_int64(tok2); - for (j=j1; j<=j2; j++) { - do_job_delete(ua, j); - } - return true; -} - /* * do_job_delete now performs the actual delete operation atomically */ @@ -1531,7 +1622,7 @@ static void do_job_delete(UAContext *ua, JobId_t JobId) 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); } /* @@ -1610,14 +1701,14 @@ int memory_cmd(UAContext *ua, const char *cmd) 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; @@ -1631,31 +1722,39 @@ static void do_mount_cmd(UAContext *ua, const char *command) 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) { - sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot); - } else { - sd->fsend("%s %s drive=%d", command, dev_name, drive); - } + sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot); while (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); } /* @@ -1663,7 +1762,7 @@ static void do_mount_cmd(UAContext *ua, const char *command) */ static int mount_cmd(UAContext *ua, const char *cmd) { - do_mount_cmd(ua, "mount"); /* mount */ + do_storage_cmd(ua, "mount") ; /* mount */ return 1; } @@ -1673,7 +1772,7 @@ static int mount_cmd(UAContext *ua, const char *cmd) */ static int unmount_cmd(UAContext *ua, const char *cmd) { - do_mount_cmd(ua, "unmount"); /* unmount */ + do_storage_cmd(ua, "unmount"); /* unmount */ return 1; } @@ -1683,7 +1782,7 @@ static int unmount_cmd(UAContext *ua, const char *cmd) */ static int release_cmd(UAContext *ua, const char *cmd) { - do_mount_cmd(ua, "release"); /* release */ + do_storage_cmd(ua, "release"); /* release */ return 1; } @@ -1750,7 +1849,7 @@ int wait_cmd(UAContext *ua, const char *cmd) for (bool running=true; running; ) { running = false; foreach_jcr(jcr) { - if (jcr->JobId != 0) { + if (!jcr->is_internal_job()) { running = true; break; } @@ -1764,7 +1863,7 @@ int wait_cmd(UAContext *ua, const char *cmd) 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]); } @@ -1810,7 +1909,7 @@ int wait_cmd(UAContext *ua, const char *cmd) } 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; @@ -1867,14 +1966,14 @@ int wait_cmd(UAContext *ua, const char *cmd) "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: @@ -1892,9 +1991,9 @@ int wait_cmd(UAContext *ua, const char *cmd) } 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) ; @@ -1911,7 +2010,7 @@ static int help_cmd(UAContext *ua, const char *cmd) for (i=0; iargc == 2) { if (!strcasecmp(ua->argk[1], commands[i].key)) { - ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key, + ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key, commands[i].help, commands[i].usage); break; } @@ -1955,7 +2054,7 @@ int qhelp_cmd(UAContext *ua, const char *cmd) 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, @@ -1964,7 +2063,7 @@ static int version_cmd(UAContext *ua, const char *cmd) } #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) { @@ -1982,27 +2081,27 @@ 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 */ - close_db(ua); 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. */ @@ -2085,9 +2184,20 @@ 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) { @@ -2105,11 +2215,15 @@ bool open_db(UAContext *ua) ua->jcr->catalog = ua->catalog; Dmsg0(100, "UA Open database\n"); - ua->db = db_init_database(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, - mult_db_conn, ua->catalog->disable_batch_insert); + 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); @@ -2120,8 +2234,17 @@ bool open_db(UAContext *ua) 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; @@ -2129,11 +2252,19 @@ bool open_db(UAContext *ua) 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; }