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], NT_("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, NT_("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;
194 * Add Volumes to an existing Pool
196 static int add_cmd(UAContext *ua, const char *cmd)
200 int num, i, max, startnum;
202 char name[MAX_NAME_LENGTH];
204 int Slot = 0, InChanger = 0;
207 "You probably don't want to be using this command since it\n"
208 "creates database records without labeling the Volumes.\n"
209 "You probably want to use the \"label\" command.\n\n"));
215 memset(&pr, 0, sizeof(pr));
216 memset(&mr, 0, sizeof(mr));
218 if (!get_pool_dbr(ua, &pr)) {
222 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
223 pr.MaxVols, pr.PoolType);
225 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
226 bsendmsg(ua, _("Pool already has maximum volumes=%d\n"), pr.MaxVols);
228 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
231 pr.MaxVols = ua->pint32_val;
236 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
237 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
238 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
242 if (pr.MaxVols == 0) {
245 max = pr.MaxVols - pr.NumVols;
249 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
250 if (!get_pint(ua, buf)) {
253 num = ua->pint32_val;
254 if (num < 0 || num > max) {
255 bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
262 if (!get_cmd(ua, _("Enter Volume name: "))) {
266 if (!get_cmd(ua, _("Enter base volume name: "))) {
270 /* Don't allow | in Volume name because it is the volume separator character */
271 if (!is_volume_name_legal(ua, ua->cmd)) {
274 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
275 bsendmsg(ua, _("Volume name too long.\n"));
278 if (strlen(ua->cmd) == 0) {
279 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
283 bstrncpy(name, ua->cmd, sizeof(name));
285 bstrncat(name, "%04d", sizeof(name));
288 if (!get_pint(ua, _("Enter the starting number: "))) {
291 startnum = ua->pint32_val;
293 bsendmsg(ua, _("Start number must be greater than zero.\n"));
303 if (store && store->autochanger) {
304 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
307 Slot = ua->pint32_val;
308 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
311 InChanger = ua->pint32_val;
314 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
315 for (i=startnum; i < num+startnum; i++) {
316 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
318 mr.InChanger = InChanger;
319 mr.StorageId = store->StorageId;
321 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
322 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
323 bsendmsg(ua, "%s", db_strerror(ua->db));
327 first_id = mr.PoolId;
331 Dmsg0(200, "Update pool record.\n");
332 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
333 bsendmsg(ua, "%s", db_strerror(ua->db));
336 bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
342 * Turn auto mount on/off
347 int automount_cmd(UAContext *ua, const char *cmd)
352 if (!get_cmd(ua, _("Turn on or off? "))) {
360 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
368 static int cancel_cmd(UAContext *ua, const char *cmd)
373 char JobName[MAX_NAME_LENGTH];
379 for (i=1; i<ua->argc; i++) {
380 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
385 JobId = str_to_int64(ua->argv[i]);
386 if (!(jcr=get_jcr_by_id(JobId))) {
387 bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
391 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
395 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
396 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
397 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
398 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
401 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
405 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
406 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
407 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
408 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
414 /* If we still do not have a jcr,
415 * throw up a list and ask the user to select one.
419 /* Count Jobs running */
421 if (jcr->JobId == 0) { /* this is us */
429 bsendmsg(ua, _("No Jobs running.\n"));
432 start_prompt(ua, _("Select Job:\n"));
435 if (jcr->JobId == 0) { /* this is us */
438 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
443 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
447 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
451 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
452 jcr = get_jcr_by_full_name(JobName);
454 bsendmsg(ua, _("Job %s not found.\n"), JobName);
459 ret = cancel_job(ua, jcr);
465 * This is a common routine to create or update a
466 * Pool DB base record from a Pool Resource. We handle
467 * the setting of MaxVols and NumVols slightly differently
468 * depending on if we are creating the Pool or we are
469 * simply bringing it into agreement with the resource (updage).
471 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
473 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
474 if (op == POOL_OP_CREATE) {
475 pr->MaxVols = pool->max_volumes;
477 } else { /* update pool */
478 if (pr->MaxVols != pool->max_volumes) {
479 pr->MaxVols = pool->max_volumes;
481 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
482 pr->MaxVols = pr->NumVols;
485 pr->LabelType = pool->LabelType;
486 pr->UseOnce = pool->use_volume_once;
487 pr->UseCatalog = pool->use_catalog;
488 pr->Recycle = pool->Recycle;
489 pr->VolRetention = pool->VolRetention;
490 pr->VolUseDuration = pool->VolUseDuration;
491 pr->MaxVolJobs = pool->MaxVolJobs;
492 pr->MaxVolFiles = pool->MaxVolFiles;
493 pr->MaxVolBytes = pool->MaxVolBytes;
494 pr->AutoPrune = pool->AutoPrune;
495 pr->Recycle = pool->Recycle;
496 if (pool->label_format) {
497 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
499 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
505 * Create a pool record from a given Pool resource
506 * Also called from backup.c
507 * Returns: -1 on error
508 * 0 record already exists
512 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
516 memset(&pr, 0, sizeof(POOL_DBR));
518 bstrncpy(pr.Name, pool->hdr.name, sizeof(pr.Name));
520 if (db_get_pool_record(jcr, db, &pr)) {
522 if (op == POOL_OP_UPDATE) { /* update request */
523 set_pooldbr_from_poolres(&pr, pool, op);
524 db_update_pool_record(jcr, db, &pr);
526 return 0; /* exists */
529 set_pooldbr_from_poolres(&pr, pool, op);
531 if (!db_create_pool_record(jcr, db, &pr)) {
532 return -1; /* error */
540 * Create a Pool Record in the database.
541 * It is always created from the Resource record.
543 static int create_cmd(UAContext *ua, const char *cmd)
551 pool = get_pool_resource(ua);
556 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
558 bsendmsg(ua, _("Error: Pool %s already exists.\n"
559 "Use update to change it.\n"), pool->hdr.name);
563 bsendmsg(ua, "%s", db_strerror(ua->db));
569 bsendmsg(ua, _("Pool %s created.\n"), pool->hdr.name);
574 extern DIRRES *director;
577 * Python control command
578 * python restart (restarts interpreter)
580 static int python_cmd(UAContext *ua, const char *cmd)
582 if (ua->argc >= 1 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
583 term_python_interpreter();
584 init_python_interpreter(director->hdr.name,
585 director->scripts_directory, "DirStartUp");
586 bsendmsg(ua, _("Python interpreter restarted.\n"));
588 bsendmsg(ua, _("Nothing done.\n"));
595 * Set a new address in a Client resource. We do this only
596 * if the Console name is the same as the Client name
597 * and the Console can access the client.
599 static int setip_cmd(UAContext *ua, const char *cmd)
603 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->hdr.name)) {
604 bsendmsg(ua, _("Illegal command from this console.\n"));
608 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->hdr.name);
611 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->hdr.name);
614 if (client->address) {
615 free(client->address);
617 /* MA Bug 6 remove ifdef */
618 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
619 client->address = bstrdup(buf);
620 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
621 client->hdr.name, client->address);
628 static void do_en_disable_cmd(UAContext *ua, bool setting)
633 i = find_arg_with_value(ua, NT_("job"));
635 job = select_job_resource(ua);
641 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
645 bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
649 if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
650 bsendmsg(ua, _("Illegal command from this console.\n"));
653 job->enabled = setting;
654 bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->hdr.name, setting?"en":"dis");
658 static int enable_cmd(UAContext *ua, const char *cmd)
660 do_en_disable_cmd(ua, true);
664 static int disable_cmd(UAContext *ua, const char *cmd)
666 do_en_disable_cmd(ua, false);
671 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
676 set_storage(jcr, store);
677 /* Try connecting for up to 15 seconds */
678 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
679 store->hdr.name, store->address, store->SDport);
680 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
681 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
684 Dmsg0(120, _("Connected to storage daemon\n"));
685 sd = jcr->store_bsock;
686 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
687 if (bnet_recv(sd) >= 0) {
688 bsendmsg(ua, "%s", sd->msg);
690 bnet_sig(sd, BNET_TERMINATE);
692 jcr->store_bsock = NULL;
696 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
700 /* Connect to File daemon */
702 ua->jcr->client = client;
703 /* Try to connect for 15 seconds */
704 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
705 client->hdr.name, client->address, client->FDport);
706 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
707 bsendmsg(ua, _("Failed to connect to Client.\n"));
710 Dmsg0(120, "Connected to file daemon\n");
711 fd = ua->jcr->file_bsock;
712 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
713 if (bnet_recv(fd) >= 0) {
714 bsendmsg(ua, "%s", fd->msg);
716 bnet_sig(fd, BNET_TERMINATE);
718 ua->jcr->file_bsock = NULL;
723 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
725 STORE *store, **unique_store;
726 CLIENT *client, **unique_client;
732 /* Count Storage items */
736 foreach_res(store, R_STORAGE) {
739 unique_store = (STORE **) malloc(i * sizeof(STORE));
740 /* Find Unique Storage address/port */
741 store = (STORE *)GetNextRes(R_STORAGE, NULL);
743 unique_store[i++] = store;
744 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
746 for (j=0; j<i; j++) {
747 if (strcmp(unique_store[j]->address, store->address) == 0 &&
748 unique_store[j]->SDport == store->SDport) {
754 unique_store[i++] = store;
755 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
760 /* Call each unique Storage daemon */
761 for (j=0; j<i; j++) {
762 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
766 /* Count Client items */
770 foreach_res(client, R_CLIENT) {
773 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
774 /* Find Unique Client address/port */
775 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
777 unique_client[i++] = client;
778 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
780 for (j=0; j<i; j++) {
781 if (strcmp(unique_client[j]->address, client->address) == 0 &&
782 unique_client[j]->FDport == client->FDport) {
788 unique_client[i++] = client;
789 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
794 /* Call each unique File daemon */
795 for (j=0; j<i; j++) {
796 do_client_setdebug(ua, unique_client[j], level, trace_flag);
802 * setdebug level=nn all trace=1/0
804 static int setdebug_cmd(UAContext *ua, const char *cmd)
815 Dmsg1(120, "setdebug:%s:\n", cmd);
818 i = find_arg_with_value(ua, "level");
820 level = atoi(ua->argv[i]);
823 if (!get_pint(ua, _("Enter new debug level: "))) {
826 level = ua->pint32_val;
829 /* Look for trace flag. -1 => not change */
830 i = find_arg_with_value(ua, "trace");
832 trace_flag = atoi(ua->argv[i]);
833 if (trace_flag > 0) {
839 for (i=1; i<ua->argc; i++) {
840 if (strcasecmp(ua->argk[i], "all") == 0) {
841 do_all_setdebug(ua, level, trace_flag);
844 if (strcasecmp(ua->argk[i], "dir") == 0 ||
845 strcasecmp(ua->argk[i], "director") == 0) {
847 set_trace(trace_flag);
850 if (strcasecmp(ua->argk[i], "client") == 0 ||
851 strcasecmp(ua->argk[i], "fd") == 0) {
854 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
856 do_client_setdebug(ua, client, level, trace_flag);
860 client = select_client_resource(ua);
862 do_client_setdebug(ua, client, level, trace_flag);
867 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
868 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
869 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
872 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
874 do_storage_setdebug(ua, store, level, trace_flag);
878 store = get_storage_resource(ua, false/*no default*/);
880 do_storage_setdebug(ua, store, level, trace_flag);
886 * We didn't find an appropriate keyword above, so
889 start_prompt(ua, _("Available daemons are: \n"));
890 add_prompt(ua, _("Director"));
891 add_prompt(ua, _("Storage"));
892 add_prompt(ua, _("Client"));
893 add_prompt(ua, _("All"));
894 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
895 case 0: /* Director */
897 set_trace(trace_flag);
900 store = get_storage_resource(ua, false/*no default*/);
902 do_storage_setdebug(ua, store, level, trace_flag);
906 client = select_client_resource(ua);
908 do_client_setdebug(ua, client, level, trace_flag);
912 do_all_setdebug(ua, level, trace_flag);
921 * Turn debug tracing to file on/off
923 static int trace_cmd(UAContext *ua, const char *cmd)
928 if (!get_cmd(ua, _("Turn on or off? "))) {
936 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
941 static int var_cmd(UAContext *ua, const char *cmd)
943 POOLMEM *val = get_pool_memory(PM_FNAME);
949 for (var=ua->cmd; *var != ' '; ) { /* skip command */
952 while (*var == ' ') { /* skip spaces */
955 Dmsg1(100, "Var=%s:\n", var);
956 variable_expansion(ua->jcr, var, &val);
957 bsendmsg(ua, "%s\n", val);
958 free_pool_memory(val);
962 static int estimate_cmd(UAContext *ua, const char *cmd)
965 CLIENT *client = NULL;
966 FILESET *fileset = NULL;
968 char since[MAXSTRING];
971 jcr->JobLevel = L_FULL;
972 for (int i=1; i<ua->argc; i++) {
973 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
974 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
976 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
980 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
982 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
986 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
988 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
992 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
996 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
997 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
998 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1003 if (!job && !(client && fileset)) {
1004 if (!(job = select_job_resource(ua))) {
1009 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1011 bsendmsg(ua, _("No job specified.\n"));
1016 client = job->client;
1019 fileset = job->fileset;
1021 jcr->client = client;
1022 jcr->fileset = fileset;
1024 ua->catalog = client->catalog;
1031 jcr->JobType = JT_BACKUP;
1032 init_jcr_job_record(jcr);
1034 if (!get_or_create_client_record(jcr)) {
1037 if (!get_or_create_fileset_record(jcr)) {
1041 get_level_since_time(ua->jcr, since, sizeof(since));
1043 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1044 job->client->hdr.name, job->client->address, job->client->FDport);
1045 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1046 bsendmsg(ua, _("Failed to connect to Client.\n"));
1050 if (!send_include_list(jcr)) {
1051 bsendmsg(ua, _("Error sending include list.\n"));
1055 if (!send_exclude_list(jcr)) {
1056 bsendmsg(ua, _("Error sending exclude list.\n"));
1060 if (!send_level_command(jcr)) {
1064 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1065 while (bnet_recv(jcr->file_bsock) >= 0) {
1066 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1070 if (jcr->file_bsock) {
1071 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1072 bnet_close(jcr->file_bsock);
1073 jcr->file_bsock = NULL;
1082 static int time_cmd(UAContext *ua, const char *cmd)
1085 time_t ttime = time(NULL);
1087 (void)localtime_r(&ttime, &tm);
1088 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1089 bsendmsg(ua, "%s\n", sdt);
1094 * reload the conf file
1096 extern "C" void reload_config(int sig);
1098 static int reload_cmd(UAContext *ua, const char *cmd)
1105 * Delete Pool records (should purge Media with it).
1107 * delete pool=<pool-name>
1108 * delete volume pool=<pool-name> volume=<name>
1111 static int delete_cmd(UAContext *ua, const char *cmd)
1113 static const char *keywords[] = {
1123 switch (find_arg_keyword(ua, keywords)) {
1132 while ((i=find_arg(ua, "jobid")) > 0) {
1134 *ua->argk[i] = 0; /* zap keyword already visited */
1142 "In general it is not a good idea to delete either a\n"
1143 "Pool or a Volume since they may contain data.\n\n"));
1145 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1156 bsendmsg(ua, _("Nothing done.\n"));
1164 * delete_job has been modified to parse JobID lists like the
1166 * delete JobID=3,4,6,7-11,14
1168 * Thanks to Phil Stracchino for the above addition.
1171 static void delete_job(UAContext *ua)
1176 int i = find_arg_with_value(ua, NT_("jobid"));
1178 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1179 s = bstrdup(ua->argv[i]);
1182 * We could use strtok() here. But we're not going to, because:
1183 * (a) strtok() is deprecated, having been replaced by strsep();
1184 * (b) strtok() is broken in significant ways.
1185 * we could use strsep() instead, but it's not universally available.
1186 * so we grow our own using strchr().
1188 sep = strchr(tok, ',');
1189 while (sep != NULL) {
1191 if (strchr(tok, '-')) {
1192 delete_job_id_range(ua, tok);
1194 JobId = str_to_int64(tok);
1195 do_job_delete(ua, JobId);
1198 sep = strchr(tok, ',');
1200 /* pick up the last token */
1201 if (strchr(tok, '-')) {
1202 delete_job_id_range(ua, tok);
1204 JobId = str_to_int64(tok);
1205 do_job_delete(ua, JobId);
1210 JobId = str_to_int64(ua->argv[i]);
1211 do_job_delete(ua, JobId);
1213 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1216 JobId = ua->int64_val;
1217 do_job_delete(ua, JobId);
1222 * we call delete_job_id_range to parse range tokens and iterate over ranges
1224 static void delete_job_id_range(UAContext *ua, char *tok)
1229 tok2 = strchr(tok, '-');
1232 j1 = str_to_int64(tok);
1233 j2 = str_to_int64(tok2);
1234 for (j=j1; j<=j2; j++) {
1235 do_job_delete(ua, j);
1240 * do_job_delete now performs the actual delete operation atomically
1241 * we always return 1 because C++ is pissy about void functions
1244 static void do_job_delete(UAContext *ua, JobId_t JobId)
1246 POOLMEM *query = get_pool_memory(PM_MESSAGE);
1249 Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(JobId, ed1));
1250 db_sql_query(ua->db, query, NULL, (void *)NULL);
1251 Mmsg(query, "DELETE FROM MAC WHERE JobId=%s", ed1);
1252 db_sql_query(ua->db, query, NULL, (void *)NULL);
1253 Mmsg(query, "DELETE FROM File WHERE JobId=%s", ed1);
1254 db_sql_query(ua->db, query, NULL, (void *)NULL);
1255 Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", ed1);
1256 db_sql_query(ua->db, query, NULL, (void *)NULL);
1257 free_pool_memory(query);
1258 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1262 * Delete media records from database -- dangerous
1264 static int delete_volume(UAContext *ua)
1268 if (!select_media_dbr(ua, &mr)) {
1271 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1272 "and all Jobs saved on that volume from the Catalog\n"),
1275 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1278 if (ua->pint32_val) {
1279 db_delete_media_record(ua->jcr, ua->db, &mr);
1285 * Delete a pool record from the database -- dangerous
1287 static int delete_pool(UAContext *ua)
1291 memset(&pr, 0, sizeof(pr));
1293 if (!get_pool_dbr(ua, &pr)) {
1296 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1299 if (ua->pint32_val) {
1300 db_delete_pool_record(ua->jcr, ua->db, &pr);
1306 static void do_mount_cmd(UAContext *ua, const char *command)
1311 char dev_name[MAX_NAME_LENGTH];
1317 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1319 store = get_storage_resource(ua, true/*arg is storage*/);
1323 set_storage(jcr, store);
1324 drive = get_storage_drive(ua, store);
1326 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1327 store->media_type, store->dev_name(), drive);
1329 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1330 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1333 sd = jcr->store_bsock;
1334 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1335 bash_spaces(dev_name);
1336 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1337 while (bnet_recv(sd) >= 0) {
1338 bsendmsg(ua, "%s", sd->msg);
1340 bnet_sig(sd, BNET_TERMINATE);
1342 jcr->store_bsock = NULL;
1346 * mount [storage=<name>] [drive=nn]
1348 static int mount_cmd(UAContext *ua, const char *cmd)
1350 do_mount_cmd(ua, "mount"); /* mount */
1356 * unmount [storage=<name>] [drive=nn]
1358 static int unmount_cmd(UAContext *ua, const char *cmd)
1360 do_mount_cmd(ua, "unmount"); /* unmount */
1366 * release [storage=<name>] [drive=nn]
1368 static int release_cmd(UAContext *ua, const char *cmd)
1370 do_mount_cmd(ua, "release"); /* release */
1377 * use catalog=<name>
1379 static int use_cmd(UAContext *ua, const char *cmd)
1381 CAT *oldcatalog, *catalog;
1384 close_db(ua); /* close any previously open db */
1385 oldcatalog = ua->catalog;
1387 if (!(catalog = get_catalog_resource(ua))) {
1388 ua->catalog = oldcatalog;
1390 ua->catalog = catalog;
1393 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1394 ua->catalog->hdr.name, ua->catalog->db_name);
1399 int quit_cmd(UAContext *ua, const char *cmd)
1405 /* Handler to get job status */
1406 static int status_handler(void *ctx, int num_fields, char **row)
1408 char *val = (char *)ctx;
1413 *val = '?'; /* Unknown by default */
1420 * Wait until no job is running
1422 int wait_cmd(UAContext *ua, const char *cmd)
1427 * Wait until no job is running
1429 if (ua->argc == 1) {
1430 bmicrosleep(0, 200000); /* let job actually start */
1431 for (bool running=true; running; ) {
1434 if (jcr->JobId != 0) {
1448 /* we have jobid, jobname or ujobid argument */
1450 uint32_t jobid = 0 ;
1453 bsendmsg(ua, _("ERR: Can't open db\n")) ;
1457 for (int i=1; i<ua->argc; i++) {
1458 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1462 jobid = str_to_int64(ua->argv[i]);
1464 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1465 strcasecmp(ua->argk[i], "job") == 0) {
1469 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1471 jobid = jcr->JobId ;
1475 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1479 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1481 jobid = jcr->JobId ;
1489 bsendmsg(ua, _("ERR: Job was not found\n"));
1494 * We wait the end of job
1497 bmicrosleep(0, 200000); /* let job actually start */
1498 for (bool running=true; running; ) {
1501 jcr=get_jcr_by_id(jobid) ;
1514 * We have to get JobStatus
1518 char jobstatus = '?'; /* Unknown by default */
1521 bsnprintf(buf, sizeof(buf),
1522 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1525 db_sql_query(ua->db, buf,
1526 status_handler, (void *)&jobstatus);
1528 switch (jobstatus) {
1530 status = 1 ; /* Warning */
1534 case JS_ErrorTerminated:
1536 status = 2 ; /* Critical */
1540 status = 0 ; /* Ok */
1544 status = 3 ; /* Unknown */
1548 bsendmsg(ua, "JobId=%i\n", jobid) ;
1549 bsendmsg(ua, "JobStatus=%s (%c)\n",
1550 job_status_to_str(jobstatus),
1554 bsendmsg(ua, "ExitStatus=%i\n", status) ;
1561 static int help_cmd(UAContext *ua, const char *cmd)
1565 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1566 for (i=0; i<comsize; i++) {
1567 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1569 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1573 int qhelp_cmd(UAContext *ua, const char *cmd)
1577 for (i=0; i<comsize; i++) {
1578 bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1583 static int version_cmd(UAContext *ua, const char *cmd)
1585 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1590 /* A bit brain damaged in that if the user has not done
1591 * a "use catalog xxx" command, we simply find the first
1592 * catalog resource and open it.
1594 bool open_db(UAContext *ua)
1601 ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1604 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1606 } else if (!acl_access_ok(ua, Catalog_ACL, ua->catalog->hdr.name)) {
1607 bsendmsg(ua, _("You must specify a \"use <catalog-name>\" command before continuing.\n"));
1611 bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1612 ua->catalog->hdr.name, ua->catalog->db_name);
1616 ua->jcr->catalog = ua->catalog;
1618 Dmsg0(150, "Open database\n");
1619 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1620 ua->catalog->db_password, ua->catalog->db_address,
1621 ua->catalog->db_port, ua->catalog->db_socket,
1622 ua->catalog->mult_db_connections);
1623 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1624 bsendmsg(ua, _("Could not open database \"%s\".\n"),
1625 ua->catalog->db_name);
1627 bsendmsg(ua, "%s", db_strerror(ua->db));
1632 ua->jcr->db = ua->db;
1633 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1637 void close_db(UAContext *ua)
1640 db_close_database(ua->jcr, ua->db);