3 * Bacula Director -- User Agent Commands
5 * Kern Sibbald, September MM
10 Copyright (C) 2000-2006 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
27 /* Imported subroutines */
29 /* Imported variables */
32 extern struct s_res resources[];
33 extern char my_name[];
34 extern jobq_t job_queue; /* job queue */
37 /* Imported functions */
38 extern int status_cmd(UAContext *ua, const char *cmd);
39 extern int list_cmd(UAContext *ua, const char *cmd);
40 extern int llist_cmd(UAContext *ua, const char *cmd);
41 extern int show_cmd(UAContext *ua, const char *cmd);
42 extern int messagescmd(UAContext *ua, const char *cmd);
43 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
44 extern int gui_cmd(UAContext *ua, const char *cmd);
45 extern int sqlquerycmd(UAContext *ua, const char *cmd);
46 extern int querycmd(UAContext *ua, const char *cmd);
47 extern int retentioncmd(UAContext *ua, const char *cmd);
48 extern int prunecmd(UAContext *ua, const char *cmd);
49 extern int purgecmd(UAContext *ua, const char *cmd);
50 extern int restore_cmd(UAContext *ua, const char *cmd);
51 extern int label_cmd(UAContext *ua, const char *cmd);
52 extern int relabel_cmd(UAContext *ua, const char *cmd);
53 extern int update_cmd(UAContext *ua, const char *cmd);
55 /* Forward referenced functions */
56 static int add_cmd(UAContext *ua, const char *cmd);
57 static int create_cmd(UAContext *ua, const char *cmd);
58 static int cancel_cmd(UAContext *ua, const char *cmd);
59 static int setdebug_cmd(UAContext *ua, const char *cmd);
60 static int trace_cmd(UAContext *ua, const char *cmd);
61 static int var_cmd(UAContext *ua, const char *cmd);
62 static int estimate_cmd(UAContext *ua, const char *cmd);
63 static int help_cmd(UAContext *ua, const char *cmd);
64 static int delete_cmd(UAContext *ua, const char *cmd);
65 static int use_cmd(UAContext *ua, const char *cmd);
66 static int unmount_cmd(UAContext *ua, const char *cmd);
67 static int version_cmd(UAContext *ua, const char *cmd);
68 static int automount_cmd(UAContext *ua, const char *cmd);
69 static int time_cmd(UAContext *ua, const char *cmd);
70 static int reload_cmd(UAContext *ua, const char *cmd);
71 static int delete_volume(UAContext *ua);
72 static int delete_pool(UAContext *ua);
73 static void delete_job(UAContext *ua);
74 static int mount_cmd(UAContext *ua, const char *cmd);
75 static int release_cmd(UAContext *ua, const char *cmd);
76 static int wait_cmd(UAContext *ua, const char *cmd);
77 static int setip_cmd(UAContext *ua, const char *cmd);
78 static int python_cmd(UAContext *ua, const char *cmd);
79 static void do_job_delete(UAContext *ua, JobId_t JobId);
80 static void delete_job_id_range(UAContext *ua, char *tok);
82 int qhelp_cmd(UAContext *ua, const char *cmd);
83 int quit_cmd(UAContext *ua, const char *cmd);
86 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
87 static struct cmdstruct commands[] = {
88 { N_("add"), add_cmd, _("add media to a pool")},
89 { N_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
90 { N_("automount"), automount_cmd, _("automount [on|off] -- after label")},
91 { N_("cancel"), cancel_cmd, _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
92 { N_("create"), create_cmd, _("create DB Pool from resource")},
93 { N_("delete"), delete_cmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
94 { N_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")},
95 { N_("exit"), quit_cmd, _("exit = quit")},
96 { N_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode")},
97 { N_("help"), help_cmd, _("print this command")},
98 { N_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
99 { N_("label"), label_cmd, _("label a tape")},
100 { N_("llist"), llist_cmd, _("full or long list like list command")},
101 { N_("messages"), messagescmd, _("messages")},
102 { N_("mount"), mount_cmd, _("mount <storage-name>")},
103 { N_("prune"), prunecmd, _("prune expired records from catalog")},
104 { N_("purge"), purgecmd, _("purge records from catalog")},
105 { N_("python"), python_cmd, _("python control commands")},
106 { N_("quit"), quit_cmd, _("quit")},
107 { N_("query"), querycmd, _("query catalog")},
108 { N_("restore"), restore_cmd, _("restore files")},
109 { N_("relabel"), relabel_cmd, _("relabel a tape")},
110 { N_("release"), release_cmd, _("release <storage-name>")},
111 { N_("reload"), reload_cmd, _("reload conf file")},
112 { N_("run"), run_cmd, _("run <job-name>")},
113 { N_("status"), status_cmd, _("status [storage | client]=<name>")},
114 { N_("setdebug"), setdebug_cmd, _("sets debug level")},
115 { N_("setip"), setip_cmd, _("sets new client address -- if authorized")},
116 { N_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")},
117 { N_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")},
118 { N_("time"), time_cmd, _("print current time")},
119 { N_("trace"), trace_cmd, _("turn on/off trace to file")},
120 { N_("unmount"), unmount_cmd, _("unmount <storage-name>")},
121 { N_("umount"), unmount_cmd, _("umount <storage-name> for old-time Unix guys")},
122 { N_("update"), update_cmd, _("update Volume, Pool or slots")},
123 { N_("use"), use_cmd, _("use catalog xxx")},
124 { N_("var"), var_cmd, _("does variable expansion")},
125 { N_("version"), version_cmd, _("print Director version")},
126 { N_("wait"), wait_cmd, _("wait until no jobs are running")},
128 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
131 * Execute a command from the UA
133 int do_a_command(UAContext *ua, const char *cmd)
141 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
146 while (ua->jcr->storage->size()) {
147 ua->jcr->storage->remove(0);
150 len = strlen(ua->argk[0]);
151 for (i=0; i<comsize; i++) { /* search for command */
152 if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) {
153 /* Check if command permitted, but "quit" is always OK */
154 if (strcmp(ua->argk[0], "quit") != 0 &&
155 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
158 stat = (*commands[i].func)(ua, cmd); /* go execute command */
164 bnet_fsend(ua->UA_sock, _("%s: is an illegal command.\n"), ua->argk[0]);
170 * This is a common routine used to stuff the Pool DB record defaults
171 * into the Media DB record just before creating a media (Volume)
174 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
176 mr->PoolId = pr->PoolId;
177 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));
178 mr->Recycle = pr->Recycle;
179 mr->VolRetention = pr->VolRetention;
180 mr->VolUseDuration = pr->VolUseDuration;
181 mr->MaxVolJobs = pr->MaxVolJobs;
182 mr->MaxVolFiles = pr->MaxVolFiles;
183 mr->MaxVolBytes = pr->MaxVolBytes;
184 mr->LabelType = pr->LabelType;
189 * Add Volumes to an existing Pool
191 static int add_cmd(UAContext *ua, const char *cmd)
195 int num, i, max, startnum;
197 char name[MAX_NAME_LENGTH];
199 int Slot = 0, InChanger = 0;
202 "You probably don't want to be using this command since it\n"
203 "creates database records without labeling the Volumes.\n"
204 "You probably want to use the \"label\" command.\n\n"));
210 memset(&pr, 0, sizeof(pr));
211 memset(&mr, 0, sizeof(mr));
213 if (!get_pool_dbr(ua, &pr)) {
217 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
218 pr.MaxVols, pr.PoolType);
220 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
221 bsendmsg(ua, _("Pool already has maximum volumes = %d\n"), pr.MaxVols);
223 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
226 pr.MaxVols = ua->pint32_val;
231 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
232 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
233 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
237 if (pr.MaxVols == 0) {
240 max = pr.MaxVols - pr.NumVols;
244 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
245 if (!get_pint(ua, buf)) {
248 num = ua->pint32_val;
249 if (num < 0 || num > max) {
250 bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
257 if (!get_cmd(ua, _("Enter Volume name: "))) {
261 if (!get_cmd(ua, _("Enter base volume name: "))) {
265 /* Don't allow | in Volume name because it is the volume separator character */
266 if (!is_volume_name_legal(ua, ua->cmd)) {
269 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
270 bsendmsg(ua, _("Volume name too long.\n"));
273 if (strlen(ua->cmd) == 0) {
274 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
278 bstrncpy(name, ua->cmd, sizeof(name));
280 bstrncat(name, "%04d", sizeof(name));
283 if (!get_pint(ua, _("Enter the starting number: "))) {
286 startnum = ua->pint32_val;
288 bsendmsg(ua, _("Start number must be greater than zero.\n"));
298 if (store && store->autochanger) {
299 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
302 Slot = ua->pint32_val;
303 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
306 InChanger = ua->pint32_val;
309 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
310 for (i=startnum; i < num+startnum; i++) {
311 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
313 mr.InChanger = InChanger;
314 mr.StorageId = store->StorageId;
315 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
316 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
317 bsendmsg(ua, "%s", db_strerror(ua->db));
321 first_id = mr.PoolId;
325 Dmsg0(200, "Update pool record.\n");
326 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
327 bsendmsg(ua, "%s", db_strerror(ua->db));
330 bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
336 * Turn auto mount on/off
341 int automount_cmd(UAContext *ua, const char *cmd)
346 if (!get_cmd(ua, _("Turn on or off? "))) {
354 ua->automount = (strcasecmp(onoff, _("off")) == 0) ? 0 : 1;
362 static int cancel_cmd(UAContext *ua, const char *cmd)
367 char JobName[MAX_NAME_LENGTH];
373 for (i=1; i<ua->argc; i++) {
374 if (strcasecmp(ua->argk[i], _("jobid")) == 0) {
379 JobId = str_to_int64(ua->argv[i]);
380 if (!(jcr=get_jcr_by_id(JobId))) {
381 bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
385 } else if (strcasecmp(ua->argk[i], _("job")) == 0) {
389 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
390 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
391 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
392 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
397 /* If we still do not have a jcr,
398 * throw up a list and ask the user to select one.
402 /* Count Jobs running */
404 if (jcr->JobId == 0) { /* this is us */
412 bsendmsg(ua, _("No Jobs running.\n"));
415 start_prompt(ua, _("Select Job:\n"));
418 if (jcr->JobId == 0) { /* this is us */
421 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
426 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
430 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
434 /* NOTE! This increments the ref_count */
435 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
436 jcr = get_jcr_by_full_name(JobName);
438 bsendmsg(ua, _("Job %s not found.\n"), JobName);
443 ret = cancel_job(ua, jcr);
449 * This is a common routine to create or update a
450 * Pool DB base record from a Pool Resource. We handle
451 * the setting of MaxVols and NumVols slightly differently
452 * depending on if we are creating the Pool or we are
453 * simply bringing it into agreement with the resource (updage).
455 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
457 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
458 if (op == POOL_OP_CREATE) {
459 pr->MaxVols = pool->max_volumes;
461 } else { /* update pool */
462 if (pr->MaxVols != pool->max_volumes) {
463 pr->MaxVols = pool->max_volumes;
465 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
466 pr->MaxVols = pr->NumVols;
469 pr->LabelType = pool->LabelType;
470 pr->UseOnce = pool->use_volume_once;
471 pr->UseCatalog = pool->use_catalog;
472 pr->AcceptAnyVolume = pool->accept_any_volume;
473 pr->Recycle = pool->Recycle;
474 pr->VolRetention = pool->VolRetention;
475 pr->VolUseDuration = pool->VolUseDuration;
476 pr->MaxVolJobs = pool->MaxVolJobs;
477 pr->MaxVolFiles = pool->MaxVolFiles;
478 pr->MaxVolBytes = pool->MaxVolBytes;
479 pr->AutoPrune = pool->AutoPrune;
480 pr->Recycle = pool->Recycle;
481 if (pool->label_format) {
482 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
484 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
490 * Create a pool record from a given Pool resource
491 * Also called from backup.c
492 * Returns: -1 on error
493 * 0 record already exists
497 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
501 memset(&pr, 0, sizeof(POOL_DBR));
503 bstrncpy(pr.Name, pool->hdr.name, sizeof(pr.Name));
505 if (db_get_pool_record(jcr, db, &pr)) {
507 if (op == POOL_OP_UPDATE) { /* update request */
508 set_pooldbr_from_poolres(&pr, pool, op);
509 db_update_pool_record(jcr, db, &pr);
511 return 0; /* exists */
514 set_pooldbr_from_poolres(&pr, pool, op);
516 if (!db_create_pool_record(jcr, db, &pr)) {
517 return -1; /* error */
525 * Create a Pool Record in the database.
526 * It is always created from the Resource record.
528 static int create_cmd(UAContext *ua, const char *cmd)
536 pool = get_pool_resource(ua);
541 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
543 bsendmsg(ua, _("Error: Pool %s already exists.\n"
544 "Use update to change it.\n"), pool->hdr.name);
548 bsendmsg(ua, "%s", db_strerror(ua->db));
554 bsendmsg(ua, _("Pool %s created.\n"), pool->hdr.name);
559 extern DIRRES *director;
562 * Python control command
563 * python restart (restarts interpreter)
565 static int python_cmd(UAContext *ua, const char *cmd)
567 if (ua->argc >= 1 && strcasecmp(ua->argk[1], _("restart")) == 0) {
568 term_python_interpreter();
569 init_python_interpreter(director->hdr.name,
570 director->scripts_directory, "DirStartUp");
571 bsendmsg(ua, _("Python interpreter restarted.\n"));
573 bsendmsg(ua, _("Nothing done.\n"));
580 * Set a new address in a Client resource. We do this only
581 * if the Console name is the same as the Client name
582 * and the Console can access the client.
584 static int setip_cmd(UAContext *ua, const char *cmd)
588 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->hdr.name)) {
589 bsendmsg(ua, _("Illegal command from this console.\n"));
593 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->hdr.name);
596 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->hdr.name);
599 if (client->address) {
600 free(client->address);
602 /* MA Bug 6 remove ifdef */
603 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
604 client->address = bstrdup(buf);
605 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
606 client->hdr.name, client->address);
613 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
618 set_storage(jcr, store);
619 /* Try connecting for up to 15 seconds */
620 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
621 store->hdr.name, store->address, store->SDport);
622 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
623 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
626 Dmsg0(120, _("Connected to storage daemon\n"));
627 sd = jcr->store_bsock;
628 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
629 if (bnet_recv(sd) >= 0) {
630 bsendmsg(ua, "%s", sd->msg);
632 bnet_sig(sd, BNET_TERMINATE);
634 jcr->store_bsock = NULL;
638 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
642 /* Connect to File daemon */
644 ua->jcr->client = client;
645 /* Try to connect for 15 seconds */
646 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
647 client->hdr.name, client->address, client->FDport);
648 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
649 bsendmsg(ua, _("Failed to connect to Client.\n"));
652 Dmsg0(120, "Connected to file daemon\n");
653 fd = ua->jcr->file_bsock;
654 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
655 if (bnet_recv(fd) >= 0) {
656 bsendmsg(ua, "%s", fd->msg);
658 bnet_sig(fd, BNET_TERMINATE);
660 ua->jcr->file_bsock = NULL;
665 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
667 STORE *store, **unique_store;
668 CLIENT *client, **unique_client;
674 /* Count Storage items */
678 foreach_res(store, R_STORAGE) {
681 unique_store = (STORE **) malloc(i * sizeof(STORE));
682 /* Find Unique Storage address/port */
683 store = (STORE *)GetNextRes(R_STORAGE, NULL);
685 unique_store[i++] = store;
686 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
688 for (j=0; j<i; j++) {
689 if (strcmp(unique_store[j]->address, store->address) == 0 &&
690 unique_store[j]->SDport == store->SDport) {
696 unique_store[i++] = store;
697 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
702 /* Call each unique Storage daemon */
703 for (j=0; j<i; j++) {
704 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
708 /* Count Client items */
712 foreach_res(client, R_CLIENT) {
715 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
716 /* Find Unique Client address/port */
717 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
719 unique_client[i++] = client;
720 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
722 for (j=0; j<i; j++) {
723 if (strcmp(unique_client[j]->address, client->address) == 0 &&
724 unique_client[j]->FDport == client->FDport) {
730 unique_client[i++] = client;
731 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
736 /* Call each unique File daemon */
737 for (j=0; j<i; j++) {
738 do_client_setdebug(ua, unique_client[j], level, trace_flag);
744 * setdebug level=nn all trace=1/0
746 static int setdebug_cmd(UAContext *ua, const char *cmd)
757 Dmsg1(120, "setdebug:%s:\n", cmd);
760 i = find_arg_with_value(ua, "level");
762 level = atoi(ua->argv[i]);
765 if (!get_pint(ua, _("Enter new debug level: "))) {
768 level = ua->pint32_val;
771 /* Look for trace flag. -1 => not change */
772 i = find_arg_with_value(ua, "trace");
774 trace_flag = atoi(ua->argv[i]);
775 if (trace_flag > 0) {
781 for (i=1; i<ua->argc; i++) {
782 if (strcasecmp(ua->argk[i], "all") == 0) {
783 do_all_setdebug(ua, level, trace_flag);
786 if (strcasecmp(ua->argk[i], "dir") == 0 ||
787 strcasecmp(ua->argk[i], "director") == 0) {
789 set_trace(trace_flag);
792 if (strcasecmp(ua->argk[i], "client") == 0 ||
793 strcasecmp(ua->argk[i], "fd") == 0) {
796 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
798 do_client_setdebug(ua, client, level, trace_flag);
802 client = select_client_resource(ua);
804 do_client_setdebug(ua, client, level, trace_flag);
809 if (strcasecmp(ua->argk[i], "store") == 0 ||
810 strcasecmp(ua->argk[i], "storage") == 0 ||
811 strcasecmp(ua->argk[i], "sd") == 0) {
814 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
816 do_storage_setdebug(ua, store, level, trace_flag);
820 store = get_storage_resource(ua, false/*no default*/);
822 do_storage_setdebug(ua, store, level, trace_flag);
828 * We didn't find an appropriate keyword above, so
831 start_prompt(ua, _("Available daemons are: \n"));
832 add_prompt(ua, "Director");
833 add_prompt(ua, "Storage");
834 add_prompt(ua, "Client");
835 add_prompt(ua, "All");
836 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
837 case 0: /* Director */
839 set_trace(trace_flag);
842 store = get_storage_resource(ua, false/*no default*/);
844 do_storage_setdebug(ua, store, level, trace_flag);
848 client = select_client_resource(ua);
850 do_client_setdebug(ua, client, level, trace_flag);
854 do_all_setdebug(ua, level, trace_flag);
863 * Turn debug tracing to file on/off
865 static int trace_cmd(UAContext *ua, const char *cmd)
870 if (!get_cmd(ua, _("Turn on or off? "))) {
878 set_trace((strcasecmp(onoff, _("off")) == 0) ? false : true);
883 static int var_cmd(UAContext *ua, const char *cmd)
885 POOLMEM *val = get_pool_memory(PM_FNAME);
891 for (var=ua->cmd; *var != ' '; ) { /* skip command */
894 while (*var == ' ') { /* skip spaces */
897 Dmsg1(100, "Var=%s:\n", var);
898 variable_expansion(ua->jcr, var, &val);
899 bsendmsg(ua, "%s\n", val);
900 free_pool_memory(val);
904 static int estimate_cmd(UAContext *ua, const char *cmd)
907 CLIENT *client = NULL;
908 FILESET *fileset = NULL;
910 char since[MAXSTRING];
913 jcr->JobLevel = L_FULL;
914 for (int i=1; i<ua->argc; i++) {
915 if (strcasecmp(ua->argk[i], "client") == 0 ||
916 strcasecmp(ua->argk[i], "fd") == 0) {
918 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
922 if (strcasecmp(ua->argk[i], "job") == 0) {
924 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
928 if (strcasecmp(ua->argk[i], "fileset") == 0) {
930 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
934 if (strcasecmp(ua->argk[i], "listing") == 0) {
938 if (strcasecmp(ua->argk[i], "level") == 0) {
939 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
940 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
945 if (!job && !(client && fileset)) {
946 if (!(job = select_job_resource(ua))) {
951 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
953 bsendmsg(ua, _("No job specified.\n"));
958 client = job->client;
961 fileset = job->fileset;
963 jcr->client = client;
964 jcr->fileset = fileset;
966 ua->catalog = client->catalog;
973 jcr->JobType = JT_BACKUP;
974 init_jcr_job_record(jcr);
976 if (!get_or_create_client_record(jcr)) {
979 if (!get_or_create_fileset_record(jcr)) {
983 get_level_since_time(ua->jcr, since, sizeof(since));
985 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
986 job->client->hdr.name, job->client->address, job->client->FDport);
987 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
988 bsendmsg(ua, _("Failed to connect to Client.\n"));
992 if (!send_include_list(jcr)) {
993 bsendmsg(ua, _("Error sending include list.\n"));
997 if (!send_exclude_list(jcr)) {
998 bsendmsg(ua, _("Error sending exclude list.\n"));
1002 if (!send_level_command(jcr)) {
1006 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1007 while (bnet_recv(jcr->file_bsock) >= 0) {
1008 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1012 if (jcr->file_bsock) {
1013 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1014 bnet_close(jcr->file_bsock);
1015 jcr->file_bsock = NULL;
1024 static int time_cmd(UAContext *ua, const char *cmd)
1027 time_t ttime = time(NULL);
1029 localtime_r(&ttime, &tm);
1030 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1031 bsendmsg(ua, "%s\n", sdt);
1036 * reload the conf file
1038 extern "C" void reload_config(int sig);
1040 static int reload_cmd(UAContext *ua, const char *cmd)
1047 * Delete Pool records (should purge Media with it).
1049 * delete pool=<pool-name>
1050 * delete volume pool=<pool-name> volume=<name>
1053 static int delete_cmd(UAContext *ua, const char *cmd)
1055 static const char *keywords[] = {
1065 switch (find_arg_keyword(ua, keywords)) {
1074 while ((i=find_arg(ua, "jobid")) > 0) {
1076 *ua->argk[i] = 0; /* zap keyword already visited */
1084 "In general it is not a good idea to delete either a\n"
1085 "Pool or a Volume since they may contain data.\n\n"));
1087 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1098 bsendmsg(ua, _("Nothing done.\n"));
1106 * delete_job has been modified to parse JobID lists like the
1108 * delete JobID=3,4,6,7-11,14
1110 * Thanks to Phil Stracchino for the above addition.
1113 static void delete_job(UAContext *ua)
1118 int i = find_arg_with_value(ua, N_("jobid"));
1120 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1121 s = bstrdup(ua->argv[i]);
1124 * We could use strtok() here. But we're not going to, because:
1125 * (a) strtok() is deprecated, having been replaced by strsep();
1126 * (b) strtok() is broken in significant ways.
1127 * we could use strsep() instead, but it's not universally available.
1128 * so we grow our own using strchr().
1130 sep = strchr(tok, ',');
1131 while (sep != NULL) {
1133 if (strchr(tok, '-')) {
1134 delete_job_id_range(ua, tok);
1136 JobId = str_to_int64(tok);
1137 do_job_delete(ua, JobId);
1140 sep = strchr(tok, ',');
1142 /* pick up the last token */
1143 if (strchr(tok, '-')) {
1144 delete_job_id_range(ua, tok);
1146 JobId = str_to_int64(tok);
1147 do_job_delete(ua, JobId);
1152 JobId = str_to_int64(ua->argv[i]);
1153 do_job_delete(ua, JobId);
1155 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1158 JobId = ua->int64_val;
1159 do_job_delete(ua, JobId);
1164 * we call delete_job_id_range to parse range tokens and iterate over ranges
1166 static void delete_job_id_range(UAContext *ua, char *tok)
1171 tok2 = strchr(tok, '-');
1174 j1 = str_to_int64(tok);
1175 j2 = str_to_int64(tok2);
1176 for (j=j1; j<=j2; j++) {
1177 do_job_delete(ua, j);
1182 * do_job_delete now performs the actual delete operation atomically
1183 * we always return 1 because C++ is pissy about void functions
1186 static void do_job_delete(UAContext *ua, JobId_t JobId)
1188 POOLMEM *query = get_pool_memory(PM_MESSAGE);
1191 Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(JobId, ed1));
1192 db_sql_query(ua->db, query, NULL, (void *)NULL);
1193 Mmsg(query, "DELETE FROM File WHERE JobId=%s", edit_int64(JobId, ed1));
1194 db_sql_query(ua->db, query, NULL, (void *)NULL);
1195 Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", edit_int64(JobId, ed1));
1196 db_sql_query(ua->db, query, NULL, (void *)NULL);
1197 free_pool_memory(query);
1198 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1202 * Delete media records from database -- dangerous
1204 static int delete_volume(UAContext *ua)
1208 if (!select_media_dbr(ua, &mr)) {
1211 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1212 "and all Jobs saved on that volume from the Catalog\n"),
1215 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1218 if (ua->pint32_val) {
1219 db_delete_media_record(ua->jcr, ua->db, &mr);
1225 * Delete a pool record from the database -- dangerous
1227 static int delete_pool(UAContext *ua)
1231 memset(&pr, 0, sizeof(pr));
1233 if (!get_pool_dbr(ua, &pr)) {
1236 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1239 if (ua->pint32_val) {
1240 db_delete_pool_record(ua->jcr, ua->db, &pr);
1246 static void do_mount_cmd(UAContext *ua, const char *command)
1251 char dev_name[MAX_NAME_LENGTH];
1257 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1259 store = get_storage_resource(ua, true/*arg is storage*/);
1263 set_storage(jcr, store);
1264 drive = get_storage_drive(ua, store);
1266 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1267 store->media_type, store->dev_name(), drive);
1269 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1270 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1273 sd = jcr->store_bsock;
1274 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1275 bash_spaces(dev_name);
1276 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1277 while (bnet_recv(sd) >= 0) {
1278 bsendmsg(ua, "%s", sd->msg);
1280 bnet_sig(sd, BNET_TERMINATE);
1282 jcr->store_bsock = NULL;
1286 * mount [storage=<name>] [drive=nn]
1288 static int mount_cmd(UAContext *ua, const char *cmd)
1290 do_mount_cmd(ua, "mount"); /* mount */
1296 * unmount [storage=<name>] [drive=nn]
1298 static int unmount_cmd(UAContext *ua, const char *cmd)
1300 do_mount_cmd(ua, "unmount"); /* unmount */
1306 * release [storage=<name>] [drive=nn]
1308 static int release_cmd(UAContext *ua, const char *cmd)
1310 do_mount_cmd(ua, "release"); /* release */
1317 * use catalog=<name>
1319 static int use_cmd(UAContext *ua, const char *cmd)
1321 CAT *oldcatalog, *catalog;
1324 close_db(ua); /* close any previously open db */
1325 oldcatalog = ua->catalog;
1327 if (!(catalog = get_catalog_resource(ua))) {
1328 ua->catalog = oldcatalog;
1330 ua->catalog = catalog;
1333 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1334 ua->catalog->hdr.name, ua->catalog->db_name);
1339 int quit_cmd(UAContext *ua, const char *cmd)
1346 * Wait until no job is running
1348 int wait_cmd(UAContext *ua, const char *cmd)
1351 bmicrosleep(0, 200000); /* let job actually start */
1352 for (bool running=true; running; ) {
1355 if (jcr->JobId != 0) {
1370 static int help_cmd(UAContext *ua, const char *cmd)
1374 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1375 for (i=0; i<comsize; i++) {
1376 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1378 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1382 int qhelp_cmd(UAContext *ua, const char *cmd)
1386 for (i=0; i<comsize; i++) {
1387 bsendmsg(ua, "%s %s\n", _(commands[i].key), _(commands[i].help));
1392 static int version_cmd(UAContext *ua, const char *cmd)
1394 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1399 /* A bit brain damaged in that if the user has not done
1400 * a "use catalog xxx" command, we simply find the first
1401 * catalog resource and open it.
1403 int open_db(UAContext *ua)
1410 ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1413 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1416 bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1417 ua->catalog->hdr.name, ua->catalog->db_name);
1421 ua->jcr->catalog = ua->catalog;
1423 Dmsg0(150, "Open database\n");
1424 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1425 ua->catalog->db_password, ua->catalog->db_address,
1426 ua->catalog->db_port, ua->catalog->db_socket,
1427 ua->catalog->mult_db_connections);
1428 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1429 bsendmsg(ua, _("Could not open database \"%s\".\n"),
1430 ua->catalog->db_name);
1432 bsendmsg(ua, "%s", db_strerror(ua->db));
1437 ua->jcr->db = ua->db;
1438 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1442 void close_db(UAContext *ua)
1445 db_close_database(ua->jcr, ua->db);