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 jobq_t job_queue; /* job queue */
36 /* Imported functions */
37 extern int status_cmd(UAContext *ua, const char *cmd);
38 extern int list_cmd(UAContext *ua, const char *cmd);
39 extern int llist_cmd(UAContext *ua, const char *cmd);
40 extern int show_cmd(UAContext *ua, const char *cmd);
41 extern int messagescmd(UAContext *ua, const char *cmd);
42 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
43 extern int gui_cmd(UAContext *ua, const char *cmd);
44 extern int sqlquerycmd(UAContext *ua, const char *cmd);
45 extern int querycmd(UAContext *ua, const char *cmd);
46 extern int retentioncmd(UAContext *ua, const char *cmd);
47 extern int prunecmd(UAContext *ua, const char *cmd);
48 extern int purgecmd(UAContext *ua, const char *cmd);
49 extern int restore_cmd(UAContext *ua, const char *cmd);
50 extern int label_cmd(UAContext *ua, const char *cmd);
51 extern int relabel_cmd(UAContext *ua, const char *cmd);
52 extern int update_cmd(UAContext *ua, const char *cmd);
54 /* Forward referenced functions */
55 static int add_cmd(UAContext *ua, const char *cmd);
56 static int create_cmd(UAContext *ua, const char *cmd);
57 static int cancel_cmd(UAContext *ua, const char *cmd);
58 static int enable_cmd(UAContext *ua, const char *cmd);
59 static int disable_cmd(UAContext *ua, const char *cmd);
60 static int setdebug_cmd(UAContext *ua, const char *cmd);
61 static int trace_cmd(UAContext *ua, const char *cmd);
62 static int var_cmd(UAContext *ua, const char *cmd);
63 static int estimate_cmd(UAContext *ua, const char *cmd);
64 static int help_cmd(UAContext *ua, const char *cmd);
65 static int delete_cmd(UAContext *ua, const char *cmd);
66 static int use_cmd(UAContext *ua, const char *cmd);
67 static int unmount_cmd(UAContext *ua, const char *cmd);
68 static int version_cmd(UAContext *ua, const char *cmd);
69 static int automount_cmd(UAContext *ua, const char *cmd);
70 static int time_cmd(UAContext *ua, const char *cmd);
71 static int reload_cmd(UAContext *ua, const char *cmd);
72 static int delete_volume(UAContext *ua);
73 static int delete_pool(UAContext *ua);
74 static void delete_job(UAContext *ua);
75 static int mount_cmd(UAContext *ua, const char *cmd);
76 static int release_cmd(UAContext *ua, const char *cmd);
77 static int wait_cmd(UAContext *ua, const char *cmd);
78 static int setip_cmd(UAContext *ua, const char *cmd);
79 static int python_cmd(UAContext *ua, const char *cmd);
80 static void do_job_delete(UAContext *ua, JobId_t JobId);
81 static void delete_job_id_range(UAContext *ua, char *tok);
83 int qhelp_cmd(UAContext *ua, const char *cmd);
84 int quit_cmd(UAContext *ua, const char *cmd);
87 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
88 static struct cmdstruct commands[] = {
89 { NT_("add"), add_cmd, _("add media to a pool")},
90 { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
91 { NT_("automount"), automount_cmd, _("automount [on|off] -- after label")},
92 { NT_("cancel"), cancel_cmd, _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
93 { NT_("create"), create_cmd, _("create DB Pool from resource")},
94 { NT_("delete"), delete_cmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
95 { NT_("disable"), disable_cmd, _("disable <job=name> -- disable a job")},
96 { NT_("enable"), enable_cmd, _("enable <job=name> -- enable a job")},
97 { NT_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")},
98 { NT_("exit"), quit_cmd, _("exit = quit")},
99 { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode")},
100 { NT_("help"), help_cmd, _("print this command")},
101 { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
102 { NT_("label"), label_cmd, _("label a tape")},
103 { NT_("llist"), llist_cmd, _("full or long list like list command")},
104 { NT_("messages"), messagescmd, _("messages")},
105 { NT_("mount"), mount_cmd, _("mount <storage-name>")},
106 { NT_("prune"), prunecmd, _("prune expired records from catalog")},
107 { NT_("purge"), purgecmd, _("purge records from catalog")},
108 { NT_("python"), python_cmd, _("python control commands")},
109 { NT_("quit"), quit_cmd, _("quit")},
110 { NT_("query"), querycmd, _("query catalog")},
111 { NT_("restore"), restore_cmd, _("restore files")},
112 { NT_("relabel"), relabel_cmd, _("relabel a tape")},
113 { NT_("release"), release_cmd, _("release <storage-name>")},
114 { NT_("reload"), reload_cmd, _("reload conf file")},
115 { NT_("run"), run_cmd, _("run <job-name>")},
116 { NT_("status"), status_cmd, _("status [storage | client]=<name>")},
117 { NT_("setdebug"), setdebug_cmd, _("sets debug level")},
118 { NT_("setip"), setip_cmd, _("sets new client address -- if authorized")},
119 { NT_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")},
120 { NT_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")},
121 { NT_("time"), time_cmd, _("print current time")},
122 { NT_("trace"), trace_cmd, _("turn on/off trace to file")},
123 { NT_("unmount"), unmount_cmd, _("unmount <storage-name>")},
124 { NT_("umount"), unmount_cmd, _("umount <storage-name> for old-time Unix guys")},
125 { NT_("update"), update_cmd, _("update Volume, Pool or slots")},
126 { NT_("use"), use_cmd, _("use catalog xxx")},
127 { NT_("var"), var_cmd, _("does variable expansion")},
128 { NT_("version"), version_cmd, _("print Director version")},
129 { NT_("wait"), wait_cmd, _("wait until no jobs are running [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>]")},
131 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
134 * Execute a command from the UA
136 int do_a_command(UAContext *ua, const char *cmd)
144 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
149 while (ua->jcr->wstorage->size()) {
150 ua->jcr->wstorage->remove(0);
153 len = strlen(ua->argk[0]);
154 for (i=0; i<comsize; i++) { /* search for command */
155 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
156 /* Check if command permitted, but "quit" is always OK */
157 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
158 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
161 stat = (*commands[i].func)(ua, cmd); /* go execute command */
167 bnet_fsend(ua->UA_sock, _("%s: is an illegal command.\n"), ua->argk[0]);
173 * This is a common routine used to stuff the Pool DB record defaults
174 * into the Media DB record just before creating a media (Volume)
177 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
179 mr->PoolId = pr->PoolId;
180 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
181 mr->Recycle = pr->Recycle;
182 mr->VolRetention = pr->VolRetention;
183 mr->VolUseDuration = pr->VolUseDuration;
184 mr->MaxVolJobs = pr->MaxVolJobs;
185 mr->MaxVolFiles = pr->MaxVolFiles;
186 mr->MaxVolBytes = pr->MaxVolBytes;
187 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;
320 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
321 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
322 bsendmsg(ua, "%s", db_strerror(ua->db));
326 first_id = mr.PoolId;
330 Dmsg0(200, "Update pool record.\n");
331 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
332 bsendmsg(ua, "%s", db_strerror(ua->db));
335 bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
341 * Turn auto mount on/off
346 int automount_cmd(UAContext *ua, const char *cmd)
351 if (!get_cmd(ua, _("Turn on or off? "))) {
359 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
367 static int cancel_cmd(UAContext *ua, const char *cmd)
372 char JobName[MAX_NAME_LENGTH];
378 for (i=1; i<ua->argc; i++) {
379 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
384 JobId = str_to_int64(ua->argv[i]);
385 if (!(jcr=get_jcr_by_id(JobId))) {
386 bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
390 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
394 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
395 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
396 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
397 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
400 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
404 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
405 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
406 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
407 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
413 /* If we still do not have a jcr,
414 * throw up a list and ask the user to select one.
418 /* Count Jobs running */
420 if (jcr->JobId == 0) { /* this is us */
428 bsendmsg(ua, _("No Jobs running.\n"));
431 start_prompt(ua, _("Select Job:\n"));
434 if (jcr->JobId == 0) { /* this is us */
437 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
442 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
446 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
450 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
451 jcr = get_jcr_by_full_name(JobName);
453 bsendmsg(ua, _("Job %s not found.\n"), JobName);
458 ret = cancel_job(ua, jcr);
464 * This is a common routine to create or update a
465 * Pool DB base record from a Pool Resource. We handle
466 * the setting of MaxVols and NumVols slightly differently
467 * depending on if we are creating the Pool or we are
468 * simply bringing it into agreement with the resource (updage).
470 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
472 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
473 if (op == POOL_OP_CREATE) {
474 pr->MaxVols = pool->max_volumes;
476 } else { /* update pool */
477 if (pr->MaxVols != pool->max_volumes) {
478 pr->MaxVols = pool->max_volumes;
480 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
481 pr->MaxVols = pr->NumVols;
484 pr->LabelType = pool->LabelType;
485 pr->UseOnce = pool->use_volume_once;
486 pr->UseCatalog = pool->use_catalog;
487 pr->Recycle = pool->Recycle;
488 pr->VolRetention = pool->VolRetention;
489 pr->VolUseDuration = pool->VolUseDuration;
490 pr->MaxVolJobs = pool->MaxVolJobs;
491 pr->MaxVolFiles = pool->MaxVolFiles;
492 pr->MaxVolBytes = pool->MaxVolBytes;
493 pr->AutoPrune = pool->AutoPrune;
494 pr->Recycle = pool->Recycle;
495 if (pool->label_format) {
496 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
498 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
504 * Create a pool record from a given Pool resource
505 * Also called from backup.c
506 * Returns: -1 on error
507 * 0 record already exists
511 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
515 memset(&pr, 0, sizeof(POOL_DBR));
517 bstrncpy(pr.Name, pool->hdr.name, sizeof(pr.Name));
519 if (db_get_pool_record(jcr, db, &pr)) {
521 if (op == POOL_OP_UPDATE) { /* update request */
522 set_pooldbr_from_poolres(&pr, pool, op);
523 db_update_pool_record(jcr, db, &pr);
525 return 0; /* exists */
528 set_pooldbr_from_poolres(&pr, pool, op);
530 if (!db_create_pool_record(jcr, db, &pr)) {
531 return -1; /* error */
539 * Create a Pool Record in the database.
540 * It is always created from the Resource record.
542 static int create_cmd(UAContext *ua, const char *cmd)
550 pool = get_pool_resource(ua);
555 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
557 bsendmsg(ua, _("Error: Pool %s already exists.\n"
558 "Use update to change it.\n"), pool->hdr.name);
562 bsendmsg(ua, "%s", db_strerror(ua->db));
568 bsendmsg(ua, _("Pool %s created.\n"), pool->hdr.name);
573 extern DIRRES *director;
576 * Python control command
577 * python restart (restarts interpreter)
579 static int python_cmd(UAContext *ua, const char *cmd)
581 if (ua->argc >= 1 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
582 term_python_interpreter();
583 init_python_interpreter(director->hdr.name,
584 director->scripts_directory, "DirStartUp");
585 bsendmsg(ua, _("Python interpreter restarted.\n"));
587 bsendmsg(ua, _("Nothing done.\n"));
594 * Set a new address in a Client resource. We do this only
595 * if the Console name is the same as the Client name
596 * and the Console can access the client.
598 static int setip_cmd(UAContext *ua, const char *cmd)
602 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->hdr.name)) {
603 bsendmsg(ua, _("Illegal command from this console.\n"));
607 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->hdr.name);
610 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->hdr.name);
613 if (client->address) {
614 free(client->address);
616 /* MA Bug 6 remove ifdef */
617 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
618 client->address = bstrdup(buf);
619 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
620 client->hdr.name, client->address);
627 static void do_en_disable_cmd(UAContext *ua, bool setting)
632 i = find_arg_with_value(ua, NT_("job"));
634 job = select_job_resource(ua);
640 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
644 bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
648 if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
649 bsendmsg(ua, _("Illegal command from this console.\n"));
652 job->enabled = setting;
653 bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->hdr.name, setting?"en":"dis");
657 static int enable_cmd(UAContext *ua, const char *cmd)
659 do_en_disable_cmd(ua, true);
663 static int disable_cmd(UAContext *ua, const char *cmd)
665 do_en_disable_cmd(ua, false);
670 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
675 set_wstorage(jcr, store);
676 /* Try connecting for up to 15 seconds */
677 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
678 store->name(), store->address, store->SDport);
679 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
680 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
683 Dmsg0(120, _("Connected to storage daemon\n"));
684 sd = jcr->store_bsock;
685 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
686 if (bnet_recv(sd) >= 0) {
687 bsendmsg(ua, "%s", sd->msg);
689 bnet_sig(sd, BNET_TERMINATE);
691 jcr->store_bsock = NULL;
695 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
699 /* Connect to File daemon */
701 ua->jcr->client = client;
702 /* Try to connect for 15 seconds */
703 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
704 client->hdr.name, client->address, client->FDport);
705 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
706 bsendmsg(ua, _("Failed to connect to Client.\n"));
709 Dmsg0(120, "Connected to file daemon\n");
710 fd = ua->jcr->file_bsock;
711 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
712 if (bnet_recv(fd) >= 0) {
713 bsendmsg(ua, "%s", fd->msg);
715 bnet_sig(fd, BNET_TERMINATE);
717 ua->jcr->file_bsock = NULL;
722 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
724 STORE *store, **unique_store;
725 CLIENT *client, **unique_client;
731 /* Count Storage items */
735 foreach_res(store, R_STORAGE) {
738 unique_store = (STORE **) malloc(i * sizeof(STORE));
739 /* Find Unique Storage address/port */
740 store = (STORE *)GetNextRes(R_STORAGE, NULL);
742 unique_store[i++] = store;
743 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
745 for (j=0; j<i; j++) {
746 if (strcmp(unique_store[j]->address, store->address) == 0 &&
747 unique_store[j]->SDport == store->SDport) {
753 unique_store[i++] = store;
754 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
759 /* Call each unique Storage daemon */
760 for (j=0; j<i; j++) {
761 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
765 /* Count Client items */
769 foreach_res(client, R_CLIENT) {
772 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
773 /* Find Unique Client address/port */
774 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
776 unique_client[i++] = client;
777 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
779 for (j=0; j<i; j++) {
780 if (strcmp(unique_client[j]->address, client->address) == 0 &&
781 unique_client[j]->FDport == client->FDport) {
787 unique_client[i++] = client;
788 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
793 /* Call each unique File daemon */
794 for (j=0; j<i; j++) {
795 do_client_setdebug(ua, unique_client[j], level, trace_flag);
801 * setdebug level=nn all trace=1/0
803 static int setdebug_cmd(UAContext *ua, const char *cmd)
814 Dmsg1(120, "setdebug:%s:\n", cmd);
817 i = find_arg_with_value(ua, "level");
819 level = atoi(ua->argv[i]);
822 if (!get_pint(ua, _("Enter new debug level: "))) {
825 level = ua->pint32_val;
828 /* Look for trace flag. -1 => not change */
829 i = find_arg_with_value(ua, "trace");
831 trace_flag = atoi(ua->argv[i]);
832 if (trace_flag > 0) {
838 for (i=1; i<ua->argc; i++) {
839 if (strcasecmp(ua->argk[i], "all") == 0) {
840 do_all_setdebug(ua, level, trace_flag);
843 if (strcasecmp(ua->argk[i], "dir") == 0 ||
844 strcasecmp(ua->argk[i], "director") == 0) {
846 set_trace(trace_flag);
849 if (strcasecmp(ua->argk[i], "client") == 0 ||
850 strcasecmp(ua->argk[i], "fd") == 0) {
853 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
855 do_client_setdebug(ua, client, level, trace_flag);
859 client = select_client_resource(ua);
861 do_client_setdebug(ua, client, level, trace_flag);
866 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
867 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
868 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
871 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
873 do_storage_setdebug(ua, store, level, trace_flag);
877 store = get_storage_resource(ua, false/*no default*/);
879 do_storage_setdebug(ua, store, level, trace_flag);
885 * We didn't find an appropriate keyword above, so
888 start_prompt(ua, _("Available daemons are: \n"));
889 add_prompt(ua, _("Director"));
890 add_prompt(ua, _("Storage"));
891 add_prompt(ua, _("Client"));
892 add_prompt(ua, _("All"));
893 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
894 case 0: /* Director */
896 set_trace(trace_flag);
899 store = get_storage_resource(ua, false/*no default*/);
901 do_storage_setdebug(ua, store, level, trace_flag);
905 client = select_client_resource(ua);
907 do_client_setdebug(ua, client, level, trace_flag);
911 do_all_setdebug(ua, level, trace_flag);
920 * Turn debug tracing to file on/off
922 static int trace_cmd(UAContext *ua, const char *cmd)
927 if (!get_cmd(ua, _("Turn on or off? "))) {
935 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
940 static int var_cmd(UAContext *ua, const char *cmd)
942 POOLMEM *val = get_pool_memory(PM_FNAME);
948 for (var=ua->cmd; *var != ' '; ) { /* skip command */
951 while (*var == ' ') { /* skip spaces */
954 Dmsg1(100, "Var=%s:\n", var);
955 variable_expansion(ua->jcr, var, &val);
956 bsendmsg(ua, "%s\n", val);
957 free_pool_memory(val);
961 static int estimate_cmd(UAContext *ua, const char *cmd)
964 CLIENT *client = NULL;
965 FILESET *fileset = NULL;
967 char since[MAXSTRING];
970 jcr->JobLevel = L_FULL;
971 for (int i=1; i<ua->argc; i++) {
972 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
973 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
975 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
979 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
981 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
985 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
987 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
991 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
995 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
996 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
997 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1002 if (!job && !(client && fileset)) {
1003 if (!(job = select_job_resource(ua))) {
1008 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1010 bsendmsg(ua, _("No job specified.\n"));
1015 client = job->client;
1018 fileset = job->fileset;
1020 jcr->client = client;
1021 jcr->fileset = fileset;
1023 ua->catalog = client->catalog;
1030 jcr->JobType = JT_BACKUP;
1031 init_jcr_job_record(jcr);
1033 if (!get_or_create_client_record(jcr)) {
1036 if (!get_or_create_fileset_record(jcr)) {
1040 get_level_since_time(ua->jcr, since, sizeof(since));
1042 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1043 job->client->hdr.name, job->client->address, job->client->FDport);
1044 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1045 bsendmsg(ua, _("Failed to connect to Client.\n"));
1049 if (!send_include_list(jcr)) {
1050 bsendmsg(ua, _("Error sending include list.\n"));
1054 if (!send_exclude_list(jcr)) {
1055 bsendmsg(ua, _("Error sending exclude list.\n"));
1059 if (!send_level_command(jcr)) {
1063 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1064 while (bnet_recv(jcr->file_bsock) >= 0) {
1065 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1069 if (jcr->file_bsock) {
1070 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1071 bnet_close(jcr->file_bsock);
1072 jcr->file_bsock = NULL;
1081 static int time_cmd(UAContext *ua, const char *cmd)
1084 time_t ttime = time(NULL);
1086 (void)localtime_r(&ttime, &tm);
1087 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1088 bsendmsg(ua, "%s\n", sdt);
1093 * reload the conf file
1095 extern "C" void reload_config(int sig);
1097 static int reload_cmd(UAContext *ua, const char *cmd)
1104 * Delete Pool records (should purge Media with it).
1106 * delete pool=<pool-name>
1107 * delete volume pool=<pool-name> volume=<name>
1110 static int delete_cmd(UAContext *ua, const char *cmd)
1112 static const char *keywords[] = {
1122 switch (find_arg_keyword(ua, keywords)) {
1131 while ((i=find_arg(ua, "jobid")) > 0) {
1133 *ua->argk[i] = 0; /* zap keyword already visited */
1141 "In general it is not a good idea to delete either a\n"
1142 "Pool or a Volume since they may contain data.\n\n"));
1144 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1155 bsendmsg(ua, _("Nothing done.\n"));
1163 * delete_job has been modified to parse JobID lists like the
1165 * delete JobID=3,4,6,7-11,14
1167 * Thanks to Phil Stracchino for the above addition.
1170 static void delete_job(UAContext *ua)
1175 int i = find_arg_with_value(ua, NT_("jobid"));
1177 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1178 s = bstrdup(ua->argv[i]);
1181 * We could use strtok() here. But we're not going to, because:
1182 * (a) strtok() is deprecated, having been replaced by strsep();
1183 * (b) strtok() is broken in significant ways.
1184 * we could use strsep() instead, but it's not universally available.
1185 * so we grow our own using strchr().
1187 sep = strchr(tok, ',');
1188 while (sep != NULL) {
1190 if (strchr(tok, '-')) {
1191 delete_job_id_range(ua, tok);
1193 JobId = str_to_int64(tok);
1194 do_job_delete(ua, JobId);
1197 sep = strchr(tok, ',');
1199 /* pick up the last token */
1200 if (strchr(tok, '-')) {
1201 delete_job_id_range(ua, tok);
1203 JobId = str_to_int64(tok);
1204 do_job_delete(ua, JobId);
1209 JobId = str_to_int64(ua->argv[i]);
1210 do_job_delete(ua, JobId);
1212 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1215 JobId = ua->int64_val;
1216 do_job_delete(ua, JobId);
1221 * we call delete_job_id_range to parse range tokens and iterate over ranges
1223 static void delete_job_id_range(UAContext *ua, char *tok)
1228 tok2 = strchr(tok, '-');
1231 j1 = str_to_int64(tok);
1232 j2 = str_to_int64(tok2);
1233 for (j=j1; j<=j2; j++) {
1234 do_job_delete(ua, j);
1239 * do_job_delete now performs the actual delete operation atomically
1242 static void do_job_delete(UAContext *ua, JobId_t JobId)
1244 POOL_MEM query(PM_MESSAGE);
1247 purge_files_from_job(ua, JobId);
1248 purge_job_from_catalog(ua, JobId);
1249 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1253 * Delete media records from database -- dangerous
1255 static int delete_volume(UAContext *ua)
1259 if (!select_media_dbr(ua, &mr)) {
1262 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1263 "and all Jobs saved on that volume from the Catalog\n"),
1266 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1269 if (ua->pint32_val) {
1270 db_delete_media_record(ua->jcr, ua->db, &mr);
1276 * Delete a pool record from the database -- dangerous
1278 static int delete_pool(UAContext *ua)
1282 memset(&pr, 0, sizeof(pr));
1284 if (!get_pool_dbr(ua, &pr)) {
1287 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1290 if (ua->pint32_val) {
1291 db_delete_pool_record(ua->jcr, ua->db, &pr);
1297 static void do_mount_cmd(UAContext *ua, const char *command)
1302 char dev_name[MAX_NAME_LENGTH];
1308 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1310 store = get_storage_resource(ua, true/*arg is storage*/);
1314 set_wstorage(jcr, store);
1315 drive = get_storage_drive(ua, store);
1317 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1318 store->media_type, store->dev_name(), drive);
1320 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1321 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1324 sd = jcr->store_bsock;
1325 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1326 bash_spaces(dev_name);
1327 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1328 while (bnet_recv(sd) >= 0) {
1329 bsendmsg(ua, "%s", sd->msg);
1331 bnet_sig(sd, BNET_TERMINATE);
1333 jcr->store_bsock = NULL;
1337 * mount [storage=<name>] [drive=nn]
1339 static int mount_cmd(UAContext *ua, const char *cmd)
1341 do_mount_cmd(ua, "mount"); /* mount */
1347 * unmount [storage=<name>] [drive=nn]
1349 static int unmount_cmd(UAContext *ua, const char *cmd)
1351 do_mount_cmd(ua, "unmount"); /* unmount */
1357 * release [storage=<name>] [drive=nn]
1359 static int release_cmd(UAContext *ua, const char *cmd)
1361 do_mount_cmd(ua, "release"); /* release */
1368 * use catalog=<name>
1370 static int use_cmd(UAContext *ua, const char *cmd)
1372 CAT *oldcatalog, *catalog;
1375 close_db(ua); /* close any previously open db */
1376 oldcatalog = ua->catalog;
1378 if (!(catalog = get_catalog_resource(ua))) {
1379 ua->catalog = oldcatalog;
1381 ua->catalog = catalog;
1384 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1385 ua->catalog->hdr.name, ua->catalog->db_name);
1390 int quit_cmd(UAContext *ua, const char *cmd)
1396 /* Handler to get job status */
1397 static int status_handler(void *ctx, int num_fields, char **row)
1399 char *val = (char *)ctx;
1404 *val = '?'; /* Unknown by default */
1411 * Wait until no job is running
1413 int wait_cmd(UAContext *ua, const char *cmd)
1418 * Wait until no job is running
1420 if (ua->argc == 1) {
1421 bmicrosleep(0, 200000); /* let job actually start */
1422 for (bool running=true; running; ) {
1425 if (jcr->JobId != 0) {
1439 /* we have jobid, jobname or ujobid argument */
1441 uint32_t jobid = 0 ;
1444 bsendmsg(ua, _("ERR: Can't open db\n")) ;
1448 for (int i=1; i<ua->argc; i++) {
1449 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1453 jobid = str_to_int64(ua->argv[i]);
1455 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1456 strcasecmp(ua->argk[i], "job") == 0) {
1460 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1462 jobid = jcr->JobId ;
1466 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1470 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1472 jobid = jcr->JobId ;
1480 bsendmsg(ua, _("ERR: Job was not found\n"));
1485 * We wait the end of job
1488 bmicrosleep(0, 200000); /* let job actually start */
1489 for (bool running=true; running; ) {
1492 jcr=get_jcr_by_id(jobid) ;
1505 * We have to get JobStatus
1509 char jobstatus = '?'; /* Unknown by default */
1512 bsnprintf(buf, sizeof(buf),
1513 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1516 db_sql_query(ua->db, buf,
1517 status_handler, (void *)&jobstatus);
1519 switch (jobstatus) {
1521 status = 1 ; /* Warning */
1525 case JS_ErrorTerminated:
1527 status = 2 ; /* Critical */
1531 status = 0 ; /* Ok */
1535 status = 3 ; /* Unknown */
1539 bsendmsg(ua, "JobId=%i\n", jobid) ;
1540 bsendmsg(ua, "JobStatus=%s (%c)\n",
1541 job_status_to_str(jobstatus),
1545 bsendmsg(ua, "ExitStatus=%i\n", status) ;
1552 static int help_cmd(UAContext *ua, const char *cmd)
1556 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1557 for (i=0; i<comsize; i++) {
1558 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1560 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1564 int qhelp_cmd(UAContext *ua, const char *cmd)
1568 for (i=0; i<comsize; i++) {
1569 bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1574 static int version_cmd(UAContext *ua, const char *cmd)
1576 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1581 /* A bit brain damaged in that if the user has not done
1582 * a "use catalog xxx" command, we simply find the first
1583 * catalog resource and open it.
1585 bool open_db(UAContext *ua)
1592 ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1595 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1597 } else if (!acl_access_ok(ua, Catalog_ACL, ua->catalog->hdr.name)) {
1598 bsendmsg(ua, _("You must specify a \"use <catalog-name>\" command before continuing.\n"));
1602 bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1603 ua->catalog->hdr.name, ua->catalog->db_name);
1607 ua->jcr->catalog = ua->catalog;
1609 Dmsg0(150, "Open database\n");
1610 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1611 ua->catalog->db_password, ua->catalog->db_address,
1612 ua->catalog->db_port, ua->catalog->db_socket,
1613 ua->catalog->mult_db_connections);
1614 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1615 bsendmsg(ua, _("Could not open database \"%s\".\n"),
1616 ua->catalog->db_name);
1618 bsendmsg(ua, "%s", db_strerror(ua->db));
1623 ua->jcr->db = ua->db;
1624 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1628 void close_db(UAContext *ua)
1631 db_close_database(ua->jcr, ua->db);