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 ammended 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, 0)) != 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 %d is not running.\n"), JobId);
379 } else if (strcasecmp(ua->argk[i], _("job")) == 0) {
383 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
384 bsendmsg(ua, _("Job %s is not running.\n"), ua->argv[i]);
390 /* If we still do not have a jcr,
391 * throw up a list and ask the user to select one.
395 /* Count Jobs running */
397 if (jcr->JobId == 0) { /* this is us */
406 bsendmsg(ua, _("No Jobs running.\n"));
409 start_prompt(ua, _("Select Job:\n"));
411 if (jcr->JobId == 0) { /* this is us */
415 bsnprintf(buf, sizeof(buf), "JobId=%d Job=%s", jcr->JobId, jcr->Job);
420 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
424 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
428 /* NOTE! This increments the ref_count */
429 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
430 jcr = get_jcr_by_full_name(JobName);
432 bsendmsg(ua, _("Job %s not found.\n"), JobName);
437 ret = cancel_job(ua, jcr);
444 * This is a common routine to create or update a
445 * Pool DB base record from a Pool Resource. We handle
446 * the setting of MaxVols and NumVols slightly differently
447 * depending on if we are creating the Pool or we are
448 * simply bringing it into agreement with the resource (updage).
450 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
452 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
453 if (op == POOL_OP_CREATE) {
454 pr->MaxVols = pool->max_volumes;
456 } else { /* update pool */
457 if (pr->MaxVols != pool->max_volumes) {
458 pr->MaxVols = pool->max_volumes;
460 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
461 pr->MaxVols = pr->NumVols;
464 pr->LabelType = pool->LabelType;
465 pr->UseOnce = pool->use_volume_once;
466 pr->UseCatalog = pool->use_catalog;
467 pr->AcceptAnyVolume = pool->accept_any_volume;
468 pr->Recycle = pool->Recycle;
469 pr->VolRetention = pool->VolRetention;
470 pr->VolUseDuration = pool->VolUseDuration;
471 pr->MaxVolJobs = pool->MaxVolJobs;
472 pr->MaxVolFiles = pool->MaxVolFiles;
473 pr->MaxVolBytes = pool->MaxVolBytes;
474 pr->AutoPrune = pool->AutoPrune;
475 pr->Recycle = pool->Recycle;
476 if (pool->label_format) {
477 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
479 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
485 * Create a pool record from a given Pool resource
486 * Also called from backup.c
487 * Returns: -1 on error
488 * 0 record already exists
492 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
496 memset(&pr, 0, sizeof(POOL_DBR));
498 bstrncpy(pr.Name, pool->hdr.name, sizeof(pr.Name));
500 if (db_get_pool_record(jcr, db, &pr)) {
502 if (op == POOL_OP_UPDATE) { /* update request */
503 set_pooldbr_from_poolres(&pr, pool, op);
504 db_update_pool_record(jcr, db, &pr);
506 return 0; /* exists */
509 set_pooldbr_from_poolres(&pr, pool, op);
511 if (!db_create_pool_record(jcr, db, &pr)) {
512 return -1; /* error */
520 * Create a Pool Record in the database.
521 * It is always created from the Resource record.
523 static int create_cmd(UAContext *ua, const char *cmd)
531 pool = get_pool_resource(ua);
536 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
538 bsendmsg(ua, _("Error: Pool %s already exists.\n"
539 "Use update to change it.\n"), pool->hdr.name);
543 bsendmsg(ua, "%s", db_strerror(ua->db));
549 bsendmsg(ua, _("Pool %s created.\n"), pool->hdr.name);
554 extern DIRRES *director;
557 * Python control command
558 * python restart (restarts interpreter)
560 static int python_cmd(UAContext *ua, const char *cmd)
562 if (strcasecmp(ua->argk[1], _("restart")) == 0) {
563 term_python_interpreter();
564 init_python_interpreter(director->hdr.name,
565 director->scripts_directory, "DirStartUp");
566 bsendmsg(ua, _("Python interpreter restarted.\n"));
568 bsendmsg(ua, _("Nothing done.\n"));
575 * Set a new address in a Client resource. We do this only
576 * if the Console name is the same as the Client name
577 * and the Console can access the client.
579 static int setip_cmd(UAContext *ua, const char *cmd)
583 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->hdr.name)) {
584 bsendmsg(ua, _("Illegal command from this console.\n"));
588 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->hdr.name);
591 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->hdr.name);
594 if (client->address) {
595 free(client->address);
597 /* MA Bug 6 remove ifdef */
598 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
599 client->address = bstrdup(buf);
600 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
601 client->hdr.name, client->address);
608 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
613 set_storage(jcr, store);
614 /* Try connecting for up to 15 seconds */
615 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
616 store->hdr.name, store->address, store->SDport);
617 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
618 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
621 Dmsg0(120, _("Connected to storage daemon\n"));
622 sd = jcr->store_bsock;
623 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
624 if (bnet_recv(sd) >= 0) {
625 bsendmsg(ua, "%s", sd->msg);
627 bnet_sig(sd, BNET_TERMINATE);
629 jcr->store_bsock = NULL;
633 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
637 /* Connect to File daemon */
639 ua->jcr->client = client;
640 /* Try to connect for 15 seconds */
641 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
642 client->hdr.name, client->address, client->FDport);
643 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
644 bsendmsg(ua, _("Failed to connect to Client.\n"));
647 Dmsg0(120, "Connected to file daemon\n");
648 fd = ua->jcr->file_bsock;
649 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
650 if (bnet_recv(fd) >= 0) {
651 bsendmsg(ua, "%s", fd->msg);
653 bnet_sig(fd, BNET_TERMINATE);
655 ua->jcr->file_bsock = NULL;
660 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
662 STORE *store, **unique_store;
663 CLIENT *client, **unique_client;
669 /* Count Storage items */
673 foreach_res(store, R_STORAGE) {
676 unique_store = (STORE **) malloc(i * sizeof(STORE));
677 /* Find Unique Storage address/port */
678 store = (STORE *)GetNextRes(R_STORAGE, NULL);
680 unique_store[i++] = store;
681 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
683 for (j=0; j<i; j++) {
684 if (strcmp(unique_store[j]->address, store->address) == 0 &&
685 unique_store[j]->SDport == store->SDport) {
691 unique_store[i++] = store;
692 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
697 /* Call each unique Storage daemon */
698 for (j=0; j<i; j++) {
699 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
703 /* Count Client items */
707 foreach_res(client, R_CLIENT) {
710 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
711 /* Find Unique Client address/port */
712 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
714 unique_client[i++] = client;
715 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
717 for (j=0; j<i; j++) {
718 if (strcmp(unique_client[j]->address, client->address) == 0 &&
719 unique_client[j]->FDport == client->FDport) {
725 unique_client[i++] = client;
726 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
731 /* Call each unique File daemon */
732 for (j=0; j<i; j++) {
733 do_client_setdebug(ua, unique_client[j], level, trace_flag);
739 * setdebug level=nn all trace=1/0
741 static int setdebug_cmd(UAContext *ua, const char *cmd)
752 Dmsg1(120, "setdebug:%s:\n", cmd);
755 i = find_arg_with_value(ua, _("level"));
757 level = atoi(ua->argv[i]);
760 if (!get_pint(ua, _("Enter new debug level: "))) {
763 level = ua->pint32_val;
766 /* Look for trace flag. -1 => not change */
767 i = find_arg_with_value(ua, _("trace"));
769 trace_flag = atoi(ua->argv[i]);
770 if (trace_flag > 0) {
776 for (i=1; i<ua->argc; i++) {
777 if (strcasecmp(ua->argk[i], _("all")) == 0) {
778 do_all_setdebug(ua, level, trace_flag);
781 if (strcasecmp(ua->argk[i], _("dir")) == 0 ||
782 strcasecmp(ua->argk[i], _("director")) == 0) {
784 set_trace(trace_flag);
787 if (strcasecmp(ua->argk[i], _("client")) == 0 ||
788 strcasecmp(ua->argk[i], _("fd")) == 0) {
791 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
793 do_client_setdebug(ua, client, level, trace_flag);
797 client = select_client_resource(ua);
799 do_client_setdebug(ua, client, level, trace_flag);
804 if (strcasecmp(ua->argk[i], _("store")) == 0 ||
805 strcasecmp(ua->argk[i], _("storage")) == 0 ||
806 strcasecmp(ua->argk[i], _("sd")) == 0) {
809 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
811 do_storage_setdebug(ua, store, level, trace_flag);
815 store = get_storage_resource(ua, 0);
817 do_storage_setdebug(ua, store, level, trace_flag);
823 * We didn't find an appropriate keyword above, so
826 start_prompt(ua, _("Available daemons are: \n"));
827 add_prompt(ua, _("Director"));
828 add_prompt(ua, _("Storage"));
829 add_prompt(ua, _("Client"));
830 add_prompt(ua, _("All"));
831 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
832 case 0: /* Director */
834 set_trace(trace_flag);
837 store = get_storage_resource(ua, 0);
839 do_storage_setdebug(ua, store, level, trace_flag);
843 client = select_client_resource(ua);
845 do_client_setdebug(ua, client, level, trace_flag);
849 do_all_setdebug(ua, level, trace_flag);
858 * Turn debug tracing to file on/off
860 static int trace_cmd(UAContext *ua, const char *cmd)
865 if (!get_cmd(ua, _("Turn on or off? "))) {
873 set_trace((strcasecmp(onoff, _("off")) == 0) ? false : true);
878 static int var_cmd(UAContext *ua, const char *cmd)
880 POOLMEM *val = get_pool_memory(PM_FNAME);
886 for (var=ua->cmd; *var != ' '; ) { /* skip command */
889 while (*var == ' ') { /* skip spaces */
892 Dmsg1(100, "Var=%s:\n", var);
893 variable_expansion(ua->jcr, var, &val);
894 bsendmsg(ua, "%s\n", val);
895 free_pool_memory(val);
899 static int estimate_cmd(UAContext *ua, const char *cmd)
902 CLIENT *client = NULL;
903 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, &fsr)) {
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 job 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, _("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];
1252 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1254 store = get_storage_resource(ua, 1);
1259 Dmsg2(120, "Found storage, MediaType=%s DevName=%s\n",
1260 store->media_type, store->dev_name());
1262 set_storage(jcr, store);
1263 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1264 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1267 sd = jcr->store_bsock;
1268 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1269 bash_spaces(dev_name);
1270 bnet_fsend(sd, "%s %s", command, dev_name);
1271 while (bnet_recv(sd) >= 0) {
1272 bsendmsg(ua, "%s", sd->msg);
1274 bnet_sig(sd, BNET_TERMINATE);
1276 jcr->store_bsock = NULL;
1280 * mount [storage | device] <name>
1282 static int mount_cmd(UAContext *ua, const char *cmd)
1284 do_mount_cmd(ua, "mount"); /* mount */
1290 * unmount [storage | device] <name>
1292 static int unmount_cmd(UAContext *ua, const char *cmd)
1294 do_mount_cmd(ua, "unmount"); /* unmount */
1300 * release [storage | device] <name>
1302 static int release_cmd(UAContext *ua, const char *cmd)
1304 do_mount_cmd(ua, "release"); /* release */
1311 * use catalog=<name>
1313 static int use_cmd(UAContext *ua, const char *cmd)
1315 CAT *oldcatalog, *catalog;
1318 close_db(ua); /* close any previously open db */
1319 oldcatalog = ua->catalog;
1321 if (!(catalog = get_catalog_resource(ua))) {
1322 ua->catalog = oldcatalog;
1324 ua->catalog = catalog;
1327 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1328 ua->catalog->hdr.name, ua->catalog->db_name);
1333 int quit_cmd(UAContext *ua, const char *cmd)
1340 * Wait until no job is running
1342 int wait_cmd(UAContext *ua, const char *cmd)
1345 bmicrosleep(0, 200000); /* let job actually start */
1346 for (bool running=true; running; ) {
1349 if (jcr->JobId != 0) {
1364 static int help_cmd(UAContext *ua, const char *cmd)
1368 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1369 for (i=0; i<comsize; i++) {
1370 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1372 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1376 int qhelp_cmd(UAContext *ua, const char *cmd)
1380 for (i=0; i<comsize; i++) {
1381 bsendmsg(ua, _("%s %s\n"), _(commands[i].key), _(commands[i].help));
1386 static int version_cmd(UAContext *ua, const char *cmd)
1388 bsendmsg(ua, "%s Version: " VERSION " (" BDATE ")\n", my_name);
1393 /* A bit brain damaged in that if the user has not done
1394 * a "use catalog xxx" command, we simply find the first
1395 * catalog resource and open it.
1397 int open_db(UAContext *ua)
1404 ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1407 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1410 bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1411 ua->catalog->hdr.name, ua->catalog->db_name);
1415 ua->jcr->catalog = ua->catalog;
1417 Dmsg0(150, "Open database\n");
1418 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1419 ua->catalog->db_password, ua->catalog->db_address,
1420 ua->catalog->db_port, ua->catalog->db_socket,
1421 ua->catalog->mult_db_connections);
1422 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1423 bsendmsg(ua, _("Could not open database \"%s\".\n"),
1424 ua->catalog->db_name);
1426 bsendmsg(ua, "%s", db_strerror(ua->db));
1431 ua->jcr->db = ua->db;
1432 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1436 void close_db(UAContext *ua)
1439 db_close_database(ua->jcr, ua->db);