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 plus additions
11 that are listed in the file LICENSE.
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 */
45 extern struct s_res resources[];
46 extern jobq_t job_queue; /* job queue */
49 /* Imported functions */
50 extern int status_cmd(UAContext *ua, const char *cmd);
51 extern int list_cmd(UAContext *ua, const char *cmd);
52 extern int llist_cmd(UAContext *ua, const char *cmd);
53 extern int show_cmd(UAContext *ua, const char *cmd);
54 extern int messagescmd(UAContext *ua, const char *cmd);
55 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
56 extern int gui_cmd(UAContext *ua, const char *cmd);
57 extern int sqlquerycmd(UAContext *ua, const char *cmd);
58 extern int querycmd(UAContext *ua, const char *cmd);
59 extern int retentioncmd(UAContext *ua, const char *cmd);
60 extern int prunecmd(UAContext *ua, const char *cmd);
61 extern int purgecmd(UAContext *ua, const char *cmd);
62 extern int restore_cmd(UAContext *ua, const char *cmd);
63 extern int label_cmd(UAContext *ua, const char *cmd);
64 extern int relabel_cmd(UAContext *ua, const char *cmd);
65 extern int update_cmd(UAContext *ua, const char *cmd);
67 /* Forward referenced functions */
68 static int add_cmd(UAContext *ua, const char *cmd);
69 static int create_cmd(UAContext *ua, const char *cmd);
70 static int cancel_cmd(UAContext *ua, const char *cmd);
71 static int enable_cmd(UAContext *ua, const char *cmd);
72 static int disable_cmd(UAContext *ua, const char *cmd);
73 static int setdebug_cmd(UAContext *ua, const char *cmd);
74 static int trace_cmd(UAContext *ua, const char *cmd);
75 static int var_cmd(UAContext *ua, const char *cmd);
76 static int estimate_cmd(UAContext *ua, const char *cmd);
77 static int help_cmd(UAContext *ua, const char *cmd);
78 static int delete_cmd(UAContext *ua, const char *cmd);
79 static int use_cmd(UAContext *ua, const char *cmd);
80 static int unmount_cmd(UAContext *ua, const char *cmd);
81 static int version_cmd(UAContext *ua, const char *cmd);
82 static int automount_cmd(UAContext *ua, const char *cmd);
83 static int time_cmd(UAContext *ua, const char *cmd);
84 static int reload_cmd(UAContext *ua, const char *cmd);
85 static int delete_volume(UAContext *ua);
86 static int delete_pool(UAContext *ua);
87 static void delete_job(UAContext *ua);
88 static int mount_cmd(UAContext *ua, const char *cmd);
89 static int release_cmd(UAContext *ua, const char *cmd);
90 static int wait_cmd(UAContext *ua, const char *cmd);
91 static int setip_cmd(UAContext *ua, const char *cmd);
92 static int python_cmd(UAContext *ua, const char *cmd);
93 static void do_job_delete(UAContext *ua, JobId_t JobId);
94 static void delete_job_id_range(UAContext *ua, char *tok);
96 int qhelp_cmd(UAContext *ua, const char *cmd);
97 int quit_cmd(UAContext *ua, const char *cmd);
100 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
101 static struct cmdstruct commands[] = {
102 { NT_("add"), add_cmd, _("add media to a pool")},
103 { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
104 { NT_("automount"), automount_cmd, _("automount [on|off] -- after label")},
105 { NT_("cancel"), cancel_cmd, _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
106 { NT_("create"), create_cmd, _("create DB Pool from resource")},
107 { NT_("delete"), delete_cmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
108 { NT_("disable"), disable_cmd, _("disable <job=name> -- disable a job")},
109 { NT_("enable"), enable_cmd, _("enable <job=name> -- enable a job")},
110 { NT_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")},
111 { NT_("exit"), quit_cmd, _("exit = quit")},
112 { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode")},
113 { NT_("help"), help_cmd, _("print this command")},
114 { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
115 { NT_("label"), label_cmd, _("label a tape")},
116 { NT_("llist"), llist_cmd, _("full or long list like list command")},
117 { NT_("messages"), messagescmd, _("messages")},
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 int do_a_command(UAContext *ua, const char *cmd)
155 BSOCK *user = ua->UA_sock;
159 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
164 while (ua->jcr->wstorage->size()) {
165 ua->jcr->wstorage->remove(0);
168 len = strlen(ua->argk[0]);
169 for (i=0; i<comsize; i++) { /* search for command */
170 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
171 /* Check if command permitted, but "quit" is always OK */
172 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
173 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
176 if (ua->api) user->signal(BNET_CMD_BEGIN);
177 ok = (*commands[i].func)(ua, cmd); /* go execute command */
183 user->fsend(_("%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);
245 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
248 pr.MaxVols = ua->pint32_val;
253 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
254 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
255 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
259 if (pr.MaxVols == 0) {
262 max = pr.MaxVols - pr.NumVols;
266 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
267 if (!get_pint(ua, buf)) {
270 num = ua->pint32_val;
271 if (num < 0 || num > max) {
272 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
279 if (!get_cmd(ua, _("Enter Volume name: "))) {
283 if (!get_cmd(ua, _("Enter base volume name: "))) {
287 /* Don't allow | in Volume name because it is the volume separator character */
288 if (!is_volume_name_legal(ua, ua->cmd)) {
291 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
292 ua->warning_msg(_("Volume name too long.\n"));
295 if (strlen(ua->cmd) == 0) {
296 ua->warning_msg(_("Volume name must be at least one character long.\n"));
300 bstrncpy(name, ua->cmd, sizeof(name));
302 bstrncat(name, "%04d", sizeof(name));
305 if (!get_pint(ua, _("Enter the starting number: "))) {
308 startnum = ua->pint32_val;
310 ua->warning_msg(_("Start number must be greater than zero.\n"));
320 if (store && store->autochanger) {
321 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
324 Slot = ua->pint32_val;
325 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
328 InChanger = ua->pint32_val;
331 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
332 for (i=startnum; i < num+startnum; i++) {
333 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
335 mr.InChanger = InChanger;
336 mr.StorageId = store->StorageId;
338 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
339 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
340 ua->error_msg("%s", db_strerror(ua->db));
344 first_id = mr.PoolId;
348 Dmsg0(200, "Update pool record.\n");
349 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
350 ua->warning_msg("%s", db_strerror(ua->db));
353 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
359 * Turn auto mount on/off
364 int automount_cmd(UAContext *ua, const char *cmd)
369 if (!get_cmd(ua, _("Turn on or off? "))) {
377 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
385 static int cancel_cmd(UAContext *ua, const char *cmd)
390 char JobName[MAX_NAME_LENGTH];
392 for (i=1; i<ua->argc; i++) {
393 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
398 JobId = str_to_int64(ua->argv[i]);
399 if (!(jcr=get_jcr_by_id(JobId))) {
400 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
404 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
408 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
409 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
410 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
411 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
414 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
418 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
419 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
420 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
421 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
428 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
429 ua->error_msg(_("Unauthorized command from this console.\n"));
434 * If we still do not have a jcr,
435 * throw up a list and ask the user to select one.
438 int tjobs = 0; /* total # number jobs */
439 /* Count Jobs running */
441 if (jcr->JobId == 0) { /* this is us */
444 tjobs++; /* count of all jobs */
445 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
446 continue; /* skip not authorized */
448 njobs++; /* count of authorized jobs */
452 if (njobs == 0) { /* no authorized */
454 ua->send_msg(_("No Jobs running.\n"));
456 ua->send_msg(_("None of your jobs are running.\n"));
461 start_prompt(ua, _("Select Job:\n"));
464 if (jcr->JobId == 0) { /* this is us */
467 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
468 continue; /* skip not authorized */
470 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
475 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
479 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
483 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
484 jcr = get_jcr_by_full_name(JobName);
486 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
491 ret = cancel_job(ua, jcr);
497 * This is a common routine to create or update a
498 * Pool DB base record from a Pool Resource. We handle
499 * the setting of MaxVols and NumVols slightly differently
500 * depending on if we are creating the Pool or we are
501 * simply bringing it into agreement with the resource (updage).
503 * Caution : RecyclePoolId isn't setup in this function.
504 * You can use set_pooldbr_recyclepoolid();
507 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
509 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
510 if (op == POOL_OP_CREATE) {
511 pr->MaxVols = pool->max_volumes;
513 } else { /* update pool */
514 if (pr->MaxVols != pool->max_volumes) {
515 pr->MaxVols = pool->max_volumes;
517 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
518 pr->MaxVols = pr->NumVols;
521 pr->LabelType = pool->LabelType;
522 pr->UseOnce = pool->use_volume_once;
523 pr->UseCatalog = pool->use_catalog;
524 pr->Recycle = pool->Recycle;
525 pr->VolRetention = pool->VolRetention;
526 pr->VolUseDuration = pool->VolUseDuration;
527 pr->MaxVolJobs = pool->MaxVolJobs;
528 pr->MaxVolFiles = pool->MaxVolFiles;
529 pr->MaxVolBytes = pool->MaxVolBytes;
530 pr->AutoPrune = pool->AutoPrune;
531 pr->Recycle = pool->Recycle;
532 if (pool->label_format) {
533 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
535 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
539 /* set/update Pool.RecyclePoolId in Catalog */
540 int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
544 if (!pool->RecyclePool) {
548 memset(&pr, 0, sizeof(POOL_DBR));
549 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
551 if (!db_get_pool_record(jcr, db, &pr)) {
552 return -1; /* not exists in database */
555 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
557 if (!set_pooldbr_recyclepoolid(jcr, db, &pr, pool)) {
558 return -1; /* error */
561 if (!db_update_pool_record(jcr, db, &pr)) {
562 return -1; /* error */
567 /* set POOL_DBR.RecyclePoolId from Pool resource
568 * works with set_pooldbr_from_poolres
570 bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
575 if (pool->RecyclePool) {
576 memset(&rpool, 0, sizeof(POOL_DBR));
578 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
579 if (db_get_pool_record(jcr, db, &rpool)) {
580 pr->RecyclePoolId = rpool.PoolId;
582 Jmsg(jcr, M_WARNING, 0,
583 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
584 "Try to update it with 'update pool=%s'\n"),
585 pool->name(), rpool.Name, rpool.Name,pool->name());
589 } else { /* no RecyclePool used, set it to 0 */
590 pr->RecyclePoolId = 0;
597 * Create a pool record from a given Pool resource
598 * Also called from backup.c
599 * Returns: -1 on error
600 * 0 record already exists
604 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
608 memset(&pr, 0, sizeof(POOL_DBR));
610 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
612 if (db_get_pool_record(jcr, db, &pr)) {
614 if (op == POOL_OP_UPDATE) { /* update request */
615 set_pooldbr_from_poolres(&pr, pool, op);
616 db_update_pool_record(jcr, db, &pr);
618 return 0; /* exists */
621 set_pooldbr_from_poolres(&pr, pool, op);
623 if (!db_create_pool_record(jcr, db, &pr)) {
624 return -1; /* error */
632 * Create a Pool Record in the database.
633 * It is always created from the Resource record.
635 static int create_cmd(UAContext *ua, const char *cmd)
639 if (!open_client_db(ua)) {
643 pool = get_pool_resource(ua);
648 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
650 ua->error_msg(_("Error: Pool %s already exists.\n"
651 "Use update to change it.\n"), pool->name());
655 ua->error_msg("%s", db_strerror(ua->db));
661 ua->send_msg(_("Pool %s created.\n"), pool->name());
666 extern DIRRES *director;
669 * Python control command
670 * python restart (restarts interpreter)
672 static int python_cmd(UAContext *ua, const char *cmd)
674 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
675 term_python_interpreter();
676 init_python_interpreter(director->name(),
677 director->scripts_directory, "DirStartUp");
678 ua->send_msg(_("Python interpreter restarted.\n"));
680 ua->warning_msg(_("Nothing done.\n"));
687 * Set a new address in a Client resource. We do this only
688 * if the Console name is the same as the Client name
689 * and the Console can access the client.
691 static int setip_cmd(UAContext *ua, const char *cmd)
695 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
696 ua->error_msg(_("Unauthorized command from this console.\n"));
700 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->name());
703 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
706 if (client->address) {
707 free(client->address);
709 /* MA Bug 6 remove ifdef */
710 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
711 client->address = bstrdup(buf);
712 ua->send_msg(_("Client \"%s\" address set to %s\n"),
713 client->name(), client->address);
720 static void do_en_disable_cmd(UAContext *ua, bool setting)
725 i = find_arg_with_value(ua, NT_("job"));
727 job = select_job_resource(ua);
733 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
737 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
741 if (!acl_access_ok(ua, Job_ACL, job->name())) {
742 ua->error_msg(_("Unauthorized command from this console.\n"));
745 job->enabled = setting;
746 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
750 static int enable_cmd(UAContext *ua, const char *cmd)
752 do_en_disable_cmd(ua, true);
756 static int disable_cmd(UAContext *ua, const char *cmd)
758 do_en_disable_cmd(ua, false);
763 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
769 lstore.store = store;
770 pm_strcpy(lstore.store_source, _("unknown source"));
771 set_wstorage(jcr, &lstore);
772 /* Try connecting for up to 15 seconds */
773 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
774 store->name(), store->address, store->SDport);
775 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
776 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
779 Dmsg0(120, _("Connected to storage daemon\n"));
780 sd = jcr->store_bsock;
781 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
782 if (bnet_recv(sd) >= 0) {
783 ua->send_msg("%s", sd->msg);
785 bnet_sig(sd, BNET_TERMINATE);
787 jcr->store_bsock = NULL;
791 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
795 /* Connect to File daemon */
797 ua->jcr->client = client;
798 /* Try to connect for 15 seconds */
799 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
800 client->name(), client->address, client->FDport);
801 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
802 ua->error_msg(_("Failed to connect to Client.\n"));
805 Dmsg0(120, "Connected to file daemon\n");
806 fd = ua->jcr->file_bsock;
807 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
808 if (bnet_recv(fd) >= 0) {
809 ua->send_msg("%s", fd->msg);
811 bnet_sig(fd, BNET_TERMINATE);
813 ua->jcr->file_bsock = NULL;
818 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
820 STORE *store, **unique_store;
821 CLIENT *client, **unique_client;
827 /* Count Storage items */
831 foreach_res(store, R_STORAGE) {
834 unique_store = (STORE **) malloc(i * sizeof(STORE));
835 /* Find Unique Storage address/port */
836 store = (STORE *)GetNextRes(R_STORAGE, NULL);
838 unique_store[i++] = store;
839 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
841 for (j=0; j<i; j++) {
842 if (strcmp(unique_store[j]->address, store->address) == 0 &&
843 unique_store[j]->SDport == store->SDport) {
849 unique_store[i++] = store;
850 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
855 /* Call each unique Storage daemon */
856 for (j=0; j<i; j++) {
857 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
861 /* Count Client items */
865 foreach_res(client, R_CLIENT) {
868 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
869 /* Find Unique Client address/port */
870 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
872 unique_client[i++] = client;
873 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
875 for (j=0; j<i; j++) {
876 if (strcmp(unique_client[j]->address, client->address) == 0 &&
877 unique_client[j]->FDport == client->FDport) {
883 unique_client[i++] = client;
884 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
889 /* Call each unique File daemon */
890 for (j=0; j<i; j++) {
891 do_client_setdebug(ua, unique_client[j], level, trace_flag);
897 * setdebug level=nn all trace=1/0
899 static int setdebug_cmd(UAContext *ua, const char *cmd)
907 if (!open_client_db(ua)) {
910 Dmsg1(120, "setdebug:%s:\n", cmd);
913 i = find_arg_with_value(ua, "level");
915 level = atoi(ua->argv[i]);
918 if (!get_pint(ua, _("Enter new debug level: "))) {
921 level = ua->pint32_val;
924 /* Look for trace flag. -1 => not change */
925 i = find_arg_with_value(ua, "trace");
927 trace_flag = atoi(ua->argv[i]);
928 if (trace_flag > 0) {
934 for (i=1; i<ua->argc; i++) {
935 if (strcasecmp(ua->argk[i], "all") == 0) {
936 do_all_setdebug(ua, level, trace_flag);
939 if (strcasecmp(ua->argk[i], "dir") == 0 ||
940 strcasecmp(ua->argk[i], "director") == 0) {
942 set_trace(trace_flag);
945 if (strcasecmp(ua->argk[i], "client") == 0 ||
946 strcasecmp(ua->argk[i], "fd") == 0) {
949 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
951 do_client_setdebug(ua, client, level, trace_flag);
955 client = select_client_resource(ua);
957 do_client_setdebug(ua, client, level, trace_flag);
962 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
963 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
964 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
967 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
969 do_storage_setdebug(ua, store, level, trace_flag);
973 store = get_storage_resource(ua, false/*no default*/);
975 do_storage_setdebug(ua, store, level, trace_flag);
981 * We didn't find an appropriate keyword above, so
984 start_prompt(ua, _("Available daemons are: \n"));
985 add_prompt(ua, _("Director"));
986 add_prompt(ua, _("Storage"));
987 add_prompt(ua, _("Client"));
988 add_prompt(ua, _("All"));
989 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
990 case 0: /* Director */
992 set_trace(trace_flag);
995 store = get_storage_resource(ua, false/*no default*/);
997 do_storage_setdebug(ua, store, level, trace_flag);
1001 client = select_client_resource(ua);
1003 do_client_setdebug(ua, client, level, trace_flag);
1007 do_all_setdebug(ua, level, trace_flag);
1016 * Turn debug tracing to file on/off
1018 static int trace_cmd(UAContext *ua, const char *cmd)
1022 if (ua->argc != 2) {
1023 if (!get_cmd(ua, _("Turn on or off? "))) {
1028 onoff = ua->argk[1];
1031 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1036 static int var_cmd(UAContext *ua, const char *cmd)
1038 POOLMEM *val = get_pool_memory(PM_FNAME);
1041 if (!open_client_db(ua)) {
1044 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1047 while (*var == ' ') { /* skip spaces */
1050 Dmsg1(100, "Var=%s:\n", var);
1051 variable_expansion(ua->jcr, var, &val);
1052 ua->send_msg("%s\n", val);
1053 free_pool_memory(val);
1057 static int estimate_cmd(UAContext *ua, const char *cmd)
1060 CLIENT *client = NULL;
1061 FILESET *fileset = NULL;
1063 char since[MAXSTRING];
1066 jcr->JobLevel = L_FULL;
1067 for (int i=1; i<ua->argc; i++) {
1068 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1069 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1071 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1075 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1077 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1078 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1079 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1085 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1087 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
1088 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1089 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1095 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1099 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1100 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1101 ua->error_msg(_("Level %s not valid.\n"), ua->argv[i]);
1106 if (!job && !(client && fileset)) {
1107 if (!(job = select_job_resource(ua))) {
1112 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1114 ua->error_msg(_("No job specified.\n"));
1117 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1118 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1123 client = job->client;
1126 fileset = job->fileset;
1128 jcr->client = client;
1129 jcr->fileset = fileset;
1131 ua->catalog = client->catalog;
1138 jcr->JobType = JT_BACKUP;
1139 init_jcr_job_record(jcr);
1141 if (!get_or_create_client_record(jcr)) {
1144 if (!get_or_create_fileset_record(jcr)) {
1148 get_level_since_time(ua->jcr, since, sizeof(since));
1150 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1151 job->client->name(), job->client->address, job->client->FDport);
1152 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1153 ua->error_msg(_("Failed to connect to Client.\n"));
1157 if (!send_include_list(jcr)) {
1158 ua->error_msg(_("Error sending include list.\n"));
1162 if (!send_exclude_list(jcr)) {
1163 ua->error_msg(_("Error sending exclude list.\n"));
1167 if (!send_level_command(jcr)) {
1171 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1172 while (bnet_recv(jcr->file_bsock) >= 0) {
1173 ua->send_msg("%s", jcr->file_bsock->msg);
1177 if (jcr->file_bsock) {
1178 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1179 bnet_close(jcr->file_bsock);
1180 jcr->file_bsock = NULL;
1189 static int time_cmd(UAContext *ua, const char *cmd)
1192 time_t ttime = time(NULL);
1194 (void)localtime_r(&ttime, &tm);
1195 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1196 ua->send_msg("%s\n", sdt);
1201 * reload the conf file
1203 extern "C" void reload_config(int sig);
1205 static int reload_cmd(UAContext *ua, const char *cmd)
1212 * Delete Pool records (should purge Media with it).
1214 * delete pool=<pool-name>
1215 * delete volume pool=<pool-name> volume=<name>
1218 static int delete_cmd(UAContext *ua, const char *cmd)
1220 static const char *keywords[] = {
1226 if (!open_client_db(ua)) {
1230 switch (find_arg_keyword(ua, keywords)) {
1239 while ((i=find_arg(ua, "jobid")) > 0) {
1241 *ua->argk[i] = 0; /* zap keyword already visited */
1249 "In general it is not a good idea to delete either a\n"
1250 "Pool or a Volume since they may contain data.\n\n"));
1252 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1263 ua->warning_msg(_("Nothing done.\n"));
1271 * delete_job has been modified to parse JobID lists like the
1273 * delete JobID=3,4,6,7-11,14
1275 * Thanks to Phil Stracchino for the above addition.
1278 static void delete_job(UAContext *ua)
1283 int i = find_arg_with_value(ua, NT_("jobid"));
1285 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1286 s = bstrdup(ua->argv[i]);
1289 * We could use strtok() here. But we're not going to, because:
1290 * (a) strtok() is deprecated, having been replaced by strsep();
1291 * (b) strtok() is broken in significant ways.
1292 * we could use strsep() instead, but it's not universally available.
1293 * so we grow our own using strchr().
1295 sep = strchr(tok, ',');
1296 while (sep != NULL) {
1298 if (strchr(tok, '-')) {
1299 delete_job_id_range(ua, tok);
1301 JobId = str_to_int64(tok);
1302 do_job_delete(ua, JobId);
1305 sep = strchr(tok, ',');
1307 /* pick up the last token */
1308 if (strchr(tok, '-')) {
1309 delete_job_id_range(ua, tok);
1311 JobId = str_to_int64(tok);
1312 do_job_delete(ua, JobId);
1317 JobId = str_to_int64(ua->argv[i]);
1318 do_job_delete(ua, JobId);
1320 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1323 JobId = ua->int64_val;
1324 do_job_delete(ua, JobId);
1329 * we call delete_job_id_range to parse range tokens and iterate over ranges
1331 static void delete_job_id_range(UAContext *ua, char *tok)
1336 tok2 = strchr(tok, '-');
1339 j1 = str_to_int64(tok);
1340 j2 = str_to_int64(tok2);
1341 for (j=j1; j<=j2; j++) {
1342 do_job_delete(ua, j);
1347 * do_job_delete now performs the actual delete operation atomically
1349 static void do_job_delete(UAContext *ua, JobId_t JobId)
1353 edit_int64(JobId, ed1);
1354 purge_jobs_from_catalog(ua, ed1);
1355 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1359 * Delete media records from database -- dangerous
1361 static int delete_volume(UAContext *ua)
1365 if (!select_media_dbr(ua, &mr)) {
1368 ua->warning_msg(_("\nThis command will delete volume %s\n"
1369 "and all Jobs saved on that volume from the Catalog\n"),
1372 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1375 if (ua->pint32_val) {
1376 db_delete_media_record(ua->jcr, ua->db, &mr);
1382 * Delete a pool record from the database -- dangerous
1384 static int delete_pool(UAContext *ua)
1388 memset(&pr, 0, sizeof(pr));
1390 if (!get_pool_dbr(ua, &pr)) {
1393 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1396 if (ua->pint32_val) {
1397 db_delete_pool_record(ua->jcr, ua->db, &pr);
1403 static void do_mount_cmd(UAContext *ua, const char *command)
1408 char dev_name[MAX_NAME_LENGTH];
1412 if (!open_client_db(ua)) {
1415 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1417 store.store = get_storage_resource(ua, true/*arg is storage*/);
1418 pm_strcpy(store.store_source, _("unknown source"));
1422 set_wstorage(jcr, &store);
1423 drive = get_storage_drive(ua, store.store);
1424 if (strcmp(command, "mount") == 0) {
1425 slot = get_storage_slot(ua, store.store);
1428 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1429 store.store->media_type, store.store->dev_name(), drive);
1431 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1432 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1435 sd = jcr->store_bsock;
1436 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1437 bash_spaces(dev_name);
1439 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1441 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1443 while (bnet_recv(sd) >= 0) {
1444 ua->send_msg("%s", sd->msg);
1446 bnet_sig(sd, BNET_TERMINATE);
1448 jcr->store_bsock = NULL;
1452 * mount [storage=<name>] [drive=nn] [slot=mm]
1454 static int mount_cmd(UAContext *ua, const char *cmd)
1456 do_mount_cmd(ua, "mount"); /* mount */
1462 * unmount [storage=<name>] [drive=nn]
1464 static int unmount_cmd(UAContext *ua, const char *cmd)
1466 do_mount_cmd(ua, "unmount"); /* unmount */
1472 * release [storage=<name>] [drive=nn]
1474 static int release_cmd(UAContext *ua, const char *cmd)
1476 do_mount_cmd(ua, "release"); /* release */
1483 * use catalog=<name>
1485 static int use_cmd(UAContext *ua, const char *cmd)
1487 CAT *oldcatalog, *catalog;
1490 close_db(ua); /* close any previously open db */
1491 oldcatalog = ua->catalog;
1493 if (!(catalog = get_catalog_resource(ua))) {
1494 ua->catalog = oldcatalog;
1496 ua->catalog = catalog;
1499 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1500 ua->catalog->name(), ua->catalog->db_name);
1505 int quit_cmd(UAContext *ua, const char *cmd)
1511 /* Handler to get job status */
1512 static int status_handler(void *ctx, int num_fields, char **row)
1514 char *val = (char *)ctx;
1519 *val = '?'; /* Unknown by default */
1526 * Wait until no job is running
1528 int wait_cmd(UAContext *ua, const char *cmd)
1533 * Wait until no job is running
1535 if (ua->argc == 1) {
1536 bmicrosleep(0, 200000); /* let job actually start */
1537 for (bool running=true; running; ) {
1540 if (jcr->JobId != 0) {
1554 /* we have jobid, jobname or ujobid argument */
1556 uint32_t jobid = 0 ;
1558 if (!open_client_db(ua)) {
1559 ua->error_msg(_("ERR: Can't open db\n")) ;
1563 for (int i=1; i<ua->argc; i++) {
1564 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1568 jobid = str_to_int64(ua->argv[i]);
1570 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1571 strcasecmp(ua->argk[i], "job") == 0) {
1575 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1577 jobid = jcr->JobId ;
1581 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1585 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1587 jobid = jcr->JobId ;
1595 ua->error_msg(_("ERR: Job was not found\n"));
1600 * We wait the end of job
1603 bmicrosleep(0, 200000); /* let job actually start */
1604 for (bool running=true; running; ) {
1607 jcr=get_jcr_by_id(jobid) ;
1620 * We have to get JobStatus
1624 char jobstatus = '?'; /* Unknown by default */
1627 bsnprintf(buf, sizeof(buf),
1628 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1631 db_sql_query(ua->db, buf,
1632 status_handler, (void *)&jobstatus);
1634 switch (jobstatus) {
1636 status = 1 ; /* Warning */
1640 case JS_ErrorTerminated:
1642 status = 2 ; /* Critical */
1646 status = 0 ; /* Ok */
1650 status = 3 ; /* Unknown */
1654 ua->send_msg("JobId=%i\n", jobid) ;
1655 ua->send_msg("JobStatus=%s (%c)\n",
1656 job_status_to_str(jobstatus),
1659 if (ua->gui || ua->api) {
1660 ua->send_msg("ExitStatus=%i\n", status) ;
1667 static int help_cmd(UAContext *ua, const char *cmd)
1671 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1672 for (i=0; i<comsize; i++) {
1673 ua->send_msg(_(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1675 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1679 int qhelp_cmd(UAContext *ua, const char *cmd)
1683 for (i=0; i<comsize; i++) {
1684 ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1689 static int version_cmd(UAContext *ua, const char *cmd)
1691 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
1692 HOST_OS, DISTNAME, DISTVER);
1697 * This call explicitly checks for a catalog=xxx and
1698 * if given, opens that catalog. It also checks for
1699 * client=xxx and if found, opens the catalog
1700 * corresponding to that client. If we still don't
1701 * have a catalog, look for a Job keyword and get the
1702 * catalog from its client record.
1704 bool open_client_db(UAContext *ua)
1711 /* Try for catalog keyword */
1712 i = find_arg_with_value(ua, NT_("catalog"));
1714 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1715 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1718 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
1720 if (ua->catalog && ua->catalog != catalog) {
1723 ua->catalog = catalog;
1728 /* Try for client keyword */
1729 i = find_arg_with_value(ua, NT_("client"));
1731 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1732 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1735 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1737 catalog = client->catalog;
1738 if (ua->catalog && ua->catalog != catalog) {
1741 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1742 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1745 ua->catalog = catalog;
1750 /* Try for Job keyword */
1751 i = find_arg_with_value(ua, NT_("job"));
1753 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1754 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1757 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1759 catalog = job->client->catalog;
1760 if (ua->catalog && ua->catalog != catalog) {
1763 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1764 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1767 ua->catalog = catalog;
1777 * Open the catalog database.
1779 bool open_db(UAContext *ua)
1785 ua->catalog = get_catalog_resource(ua);
1787 ua->error_msg( _("Could not find a Catalog resource\n"));
1792 ua->jcr->catalog = ua->catalog;
1794 Dmsg0(150, "Open database\n");
1795 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1796 ua->catalog->db_password, ua->catalog->db_address,
1797 ua->catalog->db_port, ua->catalog->db_socket,
1798 ua->catalog->mult_db_connections);
1799 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1800 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1801 ua->catalog->db_name);
1803 ua->error_msg("%s", db_strerror(ua->db));
1808 ua->jcr->db = ua->db;
1810 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
1812 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1816 void close_db(UAContext *ua)
1819 db_close_database(ua->jcr, ua->db);