3 * Bacula Director -- User Agent Commands
5 * Kern Sibbald, September MM
10 Copyright (C) 2000-2006 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
27 /* Imported subroutines */
29 /* Imported variables */
32 extern struct s_res resources[];
33 extern char my_name[];
34 extern jobq_t job_queue; /* job queue */
37 /* Imported functions */
38 extern int status_cmd(UAContext *ua, const char *cmd);
39 extern int list_cmd(UAContext *ua, const char *cmd);
40 extern int llist_cmd(UAContext *ua, const char *cmd);
41 extern int show_cmd(UAContext *ua, const char *cmd);
42 extern int messagescmd(UAContext *ua, const char *cmd);
43 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
44 extern int gui_cmd(UAContext *ua, const char *cmd);
45 extern int sqlquerycmd(UAContext *ua, const char *cmd);
46 extern int querycmd(UAContext *ua, const char *cmd);
47 extern int retentioncmd(UAContext *ua, const char *cmd);
48 extern int prunecmd(UAContext *ua, const char *cmd);
49 extern int purgecmd(UAContext *ua, const char *cmd);
50 extern int restore_cmd(UAContext *ua, const char *cmd);
51 extern int label_cmd(UAContext *ua, const char *cmd);
52 extern int relabel_cmd(UAContext *ua, const char *cmd);
53 extern int update_cmd(UAContext *ua, const char *cmd);
55 /* Forward referenced functions */
56 static int add_cmd(UAContext *ua, const char *cmd);
57 static int create_cmd(UAContext *ua, const char *cmd);
58 static int cancel_cmd(UAContext *ua, const char *cmd);
59 static int enable_cmd(UAContext *ua, const char *cmd);
60 static int disable_cmd(UAContext *ua, const char *cmd);
61 static int setdebug_cmd(UAContext *ua, const char *cmd);
62 static int trace_cmd(UAContext *ua, const char *cmd);
63 static int var_cmd(UAContext *ua, const char *cmd);
64 static int estimate_cmd(UAContext *ua, const char *cmd);
65 static int help_cmd(UAContext *ua, const char *cmd);
66 static int delete_cmd(UAContext *ua, const char *cmd);
67 static int use_cmd(UAContext *ua, const char *cmd);
68 static int unmount_cmd(UAContext *ua, const char *cmd);
69 static int version_cmd(UAContext *ua, const char *cmd);
70 static int automount_cmd(UAContext *ua, const char *cmd);
71 static int time_cmd(UAContext *ua, const char *cmd);
72 static int reload_cmd(UAContext *ua, const char *cmd);
73 static int delete_volume(UAContext *ua);
74 static int delete_pool(UAContext *ua);
75 static void delete_job(UAContext *ua);
76 static int mount_cmd(UAContext *ua, const char *cmd);
77 static int release_cmd(UAContext *ua, const char *cmd);
78 static int wait_cmd(UAContext *ua, const char *cmd);
79 static int setip_cmd(UAContext *ua, const char *cmd);
80 static int python_cmd(UAContext *ua, const char *cmd);
81 static void do_job_delete(UAContext *ua, JobId_t JobId);
82 static void delete_job_id_range(UAContext *ua, char *tok);
84 int qhelp_cmd(UAContext *ua, const char *cmd);
85 int quit_cmd(UAContext *ua, const char *cmd);
88 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
89 static struct cmdstruct commands[] = {
90 { N_("add"), add_cmd, _("add media to a pool")},
91 { N_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
92 { N_("automount"), automount_cmd, _("automount [on|off] -- after label")},
93 { N_("cancel"), cancel_cmd, _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
94 { N_("create"), create_cmd, _("create DB Pool from resource")},
95 { N_("delete"), delete_cmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
96 { N_("disable"), disable_cmd, _("disable <job=name> -- disable a job")},
97 { N_("enable"), enable_cmd, _("enable <job=name> -- enable a job")},
98 { N_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")},
99 { N_("exit"), quit_cmd, _("exit = quit")},
100 { N_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode")},
101 { N_("help"), help_cmd, _("print this command")},
102 { N_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
103 { N_("label"), label_cmd, _("label a tape")},
104 { N_("llist"), llist_cmd, _("full or long list like list command")},
105 { N_("messages"), messagescmd, _("messages")},
106 { N_("mount"), mount_cmd, _("mount <storage-name>")},
107 { N_("prune"), prunecmd, _("prune expired records from catalog")},
108 { N_("purge"), purgecmd, _("purge records from catalog")},
109 { N_("python"), python_cmd, _("python control commands")},
110 { N_("quit"), quit_cmd, _("quit")},
111 { N_("query"), querycmd, _("query catalog")},
112 { N_("restore"), restore_cmd, _("restore files")},
113 { N_("relabel"), relabel_cmd, _("relabel a tape")},
114 { N_("release"), release_cmd, _("release <storage-name>")},
115 { N_("reload"), reload_cmd, _("reload conf file")},
116 { N_("run"), run_cmd, _("run <job-name>")},
117 { N_("status"), status_cmd, _("status [storage | client]=<name>")},
118 { N_("setdebug"), setdebug_cmd, _("sets debug level")},
119 { N_("setip"), setip_cmd, _("sets new client address -- if authorized")},
120 { N_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")},
121 { N_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")},
122 { N_("time"), time_cmd, _("print current time")},
123 { N_("trace"), trace_cmd, _("turn on/off trace to file")},
124 { N_("unmount"), unmount_cmd, _("unmount <storage-name>")},
125 { N_("umount"), unmount_cmd, _("umount <storage-name> for old-time Unix guys")},
126 { N_("update"), update_cmd, _("update Volume, Pool or slots")},
127 { N_("use"), use_cmd, _("use catalog xxx")},
128 { N_("var"), var_cmd, _("does variable expansion")},
129 { N_("version"), version_cmd, _("print Director version")},
130 { N_("wait"), wait_cmd, _("wait until no jobs are running")},
132 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
135 * Execute a command from the UA
137 int do_a_command(UAContext *ua, const char *cmd)
145 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
150 while (ua->jcr->storage->size()) {
151 ua->jcr->storage->remove(0);
154 len = strlen(ua->argk[0]);
155 for (i=0; i<comsize; i++) { /* search for command */
156 if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) {
157 /* Check if command permitted, but "quit" is always OK */
158 if (strcmp(ua->argk[0], "quit") != 0 &&
159 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
162 stat = (*commands[i].func)(ua, cmd); /* go execute command */
168 bnet_fsend(ua->UA_sock, _("%s: is an illegal command.\n"), ua->argk[0]);
174 * This is a common routine used to stuff the Pool DB record defaults
175 * into the Media DB record just before creating a media (Volume)
178 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
180 mr->PoolId = pr->PoolId;
181 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));
182 mr->Recycle = pr->Recycle;
183 mr->VolRetention = pr->VolRetention;
184 mr->VolUseDuration = pr->VolUseDuration;
185 mr->MaxVolJobs = pr->MaxVolJobs;
186 mr->MaxVolFiles = pr->MaxVolFiles;
187 mr->MaxVolBytes = pr->MaxVolBytes;
188 mr->LabelType = pr->LabelType;
193 * Add Volumes to an existing Pool
195 static int add_cmd(UAContext *ua, const char *cmd)
199 int num, i, max, startnum;
201 char name[MAX_NAME_LENGTH];
203 int Slot = 0, InChanger = 0;
206 "You probably don't want to be using this command since it\n"
207 "creates database records without labeling the Volumes.\n"
208 "You probably want to use the \"label\" command.\n\n"));
214 memset(&pr, 0, sizeof(pr));
215 memset(&mr, 0, sizeof(mr));
217 if (!get_pool_dbr(ua, &pr)) {
221 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
222 pr.MaxVols, pr.PoolType);
224 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
225 bsendmsg(ua, _("Pool already has maximum volumes=%d\n"), pr.MaxVols);
227 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
230 pr.MaxVols = ua->pint32_val;
235 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
236 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
237 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
241 if (pr.MaxVols == 0) {
244 max = pr.MaxVols - pr.NumVols;
248 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
249 if (!get_pint(ua, buf)) {
252 num = ua->pint32_val;
253 if (num < 0 || num > max) {
254 bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
261 if (!get_cmd(ua, _("Enter Volume name: "))) {
265 if (!get_cmd(ua, _("Enter base volume name: "))) {
269 /* Don't allow | in Volume name because it is the volume separator character */
270 if (!is_volume_name_legal(ua, ua->cmd)) {
273 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
274 bsendmsg(ua, _("Volume name too long.\n"));
277 if (strlen(ua->cmd) == 0) {
278 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
282 bstrncpy(name, ua->cmd, sizeof(name));
284 bstrncat(name, "%04d", sizeof(name));
287 if (!get_pint(ua, _("Enter the starting number: "))) {
290 startnum = ua->pint32_val;
292 bsendmsg(ua, _("Start number must be greater than zero.\n"));
302 if (store && store->autochanger) {
303 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
306 Slot = ua->pint32_val;
307 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
310 InChanger = ua->pint32_val;
313 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
314 for (i=startnum; i < num+startnum; i++) {
315 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
317 mr.InChanger = InChanger;
318 mr.StorageId = store->StorageId;
319 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
320 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
321 bsendmsg(ua, "%s", db_strerror(ua->db));
325 first_id = mr.PoolId;
329 Dmsg0(200, "Update pool record.\n");
330 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
331 bsendmsg(ua, "%s", db_strerror(ua->db));
334 bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
340 * Turn auto mount on/off
345 int automount_cmd(UAContext *ua, const char *cmd)
350 if (!get_cmd(ua, _("Turn on or off? "))) {
358 ua->automount = (strcasecmp(onoff, _("off")) == 0) ? 0 : 1;
366 static int cancel_cmd(UAContext *ua, const char *cmd)
371 char JobName[MAX_NAME_LENGTH];
377 for (i=1; i<ua->argc; i++) {
378 if (strcasecmp(ua->argk[i], _("jobid")) == 0) {
383 JobId = str_to_int64(ua->argv[i]);
384 if (!(jcr=get_jcr_by_id(JobId))) {
385 bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
389 } else if (strcasecmp(ua->argk[i], _("job")) == 0) {
393 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
394 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
395 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
396 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
401 /* If we still do not have a jcr,
402 * throw up a list and ask the user to select one.
406 /* Count Jobs running */
408 if (jcr->JobId == 0) { /* this is us */
416 bsendmsg(ua, _("No Jobs running.\n"));
419 start_prompt(ua, _("Select Job:\n"));
422 if (jcr->JobId == 0) { /* this is us */
425 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
430 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
434 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
438 /* NOTE! This increments the ref_count */
439 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
440 jcr = get_jcr_by_full_name(JobName);
442 bsendmsg(ua, _("Job %s not found.\n"), JobName);
447 ret = cancel_job(ua, jcr);
453 * This is a common routine to create or update a
454 * Pool DB base record from a Pool Resource. We handle
455 * the setting of MaxVols and NumVols slightly differently
456 * depending on if we are creating the Pool or we are
457 * simply bringing it into agreement with the resource (updage).
459 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
461 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
462 if (op == POOL_OP_CREATE) {
463 pr->MaxVols = pool->max_volumes;
465 } else { /* update pool */
466 if (pr->MaxVols != pool->max_volumes) {
467 pr->MaxVols = pool->max_volumes;
469 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
470 pr->MaxVols = pr->NumVols;
473 pr->LabelType = pool->LabelType;
474 pr->UseOnce = pool->use_volume_once;
475 pr->UseCatalog = pool->use_catalog;
476 pr->AcceptAnyVolume = pool->accept_any_volume;
477 pr->Recycle = pool->Recycle;
478 pr->VolRetention = pool->VolRetention;
479 pr->VolUseDuration = pool->VolUseDuration;
480 pr->MaxVolJobs = pool->MaxVolJobs;
481 pr->MaxVolFiles = pool->MaxVolFiles;
482 pr->MaxVolBytes = pool->MaxVolBytes;
483 pr->AutoPrune = pool->AutoPrune;
484 pr->Recycle = pool->Recycle;
485 if (pool->label_format) {
486 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
488 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
494 * Create a pool record from a given Pool resource
495 * Also called from backup.c
496 * Returns: -1 on error
497 * 0 record already exists
501 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
505 memset(&pr, 0, sizeof(POOL_DBR));
507 bstrncpy(pr.Name, pool->hdr.name, sizeof(pr.Name));
509 if (db_get_pool_record(jcr, db, &pr)) {
511 if (op == POOL_OP_UPDATE) { /* update request */
512 set_pooldbr_from_poolres(&pr, pool, op);
513 db_update_pool_record(jcr, db, &pr);
515 return 0; /* exists */
518 set_pooldbr_from_poolres(&pr, pool, op);
520 if (!db_create_pool_record(jcr, db, &pr)) {
521 return -1; /* error */
529 * Create a Pool Record in the database.
530 * It is always created from the Resource record.
532 static int create_cmd(UAContext *ua, const char *cmd)
540 pool = get_pool_resource(ua);
545 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
547 bsendmsg(ua, _("Error: Pool %s already exists.\n"
548 "Use update to change it.\n"), pool->hdr.name);
552 bsendmsg(ua, "%s", db_strerror(ua->db));
558 bsendmsg(ua, _("Pool %s created.\n"), pool->hdr.name);
563 extern DIRRES *director;
566 * Python control command
567 * python restart (restarts interpreter)
569 static int python_cmd(UAContext *ua, const char *cmd)
571 if (ua->argc >= 1 && strcasecmp(ua->argk[1], _("restart")) == 0) {
572 term_python_interpreter();
573 init_python_interpreter(director->hdr.name,
574 director->scripts_directory, "DirStartUp");
575 bsendmsg(ua, _("Python interpreter restarted.\n"));
577 bsendmsg(ua, _("Nothing done.\n"));
584 * Set a new address in a Client resource. We do this only
585 * if the Console name is the same as the Client name
586 * and the Console can access the client.
588 static int setip_cmd(UAContext *ua, const char *cmd)
592 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->hdr.name)) {
593 bsendmsg(ua, _("Illegal command from this console.\n"));
597 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->hdr.name);
600 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->hdr.name);
603 if (client->address) {
604 free(client->address);
606 /* MA Bug 6 remove ifdef */
607 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
608 client->address = bstrdup(buf);
609 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
610 client->hdr.name, client->address);
617 static void do_en_disable_cmd(UAContext *ua, bool setting)
622 i = find_arg_with_value(ua, N_("job"));
624 job = select_job_resource(ua);
630 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
634 bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
638 if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
639 bsendmsg(ua, _("Illegal command from this console.\n"));
642 job->enabled = setting;
643 bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->hdr.name, setting?"en":"dis");
647 static int enable_cmd(UAContext *ua, const char *cmd)
649 do_en_disable_cmd(ua, true);
653 static int disable_cmd(UAContext *ua, const char *cmd)
655 do_en_disable_cmd(ua, false);
660 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
665 set_storage(jcr, store);
666 /* Try connecting for up to 15 seconds */
667 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
668 store->hdr.name, store->address, store->SDport);
669 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
670 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
673 Dmsg0(120, _("Connected to storage daemon\n"));
674 sd = jcr->store_bsock;
675 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
676 if (bnet_recv(sd) >= 0) {
677 bsendmsg(ua, "%s", sd->msg);
679 bnet_sig(sd, BNET_TERMINATE);
681 jcr->store_bsock = NULL;
685 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
689 /* Connect to File daemon */
691 ua->jcr->client = client;
692 /* Try to connect for 15 seconds */
693 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
694 client->hdr.name, client->address, client->FDport);
695 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
696 bsendmsg(ua, _("Failed to connect to Client.\n"));
699 Dmsg0(120, "Connected to file daemon\n");
700 fd = ua->jcr->file_bsock;
701 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
702 if (bnet_recv(fd) >= 0) {
703 bsendmsg(ua, "%s", fd->msg);
705 bnet_sig(fd, BNET_TERMINATE);
707 ua->jcr->file_bsock = NULL;
712 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
714 STORE *store, **unique_store;
715 CLIENT *client, **unique_client;
721 /* Count Storage items */
725 foreach_res(store, R_STORAGE) {
728 unique_store = (STORE **) malloc(i * sizeof(STORE));
729 /* Find Unique Storage address/port */
730 store = (STORE *)GetNextRes(R_STORAGE, NULL);
732 unique_store[i++] = store;
733 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
735 for (j=0; j<i; j++) {
736 if (strcmp(unique_store[j]->address, store->address) == 0 &&
737 unique_store[j]->SDport == store->SDport) {
743 unique_store[i++] = store;
744 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
749 /* Call each unique Storage daemon */
750 for (j=0; j<i; j++) {
751 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
755 /* Count Client items */
759 foreach_res(client, R_CLIENT) {
762 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
763 /* Find Unique Client address/port */
764 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
766 unique_client[i++] = client;
767 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
769 for (j=0; j<i; j++) {
770 if (strcmp(unique_client[j]->address, client->address) == 0 &&
771 unique_client[j]->FDport == client->FDport) {
777 unique_client[i++] = client;
778 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
783 /* Call each unique File daemon */
784 for (j=0; j<i; j++) {
785 do_client_setdebug(ua, unique_client[j], level, trace_flag);
791 * setdebug level=nn all trace=1/0
793 static int setdebug_cmd(UAContext *ua, const char *cmd)
804 Dmsg1(120, "setdebug:%s:\n", cmd);
807 i = find_arg_with_value(ua, "level");
809 level = atoi(ua->argv[i]);
812 if (!get_pint(ua, _("Enter new debug level: "))) {
815 level = ua->pint32_val;
818 /* Look for trace flag. -1 => not change */
819 i = find_arg_with_value(ua, "trace");
821 trace_flag = atoi(ua->argv[i]);
822 if (trace_flag > 0) {
828 for (i=1; i<ua->argc; i++) {
829 if (strcasecmp(ua->argk[i], "all") == 0) {
830 do_all_setdebug(ua, level, trace_flag);
833 if (strcasecmp(ua->argk[i], "dir") == 0 ||
834 strcasecmp(ua->argk[i], "director") == 0) {
836 set_trace(trace_flag);
839 if (strcasecmp(ua->argk[i], "client") == 0 ||
840 strcasecmp(ua->argk[i], "fd") == 0) {
843 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
845 do_client_setdebug(ua, client, level, trace_flag);
849 client = select_client_resource(ua);
851 do_client_setdebug(ua, client, level, trace_flag);
856 if (strcasecmp(ua->argk[i], "store") == 0 ||
857 strcasecmp(ua->argk[i], "storage") == 0 ||
858 strcasecmp(ua->argk[i], "sd") == 0) {
861 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
863 do_storage_setdebug(ua, store, level, trace_flag);
867 store = get_storage_resource(ua, false/*no default*/);
869 do_storage_setdebug(ua, store, level, trace_flag);
875 * We didn't find an appropriate keyword above, so
878 start_prompt(ua, _("Available daemons are: \n"));
879 add_prompt(ua, "Director");
880 add_prompt(ua, "Storage");
881 add_prompt(ua, "Client");
882 add_prompt(ua, "All");
883 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
884 case 0: /* Director */
886 set_trace(trace_flag);
889 store = get_storage_resource(ua, false/*no default*/);
891 do_storage_setdebug(ua, store, level, trace_flag);
895 client = select_client_resource(ua);
897 do_client_setdebug(ua, client, level, trace_flag);
901 do_all_setdebug(ua, level, trace_flag);
910 * Turn debug tracing to file on/off
912 static int trace_cmd(UAContext *ua, const char *cmd)
917 if (!get_cmd(ua, _("Turn on or off? "))) {
925 set_trace((strcasecmp(onoff, _("off")) == 0) ? false : true);
930 static int var_cmd(UAContext *ua, const char *cmd)
932 POOLMEM *val = get_pool_memory(PM_FNAME);
938 for (var=ua->cmd; *var != ' '; ) { /* skip command */
941 while (*var == ' ') { /* skip spaces */
944 Dmsg1(100, "Var=%s:\n", var);
945 variable_expansion(ua->jcr, var, &val);
946 bsendmsg(ua, "%s\n", val);
947 free_pool_memory(val);
951 static int estimate_cmd(UAContext *ua, const char *cmd)
954 CLIENT *client = NULL;
955 FILESET *fileset = NULL;
957 char since[MAXSTRING];
960 jcr->JobLevel = L_FULL;
961 for (int i=1; i<ua->argc; i++) {
962 if (strcasecmp(ua->argk[i], "client") == 0 ||
963 strcasecmp(ua->argk[i], "fd") == 0) {
965 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
969 if (strcasecmp(ua->argk[i], "job") == 0) {
971 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
975 if (strcasecmp(ua->argk[i], "fileset") == 0) {
977 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
981 if (strcasecmp(ua->argk[i], "listing") == 0) {
985 if (strcasecmp(ua->argk[i], "level") == 0) {
986 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
987 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
992 if (!job && !(client && fileset)) {
993 if (!(job = select_job_resource(ua))) {
998 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1000 bsendmsg(ua, _("No job specified.\n"));
1005 client = job->client;
1008 fileset = job->fileset;
1010 jcr->client = client;
1011 jcr->fileset = fileset;
1013 ua->catalog = client->catalog;
1020 jcr->JobType = JT_BACKUP;
1021 init_jcr_job_record(jcr);
1023 if (!get_or_create_client_record(jcr)) {
1026 if (!get_or_create_fileset_record(jcr)) {
1030 get_level_since_time(ua->jcr, since, sizeof(since));
1032 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1033 job->client->hdr.name, job->client->address, job->client->FDport);
1034 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1035 bsendmsg(ua, _("Failed to connect to Client.\n"));
1039 if (!send_include_list(jcr)) {
1040 bsendmsg(ua, _("Error sending include list.\n"));
1044 if (!send_exclude_list(jcr)) {
1045 bsendmsg(ua, _("Error sending exclude list.\n"));
1049 if (!send_level_command(jcr)) {
1053 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1054 while (bnet_recv(jcr->file_bsock) >= 0) {
1055 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1059 if (jcr->file_bsock) {
1060 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1061 bnet_close(jcr->file_bsock);
1062 jcr->file_bsock = NULL;
1071 static int time_cmd(UAContext *ua, const char *cmd)
1074 time_t ttime = time(NULL);
1076 localtime_r(&ttime, &tm);
1077 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1078 bsendmsg(ua, "%s\n", sdt);
1083 * reload the conf file
1085 extern "C" void reload_config(int sig);
1087 static int reload_cmd(UAContext *ua, const char *cmd)
1094 * Delete Pool records (should purge Media with it).
1096 * delete pool=<pool-name>
1097 * delete volume pool=<pool-name> volume=<name>
1100 static int delete_cmd(UAContext *ua, const char *cmd)
1102 static const char *keywords[] = {
1112 switch (find_arg_keyword(ua, keywords)) {
1121 while ((i=find_arg(ua, "jobid")) > 0) {
1123 *ua->argk[i] = 0; /* zap keyword already visited */
1131 "In general it is not a good idea to delete either a\n"
1132 "Pool or a Volume since they may contain data.\n\n"));
1134 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1145 bsendmsg(ua, _("Nothing done.\n"));
1153 * delete_job has been modified to parse JobID lists like the
1155 * delete JobID=3,4,6,7-11,14
1157 * Thanks to Phil Stracchino for the above addition.
1160 static void delete_job(UAContext *ua)
1165 int i = find_arg_with_value(ua, N_("jobid"));
1167 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1168 s = bstrdup(ua->argv[i]);
1171 * We could use strtok() here. But we're not going to, because:
1172 * (a) strtok() is deprecated, having been replaced by strsep();
1173 * (b) strtok() is broken in significant ways.
1174 * we could use strsep() instead, but it's not universally available.
1175 * so we grow our own using strchr().
1177 sep = strchr(tok, ',');
1178 while (sep != NULL) {
1180 if (strchr(tok, '-')) {
1181 delete_job_id_range(ua, tok);
1183 JobId = str_to_int64(tok);
1184 do_job_delete(ua, JobId);
1187 sep = strchr(tok, ',');
1189 /* pick up the last token */
1190 if (strchr(tok, '-')) {
1191 delete_job_id_range(ua, tok);
1193 JobId = str_to_int64(tok);
1194 do_job_delete(ua, JobId);
1199 JobId = str_to_int64(ua->argv[i]);
1200 do_job_delete(ua, JobId);
1202 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1205 JobId = ua->int64_val;
1206 do_job_delete(ua, JobId);
1211 * we call delete_job_id_range to parse range tokens and iterate over ranges
1213 static void delete_job_id_range(UAContext *ua, char *tok)
1218 tok2 = strchr(tok, '-');
1221 j1 = str_to_int64(tok);
1222 j2 = str_to_int64(tok2);
1223 for (j=j1; j<=j2; j++) {
1224 do_job_delete(ua, j);
1229 * do_job_delete now performs the actual delete operation atomically
1230 * we always return 1 because C++ is pissy about void functions
1233 static void do_job_delete(UAContext *ua, JobId_t JobId)
1235 POOLMEM *query = get_pool_memory(PM_MESSAGE);
1238 Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(JobId, ed1));
1239 db_sql_query(ua->db, query, NULL, (void *)NULL);
1240 Mmsg(query, "DELETE FROM MAC WHERE JobId=%s", ed1);
1241 db_sql_query(ua->db, query, NULL, (void *)NULL);
1242 Mmsg(query, "DELETE FROM File WHERE JobId=%s", ed1);
1243 db_sql_query(ua->db, query, NULL, (void *)NULL);
1244 Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", ed1);
1245 db_sql_query(ua->db, query, NULL, (void *)NULL);
1246 free_pool_memory(query);
1247 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1251 * Delete media records from database -- dangerous
1253 static int delete_volume(UAContext *ua)
1257 if (!select_media_dbr(ua, &mr)) {
1260 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1261 "and all Jobs saved on that volume from the Catalog\n"),
1264 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1267 if (ua->pint32_val) {
1268 db_delete_media_record(ua->jcr, ua->db, &mr);
1274 * Delete a pool record from the database -- dangerous
1276 static int delete_pool(UAContext *ua)
1280 memset(&pr, 0, sizeof(pr));
1282 if (!get_pool_dbr(ua, &pr)) {
1285 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1288 if (ua->pint32_val) {
1289 db_delete_pool_record(ua->jcr, ua->db, &pr);
1295 static void do_mount_cmd(UAContext *ua, const char *command)
1300 char dev_name[MAX_NAME_LENGTH];
1306 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1308 store = get_storage_resource(ua, true/*arg is storage*/);
1312 set_storage(jcr, store);
1313 drive = get_storage_drive(ua, store);
1315 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1316 store->media_type, store->dev_name(), drive);
1318 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1319 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1322 sd = jcr->store_bsock;
1323 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1324 bash_spaces(dev_name);
1325 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1326 while (bnet_recv(sd) >= 0) {
1327 bsendmsg(ua, "%s", sd->msg);
1329 bnet_sig(sd, BNET_TERMINATE);
1331 jcr->store_bsock = NULL;
1335 * mount [storage=<name>] [drive=nn]
1337 static int mount_cmd(UAContext *ua, const char *cmd)
1339 do_mount_cmd(ua, "mount"); /* mount */
1345 * unmount [storage=<name>] [drive=nn]
1347 static int unmount_cmd(UAContext *ua, const char *cmd)
1349 do_mount_cmd(ua, "unmount"); /* unmount */
1355 * release [storage=<name>] [drive=nn]
1357 static int release_cmd(UAContext *ua, const char *cmd)
1359 do_mount_cmd(ua, "release"); /* release */
1366 * use catalog=<name>
1368 static int use_cmd(UAContext *ua, const char *cmd)
1370 CAT *oldcatalog, *catalog;
1373 close_db(ua); /* close any previously open db */
1374 oldcatalog = ua->catalog;
1376 if (!(catalog = get_catalog_resource(ua))) {
1377 ua->catalog = oldcatalog;
1379 ua->catalog = catalog;
1382 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1383 ua->catalog->hdr.name, ua->catalog->db_name);
1388 int quit_cmd(UAContext *ua, const char *cmd)
1395 * Wait until no job is running
1397 int wait_cmd(UAContext *ua, const char *cmd)
1400 bmicrosleep(0, 200000); /* let job actually start */
1401 for (bool running=true; running; ) {
1404 if (jcr->JobId != 0) {
1419 static int help_cmd(UAContext *ua, const char *cmd)
1423 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1424 for (i=0; i<comsize; i++) {
1425 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1427 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1431 int qhelp_cmd(UAContext *ua, const char *cmd)
1435 for (i=0; i<comsize; i++) {
1436 bsendmsg(ua, "%s %s\n", _(commands[i].key), _(commands[i].help));
1441 static int version_cmd(UAContext *ua, const char *cmd)
1443 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1448 /* A bit brain damaged in that if the user has not done
1449 * a "use catalog xxx" command, we simply find the first
1450 * catalog resource and open it.
1452 bool open_db(UAContext *ua)
1459 ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1462 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1464 } else if (!acl_access_ok(ua, Catalog_ACL, ua->catalog->hdr.name)) {
1465 bsendmsg(ua, _("You must specify a \"use <catalog-name>\" command before continuing.\n"));
1469 bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1470 ua->catalog->hdr.name, ua->catalog->db_name);
1474 ua->jcr->catalog = ua->catalog;
1476 Dmsg0(150, "Open database\n");
1477 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1478 ua->catalog->db_password, ua->catalog->db_address,
1479 ua->catalog->db_port, ua->catalog->db_socket,
1480 ua->catalog->mult_db_connections);
1481 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1482 bsendmsg(ua, _("Could not open database \"%s\".\n"),
1483 ua->catalog->db_name);
1485 bsendmsg(ua, "%s", db_strerror(ua->db));
1490 ua->jcr->db = ua->db;
1491 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1495 void close_db(UAContext *ua)
1498 db_close_database(ua->jcr, ua->db);