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;
906 char since[MAXSTRING];
909 jcr->JobLevel = L_FULL;
910 for (int i=1; i<ua->argc; i++) {
911 if (strcasecmp(ua->argk[i], "client") == 0 ||
912 strcasecmp(ua->argk[i], "fd") == 0) {
914 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
918 if (strcasecmp(ua->argk[i], "job") == 0) {
920 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
924 if (strcasecmp(ua->argk[i], "fileset") == 0) {
926 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
930 if (strcasecmp(ua->argk[i], "listing") == 0) {
934 if (strcasecmp(ua->argk[i], "level") == 0) {
935 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
936 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
941 if (!job && !(client && fileset)) {
942 if (!(job = select_job_resource(ua))) {
947 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
949 bsendmsg(ua, _("No job specified.\n"));
954 client = job->client;
957 fileset = job->fileset;
959 jcr->client = client;
960 jcr->fileset = fileset;
962 ua->catalog = client->catalog;
969 jcr->JobType = JT_BACKUP;
970 init_jcr_job_record(jcr);
972 if (!get_or_create_client_record(jcr)) {
975 if (!get_or_create_fileset_record(jcr)) {
979 get_level_since_time(ua->jcr, since, sizeof(since));
981 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
982 job->client->hdr.name, job->client->address, job->client->FDport);
983 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
984 bsendmsg(ua, _("Failed to connect to Client.\n"));
988 if (!send_include_list(jcr)) {
989 bsendmsg(ua, _("Error sending include list.\n"));
993 if (!send_exclude_list(jcr)) {
994 bsendmsg(ua, _("Error sending exclude list.\n"));
998 if (!send_level_command(jcr)) {
1002 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1003 while (bnet_recv(jcr->file_bsock) >= 0) {
1004 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1008 if (jcr->file_bsock) {
1009 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1010 bnet_close(jcr->file_bsock);
1011 jcr->file_bsock = NULL;
1020 static int time_cmd(UAContext *ua, const char *cmd)
1023 time_t ttime = time(NULL);
1025 localtime_r(&ttime, &tm);
1026 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1027 bsendmsg(ua, "%s\n", sdt);
1032 * reload the conf file
1034 extern "C" void reload_config(int sig);
1036 static int reload_cmd(UAContext *ua, const char *cmd)
1043 * Delete Pool records (should purge Media with it).
1045 * delete pool=<pool-name>
1046 * delete volume pool=<pool-name> volume=<name>
1049 static int delete_cmd(UAContext *ua, const char *cmd)
1051 static const char *keywords[] = {
1061 switch (find_arg_keyword(ua, keywords)) {
1070 while ((i=find_arg(ua, "jobid")) > 0) {
1072 *ua->argk[i] = 0; /* zap keyword already visited */
1080 "In general it is not a good idea to delete either a\n"
1081 "Pool or a Volume since they may contain data.\n\n"));
1083 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1094 bsendmsg(ua, _("Nothing done.\n"));
1102 * delete_job has been modified to parse JobID lists like the
1104 * delete JobID=3,4,6,7-11,14
1106 * Thanks to Phil Stracchino for the above addition.
1109 static void delete_job(UAContext *ua)
1114 int i = find_arg_with_value(ua, N_("jobid"));
1116 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1117 s = bstrdup(ua->argv[i]);
1120 * We could use strtok() here. But we're not going to, because:
1121 * (a) strtok() is deprecated, having been replaced by strsep();
1122 * (b) strtok() is broken in significant ways.
1123 * we could use strsep() instead, but it's not universally available.
1124 * so we grow our own using strchr().
1126 sep = strchr(tok, ',');
1127 while (sep != NULL) {
1129 if (strchr(tok, '-')) {
1130 delete_job_id_range(ua, tok);
1132 JobId = str_to_int64(tok);
1133 do_job_delete(ua, JobId);
1136 sep = strchr(tok, ',');
1138 /* pick up the last token */
1139 if (strchr(tok, '-')) {
1140 delete_job_id_range(ua, tok);
1142 JobId = str_to_int64(tok);
1143 do_job_delete(ua, JobId);
1148 JobId = str_to_int64(ua->argv[i]);
1149 do_job_delete(ua, JobId);
1151 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1154 JobId = ua->int64_val;
1155 do_job_delete(ua, JobId);
1160 * we call delete_job_id_range to parse range tokens and iterate over ranges
1162 static void delete_job_id_range(UAContext *ua, char *tok)
1167 tok2 = strchr(tok, '-');
1170 j1 = str_to_int64(tok);
1171 j2 = str_to_int64(tok2);
1172 for (j=j1; j<=j2; j++) {
1173 do_job_delete(ua, j);
1178 * do_job_delete now performs the actual delete operation atomically
1179 * we always return 1 because C++ is pissy about void functions
1182 static void do_job_delete(UAContext *ua, JobId_t JobId)
1184 POOLMEM *query = get_pool_memory(PM_MESSAGE);
1187 Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(JobId, ed1));
1188 db_sql_query(ua->db, query, NULL, (void *)NULL);
1189 Mmsg(query, "DELETE FROM File WHERE JobId=%s", edit_int64(JobId, ed1));
1190 db_sql_query(ua->db, query, NULL, (void *)NULL);
1191 Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", edit_int64(JobId, ed1));
1192 db_sql_query(ua->db, query, NULL, (void *)NULL);
1193 free_pool_memory(query);
1194 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1198 * Delete media records from database -- dangerous
1200 static int delete_volume(UAContext *ua)
1204 if (!select_media_dbr(ua, &mr)) {
1207 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1208 "and all Jobs saved on that volume from the Catalog\n"),
1211 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1214 if (ua->pint32_val) {
1215 db_delete_media_record(ua->jcr, ua->db, &mr);
1221 * Delete a pool record from the database -- dangerous
1223 static int delete_pool(UAContext *ua)
1227 memset(&pr, 0, sizeof(pr));
1229 if (!get_pool_dbr(ua, &pr)) {
1232 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1235 if (ua->pint32_val) {
1236 db_delete_pool_record(ua->jcr, ua->db, &pr);
1242 static void do_mount_cmd(UAContext *ua, const char *command)
1247 char dev_name[MAX_NAME_LENGTH];
1253 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1255 store = get_storage_resource(ua, true/*use default*/);
1259 drive = get_storage_drive(ua, store);
1261 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1262 store->media_type, store->dev_name(), drive);
1264 set_storage(jcr, store);
1265 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1266 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1269 sd = jcr->store_bsock;
1270 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1271 bash_spaces(dev_name);
1272 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1273 while (bnet_recv(sd) >= 0) {
1274 bsendmsg(ua, "%s", sd->msg);
1276 bnet_sig(sd, BNET_TERMINATE);
1278 jcr->store_bsock = NULL;
1282 * mount [storage=<name>] [drive=nn]
1284 static int mount_cmd(UAContext *ua, const char *cmd)
1286 do_mount_cmd(ua, "mount"); /* mount */
1292 * unmount [storage=<name>] [drive=nn]
1294 static int unmount_cmd(UAContext *ua, const char *cmd)
1296 do_mount_cmd(ua, "unmount"); /* unmount */
1302 * release [storage=<name>] [drive=nn]
1304 static int release_cmd(UAContext *ua, const char *cmd)
1306 do_mount_cmd(ua, "release"); /* release */
1313 * use catalog=<name>
1315 static int use_cmd(UAContext *ua, const char *cmd)
1317 CAT *oldcatalog, *catalog;
1320 close_db(ua); /* close any previously open db */
1321 oldcatalog = ua->catalog;
1323 if (!(catalog = get_catalog_resource(ua))) {
1324 ua->catalog = oldcatalog;
1326 ua->catalog = catalog;
1329 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1330 ua->catalog->hdr.name, ua->catalog->db_name);
1335 int quit_cmd(UAContext *ua, const char *cmd)
1342 * Wait until no job is running
1344 int wait_cmd(UAContext *ua, const char *cmd)
1347 bmicrosleep(0, 200000); /* let job actually start */
1348 for (bool running=true; running; ) {
1351 if (jcr->JobId != 0) {
1366 static int help_cmd(UAContext *ua, const char *cmd)
1370 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1371 for (i=0; i<comsize; i++) {
1372 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1374 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1378 int qhelp_cmd(UAContext *ua, const char *cmd)
1382 for (i=0; i<comsize; i++) {
1383 bsendmsg(ua, "%s %s\n", _(commands[i].key), _(commands[i].help));
1388 static int version_cmd(UAContext *ua, const char *cmd)
1390 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1395 /* A bit brain damaged in that if the user has not done
1396 * a "use catalog xxx" command, we simply find the first
1397 * catalog resource and open it.
1399 int open_db(UAContext *ua)
1406 ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1409 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1412 bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1413 ua->catalog->hdr.name, ua->catalog->db_name);
1417 ua->jcr->catalog = ua->catalog;
1419 Dmsg0(150, "Open database\n");
1420 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1421 ua->catalog->db_password, ua->catalog->db_address,
1422 ua->catalog->db_port, ua->catalog->db_socket,
1423 ua->catalog->mult_db_connections);
1424 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1425 bsendmsg(ua, _("Could not open database \"%s\".\n"),
1426 ua->catalog->db_name);
1428 bsendmsg(ua, "%s", db_strerror(ua->db));
1433 ua->jcr->db = ua->db;
1434 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1438 void close_db(UAContext *ua)
1441 db_close_database(ua->jcr, ua->db);