2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Commands
32 * Kern Sibbald, September MM
40 /* Imported subroutines */
42 /* Imported variables */
43 extern jobq_t job_queue; /* job queue */
46 /* Imported functions */
47 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
48 extern int gui_cmd(UAContext *ua, const char *cmd);
49 extern int label_cmd(UAContext *ua, const char *cmd);
50 extern int list_cmd(UAContext *ua, const char *cmd);
51 extern int llist_cmd(UAContext *ua, const char *cmd);
52 extern int messagescmd(UAContext *ua, const char *cmd);
53 extern int prunecmd(UAContext *ua, const char *cmd);
54 extern int purgecmd(UAContext *ua, const char *cmd);
55 extern int querycmd(UAContext *ua, const char *cmd);
56 extern int relabel_cmd(UAContext *ua, const char *cmd);
57 extern int restore_cmd(UAContext *ua, const char *cmd);
58 extern int retentioncmd(UAContext *ua, const char *cmd);
59 extern int show_cmd(UAContext *ua, const char *cmd);
60 extern int sqlquerycmd(UAContext *ua, const char *cmd);
61 extern int status_cmd(UAContext *ua, const char *cmd);
62 extern int update_cmd(UAContext *ua, const char *cmd);
64 /* Forward referenced functions */
65 static int add_cmd(UAContext *ua, const char *cmd);
66 static int automount_cmd(UAContext *ua, const char *cmd);
67 static int cancel_cmd(UAContext *ua, const char *cmd);
68 static int create_cmd(UAContext *ua, const char *cmd);
69 static int delete_cmd(UAContext *ua, const char *cmd);
70 static int disable_cmd(UAContext *ua, const char *cmd);
71 static int enable_cmd(UAContext *ua, const char *cmd);
72 static int estimate_cmd(UAContext *ua, const char *cmd);
73 static int help_cmd(UAContext *ua, const char *cmd);
74 static int memory_cmd(UAContext *ua, const char *cmd);
75 static int mount_cmd(UAContext *ua, const char *cmd);
76 static int python_cmd(UAContext *ua, const char *cmd);
77 static int release_cmd(UAContext *ua, const char *cmd);
78 static int reload_cmd(UAContext *ua, const char *cmd);
79 static int setdebug_cmd(UAContext *ua, const char *cmd);
80 static int setip_cmd(UAContext *ua, const char *cmd);
81 static int time_cmd(UAContext *ua, const char *cmd);
82 static int trace_cmd(UAContext *ua, const char *cmd);
83 static int unmount_cmd(UAContext *ua, const char *cmd);
84 static int use_cmd(UAContext *ua, const char *cmd);
85 static int var_cmd(UAContext *ua, const char *cmd);
86 static int version_cmd(UAContext *ua, const char *cmd);
87 static int wait_cmd(UAContext *ua, const char *cmd);
89 static void do_job_delete(UAContext *ua, JobId_t JobId);
90 static void delete_job_id_range(UAContext *ua, char *tok);
91 static int delete_volume(UAContext *ua);
92 static int delete_pool(UAContext *ua);
93 static void delete_job(UAContext *ua);
95 int qhelp_cmd(UAContext *ua, const char *cmd);
96 int quit_cmd(UAContext *ua, const char *cmd);
99 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
100 static struct cmdstruct commands[] = {
101 { NT_("add"), add_cmd, _("add media to a pool")},
102 { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
103 { NT_("automount"), automount_cmd, _("automount [on|off] -- after label")},
104 { NT_("cancel"), cancel_cmd, _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
105 { NT_("create"), create_cmd, _("create DB Pool from resource")},
106 { NT_("delete"), delete_cmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
107 { NT_("disable"), disable_cmd, _("disable <job=name> -- disable a job")},
108 { NT_("enable"), enable_cmd, _("enable <job=name> -- enable a job")},
109 { NT_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")},
110 { NT_("exit"), quit_cmd, _("exit = quit")},
111 { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode")},
112 { NT_("help"), help_cmd, _("print this command")},
113 { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
114 { NT_("label"), label_cmd, _("label a tape")},
115 { NT_("llist"), llist_cmd, _("full or long list like list command")},
116 { NT_("messages"), messagescmd, _("messages")},
117 { NT_("memory"), memory_cmd, _("print current memory usage")},
118 { NT_("mount"), mount_cmd, _("mount <storage-name>")},
119 { NT_("prune"), prunecmd, _("prune expired records from catalog")},
120 { NT_("purge"), purgecmd, _("purge records from catalog")},
121 { NT_("python"), python_cmd, _("python control commands")},
122 { NT_("quit"), quit_cmd, _("quit")},
123 { NT_("query"), querycmd, _("query catalog")},
124 { NT_("restore"), restore_cmd, _("restore files")},
125 { NT_("relabel"), relabel_cmd, _("relabel a tape")},
126 { NT_("release"), release_cmd, _("release <storage-name>")},
127 { NT_("reload"), reload_cmd, _("reload conf file")},
128 { NT_("run"), run_cmd, _("run <job-name>")},
129 { NT_("status"), status_cmd, _("status [storage | client]=<name>")},
130 { NT_("setdebug"), setdebug_cmd, _("sets debug level")},
131 { NT_("setip"), setip_cmd, _("sets new client address -- if authorized")},
132 { NT_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")},
133 { NT_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")},
134 { NT_("time"), time_cmd, _("print current time")},
135 { NT_("trace"), trace_cmd, _("turn on/off trace to file")},
136 { NT_("unmount"), unmount_cmd, _("unmount <storage-name>")},
137 { NT_("umount"), unmount_cmd, _("umount <storage-name> for old-time Unix guys")},
138 { NT_("update"), update_cmd, _("update Volume, Pool or slots")},
139 { NT_("use"), use_cmd, _("use catalog xxx")},
140 { NT_("var"), var_cmd, _("does variable expansion")},
141 { NT_("version"), version_cmd, _("print Director version")},
142 { NT_("wait"), wait_cmd, _("wait until no jobs are running [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>]")},
144 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
147 * Execute a command from the UA
149 bool do_a_command(UAContext *ua)
155 BSOCK *user = ua->UA_sock;
158 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
163 while (ua->jcr->wstorage->size()) {
164 ua->jcr->wstorage->remove(0);
167 len = strlen(ua->argk[0]);
168 for (i=0; i<comsize; i++) { /* search for command */
169 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
170 /* Check if command permitted, but "quit" is always OK */
171 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
172 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
175 if (ua->api) user->signal(BNET_CMD_BEGIN);
176 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
182 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
185 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
190 * This is a common routine used to stuff the Pool DB record defaults
191 * into the Media DB record just before creating a media (Volume)
194 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
196 mr->PoolId = pr->PoolId;
197 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
198 mr->Recycle = pr->Recycle;
199 mr->VolRetention = pr->VolRetention;
200 mr->VolUseDuration = pr->VolUseDuration;
201 mr->RecyclePoolId = pr->RecyclePoolId;
202 mr->MaxVolJobs = pr->MaxVolJobs;
203 mr->MaxVolFiles = pr->MaxVolFiles;
204 mr->MaxVolBytes = pr->MaxVolBytes;
205 mr->LabelType = pr->LabelType;
211 * Add Volumes to an existing Pool
213 static int add_cmd(UAContext *ua, const char *cmd)
217 int num, i, max, startnum;
219 char name[MAX_NAME_LENGTH];
221 int Slot = 0, InChanger = 0;
224 "You probably don't want to be using this command since it\n"
225 "creates database records without labeling the Volumes.\n"
226 "You probably want to use the \"label\" command.\n\n"));
228 if (!open_client_db(ua)) {
232 memset(&pr, 0, sizeof(pr));
233 memset(&mr, 0, sizeof(mr));
235 if (!get_pool_dbr(ua, &pr)) {
239 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
240 pr.MaxVols, pr.PoolType);
242 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
243 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
244 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
247 pr.MaxVols = ua->pint32_val;
251 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
252 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
253 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
257 if (pr.MaxVols == 0) {
260 max = pr.MaxVols - pr.NumVols;
264 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
265 if (!get_pint(ua, buf)) {
268 num = ua->pint32_val;
269 if (num < 0 || num > max) {
270 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
278 if (!get_cmd(ua, _("Enter Volume name: "))) {
282 if (!get_cmd(ua, _("Enter base volume name: "))) {
286 /* Don't allow | in Volume name because it is the volume separator character */
287 if (!is_volume_name_legal(ua, ua->cmd)) {
290 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
291 ua->warning_msg(_("Volume name too long.\n"));
294 if (strlen(ua->cmd) == 0) {
295 ua->warning_msg(_("Volume name must be at least one character long.\n"));
301 bstrncpy(name, ua->cmd, sizeof(name));
303 bstrncat(name, "%04d", sizeof(name));
306 if (!get_pint(ua, _("Enter the starting number: "))) {
309 startnum = ua->pint32_val;
311 ua->warning_msg(_("Start number must be greater than zero.\n"));
321 if (store && store->autochanger) {
322 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
325 Slot = ua->pint32_val;
326 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
329 InChanger = ua->pint32_val;
332 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
333 for (i=startnum; i < num+startnum; i++) {
334 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
336 mr.InChanger = InChanger;
337 mr.StorageId = store->StorageId;
339 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
340 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
341 ua->error_msg("%s", db_strerror(ua->db));
345 first_id = mr.PoolId;
349 Dmsg0(200, "Update pool record.\n");
350 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
351 ua->warning_msg("%s", db_strerror(ua->db));
354 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
360 * Turn auto mount on/off
365 int automount_cmd(UAContext *ua, const char *cmd)
370 if (!get_cmd(ua, _("Turn on or off? "))) {
378 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
386 static int cancel_cmd(UAContext *ua, const char *cmd)
391 char JobName[MAX_NAME_LENGTH];
393 for (i=1; i<ua->argc; i++) {
394 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
399 JobId = str_to_int64(ua->argv[i]);
400 if (!(jcr=get_jcr_by_id(JobId))) {
401 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
405 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
409 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
410 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
411 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
412 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
415 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
419 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
420 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
421 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
422 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
429 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
430 ua->error_msg(_("Unauthorized command from this console.\n"));
435 * If we still do not have a jcr,
436 * throw up a list and ask the user to select one.
439 int tjobs = 0; /* total # number jobs */
440 /* Count Jobs running */
442 if (jcr->JobId == 0) { /* this is us */
445 tjobs++; /* count of all jobs */
446 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
447 continue; /* skip not authorized */
449 njobs++; /* count of authorized jobs */
453 if (njobs == 0) { /* no authorized */
455 ua->send_msg(_("No Jobs running.\n"));
457 ua->send_msg(_("None of your jobs are running.\n"));
462 start_prompt(ua, _("Select Job:\n"));
465 if (jcr->JobId == 0) { /* this is us */
468 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
469 continue; /* skip not authorized */
471 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
476 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
479 if (ua->api && njobs == 1) {
481 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
482 _("Confirm cancel?"));
483 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
488 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
493 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
494 jcr = get_jcr_by_full_name(JobName);
496 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
501 ret = cancel_job(ua, jcr);
507 * This is a common routine to create or update a
508 * Pool DB base record from a Pool Resource. We handle
509 * the setting of MaxVols and NumVols slightly differently
510 * depending on if we are creating the Pool or we are
511 * simply bringing it into agreement with the resource (updage).
513 * Caution : RecyclePoolId isn't setup in this function.
514 * You can use set_pooldbr_recyclepoolid();
517 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
519 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
520 if (op == POOL_OP_CREATE) {
521 pr->MaxVols = pool->max_volumes;
523 } else { /* update pool */
524 if (pr->MaxVols != pool->max_volumes) {
525 pr->MaxVols = pool->max_volumes;
527 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
528 pr->MaxVols = pr->NumVols;
531 pr->LabelType = pool->LabelType;
532 pr->UseOnce = pool->use_volume_once;
533 pr->UseCatalog = pool->use_catalog;
534 pr->Recycle = pool->Recycle;
535 pr->VolRetention = pool->VolRetention;
536 pr->VolUseDuration = pool->VolUseDuration;
537 pr->MaxVolJobs = pool->MaxVolJobs;
538 pr->MaxVolFiles = pool->MaxVolFiles;
539 pr->MaxVolBytes = pool->MaxVolBytes;
540 pr->AutoPrune = pool->AutoPrune;
541 pr->Recycle = pool->Recycle;
542 if (pool->label_format) {
543 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
545 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
549 /* set/update Pool.RecyclePoolId in Catalog */
550 int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
554 if (!pool->RecyclePool) {
558 memset(&pr, 0, sizeof(POOL_DBR));
559 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
561 if (!db_get_pool_record(jcr, db, &pr)) {
562 return -1; /* not exists in database */
565 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
567 if (!set_pooldbr_recyclepoolid(jcr, db, &pr, pool)) {
568 return -1; /* error */
571 if (!db_update_pool_record(jcr, db, &pr)) {
572 return -1; /* error */
577 /* set POOL_DBR.RecyclePoolId from Pool resource
578 * works with set_pooldbr_from_poolres
580 bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
585 if (pool->RecyclePool) {
586 memset(&rpool, 0, sizeof(POOL_DBR));
588 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
589 if (db_get_pool_record(jcr, db, &rpool)) {
590 pr->RecyclePoolId = rpool.PoolId;
592 Jmsg(jcr, M_WARNING, 0,
593 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
594 "Try to update it with 'update pool=%s'\n"),
595 pool->name(), rpool.Name, rpool.Name,pool->name());
599 } else { /* no RecyclePool used, set it to 0 */
600 pr->RecyclePoolId = 0;
607 * Create a pool record from a given Pool resource
608 * Also called from backup.c
609 * Returns: -1 on error
610 * 0 record already exists
614 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
618 memset(&pr, 0, sizeof(POOL_DBR));
620 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
622 if (db_get_pool_record(jcr, db, &pr)) {
624 if (op == POOL_OP_UPDATE) { /* update request */
625 set_pooldbr_from_poolres(&pr, pool, op);
626 db_update_pool_record(jcr, db, &pr);
628 return 0; /* exists */
631 set_pooldbr_from_poolres(&pr, pool, op);
633 if (!db_create_pool_record(jcr, db, &pr)) {
634 return -1; /* error */
642 * Create a Pool Record in the database.
643 * It is always created from the Resource record.
645 static int create_cmd(UAContext *ua, const char *cmd)
649 if (!open_client_db(ua)) {
653 pool = get_pool_resource(ua);
658 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
660 ua->error_msg(_("Error: Pool %s already exists.\n"
661 "Use update to change it.\n"), pool->name());
665 ua->error_msg("%s", db_strerror(ua->db));
671 ua->send_msg(_("Pool %s created.\n"), pool->name());
676 extern DIRRES *director;
679 * Python control command
680 * python restart (restarts interpreter)
682 static int python_cmd(UAContext *ua, const char *cmd)
684 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
685 term_python_interpreter();
686 init_python_interpreter(director->name(),
687 director->scripts_directory, "DirStartUp");
688 ua->send_msg(_("Python interpreter restarted.\n"));
690 ua->warning_msg(_("Nothing done.\n"));
697 * Set a new address in a Client resource. We do this only
698 * if the Console name is the same as the Client name
699 * and the Console can access the client.
701 static int setip_cmd(UAContext *ua, const char *cmd)
705 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
706 ua->error_msg(_("Unauthorized command from this console.\n"));
710 client = GetClientResWithName(ua->cons->name());
713 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
716 if (client->address) {
717 free(client->address);
719 /* MA Bug 6 remove ifdef */
720 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
721 client->address = bstrdup(buf);
722 ua->send_msg(_("Client \"%s\" address set to %s\n"),
723 client->name(), client->address);
730 static void do_en_disable_cmd(UAContext *ua, bool setting)
735 i = find_arg_with_value(ua, NT_("job"));
737 job = select_job_resource(ua);
743 job = GetJobResWithName(ua->argv[i]);
747 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
751 if (!acl_access_ok(ua, Job_ACL, job->name())) {
752 ua->error_msg(_("Unauthorized command from this console.\n"));
755 job->enabled = setting;
756 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
760 static int enable_cmd(UAContext *ua, const char *cmd)
762 do_en_disable_cmd(ua, true);
766 static int disable_cmd(UAContext *ua, const char *cmd)
768 do_en_disable_cmd(ua, false);
773 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
779 lstore.store = store;
780 pm_strcpy(lstore.store_source, _("unknown source"));
781 set_wstorage(jcr, &lstore);
782 /* Try connecting for up to 15 seconds */
783 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
784 store->name(), store->address, store->SDport);
785 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
786 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
789 Dmsg0(120, _("Connected to storage daemon\n"));
790 sd = jcr->store_bsock;
791 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
792 if (bnet_recv(sd) >= 0) {
793 ua->send_msg("%s", sd->msg);
795 bnet_sig(sd, BNET_TERMINATE);
797 jcr->store_bsock = NULL;
801 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
805 /* Connect to File daemon */
807 ua->jcr->client = client;
808 /* Try to connect for 15 seconds */
809 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
810 client->name(), client->address, client->FDport);
811 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
812 ua->error_msg(_("Failed to connect to Client.\n"));
815 Dmsg0(120, "Connected to file daemon\n");
816 fd = ua->jcr->file_bsock;
817 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
818 if (bnet_recv(fd) >= 0) {
819 ua->send_msg("%s", fd->msg);
821 bnet_sig(fd, BNET_TERMINATE);
823 ua->jcr->file_bsock = NULL;
828 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
830 STORE *store, **unique_store;
831 CLIENT *client, **unique_client;
837 /* Count Storage items */
841 foreach_res(store, R_STORAGE) {
844 unique_store = (STORE **) malloc(i * sizeof(STORE));
845 /* Find Unique Storage address/port */
846 store = (STORE *)GetNextRes(R_STORAGE, NULL);
848 unique_store[i++] = store;
849 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
851 for (j=0; j<i; j++) {
852 if (strcmp(unique_store[j]->address, store->address) == 0 &&
853 unique_store[j]->SDport == store->SDport) {
859 unique_store[i++] = store;
860 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
865 /* Call each unique Storage daemon */
866 for (j=0; j<i; j++) {
867 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
871 /* Count Client items */
875 foreach_res(client, R_CLIENT) {
878 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
879 /* Find Unique Client address/port */
880 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
882 unique_client[i++] = client;
883 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
885 for (j=0; j<i; j++) {
886 if (strcmp(unique_client[j]->address, client->address) == 0 &&
887 unique_client[j]->FDport == client->FDport) {
893 unique_client[i++] = client;
894 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
899 /* Call each unique File daemon */
900 for (j=0; j<i; j++) {
901 do_client_setdebug(ua, unique_client[j], level, trace_flag);
907 * setdebug level=nn all trace=1/0
909 static int setdebug_cmd(UAContext *ua, const char *cmd)
917 if (!open_client_db(ua)) {
920 Dmsg1(120, "setdebug:%s:\n", cmd);
923 i = find_arg_with_value(ua, "level");
925 level = atoi(ua->argv[i]);
928 if (!get_pint(ua, _("Enter new debug level: "))) {
931 level = ua->pint32_val;
934 /* Look for trace flag. -1 => not change */
935 i = find_arg_with_value(ua, "trace");
937 trace_flag = atoi(ua->argv[i]);
938 if (trace_flag > 0) {
944 for (i=1; i<ua->argc; i++) {
945 if (strcasecmp(ua->argk[i], "all") == 0) {
946 do_all_setdebug(ua, level, trace_flag);
949 if (strcasecmp(ua->argk[i], "dir") == 0 ||
950 strcasecmp(ua->argk[i], "director") == 0) {
952 set_trace(trace_flag);
955 if (strcasecmp(ua->argk[i], "client") == 0 ||
956 strcasecmp(ua->argk[i], "fd") == 0) {
959 client = GetClientResWithName(ua->argv[i]);
961 do_client_setdebug(ua, client, level, trace_flag);
965 client = select_client_resource(ua);
967 do_client_setdebug(ua, client, level, trace_flag);
972 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
973 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
974 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
977 store = GetStoreResWithName(ua->argv[i]);
979 do_storage_setdebug(ua, store, level, trace_flag);
983 store = get_storage_resource(ua, false/*no default*/);
985 do_storage_setdebug(ua, store, level, trace_flag);
991 * We didn't find an appropriate keyword above, so
994 start_prompt(ua, _("Available daemons are: \n"));
995 add_prompt(ua, _("Director"));
996 add_prompt(ua, _("Storage"));
997 add_prompt(ua, _("Client"));
998 add_prompt(ua, _("All"));
999 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1000 case 0: /* Director */
1001 debug_level = level;
1002 set_trace(trace_flag);
1005 store = get_storage_resource(ua, false/*no default*/);
1007 do_storage_setdebug(ua, store, level, trace_flag);
1011 client = select_client_resource(ua);
1013 do_client_setdebug(ua, client, level, trace_flag);
1017 do_all_setdebug(ua, level, trace_flag);
1026 * Turn debug tracing to file on/off
1028 static int trace_cmd(UAContext *ua, const char *cmd)
1032 if (ua->argc != 2) {
1033 if (!get_cmd(ua, _("Turn on or off? "))) {
1038 onoff = ua->argk[1];
1041 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1046 static int var_cmd(UAContext *ua, const char *cmd)
1048 POOLMEM *val = get_pool_memory(PM_FNAME);
1051 if (!open_client_db(ua)) {
1054 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1057 while (*var == ' ') { /* skip spaces */
1060 Dmsg1(100, "Var=%s:\n", var);
1061 variable_expansion(ua->jcr, var, &val);
1062 ua->send_msg("%s\n", val);
1063 free_pool_memory(val);
1067 static int estimate_cmd(UAContext *ua, const char *cmd)
1070 CLIENT *client = NULL;
1071 FILESET *fileset = NULL;
1073 char since[MAXSTRING];
1076 jcr->JobLevel = L_FULL;
1077 for (int i=1; i<ua->argc; i++) {
1078 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1079 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1081 client = GetClientResWithName(ua->argv[i]);
1083 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1089 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1091 job = GetJobResWithName(ua->argv[i]);
1093 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1096 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1097 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1103 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1105 fileset = GetFileSetResWithName(ua->argv[i]);
1107 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1110 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1111 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1117 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1121 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1122 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1123 ua->error_msg(_("Level %s not valid.\n"), ua->argv[i]);
1128 if (!job && !(client && fileset)) {
1129 if (!(job = select_job_resource(ua))) {
1134 job = GetJobResWithName(ua->argk[1]);
1136 ua->error_msg(_("No job specified.\n"));
1139 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1140 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1145 client = job->client;
1148 fileset = job->fileset;
1150 jcr->client = client;
1151 jcr->fileset = fileset;
1153 if (job->pool->catalog) {
1154 ua->catalog = job->pool->catalog;
1156 ua->catalog = client->catalog;
1164 jcr->JobType = JT_BACKUP;
1165 init_jcr_job_record(jcr);
1167 if (!get_or_create_client_record(jcr)) {
1170 if (!get_or_create_fileset_record(jcr)) {
1174 get_level_since_time(ua->jcr, since, sizeof(since));
1176 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1177 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1178 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1179 ua->error_msg(_("Failed to connect to Client.\n"));
1183 if (!send_include_list(jcr)) {
1184 ua->error_msg(_("Error sending include list.\n"));
1188 if (!send_exclude_list(jcr)) {
1189 ua->error_msg(_("Error sending exclude list.\n"));
1193 if (!send_level_command(jcr)) {
1197 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1198 while (bnet_recv(jcr->file_bsock) >= 0) {
1199 ua->send_msg("%s", jcr->file_bsock->msg);
1203 if (jcr->file_bsock) {
1204 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1205 bnet_close(jcr->file_bsock);
1206 jcr->file_bsock = NULL;
1215 static int time_cmd(UAContext *ua, const char *cmd)
1218 time_t ttime = time(NULL);
1220 (void)localtime_r(&ttime, &tm);
1221 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1222 ua->send_msg("%s\n", sdt);
1227 * reload the conf file
1229 extern "C" void reload_config(int sig);
1231 static int reload_cmd(UAContext *ua, const char *cmd)
1238 * Delete Pool records (should purge Media with it).
1240 * delete pool=<pool-name>
1241 * delete volume pool=<pool-name> volume=<name>
1244 static int delete_cmd(UAContext *ua, const char *cmd)
1246 static const char *keywords[] = {
1252 if (!open_client_db(ua)) {
1256 switch (find_arg_keyword(ua, keywords)) {
1265 while ((i=find_arg(ua, "jobid")) > 0) {
1267 *ua->argk[i] = 0; /* zap keyword already visited */
1275 "In general it is not a good idea to delete either a\n"
1276 "Pool or a Volume since they may contain data.\n\n"));
1278 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1289 ua->warning_msg(_("Nothing done.\n"));
1297 * delete_job has been modified to parse JobID lists like the
1299 * delete JobID=3,4,6,7-11,14
1301 * Thanks to Phil Stracchino for the above addition.
1304 static void delete_job(UAContext *ua)
1309 int i = find_arg_with_value(ua, NT_("jobid"));
1311 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1312 s = bstrdup(ua->argv[i]);
1315 * We could use strtok() here. But we're not going to, because:
1316 * (a) strtok() is deprecated, having been replaced by strsep();
1317 * (b) strtok() is broken in significant ways.
1318 * we could use strsep() instead, but it's not universally available.
1319 * so we grow our own using strchr().
1321 sep = strchr(tok, ',');
1322 while (sep != NULL) {
1324 if (strchr(tok, '-')) {
1325 delete_job_id_range(ua, tok);
1327 JobId = str_to_int64(tok);
1328 do_job_delete(ua, JobId);
1331 sep = strchr(tok, ',');
1333 /* pick up the last token */
1334 if (strchr(tok, '-')) {
1335 delete_job_id_range(ua, tok);
1337 JobId = str_to_int64(tok);
1338 do_job_delete(ua, JobId);
1343 JobId = str_to_int64(ua->argv[i]);
1344 do_job_delete(ua, JobId);
1346 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1349 JobId = ua->int64_val;
1350 do_job_delete(ua, JobId);
1355 * we call delete_job_id_range to parse range tokens and iterate over ranges
1357 static void delete_job_id_range(UAContext *ua, char *tok)
1362 tok2 = strchr(tok, '-');
1365 j1 = str_to_int64(tok);
1366 j2 = str_to_int64(tok2);
1367 for (j=j1; j<=j2; j++) {
1368 do_job_delete(ua, j);
1373 * do_job_delete now performs the actual delete operation atomically
1375 static void do_job_delete(UAContext *ua, JobId_t JobId)
1379 edit_int64(JobId, ed1);
1380 purge_jobs_from_catalog(ua, ed1);
1381 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1385 * Delete media records from database -- dangerous
1387 static int delete_volume(UAContext *ua)
1392 if (!select_media_dbr(ua, &mr)) {
1395 ua->warning_msg(_("\nThis command will delete volume %s\n"
1396 "and all Jobs saved on that volume from the Catalog\n"),
1399 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1401 if (!get_yesno(ua, buf)) {
1404 if (ua->pint32_val) {
1405 db_delete_media_record(ua->jcr, ua->db, &mr);
1411 * Delete a pool record from the database -- dangerous
1413 static int delete_pool(UAContext *ua)
1418 memset(&pr, 0, sizeof(pr));
1420 if (!get_pool_dbr(ua, &pr)) {
1423 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1425 if (!get_yesno(ua, buf)) {
1428 if (ua->pint32_val) {
1429 db_delete_pool_record(ua->jcr, ua->db, &pr);
1434 int memory_cmd(UAContext *ua, const char *cmd)
1436 list_dir_status_header(ua);
1437 sm_dump(false, true);
1441 static void do_mount_cmd(UAContext *ua, const char *command)
1446 char dev_name[MAX_NAME_LENGTH];
1450 if (!open_client_db(ua)) {
1453 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1455 store.store = get_storage_resource(ua, true/*arg is storage*/);
1459 pm_strcpy(store.store_source, _("unknown source"));
1460 set_wstorage(jcr, &store);
1461 drive = get_storage_drive(ua, store.store);
1462 if (strcmp(command, "mount") == 0) {
1463 slot = get_storage_slot(ua, store.store);
1466 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1467 store.store->media_type, store.store->dev_name(), drive);
1469 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1470 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1473 sd = jcr->store_bsock;
1474 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1475 bash_spaces(dev_name);
1477 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1479 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1481 while (bnet_recv(sd) >= 0) {
1482 ua->send_msg("%s", sd->msg);
1484 bnet_sig(sd, BNET_TERMINATE);
1486 jcr->store_bsock = NULL;
1490 * mount [storage=<name>] [drive=nn] [slot=mm]
1492 static int mount_cmd(UAContext *ua, const char *cmd)
1494 do_mount_cmd(ua, "mount"); /* mount */
1500 * unmount [storage=<name>] [drive=nn]
1502 static int unmount_cmd(UAContext *ua, const char *cmd)
1504 do_mount_cmd(ua, "unmount"); /* unmount */
1510 * release [storage=<name>] [drive=nn]
1512 static int release_cmd(UAContext *ua, const char *cmd)
1514 do_mount_cmd(ua, "release"); /* release */
1521 * use catalog=<name>
1523 static int use_cmd(UAContext *ua, const char *cmd)
1525 CAT *oldcatalog, *catalog;
1528 close_db(ua); /* close any previously open db */
1529 oldcatalog = ua->catalog;
1531 if (!(catalog = get_catalog_resource(ua))) {
1532 ua->catalog = oldcatalog;
1534 ua->catalog = catalog;
1537 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1538 ua->catalog->name(), ua->catalog->db_name);
1543 int quit_cmd(UAContext *ua, const char *cmd)
1549 /* Handler to get job status */
1550 static int status_handler(void *ctx, int num_fields, char **row)
1552 char *val = (char *)ctx;
1557 *val = '?'; /* Unknown by default */
1564 * Wait until no job is running
1566 int wait_cmd(UAContext *ua, const char *cmd)
1572 * Wait until no job is running
1574 if (ua->argc == 1) {
1575 bmicrosleep(0, 200000); /* let job actually start */
1576 for (bool running=true; running; ) {
1579 if (jcr->JobId != 0) {
1593 /* we have jobid, jobname or ujobid argument */
1595 uint32_t jobid = 0 ;
1597 if (!open_client_db(ua)) {
1598 ua->error_msg(_("ERR: Can't open db\n")) ;
1602 for (int i=1; i<ua->argc; i++) {
1603 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1607 jobid = str_to_int64(ua->argv[i]);
1609 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1610 strcasecmp(ua->argk[i], "job") == 0) {
1614 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1616 jobid = jcr->JobId ;
1620 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1624 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1626 jobid = jcr->JobId ;
1634 ua->error_msg(_("ERR: Job was not found\n"));
1639 * We wait the end of a specific job
1642 bmicrosleep(0, 200000); /* let job actually start */
1643 for (bool running=true; running; ) {
1646 jcr=get_jcr_by_id(jobid) ;
1659 * We have to get JobStatus
1663 char jobstatus = '?'; /* Unknown by default */
1666 bsnprintf(buf, sizeof(buf),
1667 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1670 db_sql_query(ua->db, buf,
1671 status_handler, (void *)&jobstatus);
1673 switch (jobstatus) {
1675 status = 1 ; /* Warning */
1679 case JS_ErrorTerminated:
1681 status = 2 ; /* Critical */
1685 status = 0 ; /* Ok */
1689 status = 3 ; /* Unknown */
1693 ua->send_msg("JobId=%i\n", jobid) ;
1694 ua->send_msg("JobStatus=%s (%c)\n",
1695 job_status_to_str(jobstatus),
1698 if (ua->gui || ua->api) {
1699 ua->send_msg("ExitStatus=%i\n", status) ;
1706 static int help_cmd(UAContext *ua, const char *cmd)
1710 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1711 for (i=0; i<comsize; i++) {
1712 ua->send_msg(_(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1714 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1718 int qhelp_cmd(UAContext *ua, const char *cmd)
1722 for (i=0; i<comsize; i++) {
1723 ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1729 static int version_cmd(UAContext *ua, const char *cmd)
1731 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
1732 HOST_OS, DISTNAME, DISTVER);
1737 * Test code -- turned on only for debug testing
1739 static int version_cmd(UAContext *ua, const char *cmd)
1742 POOL_MEM query(PM_MESSAGE);
1744 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1745 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1746 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1747 for (int i=0; i < ids.num_ids; i++) {
1748 ua->send_msg("id=%d\n", ids.DBId[i]);
1756 * This call explicitly checks for a catalog=xxx and
1757 * if given, opens that catalog. It also checks for
1758 * client=xxx and if found, opens the catalog
1759 * corresponding to that client. If we still don't
1760 * have a catalog, look for a Job keyword and get the
1761 * catalog from its client record.
1763 bool open_client_db(UAContext *ua)
1770 /* Try for catalog keyword */
1771 i = find_arg_with_value(ua, NT_("catalog"));
1773 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1774 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1777 catalog = GetCatalogResWithName(ua->argv[i]);
1779 if (ua->catalog && ua->catalog != catalog) {
1782 ua->catalog = catalog;
1787 /* Try for client keyword */
1788 i = find_arg_with_value(ua, NT_("client"));
1790 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1791 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1794 client = GetClientResWithName(ua->argv[i]);
1796 catalog = client->catalog;
1797 if (ua->catalog && ua->catalog != catalog) {
1800 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1801 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1804 ua->catalog = catalog;
1809 /* Try for Job keyword */
1810 i = find_arg_with_value(ua, NT_("job"));
1812 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1813 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1816 job = GetJobResWithName(ua->argv[i]);
1818 catalog = job->client->catalog;
1819 if (ua->catalog && ua->catalog != catalog) {
1822 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1823 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1826 ua->catalog = catalog;
1836 * Open the catalog database.
1838 bool open_db(UAContext *ua)
1844 ua->catalog = get_catalog_resource(ua);
1846 ua->error_msg( _("Could not find a Catalog resource\n"));
1851 ua->jcr->catalog = ua->catalog;
1853 Dmsg0(100, "UA Open database\n");
1854 ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
1855 ua->catalog->db_user,
1856 ua->catalog->db_password, ua->catalog->db_address,
1857 ua->catalog->db_port, ua->catalog->db_socket,
1858 ua->catalog->mult_db_connections);
1859 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1860 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1861 ua->catalog->db_name);
1863 ua->error_msg("%s", db_strerror(ua->db));
1868 ua->jcr->db = ua->db;
1870 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
1872 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1876 void close_db(UAContext *ua)
1879 db_close_database(ua->jcr, ua->db);