3 * Bacula Director -- User Agent Commands
5 * Kern Sibbald, September MM
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
40 /* Imported subroutines */
42 /* Imported variables */
45 extern struct s_res resources[];
46 extern jobq_t job_queue; /* job queue */
49 /* Imported functions */
50 extern int status_cmd(UAContext *ua, const char *cmd);
51 extern int list_cmd(UAContext *ua, const char *cmd);
52 extern int llist_cmd(UAContext *ua, const char *cmd);
53 extern int show_cmd(UAContext *ua, const char *cmd);
54 extern int messagescmd(UAContext *ua, const char *cmd);
55 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
56 extern int gui_cmd(UAContext *ua, const char *cmd);
57 extern int sqlquerycmd(UAContext *ua, const char *cmd);
58 extern int querycmd(UAContext *ua, const char *cmd);
59 extern int retentioncmd(UAContext *ua, const char *cmd);
60 extern int prunecmd(UAContext *ua, const char *cmd);
61 extern int purgecmd(UAContext *ua, const char *cmd);
62 extern int restore_cmd(UAContext *ua, const char *cmd);
63 extern int label_cmd(UAContext *ua, const char *cmd);
64 extern int relabel_cmd(UAContext *ua, const char *cmd);
65 extern int update_cmd(UAContext *ua, const char *cmd);
67 /* Forward referenced functions */
68 static int add_cmd(UAContext *ua, const char *cmd);
69 static int create_cmd(UAContext *ua, const char *cmd);
70 static int cancel_cmd(UAContext *ua, const char *cmd);
71 static int enable_cmd(UAContext *ua, const char *cmd);
72 static int disable_cmd(UAContext *ua, const char *cmd);
73 static int setdebug_cmd(UAContext *ua, const char *cmd);
74 static int trace_cmd(UAContext *ua, const char *cmd);
75 static int var_cmd(UAContext *ua, const char *cmd);
76 static int estimate_cmd(UAContext *ua, const char *cmd);
77 static int help_cmd(UAContext *ua, const char *cmd);
78 static int delete_cmd(UAContext *ua, const char *cmd);
79 static int use_cmd(UAContext *ua, const char *cmd);
80 static int unmount_cmd(UAContext *ua, const char *cmd);
81 static int version_cmd(UAContext *ua, const char *cmd);
82 static int automount_cmd(UAContext *ua, const char *cmd);
83 static int time_cmd(UAContext *ua, const char *cmd);
84 static int reload_cmd(UAContext *ua, const char *cmd);
85 static int delete_volume(UAContext *ua);
86 static int delete_pool(UAContext *ua);
87 static void delete_job(UAContext *ua);
88 static int mount_cmd(UAContext *ua, const char *cmd);
89 static int release_cmd(UAContext *ua, const char *cmd);
90 static int wait_cmd(UAContext *ua, const char *cmd);
91 static int setip_cmd(UAContext *ua, const char *cmd);
92 static int python_cmd(UAContext *ua, const char *cmd);
93 static void do_job_delete(UAContext *ua, JobId_t JobId);
94 static void delete_job_id_range(UAContext *ua, char *tok);
96 int qhelp_cmd(UAContext *ua, const char *cmd);
97 int quit_cmd(UAContext *ua, const char *cmd);
100 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
101 static struct cmdstruct commands[] = {
102 { NT_("add"), add_cmd, _("add media to a pool")},
103 { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
104 { NT_("automount"), automount_cmd, _("automount [on|off] -- after label")},
105 { NT_("cancel"), cancel_cmd, _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
106 { NT_("create"), create_cmd, _("create DB Pool from resource")},
107 { NT_("delete"), delete_cmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
108 { NT_("disable"), disable_cmd, _("disable <job=name> -- disable a job")},
109 { NT_("enable"), enable_cmd, _("enable <job=name> -- enable a job")},
110 { NT_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")},
111 { NT_("exit"), quit_cmd, _("exit = quit")},
112 { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode")},
113 { NT_("help"), help_cmd, _("print this command")},
114 { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
115 { NT_("label"), label_cmd, _("label a tape")},
116 { NT_("llist"), llist_cmd, _("full or long list like list command")},
117 { NT_("messages"), messagescmd, _("messages")},
118 { NT_("mount"), mount_cmd, _("mount <storage-name>")},
119 { NT_("prune"), prunecmd, _("prune expired records from catalog")},
120 { NT_("purge"), purgecmd, _("purge records from catalog")},
121 { NT_("python"), python_cmd, _("python control commands")},
122 { NT_("quit"), quit_cmd, _("quit")},
123 { NT_("query"), querycmd, _("query catalog")},
124 { NT_("restore"), restore_cmd, _("restore files")},
125 { NT_("relabel"), relabel_cmd, _("relabel a tape")},
126 { NT_("release"), release_cmd, _("release <storage-name>")},
127 { NT_("reload"), reload_cmd, _("reload conf file")},
128 { NT_("run"), run_cmd, _("run <job-name>")},
129 { NT_("status"), status_cmd, _("status [storage | client]=<name>")},
130 { NT_("setdebug"), setdebug_cmd, _("sets debug level")},
131 { NT_("setip"), setip_cmd, _("sets new client address -- if authorized")},
132 { NT_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")},
133 { NT_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")},
134 { NT_("time"), time_cmd, _("print current time")},
135 { NT_("trace"), trace_cmd, _("turn on/off trace to file")},
136 { NT_("unmount"), unmount_cmd, _("unmount <storage-name>")},
137 { NT_("umount"), unmount_cmd, _("umount <storage-name> for old-time Unix guys")},
138 { NT_("update"), update_cmd, _("update Volume, Pool or slots")},
139 { NT_("use"), use_cmd, _("use catalog xxx")},
140 { NT_("var"), var_cmd, _("does variable expansion")},
141 { NT_("version"), version_cmd, _("print Director version")},
142 { NT_("wait"), wait_cmd, _("wait until no jobs are running [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>]")},
144 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
147 * Execute a command from the UA
149 int do_a_command(UAContext *ua, const char *cmd)
157 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
162 while (ua->jcr->wstorage->size()) {
163 ua->jcr->wstorage->remove(0);
166 len = strlen(ua->argk[0]);
167 for (i=0; i<comsize; i++) { /* search for command */
168 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
169 /* Check if command permitted, but "quit" is always OK */
170 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
171 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
174 stat = (*commands[i].func)(ua, cmd); /* go execute command */
180 bnet_fsend(ua->UA_sock, _("%s: is an invalid command.\n"), ua->argk[0]);
186 * This is a common routine used to stuff the Pool DB record defaults
187 * into the Media DB record just before creating a media (Volume)
190 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
192 mr->PoolId = pr->PoolId;
193 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
194 mr->Recycle = pr->Recycle;
195 mr->VolRetention = pr->VolRetention;
196 mr->VolUseDuration = pr->VolUseDuration;
197 mr->MaxVolJobs = pr->MaxVolJobs;
198 mr->MaxVolFiles = pr->MaxVolFiles;
199 mr->MaxVolBytes = pr->MaxVolBytes;
200 mr->LabelType = pr->LabelType;
206 * Add Volumes to an existing Pool
208 static int add_cmd(UAContext *ua, const char *cmd)
212 int num, i, max, startnum;
214 char name[MAX_NAME_LENGTH];
216 int Slot = 0, InChanger = 0;
219 "You probably don't want to be using this command since it\n"
220 "creates database records without labeling the Volumes.\n"
221 "You probably want to use the \"label\" command.\n\n"));
223 if (!open_client_db(ua)) {
227 memset(&pr, 0, sizeof(pr));
228 memset(&mr, 0, sizeof(mr));
230 if (!get_pool_dbr(ua, &pr)) {
234 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
235 pr.MaxVols, pr.PoolType);
237 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
238 bsendmsg(ua, _("Pool already has maximum volumes=%d\n"), pr.MaxVols);
240 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
243 pr.MaxVols = ua->pint32_val;
248 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
249 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
250 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
254 if (pr.MaxVols == 0) {
257 max = pr.MaxVols - pr.NumVols;
261 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
262 if (!get_pint(ua, buf)) {
265 num = ua->pint32_val;
266 if (num < 0 || num > max) {
267 bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
274 if (!get_cmd(ua, _("Enter Volume name: "))) {
278 if (!get_cmd(ua, _("Enter base volume name: "))) {
282 /* Don't allow | in Volume name because it is the volume separator character */
283 if (!is_volume_name_legal(ua, ua->cmd)) {
286 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
287 bsendmsg(ua, _("Volume name too long.\n"));
290 if (strlen(ua->cmd) == 0) {
291 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
295 bstrncpy(name, ua->cmd, sizeof(name));
297 bstrncat(name, "%04d", sizeof(name));
300 if (!get_pint(ua, _("Enter the starting number: "))) {
303 startnum = ua->pint32_val;
305 bsendmsg(ua, _("Start number must be greater than zero.\n"));
315 if (store && store->autochanger) {
316 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
319 Slot = ua->pint32_val;
320 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
323 InChanger = ua->pint32_val;
326 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
327 for (i=startnum; i < num+startnum; i++) {
328 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
330 mr.InChanger = InChanger;
331 mr.StorageId = store->StorageId;
333 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
334 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
335 bsendmsg(ua, "%s", db_strerror(ua->db));
339 first_id = mr.PoolId;
343 Dmsg0(200, "Update pool record.\n");
344 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
345 bsendmsg(ua, "%s", db_strerror(ua->db));
348 bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
354 * Turn auto mount on/off
359 int automount_cmd(UAContext *ua, const char *cmd)
364 if (!get_cmd(ua, _("Turn on or off? "))) {
372 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
380 static int cancel_cmd(UAContext *ua, const char *cmd)
385 char JobName[MAX_NAME_LENGTH];
387 for (i=1; i<ua->argc; i++) {
388 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
393 JobId = str_to_int64(ua->argv[i]);
394 if (!(jcr=get_jcr_by_id(JobId))) {
395 bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
399 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
403 if (!(jcr=get_jcr_by_partial_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));
409 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
413 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
414 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
415 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
416 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
423 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
424 bsendmsg(ua, _("Unauthorized command from this console.\n"));
429 * If we still do not have a jcr,
430 * throw up a list and ask the user to select one.
433 int tjobs = 0; /* total # number jobs */
434 /* Count Jobs running */
436 if (jcr->JobId == 0) { /* this is us */
439 tjobs++; /* count of all jobs */
440 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
441 continue; /* skip not authorized */
443 njobs++; /* count of authorized jobs */
447 if (njobs == 0) { /* no authorized */
449 bsendmsg(ua, _("No Jobs running.\n"));
451 bsendmsg(ua, _("None of your jobs are running.\n"));
456 start_prompt(ua, _("Select Job:\n"));
459 if (jcr->JobId == 0) { /* this is us */
462 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
463 continue; /* skip not authorized */
465 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
470 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
474 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
478 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
479 jcr = get_jcr_by_full_name(JobName);
481 bsendmsg(ua, _("Job \"%s\" not found.\n"), JobName);
486 ret = cancel_job(ua, jcr);
492 * This is a common routine to create or update a
493 * Pool DB base record from a Pool Resource. We handle
494 * the setting of MaxVols and NumVols slightly differently
495 * depending on if we are creating the Pool or we are
496 * simply bringing it into agreement with the resource (updage).
498 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
500 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
501 if (op == POOL_OP_CREATE) {
502 pr->MaxVols = pool->max_volumes;
504 } else { /* update pool */
505 if (pr->MaxVols != pool->max_volumes) {
506 pr->MaxVols = pool->max_volumes;
508 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
509 pr->MaxVols = pr->NumVols;
512 pr->LabelType = pool->LabelType;
513 pr->UseOnce = pool->use_volume_once;
514 pr->UseCatalog = pool->use_catalog;
515 pr->Recycle = pool->Recycle;
516 pr->VolRetention = pool->VolRetention;
517 pr->VolUseDuration = pool->VolUseDuration;
518 pr->MaxVolJobs = pool->MaxVolJobs;
519 pr->MaxVolFiles = pool->MaxVolFiles;
520 pr->MaxVolBytes = pool->MaxVolBytes;
521 pr->AutoPrune = pool->AutoPrune;
522 pr->Recycle = pool->Recycle;
523 if (pool->label_format) {
524 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
526 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
532 * Create a pool record from a given Pool resource
533 * Also called from backup.c
534 * Returns: -1 on error
535 * 0 record already exists
539 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
543 memset(&pr, 0, sizeof(POOL_DBR));
545 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
547 if (db_get_pool_record(jcr, db, &pr)) {
549 if (op == POOL_OP_UPDATE) { /* update request */
550 set_pooldbr_from_poolres(&pr, pool, op);
551 db_update_pool_record(jcr, db, &pr);
553 return 0; /* exists */
556 set_pooldbr_from_poolres(&pr, pool, op);
558 if (!db_create_pool_record(jcr, db, &pr)) {
559 return -1; /* error */
567 * Create a Pool Record in the database.
568 * It is always created from the Resource record.
570 static int create_cmd(UAContext *ua, const char *cmd)
574 if (!open_client_db(ua)) {
578 pool = get_pool_resource(ua);
583 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
585 bsendmsg(ua, _("Error: Pool %s already exists.\n"
586 "Use update to change it.\n"), pool->name());
590 bsendmsg(ua, "%s", db_strerror(ua->db));
596 bsendmsg(ua, _("Pool %s created.\n"), pool->name());
601 extern DIRRES *director;
604 * Python control command
605 * python restart (restarts interpreter)
607 static int python_cmd(UAContext *ua, const char *cmd)
609 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
610 term_python_interpreter();
611 init_python_interpreter(director->name(),
612 director->scripts_directory, "DirStartUp");
613 bsendmsg(ua, _("Python interpreter restarted.\n"));
615 bsendmsg(ua, _("Nothing done.\n"));
622 * Set a new address in a Client resource. We do this only
623 * if the Console name is the same as the Client name
624 * and the Console can access the client.
626 static int setip_cmd(UAContext *ua, const char *cmd)
630 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
631 bsendmsg(ua, _("Unauthorized command from this console.\n"));
635 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->name());
638 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->name());
641 if (client->address) {
642 free(client->address);
644 /* MA Bug 6 remove ifdef */
645 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
646 client->address = bstrdup(buf);
647 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
648 client->name(), client->address);
655 static void do_en_disable_cmd(UAContext *ua, bool setting)
660 i = find_arg_with_value(ua, NT_("job"));
662 job = select_job_resource(ua);
668 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
672 bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
676 if (!acl_access_ok(ua, Job_ACL, job->name())) {
677 bsendmsg(ua, _("Unauthorized command from this console.\n"));
680 job->enabled = setting;
681 bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
685 static int enable_cmd(UAContext *ua, const char *cmd)
687 do_en_disable_cmd(ua, true);
691 static int disable_cmd(UAContext *ua, const char *cmd)
693 do_en_disable_cmd(ua, false);
698 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
704 lstore.store = store;
705 pm_strcpy(lstore.store_source, _("unknown source"));
706 set_wstorage(jcr, &lstore);
707 /* Try connecting for up to 15 seconds */
708 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
709 store->name(), store->address, store->SDport);
710 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
711 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
714 Dmsg0(120, _("Connected to storage daemon\n"));
715 sd = jcr->store_bsock;
716 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
717 if (bnet_recv(sd) >= 0) {
718 bsendmsg(ua, "%s", sd->msg);
720 bnet_sig(sd, BNET_TERMINATE);
722 jcr->store_bsock = NULL;
726 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
730 /* Connect to File daemon */
732 ua->jcr->client = client;
733 /* Try to connect for 15 seconds */
734 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
735 client->name(), client->address, client->FDport);
736 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
737 bsendmsg(ua, _("Failed to connect to Client.\n"));
740 Dmsg0(120, "Connected to file daemon\n");
741 fd = ua->jcr->file_bsock;
742 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
743 if (bnet_recv(fd) >= 0) {
744 bsendmsg(ua, "%s", fd->msg);
746 bnet_sig(fd, BNET_TERMINATE);
748 ua->jcr->file_bsock = NULL;
753 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
755 STORE *store, **unique_store;
756 CLIENT *client, **unique_client;
762 /* Count Storage items */
766 foreach_res(store, R_STORAGE) {
769 unique_store = (STORE **) malloc(i * sizeof(STORE));
770 /* Find Unique Storage address/port */
771 store = (STORE *)GetNextRes(R_STORAGE, NULL);
773 unique_store[i++] = store;
774 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
776 for (j=0; j<i; j++) {
777 if (strcmp(unique_store[j]->address, store->address) == 0 &&
778 unique_store[j]->SDport == store->SDport) {
784 unique_store[i++] = store;
785 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
790 /* Call each unique Storage daemon */
791 for (j=0; j<i; j++) {
792 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
796 /* Count Client items */
800 foreach_res(client, R_CLIENT) {
803 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
804 /* Find Unique Client address/port */
805 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
807 unique_client[i++] = client;
808 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
810 for (j=0; j<i; j++) {
811 if (strcmp(unique_client[j]->address, client->address) == 0 &&
812 unique_client[j]->FDport == client->FDport) {
818 unique_client[i++] = client;
819 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
824 /* Call each unique File daemon */
825 for (j=0; j<i; j++) {
826 do_client_setdebug(ua, unique_client[j], level, trace_flag);
832 * setdebug level=nn all trace=1/0
834 static int setdebug_cmd(UAContext *ua, const char *cmd)
842 if (!open_client_db(ua)) {
845 Dmsg1(120, "setdebug:%s:\n", cmd);
848 i = find_arg_with_value(ua, "level");
850 level = atoi(ua->argv[i]);
853 if (!get_pint(ua, _("Enter new debug level: "))) {
856 level = ua->pint32_val;
859 /* Look for trace flag. -1 => not change */
860 i = find_arg_with_value(ua, "trace");
862 trace_flag = atoi(ua->argv[i]);
863 if (trace_flag > 0) {
869 for (i=1; i<ua->argc; i++) {
870 if (strcasecmp(ua->argk[i], "all") == 0) {
871 do_all_setdebug(ua, level, trace_flag);
874 if (strcasecmp(ua->argk[i], "dir") == 0 ||
875 strcasecmp(ua->argk[i], "director") == 0) {
877 set_trace(trace_flag);
880 if (strcasecmp(ua->argk[i], "client") == 0 ||
881 strcasecmp(ua->argk[i], "fd") == 0) {
884 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
886 do_client_setdebug(ua, client, level, trace_flag);
890 client = select_client_resource(ua);
892 do_client_setdebug(ua, client, level, trace_flag);
897 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
898 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
899 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
902 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
904 do_storage_setdebug(ua, store, level, trace_flag);
908 store = get_storage_resource(ua, false/*no default*/);
910 do_storage_setdebug(ua, store, level, trace_flag);
916 * We didn't find an appropriate keyword above, so
919 start_prompt(ua, _("Available daemons are: \n"));
920 add_prompt(ua, _("Director"));
921 add_prompt(ua, _("Storage"));
922 add_prompt(ua, _("Client"));
923 add_prompt(ua, _("All"));
924 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
925 case 0: /* Director */
927 set_trace(trace_flag);
930 store = get_storage_resource(ua, false/*no default*/);
932 do_storage_setdebug(ua, store, level, trace_flag);
936 client = select_client_resource(ua);
938 do_client_setdebug(ua, client, level, trace_flag);
942 do_all_setdebug(ua, level, trace_flag);
951 * Turn debug tracing to file on/off
953 static int trace_cmd(UAContext *ua, const char *cmd)
958 if (!get_cmd(ua, _("Turn on or off? "))) {
966 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
971 static int var_cmd(UAContext *ua, const char *cmd)
973 POOLMEM *val = get_pool_memory(PM_FNAME);
976 if (!open_client_db(ua)) {
979 for (var=ua->cmd; *var != ' '; ) { /* skip command */
982 while (*var == ' ') { /* skip spaces */
985 Dmsg1(100, "Var=%s:\n", var);
986 variable_expansion(ua->jcr, var, &val);
987 bsendmsg(ua, "%s\n", val);
988 free_pool_memory(val);
992 static int estimate_cmd(UAContext *ua, const char *cmd)
995 CLIENT *client = NULL;
996 FILESET *fileset = NULL;
998 char since[MAXSTRING];
1001 jcr->JobLevel = L_FULL;
1002 for (int i=1; i<ua->argc; i++) {
1003 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1004 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1006 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1010 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1012 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1013 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1014 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1020 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1022 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
1023 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1024 bsendmsg(ua, _("No authorization for FileSet \"%s\"\n"), fileset->name());
1030 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1034 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1035 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1036 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1041 if (!job && !(client && fileset)) {
1042 if (!(job = select_job_resource(ua))) {
1047 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1049 bsendmsg(ua, _("No job specified.\n"));
1052 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1053 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1058 client = job->client;
1061 fileset = job->fileset;
1063 jcr->client = client;
1064 jcr->fileset = fileset;
1066 ua->catalog = client->catalog;
1073 jcr->JobType = JT_BACKUP;
1074 init_jcr_job_record(jcr);
1076 if (!get_or_create_client_record(jcr)) {
1079 if (!get_or_create_fileset_record(jcr)) {
1083 get_level_since_time(ua->jcr, since, sizeof(since));
1085 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1086 job->client->name(), job->client->address, job->client->FDport);
1087 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1088 bsendmsg(ua, _("Failed to connect to Client.\n"));
1092 if (!send_include_list(jcr)) {
1093 bsendmsg(ua, _("Error sending include list.\n"));
1097 if (!send_exclude_list(jcr)) {
1098 bsendmsg(ua, _("Error sending exclude list.\n"));
1102 if (!send_level_command(jcr)) {
1106 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1107 while (bnet_recv(jcr->file_bsock) >= 0) {
1108 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1112 if (jcr->file_bsock) {
1113 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1114 bnet_close(jcr->file_bsock);
1115 jcr->file_bsock = NULL;
1124 static int time_cmd(UAContext *ua, const char *cmd)
1127 time_t ttime = time(NULL);
1129 (void)localtime_r(&ttime, &tm);
1130 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1131 bsendmsg(ua, "%s\n", sdt);
1136 * reload the conf file
1138 extern "C" void reload_config(int sig);
1140 static int reload_cmd(UAContext *ua, const char *cmd)
1147 * Delete Pool records (should purge Media with it).
1149 * delete pool=<pool-name>
1150 * delete volume pool=<pool-name> volume=<name>
1153 static int delete_cmd(UAContext *ua, const char *cmd)
1155 static const char *keywords[] = {
1161 if (!open_client_db(ua)) {
1165 switch (find_arg_keyword(ua, keywords)) {
1174 while ((i=find_arg(ua, "jobid")) > 0) {
1176 *ua->argk[i] = 0; /* zap keyword already visited */
1184 "In general it is not a good idea to delete either a\n"
1185 "Pool or a Volume since they may contain data.\n\n"));
1187 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1198 bsendmsg(ua, _("Nothing done.\n"));
1206 * delete_job has been modified to parse JobID lists like the
1208 * delete JobID=3,4,6,7-11,14
1210 * Thanks to Phil Stracchino for the above addition.
1213 static void delete_job(UAContext *ua)
1218 int i = find_arg_with_value(ua, NT_("jobid"));
1220 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1221 s = bstrdup(ua->argv[i]);
1224 * We could use strtok() here. But we're not going to, because:
1225 * (a) strtok() is deprecated, having been replaced by strsep();
1226 * (b) strtok() is broken in significant ways.
1227 * we could use strsep() instead, but it's not universally available.
1228 * so we grow our own using strchr().
1230 sep = strchr(tok, ',');
1231 while (sep != NULL) {
1233 if (strchr(tok, '-')) {
1234 delete_job_id_range(ua, tok);
1236 JobId = str_to_int64(tok);
1237 do_job_delete(ua, JobId);
1240 sep = strchr(tok, ',');
1242 /* pick up the last token */
1243 if (strchr(tok, '-')) {
1244 delete_job_id_range(ua, tok);
1246 JobId = str_to_int64(tok);
1247 do_job_delete(ua, JobId);
1252 JobId = str_to_int64(ua->argv[i]);
1253 do_job_delete(ua, JobId);
1255 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1258 JobId = ua->int64_val;
1259 do_job_delete(ua, JobId);
1264 * we call delete_job_id_range to parse range tokens and iterate over ranges
1266 static void delete_job_id_range(UAContext *ua, char *tok)
1271 tok2 = strchr(tok, '-');
1274 j1 = str_to_int64(tok);
1275 j2 = str_to_int64(tok2);
1276 for (j=j1; j<=j2; j++) {
1277 do_job_delete(ua, j);
1282 * do_job_delete now performs the actual delete operation atomically
1285 static void do_job_delete(UAContext *ua, JobId_t JobId)
1287 POOL_MEM query(PM_MESSAGE);
1290 purge_files_from_job(ua, JobId);
1291 purge_job_from_catalog(ua, JobId);
1292 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1296 * Delete media records from database -- dangerous
1298 static int delete_volume(UAContext *ua)
1302 if (!select_media_dbr(ua, &mr)) {
1305 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1306 "and all Jobs saved on that volume from the Catalog\n"),
1309 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1312 if (ua->pint32_val) {
1313 db_delete_media_record(ua->jcr, ua->db, &mr);
1319 * Delete a pool record from the database -- dangerous
1321 static int delete_pool(UAContext *ua)
1325 memset(&pr, 0, sizeof(pr));
1327 if (!get_pool_dbr(ua, &pr)) {
1330 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1333 if (ua->pint32_val) {
1334 db_delete_pool_record(ua->jcr, ua->db, &pr);
1340 static void do_mount_cmd(UAContext *ua, const char *command)
1345 char dev_name[MAX_NAME_LENGTH];
1349 if (!open_client_db(ua)) {
1352 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1354 store.store = get_storage_resource(ua, true/*arg is storage*/);
1355 pm_strcpy(store.store_source, _("unknown source"));
1359 set_wstorage(jcr, &store);
1360 drive = get_storage_drive(ua, store.store);
1361 if (strcmp(command, "mount") == 0) {
1362 slot = get_storage_slot(ua, store.store);
1365 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1366 store.store->media_type, store.store->dev_name(), drive);
1368 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1369 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1372 sd = jcr->store_bsock;
1373 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1374 bash_spaces(dev_name);
1376 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1378 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1380 while (bnet_recv(sd) >= 0) {
1381 bsendmsg(ua, "%s", sd->msg);
1383 bnet_sig(sd, BNET_TERMINATE);
1385 jcr->store_bsock = NULL;
1389 * mount [storage=<name>] [drive=nn] [slot=mm]
1391 static int mount_cmd(UAContext *ua, const char *cmd)
1393 do_mount_cmd(ua, "mount"); /* mount */
1399 * unmount [storage=<name>] [drive=nn]
1401 static int unmount_cmd(UAContext *ua, const char *cmd)
1403 do_mount_cmd(ua, "unmount"); /* unmount */
1409 * release [storage=<name>] [drive=nn]
1411 static int release_cmd(UAContext *ua, const char *cmd)
1413 do_mount_cmd(ua, "release"); /* release */
1420 * use catalog=<name>
1422 static int use_cmd(UAContext *ua, const char *cmd)
1424 CAT *oldcatalog, *catalog;
1427 close_db(ua); /* close any previously open db */
1428 oldcatalog = ua->catalog;
1430 if (!(catalog = get_catalog_resource(ua))) {
1431 ua->catalog = oldcatalog;
1433 ua->catalog = catalog;
1436 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1437 ua->catalog->name(), ua->catalog->db_name);
1442 int quit_cmd(UAContext *ua, const char *cmd)
1448 /* Handler to get job status */
1449 static int status_handler(void *ctx, int num_fields, char **row)
1451 char *val = (char *)ctx;
1456 *val = '?'; /* Unknown by default */
1463 * Wait until no job is running
1465 int wait_cmd(UAContext *ua, const char *cmd)
1470 * Wait until no job is running
1472 if (ua->argc == 1) {
1473 bmicrosleep(0, 200000); /* let job actually start */
1474 for (bool running=true; running; ) {
1477 if (jcr->JobId != 0) {
1491 /* we have jobid, jobname or ujobid argument */
1493 uint32_t jobid = 0 ;
1495 if (!open_client_db(ua)) {
1496 bsendmsg(ua, _("ERR: Can't open db\n")) ;
1500 for (int i=1; i<ua->argc; i++) {
1501 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1505 jobid = str_to_int64(ua->argv[i]);
1507 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1508 strcasecmp(ua->argk[i], "job") == 0) {
1512 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1514 jobid = jcr->JobId ;
1518 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1522 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1524 jobid = jcr->JobId ;
1532 bsendmsg(ua, _("ERR: Job was not found\n"));
1537 * We wait the end of job
1540 bmicrosleep(0, 200000); /* let job actually start */
1541 for (bool running=true; running; ) {
1544 jcr=get_jcr_by_id(jobid) ;
1557 * We have to get JobStatus
1561 char jobstatus = '?'; /* Unknown by default */
1564 bsnprintf(buf, sizeof(buf),
1565 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1568 db_sql_query(ua->db, buf,
1569 status_handler, (void *)&jobstatus);
1571 switch (jobstatus) {
1573 status = 1 ; /* Warning */
1577 case JS_ErrorTerminated:
1579 status = 2 ; /* Critical */
1583 status = 0 ; /* Ok */
1587 status = 3 ; /* Unknown */
1591 bsendmsg(ua, "JobId=%i\n", jobid) ;
1592 bsendmsg(ua, "JobStatus=%s (%c)\n",
1593 job_status_to_str(jobstatus),
1597 bsendmsg(ua, "ExitStatus=%i\n", status) ;
1604 static int help_cmd(UAContext *ua, const char *cmd)
1608 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1609 for (i=0; i<comsize; i++) {
1610 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1612 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1616 int qhelp_cmd(UAContext *ua, const char *cmd)
1620 for (i=0; i<comsize; i++) {
1621 bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1626 static int version_cmd(UAContext *ua, const char *cmd)
1628 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1633 * This call explicitly checks for a catalog=xxx and
1634 * if given, opens that catalog. It also checks for
1635 * client=xxx and if found, opens the catalog
1636 * corresponding to that client. If we still don't
1637 * have a catalog, look for a Job keyword and get the
1638 * catalog from its client record.
1640 bool open_client_db(UAContext *ua)
1647 /* Try for catalog keyword */
1648 i = find_arg_with_value(ua, NT_("catalog"));
1650 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1651 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1654 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
1656 if (ua->catalog && ua->catalog != catalog) {
1659 ua->catalog = catalog;
1664 /* Try for client keyword */
1665 i = find_arg_with_value(ua, NT_("client"));
1667 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1668 bsendmsg(ua, _("No authorization for Client \"%s\"\n"), ua->argv[i]);
1671 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1673 catalog = client->catalog;
1674 if (ua->catalog && ua->catalog != catalog) {
1677 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1678 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1681 ua->catalog = catalog;
1686 /* Try for Job keyword */
1687 i = find_arg_with_value(ua, NT_("job"));
1689 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1690 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), ua->argv[i]);
1693 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1695 catalog = job->client->catalog;
1696 if (ua->catalog && ua->catalog != catalog) {
1699 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1700 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1703 ua->catalog = catalog;
1713 * Open the catalog database.
1715 bool open_db(UAContext *ua)
1721 ua->catalog = get_catalog_resource(ua);
1723 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1728 ua->jcr->catalog = ua->catalog;
1730 Dmsg0(150, "Open database\n");
1731 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1732 ua->catalog->db_password, ua->catalog->db_address,
1733 ua->catalog->db_port, ua->catalog->db_socket,
1734 ua->catalog->mult_db_connections);
1735 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1736 bsendmsg(ua, _("Could not open catalog database \"%s\".\n"),
1737 ua->catalog->db_name);
1739 bsendmsg(ua, "%s", db_strerror(ua->db));
1744 ua->jcr->db = ua->db;
1745 bsendmsg(ua, _("Using Catalog \"%s\"\n"), ua->catalog->name());
1746 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1750 void close_db(UAContext *ua)
1753 db_close_database(ua->jcr, ua->db);