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 { NT_("add"), add_cmd, _("add media to a pool")},
91 { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
92 { NT_("automount"), automount_cmd, _("automount [on|off] -- after label")},
93 { NT_("cancel"), cancel_cmd, _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
94 { NT_("create"), create_cmd, _("create DB Pool from resource")},
95 { NT_("delete"), delete_cmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
96 { NT_("disable"), disable_cmd, _("disable <job=name> -- disable a job")},
97 { NT_("enable"), enable_cmd, _("enable <job=name> -- enable a job")},
98 { NT_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")},
99 { NT_("exit"), quit_cmd, _("exit = quit")},
100 { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode")},
101 { NT_("help"), help_cmd, _("print this command")},
102 { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
103 { NT_("label"), label_cmd, _("label a tape")},
104 { NT_("llist"), llist_cmd, _("full or long list like list command")},
105 { NT_("messages"), messagescmd, _("messages")},
106 { NT_("mount"), mount_cmd, _("mount <storage-name>")},
107 { NT_("prune"), prunecmd, _("prune expired records from catalog")},
108 { NT_("purge"), purgecmd, _("purge records from catalog")},
109 { NT_("python"), python_cmd, _("python control commands")},
110 { NT_("quit"), quit_cmd, _("quit")},
111 { NT_("query"), querycmd, _("query catalog")},
112 { NT_("restore"), restore_cmd, _("restore files")},
113 { NT_("relabel"), relabel_cmd, _("relabel a tape")},
114 { NT_("release"), release_cmd, _("release <storage-name>")},
115 { NT_("reload"), reload_cmd, _("reload conf file")},
116 { NT_("run"), run_cmd, _("run <job-name>")},
117 { NT_("status"), status_cmd, _("status [storage | client]=<name>")},
118 { NT_("setdebug"), setdebug_cmd, _("sets debug level")},
119 { NT_("setip"), setip_cmd, _("sets new client address -- if authorized")},
120 { NT_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")},
121 { NT_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")},
122 { NT_("time"), time_cmd, _("print current time")},
123 { NT_("trace"), trace_cmd, _("turn on/off trace to file")},
124 { NT_("unmount"), unmount_cmd, _("unmount <storage-name>")},
125 { NT_("umount"), unmount_cmd, _("umount <storage-name> for old-time Unix guys")},
126 { NT_("update"), update_cmd, _("update Volume, Pool or slots")},
127 { NT_("use"), use_cmd, _("use catalog xxx")},
128 { NT_("var"), var_cmd, _("does variable expansion")},
129 { NT_("version"), version_cmd, _("print Director version")},
130 { NT_("wait"), wait_cmd, _("wait until no jobs are running [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>]")},
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));
399 } else if (strcasecmp(ua->argk[i], _("ujobid")) == 0) {
403 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
404 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
405 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
406 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
412 /* If we still do not have a jcr,
413 * throw up a list and ask the user to select one.
417 /* Count Jobs running */
419 if (jcr->JobId == 0) { /* this is us */
427 bsendmsg(ua, _("No Jobs running.\n"));
430 start_prompt(ua, _("Select Job:\n"));
433 if (jcr->JobId == 0) { /* this is us */
436 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
441 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
445 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
449 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
450 jcr = get_jcr_by_full_name(JobName);
452 bsendmsg(ua, _("Job %s not found.\n"), JobName);
457 ret = cancel_job(ua, jcr);
463 * This is a common routine to create or update a
464 * Pool DB base record from a Pool Resource. We handle
465 * the setting of MaxVols and NumVols slightly differently
466 * depending on if we are creating the Pool or we are
467 * simply bringing it into agreement with the resource (updage).
469 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
471 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
472 if (op == POOL_OP_CREATE) {
473 pr->MaxVols = pool->max_volumes;
475 } else { /* update pool */
476 if (pr->MaxVols != pool->max_volumes) {
477 pr->MaxVols = pool->max_volumes;
479 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
480 pr->MaxVols = pr->NumVols;
483 pr->LabelType = pool->LabelType;
484 pr->UseOnce = pool->use_volume_once;
485 pr->UseCatalog = pool->use_catalog;
486 pr->AcceptAnyVolume = pool->accept_any_volume;
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], _("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_storage(jcr, store);
676 /* Try connecting for up to 15 seconds */
677 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
678 store->hdr.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], "store") == 0 ||
867 strcasecmp(ua->argk[i], "storage") == 0 ||
868 strcasecmp(ua->argk[i], "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, _("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], "client") == 0 ||
973 strcasecmp(ua->argk[i], "fd") == 0) {
975 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
979 if (strcasecmp(ua->argk[i], "job") == 0) {
981 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
985 if (strcasecmp(ua->argk[i], "fileset") == 0) {
987 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
991 if (strcasecmp(ua->argk[i], "listing") == 0) {
995 if (strcasecmp(ua->argk[i], "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 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
1240 * we always return 1 because C++ is pissy about void functions
1243 static void do_job_delete(UAContext *ua, JobId_t JobId)
1245 POOLMEM *query = get_pool_memory(PM_MESSAGE);
1248 Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(JobId, ed1));
1249 db_sql_query(ua->db, query, NULL, (void *)NULL);
1250 Mmsg(query, "DELETE FROM MAC WHERE JobId=%s", ed1);
1251 db_sql_query(ua->db, query, NULL, (void *)NULL);
1252 Mmsg(query, "DELETE FROM File WHERE JobId=%s", ed1);
1253 db_sql_query(ua->db, query, NULL, (void *)NULL);
1254 Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", ed1);
1255 db_sql_query(ua->db, query, NULL, (void *)NULL);
1256 free_pool_memory(query);
1257 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1261 * Delete media records from database -- dangerous
1263 static int delete_volume(UAContext *ua)
1267 if (!select_media_dbr(ua, &mr)) {
1270 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1271 "and all Jobs saved on that volume from the Catalog\n"),
1274 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1277 if (ua->pint32_val) {
1278 db_delete_media_record(ua->jcr, ua->db, &mr);
1284 * Delete a pool record from the database -- dangerous
1286 static int delete_pool(UAContext *ua)
1290 memset(&pr, 0, sizeof(pr));
1292 if (!get_pool_dbr(ua, &pr)) {
1295 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1298 if (ua->pint32_val) {
1299 db_delete_pool_record(ua->jcr, ua->db, &pr);
1305 static void do_mount_cmd(UAContext *ua, const char *command)
1310 char dev_name[MAX_NAME_LENGTH];
1316 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1318 store = get_storage_resource(ua, true/*arg is storage*/);
1322 set_storage(jcr, store);
1323 drive = get_storage_drive(ua, store);
1325 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1326 store->media_type, store->dev_name(), drive);
1328 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1329 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1332 sd = jcr->store_bsock;
1333 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1334 bash_spaces(dev_name);
1335 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1336 while (bnet_recv(sd) >= 0) {
1337 bsendmsg(ua, "%s", sd->msg);
1339 bnet_sig(sd, BNET_TERMINATE);
1341 jcr->store_bsock = NULL;
1345 * mount [storage=<name>] [drive=nn]
1347 static int mount_cmd(UAContext *ua, const char *cmd)
1349 do_mount_cmd(ua, "mount"); /* mount */
1355 * unmount [storage=<name>] [drive=nn]
1357 static int unmount_cmd(UAContext *ua, const char *cmd)
1359 do_mount_cmd(ua, "unmount"); /* unmount */
1365 * release [storage=<name>] [drive=nn]
1367 static int release_cmd(UAContext *ua, const char *cmd)
1369 do_mount_cmd(ua, "release"); /* release */
1376 * use catalog=<name>
1378 static int use_cmd(UAContext *ua, const char *cmd)
1380 CAT *oldcatalog, *catalog;
1383 close_db(ua); /* close any previously open db */
1384 oldcatalog = ua->catalog;
1386 if (!(catalog = get_catalog_resource(ua))) {
1387 ua->catalog = oldcatalog;
1389 ua->catalog = catalog;
1392 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1393 ua->catalog->hdr.name, ua->catalog->db_name);
1398 int quit_cmd(UAContext *ua, const char *cmd)
1404 /* Handler to get job status */
1405 static int status_handler(void *ctx, int num_fields, char **row)
1407 char *val = (char *)ctx;
1412 *val = '?'; /* Unknown by default */
1419 * Wait until no job is running
1421 int wait_cmd(UAContext *ua, const char *cmd)
1426 * Wait until no job is running
1428 if (ua->argc == 1) {
1429 bmicrosleep(0, 200000); /* let job actually start */
1430 for (bool running=true; running; ) {
1433 if (jcr->JobId != 0) {
1447 /* we have jobid, jobname or ujobid argument */
1449 uint32_t jobid = 0 ;
1452 bsendmsg(ua, _("ERR: Can't open db\n")) ;
1456 for (int i=1; i<ua->argc; i++) {
1457 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1461 jobid = str_to_int64(ua->argv[i]);
1463 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1464 strcasecmp(ua->argk[i], "job") == 0) {
1468 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1470 jobid = jcr->JobId ;
1474 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1478 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1480 jobid = jcr->JobId ;
1488 bsendmsg(ua, _("ERR: Job was not found\n"));
1493 * We wait the end of job
1496 bmicrosleep(0, 200000); /* let job actually start */
1497 for (bool running=true; running; ) {
1500 jcr=get_jcr_by_id(jobid) ;
1513 * We have to get JobStatus
1517 char jobstatus = '?'; /* Unknown by default */
1520 bsnprintf(buf, sizeof(buf),
1521 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1524 db_sql_query(ua->db, buf,
1525 status_handler, (void *)&jobstatus);
1527 switch (jobstatus) {
1529 status = 1 ; /* Warning */
1533 case JS_ErrorTerminated:
1535 status = 2 ; /* Critical */
1539 status = 0 ; /* Ok */
1543 status = 3 ; /* Unknown */
1547 bsendmsg(ua, "JobId=%i\n", jobid) ;
1548 bsendmsg(ua, "JobStatus=%s (%c)\n",
1549 job_status_to_str(jobstatus),
1553 bsendmsg(ua, "ExitStatus=%i\n", status) ;
1560 static int help_cmd(UAContext *ua, const char *cmd)
1564 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1565 for (i=0; i<comsize; i++) {
1566 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1568 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1572 int qhelp_cmd(UAContext *ua, const char *cmd)
1576 for (i=0; i<comsize; i++) {
1577 bsendmsg(ua, "%s %s\n", _(commands[i].key), _(commands[i].help));
1582 static int version_cmd(UAContext *ua, const char *cmd)
1584 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1589 /* A bit brain damaged in that if the user has not done
1590 * a "use catalog xxx" command, we simply find the first
1591 * catalog resource and open it.
1593 bool open_db(UAContext *ua)
1600 ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1603 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1605 } else if (!acl_access_ok(ua, Catalog_ACL, ua->catalog->hdr.name)) {
1606 bsendmsg(ua, _("You must specify a \"use <catalog-name>\" command before continuing.\n"));
1610 bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1611 ua->catalog->hdr.name, ua->catalog->db_name);
1615 ua->jcr->catalog = ua->catalog;
1617 Dmsg0(150, "Open database\n");
1618 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1619 ua->catalog->db_password, ua->catalog->db_address,
1620 ua->catalog->db_port, ua->catalog->db_socket,
1621 ua->catalog->mult_db_connections);
1622 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1623 bsendmsg(ua, _("Could not open database \"%s\".\n"),
1624 ua->catalog->db_name);
1626 bsendmsg(ua, "%s", db_strerror(ua->db));
1631 ua->jcr->db = ua->db;
1632 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1636 void close_db(UAContext *ua)
1639 db_close_database(ua->jcr, ua->db);