3 * Bacula Director -- User Agent Commands
5 * Kern Sibbald, September MM
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
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)
157 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
162 while (ua->jcr->wstorage->size()) {
163 ua->jcr->wstorage->remove(0);
166 len = strlen(ua->argk[0]);
167 for (i=0; i<comsize; i++) { /* search for command */
168 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
169 /* Check if command permitted, but "quit" is always OK */
170 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
171 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
174 stat = (*commands[i].func)(ua, cmd); /* go execute command */
180 bnet_fsend(ua->UA_sock, _("%s: is an invalid command.\n"), ua->argk[0]);
186 * This is a common routine used to stuff the Pool DB record defaults
187 * into the Media DB record just before creating a media (Volume)
190 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
192 mr->PoolId = pr->PoolId;
193 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
194 mr->Recycle = pr->Recycle;
195 mr->VolRetention = pr->VolRetention;
196 mr->VolUseDuration = pr->VolUseDuration;
197 mr->MaxVolJobs = pr->MaxVolJobs;
198 mr->MaxVolFiles = pr->MaxVolFiles;
199 mr->MaxVolBytes = pr->MaxVolBytes;
200 mr->LabelType = pr->LabelType;
206 * Add Volumes to an existing Pool
208 static int add_cmd(UAContext *ua, const char *cmd)
212 int num, i, max, startnum;
214 char name[MAX_NAME_LENGTH];
216 int Slot = 0, InChanger = 0;
219 "You probably don't want to be using this command since it\n"
220 "creates database records without labeling the Volumes.\n"
221 "You probably want to use the \"label\" command.\n\n"));
223 if (!open_client_db(ua)) {
227 memset(&pr, 0, sizeof(pr));
228 memset(&mr, 0, sizeof(mr));
230 if (!get_pool_dbr(ua, &pr)) {
234 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
235 pr.MaxVols, pr.PoolType);
237 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
238 bsendmsg(ua, _("Pool already has maximum volumes=%d\n"), pr.MaxVols);
240 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
243 pr.MaxVols = ua->pint32_val;
248 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
249 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
250 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
254 if (pr.MaxVols == 0) {
257 max = pr.MaxVols - pr.NumVols;
261 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
262 if (!get_pint(ua, buf)) {
265 num = ua->pint32_val;
266 if (num < 0 || num > max) {
267 bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
274 if (!get_cmd(ua, _("Enter Volume name: "))) {
278 if (!get_cmd(ua, _("Enter base volume name: "))) {
282 /* Don't allow | in Volume name because it is the volume separator character */
283 if (!is_volume_name_legal(ua, ua->cmd)) {
286 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
287 bsendmsg(ua, _("Volume name too long.\n"));
290 if (strlen(ua->cmd) == 0) {
291 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
295 bstrncpy(name, ua->cmd, sizeof(name));
297 bstrncat(name, "%04d", sizeof(name));
300 if (!get_pint(ua, _("Enter the starting number: "))) {
303 startnum = ua->pint32_val;
305 bsendmsg(ua, _("Start number must be greater than zero.\n"));
315 if (store && store->autochanger) {
316 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
319 Slot = ua->pint32_val;
320 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
323 InChanger = ua->pint32_val;
326 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
327 for (i=startnum; i < num+startnum; i++) {
328 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
330 mr.InChanger = InChanger;
331 mr.StorageId = store->StorageId;
333 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
334 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
335 bsendmsg(ua, "%s", db_strerror(ua->db));
339 first_id = mr.PoolId;
343 Dmsg0(200, "Update pool record.\n");
344 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
345 bsendmsg(ua, "%s", db_strerror(ua->db));
348 bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
354 * Turn auto mount on/off
359 int automount_cmd(UAContext *ua, const char *cmd)
364 if (!get_cmd(ua, _("Turn on or off? "))) {
372 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
380 static int cancel_cmd(UAContext *ua, const char *cmd)
385 char JobName[MAX_NAME_LENGTH];
387 if (!open_client_db(ua)) {
391 for (i=1; i<ua->argc; i++) {
392 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
397 JobId = str_to_int64(ua->argv[i]);
398 if (!(jcr=get_jcr_by_id(JobId))) {
399 bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
403 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
407 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
408 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
409 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
410 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
413 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
417 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
418 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
419 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
420 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
426 /* If we still do not have a jcr,
427 * throw up a list and ask the user to select one.
431 /* Count Jobs running */
433 if (jcr->JobId == 0) { /* this is us */
441 bsendmsg(ua, _("No Jobs running.\n"));
444 start_prompt(ua, _("Select Job:\n"));
447 if (jcr->JobId == 0) { /* this is us */
450 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
455 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
459 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
463 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
464 jcr = get_jcr_by_full_name(JobName);
466 bsendmsg(ua, _("Job %s not found.\n"), JobName);
471 ret = cancel_job(ua, jcr);
477 * This is a common routine to create or update a
478 * Pool DB base record from a Pool Resource. We handle
479 * the setting of MaxVols and NumVols slightly differently
480 * depending on if we are creating the Pool or we are
481 * simply bringing it into agreement with the resource (updage).
483 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
485 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
486 if (op == POOL_OP_CREATE) {
487 pr->MaxVols = pool->max_volumes;
489 } else { /* update pool */
490 if (pr->MaxVols != pool->max_volumes) {
491 pr->MaxVols = pool->max_volumes;
493 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
494 pr->MaxVols = pr->NumVols;
497 pr->LabelType = pool->LabelType;
498 pr->UseOnce = pool->use_volume_once;
499 pr->UseCatalog = pool->use_catalog;
500 pr->Recycle = pool->Recycle;
501 pr->VolRetention = pool->VolRetention;
502 pr->VolUseDuration = pool->VolUseDuration;
503 pr->MaxVolJobs = pool->MaxVolJobs;
504 pr->MaxVolFiles = pool->MaxVolFiles;
505 pr->MaxVolBytes = pool->MaxVolBytes;
506 pr->AutoPrune = pool->AutoPrune;
507 pr->Recycle = pool->Recycle;
508 if (pool->label_format) {
509 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
511 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
517 * Create a pool record from a given Pool resource
518 * Also called from backup.c
519 * Returns: -1 on error
520 * 0 record already exists
524 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
528 memset(&pr, 0, sizeof(POOL_DBR));
530 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
532 if (db_get_pool_record(jcr, db, &pr)) {
534 if (op == POOL_OP_UPDATE) { /* update request */
535 set_pooldbr_from_poolres(&pr, pool, op);
536 db_update_pool_record(jcr, db, &pr);
538 return 0; /* exists */
541 set_pooldbr_from_poolres(&pr, pool, op);
543 if (!db_create_pool_record(jcr, db, &pr)) {
544 return -1; /* error */
552 * Create a Pool Record in the database.
553 * It is always created from the Resource record.
555 static int create_cmd(UAContext *ua, const char *cmd)
559 if (!open_client_db(ua)) {
563 pool = get_pool_resource(ua);
568 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
570 bsendmsg(ua, _("Error: Pool %s already exists.\n"
571 "Use update to change it.\n"), pool->name());
575 bsendmsg(ua, "%s", db_strerror(ua->db));
581 bsendmsg(ua, _("Pool %s created.\n"), pool->name());
586 extern DIRRES *director;
589 * Python control command
590 * python restart (restarts interpreter)
592 static int python_cmd(UAContext *ua, const char *cmd)
594 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
595 term_python_interpreter();
596 init_python_interpreter(director->name(),
597 director->scripts_directory, "DirStartUp");
598 bsendmsg(ua, _("Python interpreter restarted.\n"));
600 bsendmsg(ua, _("Nothing done.\n"));
607 * Set a new address in a Client resource. We do this only
608 * if the Console name is the same as the Client name
609 * and the Console can access the client.
611 static int setip_cmd(UAContext *ua, const char *cmd)
615 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
616 bsendmsg(ua, _("Unauthorized command from this console.\n"));
620 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->name());
623 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->name());
626 if (client->address) {
627 free(client->address);
629 /* MA Bug 6 remove ifdef */
630 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
631 client->address = bstrdup(buf);
632 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
633 client->name(), client->address);
640 static void do_en_disable_cmd(UAContext *ua, bool setting)
645 i = find_arg_with_value(ua, NT_("job"));
647 job = select_job_resource(ua);
653 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
657 bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
661 if (!acl_access_ok(ua, Job_ACL, job->name())) {
662 bsendmsg(ua, _("Unauthorized command from this console.\n"));
665 job->enabled = setting;
666 bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
670 static int enable_cmd(UAContext *ua, const char *cmd)
672 do_en_disable_cmd(ua, true);
676 static int disable_cmd(UAContext *ua, const char *cmd)
678 do_en_disable_cmd(ua, false);
683 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
689 lstore.store = store;
690 pm_strcpy(lstore.store_source, _("unknown source"));
691 set_wstorage(jcr, &lstore);
692 /* Try connecting for up to 15 seconds */
693 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
694 store->name(), store->address, store->SDport);
695 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
696 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
699 Dmsg0(120, _("Connected to storage daemon\n"));
700 sd = jcr->store_bsock;
701 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
702 if (bnet_recv(sd) >= 0) {
703 bsendmsg(ua, "%s", sd->msg);
705 bnet_sig(sd, BNET_TERMINATE);
707 jcr->store_bsock = NULL;
711 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
715 /* Connect to File daemon */
717 ua->jcr->client = client;
718 /* Try to connect for 15 seconds */
719 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
720 client->name(), client->address, client->FDport);
721 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
722 bsendmsg(ua, _("Failed to connect to Client.\n"));
725 Dmsg0(120, "Connected to file daemon\n");
726 fd = ua->jcr->file_bsock;
727 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
728 if (bnet_recv(fd) >= 0) {
729 bsendmsg(ua, "%s", fd->msg);
731 bnet_sig(fd, BNET_TERMINATE);
733 ua->jcr->file_bsock = NULL;
738 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
740 STORE *store, **unique_store;
741 CLIENT *client, **unique_client;
747 /* Count Storage items */
751 foreach_res(store, R_STORAGE) {
754 unique_store = (STORE **) malloc(i * sizeof(STORE));
755 /* Find Unique Storage address/port */
756 store = (STORE *)GetNextRes(R_STORAGE, NULL);
758 unique_store[i++] = store;
759 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
761 for (j=0; j<i; j++) {
762 if (strcmp(unique_store[j]->address, store->address) == 0 &&
763 unique_store[j]->SDport == store->SDport) {
769 unique_store[i++] = store;
770 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
775 /* Call each unique Storage daemon */
776 for (j=0; j<i; j++) {
777 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
781 /* Count Client items */
785 foreach_res(client, R_CLIENT) {
788 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
789 /* Find Unique Client address/port */
790 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
792 unique_client[i++] = client;
793 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
795 for (j=0; j<i; j++) {
796 if (strcmp(unique_client[j]->address, client->address) == 0 &&
797 unique_client[j]->FDport == client->FDport) {
803 unique_client[i++] = client;
804 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
809 /* Call each unique File daemon */
810 for (j=0; j<i; j++) {
811 do_client_setdebug(ua, unique_client[j], level, trace_flag);
817 * setdebug level=nn all trace=1/0
819 static int setdebug_cmd(UAContext *ua, const char *cmd)
827 if (!open_client_db(ua)) {
830 Dmsg1(120, "setdebug:%s:\n", cmd);
833 i = find_arg_with_value(ua, "level");
835 level = atoi(ua->argv[i]);
838 if (!get_pint(ua, _("Enter new debug level: "))) {
841 level = ua->pint32_val;
844 /* Look for trace flag. -1 => not change */
845 i = find_arg_with_value(ua, "trace");
847 trace_flag = atoi(ua->argv[i]);
848 if (trace_flag > 0) {
854 for (i=1; i<ua->argc; i++) {
855 if (strcasecmp(ua->argk[i], "all") == 0) {
856 do_all_setdebug(ua, level, trace_flag);
859 if (strcasecmp(ua->argk[i], "dir") == 0 ||
860 strcasecmp(ua->argk[i], "director") == 0) {
862 set_trace(trace_flag);
865 if (strcasecmp(ua->argk[i], "client") == 0 ||
866 strcasecmp(ua->argk[i], "fd") == 0) {
869 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
871 do_client_setdebug(ua, client, level, trace_flag);
875 client = select_client_resource(ua);
877 do_client_setdebug(ua, client, level, trace_flag);
882 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
883 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
884 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
887 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
889 do_storage_setdebug(ua, store, level, trace_flag);
893 store = get_storage_resource(ua, false/*no default*/);
895 do_storage_setdebug(ua, store, level, trace_flag);
901 * We didn't find an appropriate keyword above, so
904 start_prompt(ua, _("Available daemons are: \n"));
905 add_prompt(ua, _("Director"));
906 add_prompt(ua, _("Storage"));
907 add_prompt(ua, _("Client"));
908 add_prompt(ua, _("All"));
909 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
910 case 0: /* Director */
912 set_trace(trace_flag);
915 store = get_storage_resource(ua, false/*no default*/);
917 do_storage_setdebug(ua, store, level, trace_flag);
921 client = select_client_resource(ua);
923 do_client_setdebug(ua, client, level, trace_flag);
927 do_all_setdebug(ua, level, trace_flag);
936 * Turn debug tracing to file on/off
938 static int trace_cmd(UAContext *ua, const char *cmd)
943 if (!get_cmd(ua, _("Turn on or off? "))) {
951 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
956 static int var_cmd(UAContext *ua, const char *cmd)
958 POOLMEM *val = get_pool_memory(PM_FNAME);
961 if (!open_client_db(ua)) {
964 for (var=ua->cmd; *var != ' '; ) { /* skip command */
967 while (*var == ' ') { /* skip spaces */
970 Dmsg1(100, "Var=%s:\n", var);
971 variable_expansion(ua->jcr, var, &val);
972 bsendmsg(ua, "%s\n", val);
973 free_pool_memory(val);
977 static int estimate_cmd(UAContext *ua, const char *cmd)
980 CLIENT *client = NULL;
981 FILESET *fileset = NULL;
983 char since[MAXSTRING];
986 jcr->JobLevel = L_FULL;
987 for (int i=1; i<ua->argc; i++) {
988 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
989 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
991 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
995 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
997 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
998 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
999 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1005 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1007 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
1008 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1009 bsendmsg(ua, _("No authorization for FileSet \"%s\"\n"), fileset->name());
1015 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1019 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1020 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1021 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1026 if (!job && !(client && fileset)) {
1027 if (!(job = select_job_resource(ua))) {
1032 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1034 bsendmsg(ua, _("No job specified.\n"));
1037 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1038 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1043 client = job->client;
1046 fileset = job->fileset;
1048 jcr->client = client;
1049 jcr->fileset = fileset;
1051 ua->catalog = client->catalog;
1058 jcr->JobType = JT_BACKUP;
1059 init_jcr_job_record(jcr);
1061 if (!get_or_create_client_record(jcr)) {
1064 if (!get_or_create_fileset_record(jcr)) {
1068 get_level_since_time(ua->jcr, since, sizeof(since));
1070 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1071 job->client->name(), job->client->address, job->client->FDport);
1072 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1073 bsendmsg(ua, _("Failed to connect to Client.\n"));
1077 if (!send_include_list(jcr)) {
1078 bsendmsg(ua, _("Error sending include list.\n"));
1082 if (!send_exclude_list(jcr)) {
1083 bsendmsg(ua, _("Error sending exclude list.\n"));
1087 if (!send_level_command(jcr)) {
1091 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1092 while (bnet_recv(jcr->file_bsock) >= 0) {
1093 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1097 if (jcr->file_bsock) {
1098 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1099 bnet_close(jcr->file_bsock);
1100 jcr->file_bsock = NULL;
1109 static int time_cmd(UAContext *ua, const char *cmd)
1112 time_t ttime = time(NULL);
1114 (void)localtime_r(&ttime, &tm);
1115 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1116 bsendmsg(ua, "%s\n", sdt);
1121 * reload the conf file
1123 extern "C" void reload_config(int sig);
1125 static int reload_cmd(UAContext *ua, const char *cmd)
1132 * Delete Pool records (should purge Media with it).
1134 * delete pool=<pool-name>
1135 * delete volume pool=<pool-name> volume=<name>
1138 static int delete_cmd(UAContext *ua, const char *cmd)
1140 static const char *keywords[] = {
1146 if (!open_client_db(ua)) {
1150 switch (find_arg_keyword(ua, keywords)) {
1159 while ((i=find_arg(ua, "jobid")) > 0) {
1161 *ua->argk[i] = 0; /* zap keyword already visited */
1169 "In general it is not a good idea to delete either a\n"
1170 "Pool or a Volume since they may contain data.\n\n"));
1172 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1183 bsendmsg(ua, _("Nothing done.\n"));
1191 * delete_job has been modified to parse JobID lists like the
1193 * delete JobID=3,4,6,7-11,14
1195 * Thanks to Phil Stracchino for the above addition.
1198 static void delete_job(UAContext *ua)
1203 int i = find_arg_with_value(ua, NT_("jobid"));
1205 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1206 s = bstrdup(ua->argv[i]);
1209 * We could use strtok() here. But we're not going to, because:
1210 * (a) strtok() is deprecated, having been replaced by strsep();
1211 * (b) strtok() is broken in significant ways.
1212 * we could use strsep() instead, but it's not universally available.
1213 * so we grow our own using strchr().
1215 sep = strchr(tok, ',');
1216 while (sep != NULL) {
1218 if (strchr(tok, '-')) {
1219 delete_job_id_range(ua, tok);
1221 JobId = str_to_int64(tok);
1222 do_job_delete(ua, JobId);
1225 sep = strchr(tok, ',');
1227 /* pick up the last token */
1228 if (strchr(tok, '-')) {
1229 delete_job_id_range(ua, tok);
1231 JobId = str_to_int64(tok);
1232 do_job_delete(ua, JobId);
1237 JobId = str_to_int64(ua->argv[i]);
1238 do_job_delete(ua, JobId);
1240 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1243 JobId = ua->int64_val;
1244 do_job_delete(ua, JobId);
1249 * we call delete_job_id_range to parse range tokens and iterate over ranges
1251 static void delete_job_id_range(UAContext *ua, char *tok)
1256 tok2 = strchr(tok, '-');
1259 j1 = str_to_int64(tok);
1260 j2 = str_to_int64(tok2);
1261 for (j=j1; j<=j2; j++) {
1262 do_job_delete(ua, j);
1267 * do_job_delete now performs the actual delete operation atomically
1270 static void do_job_delete(UAContext *ua, JobId_t JobId)
1272 POOL_MEM query(PM_MESSAGE);
1275 purge_files_from_job(ua, JobId);
1276 purge_job_from_catalog(ua, JobId);
1277 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1281 * Delete media records from database -- dangerous
1283 static int delete_volume(UAContext *ua)
1287 if (!select_media_dbr(ua, &mr)) {
1290 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1291 "and all Jobs saved on that volume from the Catalog\n"),
1294 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1297 if (ua->pint32_val) {
1298 db_delete_media_record(ua->jcr, ua->db, &mr);
1304 * Delete a pool record from the database -- dangerous
1306 static int delete_pool(UAContext *ua)
1310 memset(&pr, 0, sizeof(pr));
1312 if (!get_pool_dbr(ua, &pr)) {
1315 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1318 if (ua->pint32_val) {
1319 db_delete_pool_record(ua->jcr, ua->db, &pr);
1325 static void do_mount_cmd(UAContext *ua, const char *command)
1330 char dev_name[MAX_NAME_LENGTH];
1334 if (!open_client_db(ua)) {
1337 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1339 store.store = get_storage_resource(ua, true/*arg is storage*/);
1340 pm_strcpy(store.store_source, _("unknown source"));
1344 set_wstorage(jcr, &store);
1345 drive = get_storage_drive(ua, store.store);
1346 if (strcmp(command, "mount") == 0) {
1347 slot = get_storage_slot(ua, store.store);
1350 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1351 store.store->media_type, store.store->dev_name(), drive);
1353 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1354 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1357 sd = jcr->store_bsock;
1358 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1359 bash_spaces(dev_name);
1361 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1363 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1365 while (bnet_recv(sd) >= 0) {
1366 bsendmsg(ua, "%s", sd->msg);
1368 bnet_sig(sd, BNET_TERMINATE);
1370 jcr->store_bsock = NULL;
1374 * mount [storage=<name>] [drive=nn] [slot=mm]
1376 static int mount_cmd(UAContext *ua, const char *cmd)
1378 do_mount_cmd(ua, "mount"); /* mount */
1384 * unmount [storage=<name>] [drive=nn]
1386 static int unmount_cmd(UAContext *ua, const char *cmd)
1388 do_mount_cmd(ua, "unmount"); /* unmount */
1394 * release [storage=<name>] [drive=nn]
1396 static int release_cmd(UAContext *ua, const char *cmd)
1398 do_mount_cmd(ua, "release"); /* release */
1405 * use catalog=<name>
1407 static int use_cmd(UAContext *ua, const char *cmd)
1409 CAT *oldcatalog, *catalog;
1412 close_db(ua); /* close any previously open db */
1413 oldcatalog = ua->catalog;
1415 if (!(catalog = get_catalog_resource(ua))) {
1416 ua->catalog = oldcatalog;
1418 ua->catalog = catalog;
1421 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1422 ua->catalog->name(), ua->catalog->db_name);
1427 int quit_cmd(UAContext *ua, const char *cmd)
1433 /* Handler to get job status */
1434 static int status_handler(void *ctx, int num_fields, char **row)
1436 char *val = (char *)ctx;
1441 *val = '?'; /* Unknown by default */
1448 * Wait until no job is running
1450 int wait_cmd(UAContext *ua, const char *cmd)
1455 * Wait until no job is running
1457 if (ua->argc == 1) {
1458 bmicrosleep(0, 200000); /* let job actually start */
1459 for (bool running=true; running; ) {
1462 if (jcr->JobId != 0) {
1476 /* we have jobid, jobname or ujobid argument */
1478 uint32_t jobid = 0 ;
1480 if (!open_client_db(ua)) {
1481 bsendmsg(ua, _("ERR: Can't open db\n")) ;
1485 for (int i=1; i<ua->argc; i++) {
1486 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1490 jobid = str_to_int64(ua->argv[i]);
1492 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1493 strcasecmp(ua->argk[i], "job") == 0) {
1497 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1499 jobid = jcr->JobId ;
1503 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1507 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1509 jobid = jcr->JobId ;
1517 bsendmsg(ua, _("ERR: Job was not found\n"));
1522 * We wait the end of job
1525 bmicrosleep(0, 200000); /* let job actually start */
1526 for (bool running=true; running; ) {
1529 jcr=get_jcr_by_id(jobid) ;
1542 * We have to get JobStatus
1546 char jobstatus = '?'; /* Unknown by default */
1549 bsnprintf(buf, sizeof(buf),
1550 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1553 db_sql_query(ua->db, buf,
1554 status_handler, (void *)&jobstatus);
1556 switch (jobstatus) {
1558 status = 1 ; /* Warning */
1562 case JS_ErrorTerminated:
1564 status = 2 ; /* Critical */
1568 status = 0 ; /* Ok */
1572 status = 3 ; /* Unknown */
1576 bsendmsg(ua, "JobId=%i\n", jobid) ;
1577 bsendmsg(ua, "JobStatus=%s (%c)\n",
1578 job_status_to_str(jobstatus),
1582 bsendmsg(ua, "ExitStatus=%i\n", status) ;
1589 static int help_cmd(UAContext *ua, const char *cmd)
1593 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1594 for (i=0; i<comsize; i++) {
1595 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1597 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1601 int qhelp_cmd(UAContext *ua, const char *cmd)
1605 for (i=0; i<comsize; i++) {
1606 bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1611 static int version_cmd(UAContext *ua, const char *cmd)
1613 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1618 * This call explicitly checks for a catalog=xxx and
1619 * if given, opens that catalog. It also checks for
1620 * client=xxx and if found, opens the catalog
1621 * corresponding to that client. If we still don't
1622 * have a catalog, look for a Job keyword and get the
1623 * catalog from its client record.
1625 bool open_client_db(UAContext *ua)
1632 /* Try for catalog keyword */
1633 i = find_arg_with_value(ua, NT_("catalog"));
1635 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1636 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1639 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
1641 if (ua->catalog && ua->catalog != catalog) {
1644 ua->catalog = catalog;
1649 /* Try for client keyword */
1650 i = find_arg_with_value(ua, NT_("client"));
1652 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1653 bsendmsg(ua, _("No authorization for Client \"%s\"\n"), ua->argv[i]);
1656 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1658 catalog = client->catalog;
1659 if (ua->catalog && ua->catalog != catalog) {
1662 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1663 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1666 ua->catalog = catalog;
1671 /* Try for Job keyword */
1672 i = find_arg_with_value(ua, NT_("job"));
1674 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1675 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), ua->argv[i]);
1678 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1680 catalog = job->client->catalog;
1681 if (ua->catalog && ua->catalog != catalog) {
1684 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1685 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1688 ua->catalog = catalog;
1698 * Open the catalog database.
1700 bool open_db(UAContext *ua)
1706 ua->catalog = get_catalog_resource(ua);
1708 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1713 ua->jcr->catalog = ua->catalog;
1715 Dmsg0(150, "Open database\n");
1716 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1717 ua->catalog->db_password, ua->catalog->db_address,
1718 ua->catalog->db_port, ua->catalog->db_socket,
1719 ua->catalog->mult_db_connections);
1720 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1721 bsendmsg(ua, _("Could not open catalog database \"%s\".\n"),
1722 ua->catalog->db_name);
1724 bsendmsg(ua, "%s", db_strerror(ua->db));
1729 ua->jcr->db = ua->db;
1730 bsendmsg(ua, _("Using Catalog \"%s\"\n"), ua->catalog->name());
1731 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1735 void close_db(UAContext *ua)
1738 db_close_database(ua->jcr, ua->db);