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->RecyclePoolId = pr->RecyclePoolId;
198 mr->MaxVolJobs = pr->MaxVolJobs;
199 mr->MaxVolFiles = pr->MaxVolFiles;
200 mr->MaxVolBytes = pr->MaxVolBytes;
201 mr->LabelType = pr->LabelType;
207 * Add Volumes to an existing Pool
209 static int add_cmd(UAContext *ua, const char *cmd)
213 int num, i, max, startnum;
215 char name[MAX_NAME_LENGTH];
217 int Slot = 0, InChanger = 0;
220 "You probably don't want to be using this command since it\n"
221 "creates database records without labeling the Volumes.\n"
222 "You probably want to use the \"label\" command.\n\n"));
224 if (!open_client_db(ua)) {
228 memset(&pr, 0, sizeof(pr));
229 memset(&mr, 0, sizeof(mr));
231 if (!get_pool_dbr(ua, &pr)) {
235 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
236 pr.MaxVols, pr.PoolType);
238 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
239 bsendmsg(ua, _("Pool already has maximum volumes=%d\n"), pr.MaxVols);
241 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
244 pr.MaxVols = ua->pint32_val;
249 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
250 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
251 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
255 if (pr.MaxVols == 0) {
258 max = pr.MaxVols - pr.NumVols;
262 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
263 if (!get_pint(ua, buf)) {
266 num = ua->pint32_val;
267 if (num < 0 || num > max) {
268 bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
275 if (!get_cmd(ua, _("Enter Volume name: "))) {
279 if (!get_cmd(ua, _("Enter base volume name: "))) {
283 /* Don't allow | in Volume name because it is the volume separator character */
284 if (!is_volume_name_legal(ua, ua->cmd)) {
287 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
288 bsendmsg(ua, _("Volume name too long.\n"));
291 if (strlen(ua->cmd) == 0) {
292 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
296 bstrncpy(name, ua->cmd, sizeof(name));
298 bstrncat(name, "%04d", sizeof(name));
301 if (!get_pint(ua, _("Enter the starting number: "))) {
304 startnum = ua->pint32_val;
306 bsendmsg(ua, _("Start number must be greater than zero.\n"));
316 if (store && store->autochanger) {
317 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
320 Slot = ua->pint32_val;
321 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
324 InChanger = ua->pint32_val;
327 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
328 for (i=startnum; i < num+startnum; i++) {
329 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
331 mr.InChanger = InChanger;
332 mr.StorageId = store->StorageId;
334 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
335 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
336 bsendmsg(ua, "%s", db_strerror(ua->db));
340 first_id = mr.PoolId;
344 Dmsg0(200, "Update pool record.\n");
345 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
346 bsendmsg(ua, "%s", db_strerror(ua->db));
349 bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
355 * Turn auto mount on/off
360 int automount_cmd(UAContext *ua, const char *cmd)
365 if (!get_cmd(ua, _("Turn on or off? "))) {
373 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
381 static int cancel_cmd(UAContext *ua, const char *cmd)
386 char JobName[MAX_NAME_LENGTH];
388 for (i=1; i<ua->argc; i++) {
389 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
394 JobId = str_to_int64(ua->argv[i]);
395 if (!(jcr=get_jcr_by_id(JobId))) {
396 bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
400 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
404 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
405 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
406 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
407 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
410 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
414 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
415 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
416 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
417 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
424 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
425 bsendmsg(ua, _("Unauthorized command from this console.\n"));
430 * If we still do not have a jcr,
431 * throw up a list and ask the user to select one.
434 int tjobs = 0; /* total # number jobs */
435 /* Count Jobs running */
437 if (jcr->JobId == 0) { /* this is us */
440 tjobs++; /* count of all jobs */
441 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
442 continue; /* skip not authorized */
444 njobs++; /* count of authorized jobs */
448 if (njobs == 0) { /* no authorized */
450 bsendmsg(ua, _("No Jobs running.\n"));
452 bsendmsg(ua, _("None of your jobs are running.\n"));
457 start_prompt(ua, _("Select Job:\n"));
460 if (jcr->JobId == 0) { /* this is us */
463 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
464 continue; /* skip not authorized */
466 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
471 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
475 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
479 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
480 jcr = get_jcr_by_full_name(JobName);
482 bsendmsg(ua, _("Job \"%s\" not found.\n"), JobName);
487 ret = cancel_job(ua, jcr);
493 * This is a common routine to create or update a
494 * Pool DB base record from a Pool Resource. We handle
495 * the setting of MaxVols and NumVols slightly differently
496 * depending on if we are creating the Pool or we are
497 * simply bringing it into agreement with the resource (update).
499 * Caution : RecyclePoolId isn't setup in this function.
500 * You can use set_pooldbr_recyclepoolid();
503 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
505 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
506 if (op == POOL_OP_CREATE) {
507 pr->MaxVols = pool->max_volumes;
509 } else { /* update pool */
510 if (pr->MaxVols != pool->max_volumes) {
511 pr->MaxVols = pool->max_volumes;
513 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
514 pr->MaxVols = pr->NumVols;
517 pr->LabelType = pool->LabelType;
518 pr->UseOnce = pool->use_volume_once;
519 pr->UseCatalog = pool->use_catalog;
520 pr->Recycle = pool->Recycle;
521 pr->VolRetention = pool->VolRetention;
522 pr->VolUseDuration = pool->VolUseDuration;
523 pr->MaxVolJobs = pool->MaxVolJobs;
524 pr->MaxVolFiles = pool->MaxVolFiles;
525 pr->MaxVolBytes = pool->MaxVolBytes;
526 pr->AutoPrune = pool->AutoPrune;
527 pr->Recycle = pool->Recycle;
528 if (pool->label_format) {
529 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
531 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
535 bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
540 if (pool->RecyclePool) {
541 memset(&rpool, 0, sizeof(POOL_DBR));
543 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
544 if (db_get_pool_record(jcr, db, &rpool)) {
545 pr->RecyclePoolId = rpool.PoolId;
547 Jmsg(jcr, M_WARNING, 0,
548 _("Can't set %s RecyclePool to %s, %s is not in database, try to update it with 'update pool=%s'\n"),pool->name(),rpool.Name, rpool.Name,pool->name());
552 } else { /* no RecyclePool used, set it to 0 */
553 pr->RecyclePoolId = 0;
559 * Create a pool record from a given Pool resource
560 * Also called from backup.c
561 * Returns: -1 on error
562 * 0 record already exists
566 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
570 memset(&pr, 0, sizeof(POOL_DBR));
572 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
574 if (db_get_pool_record(jcr, db, &pr)) {
576 if (op == POOL_OP_UPDATE) { /* update request */
577 set_pooldbr_from_poolres(&pr, pool, op);
578 db_update_pool_record(jcr, db, &pr);
580 return 0; /* exists */
583 set_pooldbr_from_poolres(&pr, pool, op);
584 set_pooldbr_recyclepoolid(jcr, db, &pr, pool);
586 if (!db_create_pool_record(jcr, db, &pr)) {
587 return -1; /* error */
595 * Create a Pool Record in the database.
596 * It is always created from the Resource record.
598 static int create_cmd(UAContext *ua, const char *cmd)
602 if (!open_client_db(ua)) {
606 pool = get_pool_resource(ua);
611 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
613 bsendmsg(ua, _("Error: Pool %s already exists.\n"
614 "Use update to change it.\n"), pool->name());
618 bsendmsg(ua, "%s", db_strerror(ua->db));
624 bsendmsg(ua, _("Pool %s created.\n"), pool->name());
629 extern DIRRES *director;
632 * Python control command
633 * python restart (restarts interpreter)
635 static int python_cmd(UAContext *ua, const char *cmd)
637 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
638 term_python_interpreter();
639 init_python_interpreter(director->name(),
640 director->scripts_directory, "DirStartUp");
641 bsendmsg(ua, _("Python interpreter restarted.\n"));
643 bsendmsg(ua, _("Nothing done.\n"));
650 * Set a new address in a Client resource. We do this only
651 * if the Console name is the same as the Client name
652 * and the Console can access the client.
654 static int setip_cmd(UAContext *ua, const char *cmd)
658 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
659 bsendmsg(ua, _("Unauthorized command from this console.\n"));
663 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->name());
666 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->name());
669 if (client->address) {
670 free(client->address);
672 /* MA Bug 6 remove ifdef */
673 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
674 client->address = bstrdup(buf);
675 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
676 client->name(), client->address);
683 static void do_en_disable_cmd(UAContext *ua, bool setting)
688 i = find_arg_with_value(ua, NT_("job"));
690 job = select_job_resource(ua);
696 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
700 bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
704 if (!acl_access_ok(ua, Job_ACL, job->name())) {
705 bsendmsg(ua, _("Unauthorized command from this console.\n"));
708 job->enabled = setting;
709 bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
713 static int enable_cmd(UAContext *ua, const char *cmd)
715 do_en_disable_cmd(ua, true);
719 static int disable_cmd(UAContext *ua, const char *cmd)
721 do_en_disable_cmd(ua, false);
726 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
732 lstore.store = store;
733 pm_strcpy(lstore.store_source, _("unknown source"));
734 set_wstorage(jcr, &lstore);
735 /* Try connecting for up to 15 seconds */
736 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
737 store->name(), store->address, store->SDport);
738 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
739 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
742 Dmsg0(120, _("Connected to storage daemon\n"));
743 sd = jcr->store_bsock;
744 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
745 if (bnet_recv(sd) >= 0) {
746 bsendmsg(ua, "%s", sd->msg);
748 bnet_sig(sd, BNET_TERMINATE);
750 jcr->store_bsock = NULL;
754 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
758 /* Connect to File daemon */
760 ua->jcr->client = client;
761 /* Try to connect for 15 seconds */
762 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
763 client->name(), client->address, client->FDport);
764 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
765 bsendmsg(ua, _("Failed to connect to Client.\n"));
768 Dmsg0(120, "Connected to file daemon\n");
769 fd = ua->jcr->file_bsock;
770 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
771 if (bnet_recv(fd) >= 0) {
772 bsendmsg(ua, "%s", fd->msg);
774 bnet_sig(fd, BNET_TERMINATE);
776 ua->jcr->file_bsock = NULL;
781 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
783 STORE *store, **unique_store;
784 CLIENT *client, **unique_client;
790 /* Count Storage items */
794 foreach_res(store, R_STORAGE) {
797 unique_store = (STORE **) malloc(i * sizeof(STORE));
798 /* Find Unique Storage address/port */
799 store = (STORE *)GetNextRes(R_STORAGE, NULL);
801 unique_store[i++] = store;
802 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
804 for (j=0; j<i; j++) {
805 if (strcmp(unique_store[j]->address, store->address) == 0 &&
806 unique_store[j]->SDport == store->SDport) {
812 unique_store[i++] = store;
813 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
818 /* Call each unique Storage daemon */
819 for (j=0; j<i; j++) {
820 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
824 /* Count Client items */
828 foreach_res(client, R_CLIENT) {
831 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
832 /* Find Unique Client address/port */
833 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
835 unique_client[i++] = client;
836 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
838 for (j=0; j<i; j++) {
839 if (strcmp(unique_client[j]->address, client->address) == 0 &&
840 unique_client[j]->FDport == client->FDport) {
846 unique_client[i++] = client;
847 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
852 /* Call each unique File daemon */
853 for (j=0; j<i; j++) {
854 do_client_setdebug(ua, unique_client[j], level, trace_flag);
860 * setdebug level=nn all trace=1/0
862 static int setdebug_cmd(UAContext *ua, const char *cmd)
870 if (!open_client_db(ua)) {
873 Dmsg1(120, "setdebug:%s:\n", cmd);
876 i = find_arg_with_value(ua, "level");
878 level = atoi(ua->argv[i]);
881 if (!get_pint(ua, _("Enter new debug level: "))) {
884 level = ua->pint32_val;
887 /* Look for trace flag. -1 => not change */
888 i = find_arg_with_value(ua, "trace");
890 trace_flag = atoi(ua->argv[i]);
891 if (trace_flag > 0) {
897 for (i=1; i<ua->argc; i++) {
898 if (strcasecmp(ua->argk[i], "all") == 0) {
899 do_all_setdebug(ua, level, trace_flag);
902 if (strcasecmp(ua->argk[i], "dir") == 0 ||
903 strcasecmp(ua->argk[i], "director") == 0) {
905 set_trace(trace_flag);
908 if (strcasecmp(ua->argk[i], "client") == 0 ||
909 strcasecmp(ua->argk[i], "fd") == 0) {
912 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
914 do_client_setdebug(ua, client, level, trace_flag);
918 client = select_client_resource(ua);
920 do_client_setdebug(ua, client, level, trace_flag);
925 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
926 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
927 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
930 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
932 do_storage_setdebug(ua, store, level, trace_flag);
936 store = get_storage_resource(ua, false/*no default*/);
938 do_storage_setdebug(ua, store, level, trace_flag);
944 * We didn't find an appropriate keyword above, so
947 start_prompt(ua, _("Available daemons are: \n"));
948 add_prompt(ua, _("Director"));
949 add_prompt(ua, _("Storage"));
950 add_prompt(ua, _("Client"));
951 add_prompt(ua, _("All"));
952 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
953 case 0: /* Director */
955 set_trace(trace_flag);
958 store = get_storage_resource(ua, false/*no default*/);
960 do_storage_setdebug(ua, store, level, trace_flag);
964 client = select_client_resource(ua);
966 do_client_setdebug(ua, client, level, trace_flag);
970 do_all_setdebug(ua, level, trace_flag);
979 * Turn debug tracing to file on/off
981 static int trace_cmd(UAContext *ua, const char *cmd)
986 if (!get_cmd(ua, _("Turn on or off? "))) {
994 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
999 static int var_cmd(UAContext *ua, const char *cmd)
1001 POOLMEM *val = get_pool_memory(PM_FNAME);
1004 if (!open_client_db(ua)) {
1007 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1010 while (*var == ' ') { /* skip spaces */
1013 Dmsg1(100, "Var=%s:\n", var);
1014 variable_expansion(ua->jcr, var, &val);
1015 bsendmsg(ua, "%s\n", val);
1016 free_pool_memory(val);
1020 static int estimate_cmd(UAContext *ua, const char *cmd)
1023 CLIENT *client = NULL;
1024 FILESET *fileset = NULL;
1026 char since[MAXSTRING];
1029 jcr->JobLevel = L_FULL;
1030 for (int i=1; i<ua->argc; i++) {
1031 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1032 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1034 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1038 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1040 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1041 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1042 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1048 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1050 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
1051 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1052 bsendmsg(ua, _("No authorization for FileSet \"%s\"\n"), fileset->name());
1058 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1062 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1063 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1064 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1069 if (!job && !(client && fileset)) {
1070 if (!(job = select_job_resource(ua))) {
1075 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1077 bsendmsg(ua, _("No job specified.\n"));
1080 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1081 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1086 client = job->client;
1089 fileset = job->fileset;
1091 jcr->client = client;
1092 jcr->fileset = fileset;
1094 ua->catalog = client->catalog;
1101 jcr->JobType = JT_BACKUP;
1102 init_jcr_job_record(jcr);
1104 if (!get_or_create_client_record(jcr)) {
1107 if (!get_or_create_fileset_record(jcr)) {
1111 get_level_since_time(ua->jcr, since, sizeof(since));
1113 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1114 job->client->name(), job->client->address, job->client->FDport);
1115 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1116 bsendmsg(ua, _("Failed to connect to Client.\n"));
1120 if (!send_include_list(jcr)) {
1121 bsendmsg(ua, _("Error sending include list.\n"));
1125 if (!send_exclude_list(jcr)) {
1126 bsendmsg(ua, _("Error sending exclude list.\n"));
1130 if (!send_level_command(jcr)) {
1134 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1135 while (bnet_recv(jcr->file_bsock) >= 0) {
1136 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1140 if (jcr->file_bsock) {
1141 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1142 bnet_close(jcr->file_bsock);
1143 jcr->file_bsock = NULL;
1152 static int time_cmd(UAContext *ua, const char *cmd)
1155 time_t ttime = time(NULL);
1157 (void)localtime_r(&ttime, &tm);
1158 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1159 bsendmsg(ua, "%s\n", sdt);
1164 * reload the conf file
1166 extern "C" void reload_config(int sig);
1168 static int reload_cmd(UAContext *ua, const char *cmd)
1175 * Delete Pool records (should purge Media with it).
1177 * delete pool=<pool-name>
1178 * delete volume pool=<pool-name> volume=<name>
1181 static int delete_cmd(UAContext *ua, const char *cmd)
1183 static const char *keywords[] = {
1189 if (!open_client_db(ua)) {
1193 switch (find_arg_keyword(ua, keywords)) {
1202 while ((i=find_arg(ua, "jobid")) > 0) {
1204 *ua->argk[i] = 0; /* zap keyword already visited */
1212 "In general it is not a good idea to delete either a\n"
1213 "Pool or a Volume since they may contain data.\n\n"));
1215 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1226 bsendmsg(ua, _("Nothing done.\n"));
1234 * delete_job has been modified to parse JobID lists like the
1236 * delete JobID=3,4,6,7-11,14
1238 * Thanks to Phil Stracchino for the above addition.
1241 static void delete_job(UAContext *ua)
1246 int i = find_arg_with_value(ua, NT_("jobid"));
1248 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1249 s = bstrdup(ua->argv[i]);
1252 * We could use strtok() here. But we're not going to, because:
1253 * (a) strtok() is deprecated, having been replaced by strsep();
1254 * (b) strtok() is broken in significant ways.
1255 * we could use strsep() instead, but it's not universally available.
1256 * so we grow our own using strchr().
1258 sep = strchr(tok, ',');
1259 while (sep != NULL) {
1261 if (strchr(tok, '-')) {
1262 delete_job_id_range(ua, tok);
1264 JobId = str_to_int64(tok);
1265 do_job_delete(ua, JobId);
1268 sep = strchr(tok, ',');
1270 /* pick up the last token */
1271 if (strchr(tok, '-')) {
1272 delete_job_id_range(ua, tok);
1274 JobId = str_to_int64(tok);
1275 do_job_delete(ua, JobId);
1280 JobId = str_to_int64(ua->argv[i]);
1281 do_job_delete(ua, JobId);
1283 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1286 JobId = ua->int64_val;
1287 do_job_delete(ua, JobId);
1292 * we call delete_job_id_range to parse range tokens and iterate over ranges
1294 static void delete_job_id_range(UAContext *ua, char *tok)
1299 tok2 = strchr(tok, '-');
1302 j1 = str_to_int64(tok);
1303 j2 = str_to_int64(tok2);
1304 for (j=j1; j<=j2; j++) {
1305 do_job_delete(ua, j);
1310 * do_job_delete now performs the actual delete operation atomically
1313 static void do_job_delete(UAContext *ua, JobId_t JobId)
1315 POOL_MEM query(PM_MESSAGE);
1318 purge_files_from_job(ua, JobId);
1319 purge_job_from_catalog(ua, JobId);
1320 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1324 * Delete media records from database -- dangerous
1326 static int delete_volume(UAContext *ua)
1330 if (!select_media_dbr(ua, &mr)) {
1333 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1334 "and all Jobs saved on that volume from the Catalog\n"),
1337 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1340 if (ua->pint32_val) {
1341 db_delete_media_record(ua->jcr, ua->db, &mr);
1347 * Delete a pool record from the database -- dangerous
1349 static int delete_pool(UAContext *ua)
1353 memset(&pr, 0, sizeof(pr));
1355 if (!get_pool_dbr(ua, &pr)) {
1358 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1361 if (ua->pint32_val) {
1362 db_delete_pool_record(ua->jcr, ua->db, &pr);
1368 static void do_mount_cmd(UAContext *ua, const char *command)
1373 char dev_name[MAX_NAME_LENGTH];
1377 if (!open_client_db(ua)) {
1380 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1382 store.store = get_storage_resource(ua, true/*arg is storage*/);
1383 pm_strcpy(store.store_source, _("unknown source"));
1387 set_wstorage(jcr, &store);
1388 drive = get_storage_drive(ua, store.store);
1389 if (strcmp(command, "mount") == 0) {
1390 slot = get_storage_slot(ua, store.store);
1393 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1394 store.store->media_type, store.store->dev_name(), drive);
1396 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1397 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1400 sd = jcr->store_bsock;
1401 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1402 bash_spaces(dev_name);
1404 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1406 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1408 while (bnet_recv(sd) >= 0) {
1409 bsendmsg(ua, "%s", sd->msg);
1411 bnet_sig(sd, BNET_TERMINATE);
1413 jcr->store_bsock = NULL;
1417 * mount [storage=<name>] [drive=nn] [slot=mm]
1419 static int mount_cmd(UAContext *ua, const char *cmd)
1421 do_mount_cmd(ua, "mount"); /* mount */
1427 * unmount [storage=<name>] [drive=nn]
1429 static int unmount_cmd(UAContext *ua, const char *cmd)
1431 do_mount_cmd(ua, "unmount"); /* unmount */
1437 * release [storage=<name>] [drive=nn]
1439 static int release_cmd(UAContext *ua, const char *cmd)
1441 do_mount_cmd(ua, "release"); /* release */
1448 * use catalog=<name>
1450 static int use_cmd(UAContext *ua, const char *cmd)
1452 CAT *oldcatalog, *catalog;
1455 close_db(ua); /* close any previously open db */
1456 oldcatalog = ua->catalog;
1458 if (!(catalog = get_catalog_resource(ua))) {
1459 ua->catalog = oldcatalog;
1461 ua->catalog = catalog;
1464 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1465 ua->catalog->name(), ua->catalog->db_name);
1470 int quit_cmd(UAContext *ua, const char *cmd)
1476 /* Handler to get job status */
1477 static int status_handler(void *ctx, int num_fields, char **row)
1479 char *val = (char *)ctx;
1484 *val = '?'; /* Unknown by default */
1491 * Wait until no job is running
1493 int wait_cmd(UAContext *ua, const char *cmd)
1498 * Wait until no job is running
1500 if (ua->argc == 1) {
1501 bmicrosleep(0, 200000); /* let job actually start */
1502 for (bool running=true; running; ) {
1505 if (jcr->JobId != 0) {
1519 /* we have jobid, jobname or ujobid argument */
1521 uint32_t jobid = 0 ;
1523 if (!open_client_db(ua)) {
1524 bsendmsg(ua, _("ERR: Can't open db\n")) ;
1528 for (int i=1; i<ua->argc; i++) {
1529 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1533 jobid = str_to_int64(ua->argv[i]);
1535 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1536 strcasecmp(ua->argk[i], "job") == 0) {
1540 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1542 jobid = jcr->JobId ;
1546 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1550 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1552 jobid = jcr->JobId ;
1560 bsendmsg(ua, _("ERR: Job was not found\n"));
1565 * We wait the end of job
1568 bmicrosleep(0, 200000); /* let job actually start */
1569 for (bool running=true; running; ) {
1572 jcr=get_jcr_by_id(jobid) ;
1585 * We have to get JobStatus
1589 char jobstatus = '?'; /* Unknown by default */
1592 bsnprintf(buf, sizeof(buf),
1593 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1596 db_sql_query(ua->db, buf,
1597 status_handler, (void *)&jobstatus);
1599 switch (jobstatus) {
1601 status = 1 ; /* Warning */
1605 case JS_ErrorTerminated:
1607 status = 2 ; /* Critical */
1611 status = 0 ; /* Ok */
1615 status = 3 ; /* Unknown */
1619 bsendmsg(ua, "JobId=%i\n", jobid) ;
1620 bsendmsg(ua, "JobStatus=%s (%c)\n",
1621 job_status_to_str(jobstatus),
1625 bsendmsg(ua, "ExitStatus=%i\n", status) ;
1632 static int help_cmd(UAContext *ua, const char *cmd)
1636 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1637 for (i=0; i<comsize; i++) {
1638 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1640 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1644 int qhelp_cmd(UAContext *ua, const char *cmd)
1648 for (i=0; i<comsize; i++) {
1649 bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1654 static int version_cmd(UAContext *ua, const char *cmd)
1656 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1661 * This call explicitly checks for a catalog=xxx and
1662 * if given, opens that catalog. It also checks for
1663 * client=xxx and if found, opens the catalog
1664 * corresponding to that client. If we still don't
1665 * have a catalog, look for a Job keyword and get the
1666 * catalog from its client record.
1668 bool open_client_db(UAContext *ua)
1675 /* Try for catalog keyword */
1676 i = find_arg_with_value(ua, NT_("catalog"));
1678 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1679 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1682 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
1684 if (ua->catalog && ua->catalog != catalog) {
1687 ua->catalog = catalog;
1692 /* Try for client keyword */
1693 i = find_arg_with_value(ua, NT_("client"));
1695 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1696 bsendmsg(ua, _("No authorization for Client \"%s\"\n"), ua->argv[i]);
1699 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1701 catalog = client->catalog;
1702 if (ua->catalog && ua->catalog != catalog) {
1705 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1706 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1709 ua->catalog = catalog;
1714 /* Try for Job keyword */
1715 i = find_arg_with_value(ua, NT_("job"));
1717 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1718 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), ua->argv[i]);
1721 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1723 catalog = job->client->catalog;
1724 if (ua->catalog && ua->catalog != catalog) {
1727 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1728 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1731 ua->catalog = catalog;
1741 * Open the catalog database.
1743 bool open_db(UAContext *ua)
1749 ua->catalog = get_catalog_resource(ua);
1751 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1756 ua->jcr->catalog = ua->catalog;
1758 Dmsg0(150, "Open database\n");
1759 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1760 ua->catalog->db_password, ua->catalog->db_address,
1761 ua->catalog->db_port, ua->catalog->db_socket,
1762 ua->catalog->mult_db_connections);
1763 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1764 bsendmsg(ua, _("Could not open catalog database \"%s\".\n"),
1765 ua->catalog->db_name);
1767 bsendmsg(ua, "%s", db_strerror(ua->db));
1772 ua->jcr->db = ua->db;
1773 bsendmsg(ua, _("Using Catalog \"%s\"\n"), ua->catalog->name());
1774 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1778 void close_db(UAContext *ua)
1781 db_close_database(ua->jcr, ua->db);