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 len = strlen(ua->argk[0]);
147 for (i=0; i<comsize; i++) { /* search for command */
148 if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) {
149 if (!acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
152 stat = (*commands[i].func)(ua, cmd); /* go execute command */
158 bnet_fsend(ua->UA_sock, _("%s: is an illegal command.\n"), ua->argk[0]);
164 * This is a common routine used to stuff the Pool DB record defaults
165 * into the Media DB record just before creating a media (Volume)
168 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
170 mr->PoolId = pr->PoolId;
171 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));
172 mr->Recycle = pr->Recycle;
173 mr->VolRetention = pr->VolRetention;
174 mr->VolUseDuration = pr->VolUseDuration;
175 mr->MaxVolJobs = pr->MaxVolJobs;
176 mr->MaxVolFiles = pr->MaxVolFiles;
177 mr->MaxVolBytes = pr->MaxVolBytes;
178 mr->LabelType = pr->LabelType;
183 * Add Volumes to an existing Pool
185 static int add_cmd(UAContext *ua, const char *cmd)
189 int num, i, max, startnum;
191 char name[MAX_NAME_LENGTH];
193 int Slot = 0, InChanger = 0;
196 "You probably don't want to be using this command since it\n"
197 "creates database records without labeling the Volumes.\n"
198 "You probably want to use the \"label\" command.\n\n"));
204 memset(&pr, 0, sizeof(pr));
205 memset(&mr, 0, sizeof(mr));
207 if (!get_pool_dbr(ua, &pr)) {
211 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
212 pr.MaxVols, pr.PoolType);
214 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
215 bsendmsg(ua, _("Pool already has maximum volumes = %d\n"), pr.MaxVols);
217 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
220 pr.MaxVols = ua->pint32_val;
225 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
226 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
227 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
231 if (pr.MaxVols == 0) {
234 max = pr.MaxVols - pr.NumVols;
238 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
239 if (!get_pint(ua, buf)) {
242 num = ua->pint32_val;
243 if (num < 0 || num > max) {
244 bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
251 if (!get_cmd(ua, _("Enter Volume name: "))) {
255 if (!get_cmd(ua, _("Enter base volume name: "))) {
259 /* Don't allow | in Volume name because it is the volume separator character */
260 if (!is_volume_name_legal(ua, ua->cmd)) {
263 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
264 bsendmsg(ua, _("Volume name too long.\n"));
267 if (strlen(ua->cmd) == 0) {
268 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
272 bstrncpy(name, ua->cmd, sizeof(name));
274 bstrncat(name, "%04d", sizeof(name));
277 if (!get_pint(ua, _("Enter the starting number: "))) {
280 startnum = ua->pint32_val;
282 bsendmsg(ua, _("Start number must be greater than zero.\n"));
292 if (store && store->autochanger) {
293 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
296 Slot = ua->pint32_val;
297 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
300 InChanger = ua->pint32_val;
303 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
304 for (i=startnum; i < num+startnum; i++) {
305 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
307 mr.InChanger = InChanger;
308 mr.StorageId = store->StorageId;
309 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
310 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
311 bsendmsg(ua, "%s", db_strerror(ua->db));
315 first_id = mr.PoolId;
319 Dmsg0(200, "Update pool record.\n");
320 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
321 bsendmsg(ua, "%s", db_strerror(ua->db));
324 bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
330 * Turn auto mount on/off
335 int automount_cmd(UAContext *ua, const char *cmd)
340 if (!get_cmd(ua, _("Turn on or off? "))) {
348 ua->automount = (strcasecmp(onoff, _("off")) == 0) ? 0 : 1;
356 static int cancel_cmd(UAContext *ua, const char *cmd)
361 char JobName[MAX_NAME_LENGTH];
367 for (i=1; i<ua->argc; i++) {
368 if (strcasecmp(ua->argk[i], _("jobid")) == 0) {
373 JobId = str_to_int64(ua->argv[i]);
374 if (!(jcr=get_jcr_by_id(JobId))) {
375 bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
379 } else if (strcasecmp(ua->argk[i], _("job")) == 0) {
383 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
384 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
385 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
386 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
391 /* If we still do not have a jcr,
392 * throw up a list and ask the user to select one.
396 /* Count Jobs running */
398 if (jcr->JobId == 0) { /* this is us */
407 bsendmsg(ua, _("No Jobs running.\n"));
410 start_prompt(ua, _("Select Job:\n"));
413 if (jcr->JobId == 0) { /* this is us */
417 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
422 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
426 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
430 /* NOTE! This increments the ref_count */
431 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
432 jcr = get_jcr_by_full_name(JobName);
434 bsendmsg(ua, _("Job %s not found.\n"), JobName);
439 ret = cancel_job(ua, jcr);
445 * This is a common routine to create or update a
446 * Pool DB base record from a Pool Resource. We handle
447 * the setting of MaxVols and NumVols slightly differently
448 * depending on if we are creating the Pool or we are
449 * simply bringing it into agreement with the resource (updage).
451 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
453 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
454 if (op == POOL_OP_CREATE) {
455 pr->MaxVols = pool->max_volumes;
457 } else { /* update pool */
458 if (pr->MaxVols != pool->max_volumes) {
459 pr->MaxVols = pool->max_volumes;
461 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
462 pr->MaxVols = pr->NumVols;
465 pr->LabelType = pool->LabelType;
466 pr->UseOnce = pool->use_volume_once;
467 pr->UseCatalog = pool->use_catalog;
468 pr->AcceptAnyVolume = pool->accept_any_volume;
469 pr->Recycle = pool->Recycle;
470 pr->VolRetention = pool->VolRetention;
471 pr->VolUseDuration = pool->VolUseDuration;
472 pr->MaxVolJobs = pool->MaxVolJobs;
473 pr->MaxVolFiles = pool->MaxVolFiles;
474 pr->MaxVolBytes = pool->MaxVolBytes;
475 pr->AutoPrune = pool->AutoPrune;
476 pr->Recycle = pool->Recycle;
477 if (pool->label_format) {
478 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
480 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
486 * Create a pool record from a given Pool resource
487 * Also called from backup.c
488 * Returns: -1 on error
489 * 0 record already exists
493 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
497 memset(&pr, 0, sizeof(POOL_DBR));
499 bstrncpy(pr.Name, pool->hdr.name, sizeof(pr.Name));
501 if (db_get_pool_record(jcr, db, &pr)) {
503 if (op == POOL_OP_UPDATE) { /* update request */
504 set_pooldbr_from_poolres(&pr, pool, op);
505 db_update_pool_record(jcr, db, &pr);
507 return 0; /* exists */
510 set_pooldbr_from_poolres(&pr, pool, op);
512 if (!db_create_pool_record(jcr, db, &pr)) {
513 return -1; /* error */
521 * Create a Pool Record in the database.
522 * It is always created from the Resource record.
524 static int create_cmd(UAContext *ua, const char *cmd)
532 pool = get_pool_resource(ua);
537 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
539 bsendmsg(ua, _("Error: Pool %s already exists.\n"
540 "Use update to change it.\n"), pool->hdr.name);
544 bsendmsg(ua, "%s", db_strerror(ua->db));
550 bsendmsg(ua, _("Pool %s created.\n"), pool->hdr.name);
555 extern DIRRES *director;
558 * Python control command
559 * python restart (restarts interpreter)
561 static int python_cmd(UAContext *ua, const char *cmd)
563 if (ua->argc >= 1 && strcasecmp(ua->argk[1], _("restart")) == 0) {
564 term_python_interpreter();
565 init_python_interpreter(director->hdr.name,
566 director->scripts_directory, "DirStartUp");
567 bsendmsg(ua, _("Python interpreter restarted.\n"));
569 bsendmsg(ua, _("Nothing done.\n"));
576 * Set a new address in a Client resource. We do this only
577 * if the Console name is the same as the Client name
578 * and the Console can access the client.
580 static int setip_cmd(UAContext *ua, const char *cmd)
584 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->hdr.name)) {
585 bsendmsg(ua, _("Illegal command from this console.\n"));
589 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->hdr.name);
592 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->hdr.name);
595 if (client->address) {
596 free(client->address);
598 /* MA Bug 6 remove ifdef */
599 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
600 client->address = bstrdup(buf);
601 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
602 client->hdr.name, client->address);
609 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
614 set_storage(jcr, store);
615 /* Try connecting for up to 15 seconds */
616 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
617 store->hdr.name, store->address, store->SDport);
618 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
619 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
622 Dmsg0(120, _("Connected to storage daemon\n"));
623 sd = jcr->store_bsock;
624 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
625 if (bnet_recv(sd) >= 0) {
626 bsendmsg(ua, "%s", sd->msg);
628 bnet_sig(sd, BNET_TERMINATE);
630 jcr->store_bsock = NULL;
634 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
638 /* Connect to File daemon */
640 ua->jcr->client = client;
641 /* Try to connect for 15 seconds */
642 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
643 client->hdr.name, client->address, client->FDport);
644 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
645 bsendmsg(ua, _("Failed to connect to Client.\n"));
648 Dmsg0(120, "Connected to file daemon\n");
649 fd = ua->jcr->file_bsock;
650 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
651 if (bnet_recv(fd) >= 0) {
652 bsendmsg(ua, "%s", fd->msg);
654 bnet_sig(fd, BNET_TERMINATE);
656 ua->jcr->file_bsock = NULL;
661 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
663 STORE *store, **unique_store;
664 CLIENT *client, **unique_client;
670 /* Count Storage items */
674 foreach_res(store, R_STORAGE) {
677 unique_store = (STORE **) malloc(i * sizeof(STORE));
678 /* Find Unique Storage address/port */
679 store = (STORE *)GetNextRes(R_STORAGE, NULL);
681 unique_store[i++] = store;
682 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
684 for (j=0; j<i; j++) {
685 if (strcmp(unique_store[j]->address, store->address) == 0 &&
686 unique_store[j]->SDport == store->SDport) {
692 unique_store[i++] = store;
693 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
698 /* Call each unique Storage daemon */
699 for (j=0; j<i; j++) {
700 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
704 /* Count Client items */
708 foreach_res(client, R_CLIENT) {
711 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
712 /* Find Unique Client address/port */
713 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
715 unique_client[i++] = client;
716 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
718 for (j=0; j<i; j++) {
719 if (strcmp(unique_client[j]->address, client->address) == 0 &&
720 unique_client[j]->FDport == client->FDport) {
726 unique_client[i++] = client;
727 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
732 /* Call each unique File daemon */
733 for (j=0; j<i; j++) {
734 do_client_setdebug(ua, unique_client[j], level, trace_flag);
740 * setdebug level=nn all trace=1/0
742 static int setdebug_cmd(UAContext *ua, const char *cmd)
753 Dmsg1(120, "setdebug:%s:\n", cmd);
756 i = find_arg_with_value(ua, "level");
758 level = atoi(ua->argv[i]);
761 if (!get_pint(ua, _("Enter new debug level: "))) {
764 level = ua->pint32_val;
767 /* Look for trace flag. -1 => not change */
768 i = find_arg_with_value(ua, "trace");
770 trace_flag = atoi(ua->argv[i]);
771 if (trace_flag > 0) {
777 for (i=1; i<ua->argc; i++) {
778 if (strcasecmp(ua->argk[i], "all") == 0) {
779 do_all_setdebug(ua, level, trace_flag);
782 if (strcasecmp(ua->argk[i], "dir") == 0 ||
783 strcasecmp(ua->argk[i], "director") == 0) {
785 set_trace(trace_flag);
788 if (strcasecmp(ua->argk[i], "client") == 0 ||
789 strcasecmp(ua->argk[i], "fd") == 0) {
792 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
794 do_client_setdebug(ua, client, level, trace_flag);
798 client = select_client_resource(ua);
800 do_client_setdebug(ua, client, level, trace_flag);
805 if (strcasecmp(ua->argk[i], "store") == 0 ||
806 strcasecmp(ua->argk[i], "storage") == 0 ||
807 strcasecmp(ua->argk[i], "sd") == 0) {
810 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
812 do_storage_setdebug(ua, store, level, trace_flag);
816 store = get_storage_resource(ua, false/*no default*/);
818 do_storage_setdebug(ua, store, level, trace_flag);
824 * We didn't find an appropriate keyword above, so
827 start_prompt(ua, _("Available daemons are: \n"));
828 add_prompt(ua, "Director");
829 add_prompt(ua, "Storage");
830 add_prompt(ua, "Client");
831 add_prompt(ua, "All");
832 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
833 case 0: /* Director */
835 set_trace(trace_flag);
838 store = get_storage_resource(ua, false/*no default*/);
840 do_storage_setdebug(ua, store, level, trace_flag);
844 client = select_client_resource(ua);
846 do_client_setdebug(ua, client, level, trace_flag);
850 do_all_setdebug(ua, level, trace_flag);
859 * Turn debug tracing to file on/off
861 static int trace_cmd(UAContext *ua, const char *cmd)
866 if (!get_cmd(ua, _("Turn on or off? "))) {
874 set_trace((strcasecmp(onoff, _("off")) == 0) ? false : true);
879 static int var_cmd(UAContext *ua, const char *cmd)
881 POOLMEM *val = get_pool_memory(PM_FNAME);
887 for (var=ua->cmd; *var != ' '; ) { /* skip command */
890 while (*var == ' ') { /* skip spaces */
893 Dmsg1(100, "Var=%s:\n", var);
894 variable_expansion(ua->jcr, var, &val);
895 bsendmsg(ua, "%s\n", val);
896 free_pool_memory(val);
900 static int estimate_cmd(UAContext *ua, const char *cmd)
903 CLIENT *client = NULL;
904 FILESET *fileset = NULL;
907 char since[MAXSTRING];
910 jcr->JobLevel = L_FULL;
911 for (int i=1; i<ua->argc; i++) {
912 if (strcasecmp(ua->argk[i], "client") == 0 ||
913 strcasecmp(ua->argk[i], "fd") == 0) {
915 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
919 if (strcasecmp(ua->argk[i], "job") == 0) {
921 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
925 if (strcasecmp(ua->argk[i], "fileset") == 0) {
927 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
931 if (strcasecmp(ua->argk[i], "listing") == 0) {
935 if (strcasecmp(ua->argk[i], "level") == 0) {
936 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
937 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
942 if (!job && !(client && fileset)) {
943 if (!(job = select_job_resource(ua))) {
948 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
950 bsendmsg(ua, _("No job specified.\n"));
955 client = job->client;
958 fileset = job->fileset;
960 jcr->client = client;
961 jcr->fileset = fileset;
963 ua->catalog = client->catalog;
970 jcr->JobType = JT_BACKUP;
971 init_jcr_job_record(jcr);
973 if (!get_or_create_client_record(jcr)) {
976 if (!get_or_create_fileset_record(jcr, &fsr)) {
980 get_level_since_time(ua->jcr, since, sizeof(since));
982 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
983 job->client->hdr.name, job->client->address, job->client->FDport);
984 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
985 bsendmsg(ua, _("Failed to connect to Client.\n"));
989 if (!send_include_list(jcr)) {
990 bsendmsg(ua, _("Error sending include list.\n"));
994 if (!send_exclude_list(jcr)) {
995 bsendmsg(ua, _("Error sending exclude list.\n"));
999 if (!send_level_command(jcr)) {
1003 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1004 while (bnet_recv(jcr->file_bsock) >= 0) {
1005 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1009 if (jcr->file_bsock) {
1010 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1011 bnet_close(jcr->file_bsock);
1012 jcr->file_bsock = NULL;
1021 static int time_cmd(UAContext *ua, const char *cmd)
1024 time_t ttime = time(NULL);
1026 localtime_r(&ttime, &tm);
1027 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1028 bsendmsg(ua, "%s\n", sdt);
1033 * reload the conf file
1035 extern "C" void reload_config(int sig);
1037 static int reload_cmd(UAContext *ua, const char *cmd)
1044 * Delete Pool records (should purge Media with it).
1046 * delete pool=<pool-name>
1047 * delete volume pool=<pool-name> volume=<name>
1050 static int delete_cmd(UAContext *ua, const char *cmd)
1052 static const char *keywords[] = {
1062 switch (find_arg_keyword(ua, keywords)) {
1071 while ((i=find_arg(ua, "jobid")) > 0) {
1073 *ua->argk[i] = 0; /* zap keyword already visited */
1081 "In general it is not a good idea to delete either a\n"
1082 "Pool or a Volume since they may contain data.\n\n"));
1084 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1095 bsendmsg(ua, _("Nothing done.\n"));
1103 * delete_job has been modified to parse JobID lists like the
1105 * delete JobID=3,4,6,7-11,14
1107 * Thanks to Phil Stracchino for the above addition.
1110 static void delete_job(UAContext *ua)
1115 int i = find_arg_with_value(ua, N_("jobid"));
1117 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1118 s = bstrdup(ua->argv[i]);
1121 * We could use strtok() here. But we're not going to, because:
1122 * (a) strtok() is deprecated, having been replaced by strsep();
1123 * (b) strtok() is broken in significant ways.
1124 * we could use strsep() instead, but it's not universally available.
1125 * so we grow our own using strchr().
1127 sep = strchr(tok, ',');
1128 while (sep != NULL) {
1130 if (strchr(tok, '-')) {
1131 delete_job_id_range(ua, tok);
1133 JobId = str_to_int64(tok);
1134 do_job_delete(ua, JobId);
1137 sep = strchr(tok, ',');
1139 /* pick up the last token */
1140 if (strchr(tok, '-')) {
1141 delete_job_id_range(ua, tok);
1143 JobId = str_to_int64(tok);
1144 do_job_delete(ua, JobId);
1149 JobId = str_to_int64(ua->argv[i]);
1150 do_job_delete(ua, JobId);
1152 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1155 JobId = ua->int64_val;
1156 do_job_delete(ua, JobId);
1161 * we call delete_job_id_range to parse range tokens and iterate over ranges
1163 static void delete_job_id_range(UAContext *ua, char *tok)
1168 tok2 = strchr(tok, '-');
1171 j1 = str_to_int64(tok);
1172 j2 = str_to_int64(tok2);
1173 for (j=j1; j<=j2; j++) {
1174 do_job_delete(ua, j);
1179 * do_job_delete now performs the actual delete operation atomically
1180 * we always return 1 because C++ is pissy about void functions
1183 static void do_job_delete(UAContext *ua, JobId_t JobId)
1185 POOLMEM *query = get_pool_memory(PM_MESSAGE);
1188 Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(JobId, ed1));
1189 db_sql_query(ua->db, query, NULL, (void *)NULL);
1190 Mmsg(query, "DELETE FROM File WHERE JobId=%s", edit_int64(JobId, ed1));
1191 db_sql_query(ua->db, query, NULL, (void *)NULL);
1192 Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", edit_int64(JobId, ed1));
1193 db_sql_query(ua->db, query, NULL, (void *)NULL);
1194 free_pool_memory(query);
1195 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1199 * Delete media records from database -- dangerous
1201 static int delete_volume(UAContext *ua)
1205 if (!select_media_dbr(ua, &mr)) {
1208 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1209 "and all Jobs saved on that volume from the Catalog\n"),
1212 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1215 if (ua->pint32_val) {
1216 db_delete_media_record(ua->jcr, ua->db, &mr);
1222 * Delete a pool record from the database -- dangerous
1224 static int delete_pool(UAContext *ua)
1228 memset(&pr, 0, sizeof(pr));
1230 if (!get_pool_dbr(ua, &pr)) {
1233 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1236 if (ua->pint32_val) {
1237 db_delete_pool_record(ua->jcr, ua->db, &pr);
1243 static void do_mount_cmd(UAContext *ua, const char *command)
1248 char dev_name[MAX_NAME_LENGTH];
1254 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1256 store = get_storage_resource(ua, true/*use default*/);
1260 drive = ua->int32_val;
1262 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1263 store->media_type, store->dev_name(), drive);
1265 set_storage(jcr, store);
1266 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1267 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1270 sd = jcr->store_bsock;
1271 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1272 bash_spaces(dev_name);
1273 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1274 while (bnet_recv(sd) >= 0) {
1275 bsendmsg(ua, "%s", sd->msg);
1277 bnet_sig(sd, BNET_TERMINATE);
1279 jcr->store_bsock = NULL;
1283 * mount [storage=<name>] [drive=nn]
1285 static int mount_cmd(UAContext *ua, const char *cmd)
1287 do_mount_cmd(ua, "mount"); /* mount */
1293 * unmount [storage=<name>] [drive=nn]
1295 static int unmount_cmd(UAContext *ua, const char *cmd)
1297 do_mount_cmd(ua, "unmount"); /* unmount */
1303 * release [storage=<name>] [drive=nn]
1305 static int release_cmd(UAContext *ua, const char *cmd)
1307 do_mount_cmd(ua, "release"); /* release */
1314 * use catalog=<name>
1316 static int use_cmd(UAContext *ua, const char *cmd)
1318 CAT *oldcatalog, *catalog;
1321 close_db(ua); /* close any previously open db */
1322 oldcatalog = ua->catalog;
1324 if (!(catalog = get_catalog_resource(ua))) {
1325 ua->catalog = oldcatalog;
1327 ua->catalog = catalog;
1330 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1331 ua->catalog->hdr.name, ua->catalog->db_name);
1336 int quit_cmd(UAContext *ua, const char *cmd)
1343 * Wait until no job is running
1345 int wait_cmd(UAContext *ua, const char *cmd)
1348 bmicrosleep(0, 200000); /* let job actually start */
1349 for (bool running=true; running; ) {
1352 if (jcr->JobId != 0) {
1367 static int help_cmd(UAContext *ua, const char *cmd)
1371 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1372 for (i=0; i<comsize; i++) {
1373 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1375 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1379 int qhelp_cmd(UAContext *ua, const char *cmd)
1383 for (i=0; i<comsize; i++) {
1384 bsendmsg(ua, "%s %s\n", _(commands[i].key), _(commands[i].help));
1389 static int version_cmd(UAContext *ua, const char *cmd)
1391 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1396 /* A bit brain damaged in that if the user has not done
1397 * a "use catalog xxx" command, we simply find the first
1398 * catalog resource and open it.
1400 int open_db(UAContext *ua)
1407 ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1410 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1413 bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1414 ua->catalog->hdr.name, ua->catalog->db_name);
1418 ua->jcr->catalog = ua->catalog;
1420 Dmsg0(150, "Open database\n");
1421 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1422 ua->catalog->db_password, ua->catalog->db_address,
1423 ua->catalog->db_port, ua->catalog->db_socket,
1424 ua->catalog->mult_db_connections);
1425 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1426 bsendmsg(ua, _("Could not open database \"%s\".\n"),
1427 ua->catalog->db_name);
1429 bsendmsg(ua, "%s", db_strerror(ua->db));
1434 ua->jcr->db = ua->db;
1435 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1439 void close_db(UAContext *ua)
1442 db_close_database(ua->jcr, ua->db);