3 * Bacula Director -- User Agent Commands
5 * Kern Sibbald, September MM
10 Copyright (C) 2000-2005 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 */
413 bsendmsg(ua, _("No Jobs running.\n"));
416 start_prompt(ua, _("Select Job:\n"));
419 if (jcr->JobId == 0) { /* this is us */
423 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
428 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
432 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
436 /* NOTE! This increments the ref_count */
437 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
438 jcr = get_jcr_by_full_name(JobName);
440 bsendmsg(ua, _("Job %s not found.\n"), JobName);
445 ret = cancel_job(ua, jcr);
451 * This is a common routine to create or update a
452 * Pool DB base record from a Pool Resource. We handle
453 * the setting of MaxVols and NumVols slightly differently
454 * depending on if we are creating the Pool or we are
455 * simply bringing it into agreement with the resource (updage).
457 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
459 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
460 if (op == POOL_OP_CREATE) {
461 pr->MaxVols = pool->max_volumes;
463 } else { /* update pool */
464 if (pr->MaxVols != pool->max_volumes) {
465 pr->MaxVols = pool->max_volumes;
467 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
468 pr->MaxVols = pr->NumVols;
471 pr->LabelType = pool->LabelType;
472 pr->UseOnce = pool->use_volume_once;
473 pr->UseCatalog = pool->use_catalog;
474 pr->AcceptAnyVolume = pool->accept_any_volume;
475 pr->Recycle = pool->Recycle;
476 pr->VolRetention = pool->VolRetention;
477 pr->VolUseDuration = pool->VolUseDuration;
478 pr->MaxVolJobs = pool->MaxVolJobs;
479 pr->MaxVolFiles = pool->MaxVolFiles;
480 pr->MaxVolBytes = pool->MaxVolBytes;
481 pr->AutoPrune = pool->AutoPrune;
482 pr->Recycle = pool->Recycle;
483 if (pool->label_format) {
484 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
486 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
492 * Create a pool record from a given Pool resource
493 * Also called from backup.c
494 * Returns: -1 on error
495 * 0 record already exists
499 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
503 memset(&pr, 0, sizeof(POOL_DBR));
505 bstrncpy(pr.Name, pool->hdr.name, sizeof(pr.Name));
507 if (db_get_pool_record(jcr, db, &pr)) {
509 if (op == POOL_OP_UPDATE) { /* update request */
510 set_pooldbr_from_poolres(&pr, pool, op);
511 db_update_pool_record(jcr, db, &pr);
513 return 0; /* exists */
516 set_pooldbr_from_poolres(&pr, pool, op);
518 if (!db_create_pool_record(jcr, db, &pr)) {
519 return -1; /* error */
527 * Create a Pool Record in the database.
528 * It is always created from the Resource record.
530 static int create_cmd(UAContext *ua, const char *cmd)
538 pool = get_pool_resource(ua);
543 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
545 bsendmsg(ua, _("Error: Pool %s already exists.\n"
546 "Use update to change it.\n"), pool->hdr.name);
550 bsendmsg(ua, "%s", db_strerror(ua->db));
556 bsendmsg(ua, _("Pool %s created.\n"), pool->hdr.name);
561 extern DIRRES *director;
564 * Python control command
565 * python restart (restarts interpreter)
567 static int python_cmd(UAContext *ua, const char *cmd)
569 if (ua->argc >= 1 && strcasecmp(ua->argk[1], _("restart")) == 0) {
570 term_python_interpreter();
571 init_python_interpreter(director->hdr.name,
572 director->scripts_directory, "DirStartUp");
573 bsendmsg(ua, _("Python interpreter restarted.\n"));
575 bsendmsg(ua, _("Nothing done.\n"));
582 * Set a new address in a Client resource. We do this only
583 * if the Console name is the same as the Client name
584 * and the Console can access the client.
586 static int setip_cmd(UAContext *ua, const char *cmd)
590 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->hdr.name)) {
591 bsendmsg(ua, _("Illegal command from this console.\n"));
595 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->hdr.name);
598 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->hdr.name);
601 if (client->address) {
602 free(client->address);
604 /* MA Bug 6 remove ifdef */
605 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
606 client->address = bstrdup(buf);
607 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
608 client->hdr.name, client->address);
615 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
620 set_storage(jcr, store);
621 /* Try connecting for up to 15 seconds */
622 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
623 store->hdr.name, store->address, store->SDport);
624 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
625 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
628 Dmsg0(120, _("Connected to storage daemon\n"));
629 sd = jcr->store_bsock;
630 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
631 if (bnet_recv(sd) >= 0) {
632 bsendmsg(ua, "%s", sd->msg);
634 bnet_sig(sd, BNET_TERMINATE);
636 jcr->store_bsock = NULL;
640 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
644 /* Connect to File daemon */
646 ua->jcr->client = client;
647 /* Try to connect for 15 seconds */
648 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
649 client->hdr.name, client->address, client->FDport);
650 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
651 bsendmsg(ua, _("Failed to connect to Client.\n"));
654 Dmsg0(120, "Connected to file daemon\n");
655 fd = ua->jcr->file_bsock;
656 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
657 if (bnet_recv(fd) >= 0) {
658 bsendmsg(ua, "%s", fd->msg);
660 bnet_sig(fd, BNET_TERMINATE);
662 ua->jcr->file_bsock = NULL;
667 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
669 STORE *store, **unique_store;
670 CLIENT *client, **unique_client;
676 /* Count Storage items */
680 foreach_res(store, R_STORAGE) {
683 unique_store = (STORE **) malloc(i * sizeof(STORE));
684 /* Find Unique Storage address/port */
685 store = (STORE *)GetNextRes(R_STORAGE, NULL);
687 unique_store[i++] = store;
688 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
690 for (j=0; j<i; j++) {
691 if (strcmp(unique_store[j]->address, store->address) == 0 &&
692 unique_store[j]->SDport == store->SDport) {
698 unique_store[i++] = store;
699 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
704 /* Call each unique Storage daemon */
705 for (j=0; j<i; j++) {
706 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
710 /* Count Client items */
714 foreach_res(client, R_CLIENT) {
717 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
718 /* Find Unique Client address/port */
719 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
721 unique_client[i++] = client;
722 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
724 for (j=0; j<i; j++) {
725 if (strcmp(unique_client[j]->address, client->address) == 0 &&
726 unique_client[j]->FDport == client->FDport) {
732 unique_client[i++] = client;
733 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
738 /* Call each unique File daemon */
739 for (j=0; j<i; j++) {
740 do_client_setdebug(ua, unique_client[j], level, trace_flag);
746 * setdebug level=nn all trace=1/0
748 static int setdebug_cmd(UAContext *ua, const char *cmd)
759 Dmsg1(120, "setdebug:%s:\n", cmd);
762 i = find_arg_with_value(ua, "level");
764 level = atoi(ua->argv[i]);
767 if (!get_pint(ua, _("Enter new debug level: "))) {
770 level = ua->pint32_val;
773 /* Look for trace flag. -1 => not change */
774 i = find_arg_with_value(ua, "trace");
776 trace_flag = atoi(ua->argv[i]);
777 if (trace_flag > 0) {
783 for (i=1; i<ua->argc; i++) {
784 if (strcasecmp(ua->argk[i], "all") == 0) {
785 do_all_setdebug(ua, level, trace_flag);
788 if (strcasecmp(ua->argk[i], "dir") == 0 ||
789 strcasecmp(ua->argk[i], "director") == 0) {
791 set_trace(trace_flag);
794 if (strcasecmp(ua->argk[i], "client") == 0 ||
795 strcasecmp(ua->argk[i], "fd") == 0) {
798 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
800 do_client_setdebug(ua, client, level, trace_flag);
804 client = select_client_resource(ua);
806 do_client_setdebug(ua, client, level, trace_flag);
811 if (strcasecmp(ua->argk[i], "store") == 0 ||
812 strcasecmp(ua->argk[i], "storage") == 0 ||
813 strcasecmp(ua->argk[i], "sd") == 0) {
816 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
818 do_storage_setdebug(ua, store, level, trace_flag);
822 store = get_storage_resource(ua, false/*no default*/);
824 do_storage_setdebug(ua, store, level, trace_flag);
830 * We didn't find an appropriate keyword above, so
833 start_prompt(ua, _("Available daemons are: \n"));
834 add_prompt(ua, "Director");
835 add_prompt(ua, "Storage");
836 add_prompt(ua, "Client");
837 add_prompt(ua, "All");
838 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
839 case 0: /* Director */
841 set_trace(trace_flag);
844 store = get_storage_resource(ua, false/*no default*/);
846 do_storage_setdebug(ua, store, level, trace_flag);
850 client = select_client_resource(ua);
852 do_client_setdebug(ua, client, level, trace_flag);
856 do_all_setdebug(ua, level, trace_flag);
865 * Turn debug tracing to file on/off
867 static int trace_cmd(UAContext *ua, const char *cmd)
872 if (!get_cmd(ua, _("Turn on or off? "))) {
880 set_trace((strcasecmp(onoff, _("off")) == 0) ? false : true);
885 static int var_cmd(UAContext *ua, const char *cmd)
887 POOLMEM *val = get_pool_memory(PM_FNAME);
893 for (var=ua->cmd; *var != ' '; ) { /* skip command */
896 while (*var == ' ') { /* skip spaces */
899 Dmsg1(100, "Var=%s:\n", var);
900 variable_expansion(ua->jcr, var, &val);
901 bsendmsg(ua, "%s\n", val);
902 free_pool_memory(val);
906 static int estimate_cmd(UAContext *ua, const char *cmd)
909 CLIENT *client = NULL;
910 FILESET *fileset = NULL;
912 char since[MAXSTRING];
915 jcr->JobLevel = L_FULL;
916 for (int i=1; i<ua->argc; i++) {
917 if (strcasecmp(ua->argk[i], "client") == 0 ||
918 strcasecmp(ua->argk[i], "fd") == 0) {
920 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
924 if (strcasecmp(ua->argk[i], "job") == 0) {
926 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
930 if (strcasecmp(ua->argk[i], "fileset") == 0) {
932 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
936 if (strcasecmp(ua->argk[i], "listing") == 0) {
940 if (strcasecmp(ua->argk[i], "level") == 0) {
941 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
942 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
947 if (!job && !(client && fileset)) {
948 if (!(job = select_job_resource(ua))) {
953 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
955 bsendmsg(ua, _("No job specified.\n"));
960 client = job->client;
963 fileset = job->fileset;
965 jcr->client = client;
966 jcr->fileset = fileset;
968 ua->catalog = client->catalog;
975 jcr->JobType = JT_BACKUP;
976 init_jcr_job_record(jcr);
978 if (!get_or_create_client_record(jcr)) {
981 if (!get_or_create_fileset_record(jcr)) {
985 get_level_since_time(ua->jcr, since, sizeof(since));
987 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
988 job->client->hdr.name, job->client->address, job->client->FDport);
989 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
990 bsendmsg(ua, _("Failed to connect to Client.\n"));
994 if (!send_include_list(jcr)) {
995 bsendmsg(ua, _("Error sending include list.\n"));
999 if (!send_exclude_list(jcr)) {
1000 bsendmsg(ua, _("Error sending exclude list.\n"));
1004 if (!send_level_command(jcr)) {
1008 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1009 while (bnet_recv(jcr->file_bsock) >= 0) {
1010 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1014 if (jcr->file_bsock) {
1015 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1016 bnet_close(jcr->file_bsock);
1017 jcr->file_bsock = NULL;
1026 static int time_cmd(UAContext *ua, const char *cmd)
1029 time_t ttime = time(NULL);
1031 localtime_r(&ttime, &tm);
1032 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1033 bsendmsg(ua, "%s\n", sdt);
1038 * reload the conf file
1040 extern "C" void reload_config(int sig);
1042 static int reload_cmd(UAContext *ua, const char *cmd)
1049 * Delete Pool records (should purge Media with it).
1051 * delete pool=<pool-name>
1052 * delete volume pool=<pool-name> volume=<name>
1055 static int delete_cmd(UAContext *ua, const char *cmd)
1057 static const char *keywords[] = {
1067 switch (find_arg_keyword(ua, keywords)) {
1076 while ((i=find_arg(ua, "jobid")) > 0) {
1078 *ua->argk[i] = 0; /* zap keyword already visited */
1086 "In general it is not a good idea to delete either a\n"
1087 "Pool or a Volume since they may contain data.\n\n"));
1089 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1100 bsendmsg(ua, _("Nothing done.\n"));
1108 * delete_job has been modified to parse JobID lists like the
1110 * delete JobID=3,4,6,7-11,14
1112 * Thanks to Phil Stracchino for the above addition.
1115 static void delete_job(UAContext *ua)
1120 int i = find_arg_with_value(ua, N_("jobid"));
1122 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1123 s = bstrdup(ua->argv[i]);
1126 * We could use strtok() here. But we're not going to, because:
1127 * (a) strtok() is deprecated, having been replaced by strsep();
1128 * (b) strtok() is broken in significant ways.
1129 * we could use strsep() instead, but it's not universally available.
1130 * so we grow our own using strchr().
1132 sep = strchr(tok, ',');
1133 while (sep != NULL) {
1135 if (strchr(tok, '-')) {
1136 delete_job_id_range(ua, tok);
1138 JobId = str_to_int64(tok);
1139 do_job_delete(ua, JobId);
1142 sep = strchr(tok, ',');
1144 /* pick up the last token */
1145 if (strchr(tok, '-')) {
1146 delete_job_id_range(ua, tok);
1148 JobId = str_to_int64(tok);
1149 do_job_delete(ua, JobId);
1154 JobId = str_to_int64(ua->argv[i]);
1155 do_job_delete(ua, JobId);
1157 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1160 JobId = ua->int64_val;
1161 do_job_delete(ua, JobId);
1166 * we call delete_job_id_range to parse range tokens and iterate over ranges
1168 static void delete_job_id_range(UAContext *ua, char *tok)
1173 tok2 = strchr(tok, '-');
1176 j1 = str_to_int64(tok);
1177 j2 = str_to_int64(tok2);
1178 for (j=j1; j<=j2; j++) {
1179 do_job_delete(ua, j);
1184 * do_job_delete now performs the actual delete operation atomically
1185 * we always return 1 because C++ is pissy about void functions
1188 static void do_job_delete(UAContext *ua, JobId_t JobId)
1190 POOLMEM *query = get_pool_memory(PM_MESSAGE);
1193 Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(JobId, ed1));
1194 db_sql_query(ua->db, query, NULL, (void *)NULL);
1195 Mmsg(query, "DELETE FROM File WHERE JobId=%s", edit_int64(JobId, ed1));
1196 db_sql_query(ua->db, query, NULL, (void *)NULL);
1197 Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", edit_int64(JobId, ed1));
1198 db_sql_query(ua->db, query, NULL, (void *)NULL);
1199 free_pool_memory(query);
1200 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1204 * Delete media records from database -- dangerous
1206 static int delete_volume(UAContext *ua)
1210 if (!select_media_dbr(ua, &mr)) {
1213 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1214 "and all Jobs saved on that volume from the Catalog\n"),
1217 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1220 if (ua->pint32_val) {
1221 db_delete_media_record(ua->jcr, ua->db, &mr);
1227 * Delete a pool record from the database -- dangerous
1229 static int delete_pool(UAContext *ua)
1233 memset(&pr, 0, sizeof(pr));
1235 if (!get_pool_dbr(ua, &pr)) {
1238 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1241 if (ua->pint32_val) {
1242 db_delete_pool_record(ua->jcr, ua->db, &pr);
1248 static void do_mount_cmd(UAContext *ua, const char *command)
1253 char dev_name[MAX_NAME_LENGTH];
1259 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1261 store = get_storage_resource(ua, true/*arg is storage*/);
1265 set_storage(jcr, store);
1266 drive = get_storage_drive(ua, store);
1268 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1269 store->media_type, store->dev_name(), drive);
1271 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1272 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1275 sd = jcr->store_bsock;
1276 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1277 bash_spaces(dev_name);
1278 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1279 while (bnet_recv(sd) >= 0) {
1280 bsendmsg(ua, "%s", sd->msg);
1282 bnet_sig(sd, BNET_TERMINATE);
1284 jcr->store_bsock = NULL;
1288 * mount [storage=<name>] [drive=nn]
1290 static int mount_cmd(UAContext *ua, const char *cmd)
1292 do_mount_cmd(ua, "mount"); /* mount */
1298 * unmount [storage=<name>] [drive=nn]
1300 static int unmount_cmd(UAContext *ua, const char *cmd)
1302 do_mount_cmd(ua, "unmount"); /* unmount */
1308 * release [storage=<name>] [drive=nn]
1310 static int release_cmd(UAContext *ua, const char *cmd)
1312 do_mount_cmd(ua, "release"); /* release */
1319 * use catalog=<name>
1321 static int use_cmd(UAContext *ua, const char *cmd)
1323 CAT *oldcatalog, *catalog;
1326 close_db(ua); /* close any previously open db */
1327 oldcatalog = ua->catalog;
1329 if (!(catalog = get_catalog_resource(ua))) {
1330 ua->catalog = oldcatalog;
1332 ua->catalog = catalog;
1335 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1336 ua->catalog->hdr.name, ua->catalog->db_name);
1341 int quit_cmd(UAContext *ua, const char *cmd)
1348 * Wait until no job is running
1350 int wait_cmd(UAContext *ua, const char *cmd)
1353 bmicrosleep(0, 200000); /* let job actually start */
1354 for (bool running=true; running; ) {
1357 if (jcr->JobId != 0) {
1372 static int help_cmd(UAContext *ua, const char *cmd)
1376 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1377 for (i=0; i<comsize; i++) {
1378 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1380 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1384 int qhelp_cmd(UAContext *ua, const char *cmd)
1388 for (i=0; i<comsize; i++) {
1389 bsendmsg(ua, "%s %s\n", _(commands[i].key), _(commands[i].help));
1394 static int version_cmd(UAContext *ua, const char *cmd)
1396 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1401 /* A bit brain damaged in that if the user has not done
1402 * a "use catalog xxx" command, we simply find the first
1403 * catalog resource and open it.
1405 int open_db(UAContext *ua)
1412 ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1415 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1418 bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1419 ua->catalog->hdr.name, ua->catalog->db_name);
1423 ua->jcr->catalog = ua->catalog;
1425 Dmsg0(150, "Open database\n");
1426 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1427 ua->catalog->db_password, ua->catalog->db_address,
1428 ua->catalog->db_port, ua->catalog->db_socket,
1429 ua->catalog->mult_db_connections);
1430 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1431 bsendmsg(ua, _("Could not open database \"%s\".\n"),
1432 ua->catalog->db_name);
1434 bsendmsg(ua, "%s", db_strerror(ua->db));
1439 ua->jcr->db = ua->db;
1440 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1444 void close_db(UAContext *ua)
1447 db_close_database(ua->jcr, ua->db);